001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.uif.container; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.krad.uif.component.Component; 020import org.kuali.rice.krad.uif.component.ComponentBase; 021import org.kuali.rice.krad.uif.field.FieldGroup; 022import org.kuali.rice.krad.uif.field.InputField; 023import org.kuali.rice.krad.uif.field.ErrorsField; 024import org.kuali.rice.krad.uif.field.HeaderField; 025import org.kuali.rice.krad.uif.field.MessageField; 026import org.kuali.rice.krad.uif.layout.LayoutManager; 027import org.kuali.rice.krad.uif.util.ComponentUtils; 028import org.kuali.rice.krad.uif.view.View; 029import org.kuali.rice.krad.uif.widget.Help; 030 031import java.util.ArrayList; 032import java.util.List; 033 034/** 035 * Base <code>Container</code> implementation which container implementations 036 * can extend 037 * 038 * <p> 039 * Provides properties for the basic <code>Container</code> functionality in 040 * addition to default implementation of the lifecycle methods including some 041 * setup of the header, items list, and layout manager 042 * </p> 043 * 044 * @author Kuali Rice Team (rice.collab@kuali.org) 045 */ 046public abstract class ContainerBase extends ComponentBase implements Container { 047 private static final long serialVersionUID = -4182226230601746657L; 048 049 private int itemOrderingSequence; 050 051 private String additionalMessageKeys; 052 private ErrorsField errorsField; 053 054 private Help help; 055 private LayoutManager layoutManager; 056 057 private HeaderField header; 058 private Group footer; 059 060 private String instructionalText; 061 private MessageField instructionalMessageField; 062 063 private boolean fieldContainer; 064 065 /** 066 * Default Constructor 067 */ 068 public ContainerBase() { 069 itemOrderingSequence = 1; 070 } 071 072 /** 073 * The following initialization is performed: 074 * 075 * <ul> 076 * <li>Sorts the containers list of components</li> 077 * <li>Initializes LayoutManager</li> 078 * </ul> 079 * 080 * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View, java.lang.Object) 081 */ 082 @SuppressWarnings("unchecked") 083 @Override 084 public void performInitialization(View view, Object model) { 085 super.performInitialization(view, model); 086 087 // sort items list by the order property 088 List<? extends Component> sortedItems = (List<? extends Component>) ComponentUtils.sort(getItems(), 089 itemOrderingSequence); 090 setItems(sortedItems); 091 092 if (layoutManager != null) { 093 layoutManager.performInitialization(view, model, this); 094 } 095 } 096 097 /** 098 * @see org.kuali.rice.krad.uif.component.ComponentBase#performApplyModel(org.kuali.rice.krad.uif.view.View, 099 * java.lang.Object, org.kuali.rice.krad.uif.component.Component) 100 */ 101 @Override 102 public void performApplyModel(View view, Object model, Component parent) { 103 super.performApplyModel(view, model, parent); 104 105 if (layoutManager != null) { 106 layoutManager.performApplyModel(view, model, this); 107 } 108 } 109 110 /** 111 * The following finalization is performed: 112 * 113 * <ul> 114 * <li>Sets the headerText of the header Group if it is blank</li> 115 * <li>Set the messageText of the summary MessageField if it is blank</li> 116 * <li>Finalizes LayoutManager</li> 117 * </ul> 118 * 119 * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View, 120 * java.lang.Object, org.kuali.rice.krad.uif.component.Component) 121 */ 122 @Override 123 public void performFinalize(View view, Object model, Component parent) { 124 super.performFinalize(view, model, parent); 125 126 // if header title not given, use the container title 127 if (header != null && StringUtils.isBlank(header.getHeaderText())) { 128 header.setHeaderText(this.getTitle()); 129 } 130 131 // setup summary message field if necessary 132 if (instructionalMessageField != null && StringUtils.isBlank(instructionalMessageField.getMessageText())) { 133 instructionalMessageField.setMessageText(instructionalText); 134 } 135 136 if (layoutManager != null) { 137 layoutManager.performFinalize(view, model, this); 138 } 139 } 140 141 /** 142 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle() 143 */ 144 @Override 145 public List<Component> getComponentsForLifecycle() { 146 List<Component> components = super.getComponentsForLifecycle(); 147 148 components.add(header); 149 components.add(footer); 150 components.add(errorsField); 151 components.add(help); 152 components.add(instructionalMessageField); 153 154 for (Component component : getItems()) { 155 components.add(component); 156 } 157 158 if (layoutManager != null) { 159 components.addAll(layoutManager.getComponentsForLifecycle()); 160 } 161 162 return components; 163 } 164 165 /** 166 * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes() 167 */ 168 @Override 169 public List<Component> getComponentPrototypes() { 170 List<Component> components = super.getComponentPrototypes(); 171 172 if (layoutManager != null) { 173 components.addAll(layoutManager.getComponentPrototypes()); 174 } 175 176 return components; 177 } 178 179 /** 180 * Additional keys that should be matching on when gathering errors or other 181 * messages for the <code>Container</code> 182 * 183 * <p> 184 * Messages associated with the container will be displayed with the 185 * container grouping in the user interface. Typically, these are a result 186 * of problems with the containers fields or some other business logic 187 * associated with the containers information. The framework will by default 188 * include all the error keys for fields in the container, and also an 189 * errors associated with the containers id. Keys given here will be matched 190 * in addition to those defaults. 191 * </p> 192 * 193 * <p> 194 * Multple keys can be given using the comma delimiter, the * wildcard is 195 * also allowed in the message key 196 * </p> 197 * 198 * @return String additional message key string 199 */ 200 public String getAdditionalMessageKeys() { 201 return this.additionalMessageKeys; 202 } 203 204 /** 205 * Setter for the components additional message key string 206 * 207 * @param additionalMessageKeys 208 */ 209 public void setAdditionalMessageKeys(String additionalMessageKeys) { 210 this.additionalMessageKeys = additionalMessageKeys; 211 } 212 213 /** 214 * @see org.kuali.rice.krad.uif.container.Container#getErrorsField() 215 */ 216 @Override 217 public ErrorsField getErrorsField() { 218 return this.errorsField; 219 } 220 221 /** 222 * @see org.kuali.rice.krad.uif.container.Container#setErrorsField(org.kuali.rice.krad.uif.field.ErrorsField) 223 */ 224 @Override 225 public void setErrorsField(ErrorsField errorsField) { 226 this.errorsField = errorsField; 227 } 228 229 /** 230 * @see org.kuali.rice.krad.uif.container.Container#getHelp() 231 */ 232 @Override 233 public Help getHelp() { 234 return this.help; 235 } 236 237 /** 238 * @see org.kuali.rice.krad.uif.container.Container#setHelp(org.kuali.rice.krad.uif.widget.Help) 239 */ 240 @Override 241 public void setHelp(Help help) { 242 this.help = help; 243 } 244 245 /** 246 * @see org.kuali.rice.krad.uif.container.Container#getItems() 247 */ 248 @Override 249 public abstract List<? extends Component> getItems(); 250 251 /** 252 * Setter for the containers list of components 253 * 254 * @param items 255 */ 256 public abstract void setItems(List<? extends Component> items); 257 258 /** 259 * For <code>Component</code> instances in the container's items list that 260 * do not have an order set, a default order number will be assigned using 261 * this property. The first component found in the list without an order 262 * will be assigned the configured initial value, and incremented by one for 263 * each component (without an order) found afterwards 264 * 265 * @return int order sequence 266 */ 267 public int getItemOrderingSequence() { 268 return this.itemOrderingSequence; 269 } 270 271 /** 272 * Setter for the container's item ordering sequence number (initial value) 273 * 274 * @param itemOrderingSequence 275 */ 276 public void setItemOrderingSequence(int itemOrderingSequence) { 277 this.itemOrderingSequence = itemOrderingSequence; 278 } 279 280 /** 281 * @see org.kuali.rice.krad.uif.container.Container#getLayoutManager() 282 */ 283 @Override 284 public LayoutManager getLayoutManager() { 285 return this.layoutManager; 286 } 287 288 /** 289 * @see org.kuali.rice.krad.uif.container.Container#setLayoutManager(org.kuali.rice.krad.uif.layout.LayoutManager) 290 */ 291 @Override 292 public void setLayoutManager(LayoutManager layoutManager) { 293 this.layoutManager = layoutManager; 294 } 295 296 /** 297 * @see org.kuali.rice.krad.uif.container.Container#getHeader() 298 */ 299 @Override 300 public HeaderField getHeader() { 301 return this.header; 302 } 303 304 /** 305 * @see org.kuali.rice.krad.uif.container.Container#setHeader(org.kuali.rice.krad.uif.field.HeaderField) 306 */ 307 @Override 308 public void setHeader(HeaderField header) { 309 this.header = header; 310 } 311 312 /** 313 * @see org.kuali.rice.krad.uif.container.Container#getFooter() 314 */ 315 @Override 316 public Group getFooter() { 317 return this.footer; 318 } 319 320 /** 321 * @see org.kuali.rice.krad.uif.container.Container#setFooter(org.kuali.rice.krad.uif.container.Group) 322 */ 323 @Override 324 public void setFooter(Group footer) { 325 this.footer = footer; 326 } 327 328 /** 329 * Convenience setter for configuration to turn rendering of the header 330 * on/off 331 * 332 * <p> 333 * For nested groups (like Field Groups) it is often necessary to only show 334 * the container body (the contained components). This method allows the 335 * header to not be displayed 336 * </p> 337 * 338 * @param renderHeader 339 */ 340 public void setRenderHeader(boolean renderHeader) { 341 if (header != null) { 342 header.setRender(renderHeader); 343 } 344 } 345 346 /** 347 * Convenience setter for configuration to turn rendering of the footer 348 * on/off 349 * 350 * <p> 351 * For nested groups it is often necessary to only show the container body 352 * (the contained components). This method allows the footer to not be 353 * displayed 354 * </p> 355 * 356 * @param renderFooter 357 */ 358 public void setRenderFooter(boolean renderFooter) { 359 if (footer != null) { 360 footer.setRender(renderFooter); 361 } 362 } 363 364 /** 365 * Text explaining how complete the group inputs, including things like what values should be selected 366 * in certain cases, what fields should be completed and so on (instructions) 367 * 368 * @return String instructional message 369 */ 370 public String getInstructionalText() { 371 return this.instructionalText; 372 } 373 374 /** 375 * Setter for the instructional message 376 * 377 * @param instructionalText 378 */ 379 public void setInstructionalText(String instructionalText) { 380 this.instructionalText = instructionalText; 381 } 382 383 /** 384 * Message field that displays instructional text 385 * 386 * <p> 387 * This message field can be configured to for adjusting how the instructional text will display. Generally 388 * the styleClasses property will be of most interest 389 * </p> 390 * 391 * @return MessageField instructional message field 392 */ 393 public MessageField getInstructionalMessageField() { 394 return this.instructionalMessageField; 395 } 396 397 /** 398 * Setter for the instructional text message field 399 * 400 * <p> 401 * Note this is the setter for the field that will render the instructional text. The actual text can be 402 * set on the field but can also be set using {@link #setInstructionalText(String)} 403 * </p> 404 * 405 * @param instructionalMessageField 406 */ 407 public void setInstructionalMessageField(MessageField instructionalMessageField) { 408 this.instructionalMessageField = instructionalMessageField; 409 } 410 411 /** 412 * Gets only the data fields that are nested in this container. This is a subset of 413 * what getComponentsForLifecycle() returns 414 * 415 * @return 416 */ 417 public List<InputField> getInputFields(){ 418 List<InputField> inputFields = new ArrayList<InputField>(); 419 for(Component c: this.getComponentsForLifecycle()){ 420 if(c instanceof InputField){ 421 inputFields.add((InputField)c); 422 } 423 } 424 return inputFields; 425 426 } 427 428 /** 429 * getAllInputFields gets all the input fields contained in this container, but also in 430 * every sub-container that is a child of this container. When called from the top level 431 * View this will be every InputField across all pages. 432 * @return every InputField that is a child at any level of this container 433 */ 434 public List<InputField> getAllInputFields(){ 435 List<InputField> inputFields = new ArrayList<InputField>(); 436 for(Component c: this.getComponentsForLifecycle()){ 437 if(c instanceof InputField){ 438 inputFields.add((InputField)c); 439 } 440 else if(c instanceof ContainerBase){ 441 inputFields.addAll( ((ContainerBase) c).getAllInputFields()); 442 } 443 else if(c instanceof FieldGroup){ 444 ContainerBase cb = ((FieldGroup) c).getGroup(); 445 inputFields.addAll(cb.getAllInputFields()); 446 } 447 } 448 return inputFields; 449 } 450 451 /** 452 * This property is true if the container is used to display a group of fields that is visually a single 453 * field. 454 * @return the fieldContainer 455 */ 456 public boolean isFieldContainer() { 457 return this.fieldContainer; 458 } 459 460 /** 461 * @param fieldContainer the fieldContainer to set 462 */ 463 public void setFieldContainer(boolean fieldContainer) { 464 this.fieldContainer = fieldContainer; 465 } 466 467}