001/** 002 * Copyright 2005-2018 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.datadictionary.parse.BeanTagAttribute; 020import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 021import org.kuali.rice.krad.uif.UifConstants; 022import org.kuali.rice.krad.uif.component.Component; 023import org.kuali.rice.krad.uif.component.ComponentBase; 024import org.kuali.rice.krad.uif.component.DelayedCopy; 025import org.kuali.rice.krad.uif.element.Header; 026import org.kuali.rice.krad.uif.element.Message; 027import org.kuali.rice.krad.uif.element.ValidationMessages; 028import org.kuali.rice.krad.uif.layout.LayoutManager; 029import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 030import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction; 031import org.kuali.rice.krad.uif.util.ComponentFactory; 032import org.kuali.rice.krad.uif.util.ComponentUtils; 033import org.kuali.rice.krad.uif.util.LifecycleElement; 034import org.kuali.rice.krad.uif.widget.Help; 035import org.kuali.rice.krad.uif.widget.Tooltip; 036 037import java.util.ArrayList; 038import java.util.List; 039 040/** 041 * Base <code>Container</code> implementation which container implementations 042 * can extend 043 * 044 * <p> 045 * Provides properties for the basic <code>Container</code> functionality in 046 * addition to default implementation of the lifecycle methods including some 047 * setup of the header, items list, and layout manager 048 * </p> 049 * 050 * @author Kuali Rice Team (rice.collab@kuali.org) 051 */ 052public abstract class ContainerBase extends ComponentBase implements Container { 053 private static final long serialVersionUID = -4182226230601746657L; 054 055 private int defaultItemPosition; 056 057 private Help help; 058 059 private LayoutManager layoutManager; 060 061 private Header header; 062 private Group footer; 063 064 private String instructionalText; 065 private Message instructionalMessage; 066 067 @DelayedCopy 068 private ValidationMessages validationMessages; 069 070 private String enterKeyAction; 071 072 /** 073 * Default Constructor 074 */ 075 public ContainerBase() { 076 defaultItemPosition = 1; 077 } 078 079 /** 080 * {@inheritDoc} 081 */ 082 @Override 083 public boolean isProcessRemoteFieldHolders() { 084 return true; 085 } 086 087 /** 088 * The following initialization is performed: 089 * 090 * <ul> 091 * <li>Sorts the containers list of components</li> 092 * <li>Initializes the instructional field if necessary</li> 093 * <li>Initializes LayoutManager</li> 094 * </ul> 095 * 096 * {@inheritDoc} 097 */ 098 @SuppressWarnings("deprecation") 099 @Override 100 public void performInitialization(Object model) { 101 super.performInitialization(model); 102 103 if ((StringUtils.isNotBlank(instructionalText) || (getPropertyExpression("instructionalText") != null)) && ( 104 instructionalMessage 105 == null)) { 106 instructionalMessage = ComponentFactory.getInstructionalMessage(); 107 } 108 109 if (layoutManager != null && !this.getItems().isEmpty()) { 110 layoutManager.performInitialization(model); 111 } 112 } 113 114 /** 115 * {@inheritDoc} 116 */ 117 @SuppressWarnings("deprecation") 118 @Override 119 public void performApplyModel(Object model, LifecycleElement parent) { 120 super.performApplyModel(model, parent); 121 122 // setup summary message field if necessary 123 if (instructionalMessage != null && StringUtils.isBlank(instructionalMessage.getMessageText())) { 124 instructionalMessage.setMessageText(instructionalText); 125 } 126 127 if (layoutManager != null && !this.getItems().isEmpty()) { 128 layoutManager.performApplyModel(model, this); 129 } 130 } 131 132 /** 133 * The following finalization is performed: 134 * 135 * <ul> 136 * <li>Sets the headerText of the header Group if it is blank</li> 137 * <li>Set the messageText of the summary Message if it is blank</li> 138 * <li>Finalizes LayoutManager</li> 139 * </ul> 140 * 141 * {@inheritDoc} 142 */ 143 @SuppressWarnings("deprecation") 144 @Override 145 public void performFinalize(Object model, LifecycleElement parent) { 146 super.performFinalize(model, parent); 147 148 if (header != null) { 149 header.addDataAttribute(UifConstants.DataAttributes.HEADER_FOR, this.getId()); 150 } 151 152 if (layoutManager != null && !this.getItems().isEmpty()) { 153 layoutManager.performFinalize(model, this); 154 } 155 156 // Generate validation messages 157 if (validationMessages != null) { 158 validationMessages.generateMessages(ViewLifecycle.getView(), model, this); 159 } 160 161 // add data attributes to help identify enter key actions 162 if (this.getEnterKeyAction() != null && StringUtils.isNotBlank(this.getEnterKeyAction())) { 163 this.addDataAttribute(UifConstants.DataAttributes.ENTER_KEY, this.getEnterKeyAction()); 164 } 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override 171 public List<String> getAdditionalTemplates() { 172 List<String> additionalTemplates = super.getAdditionalTemplates(); 173 174 if (layoutManager != null) { 175 if (additionalTemplates.isEmpty()) { 176 additionalTemplates = new ArrayList<String>(); 177 } 178 additionalTemplates.add(layoutManager.getTemplate()); 179 } 180 181 return additionalTemplates; 182 } 183 184 /** 185 * Performs sorting of the container items based on the order property 186 */ 187 @Override 188 public void sortItems() { 189 // sort items list by the order property 190 List<? extends Component> sortedItems = ComponentUtils.sort(getItems(), defaultItemPosition); 191 setItems(sortedItems); 192 } 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override 198 @ViewLifecycleRestriction 199 @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 200 public ValidationMessages getValidationMessages() { 201 return this.validationMessages; 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override 208 public void setValidationMessages(ValidationMessages validationMessages) { 209 this.validationMessages = validationMessages; 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override 216 @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 217 public Help getHelp() { 218 return this.help; 219 } 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override 225 public void setHelp(Help help) { 226 this.help = help; 227 } 228 229 /** 230 * {@inheritDoc} 231 */ 232 @Override 233 public void setTooltipOfComponent(Tooltip tooltip) { 234 getHeader().setToolTip(tooltip); 235 } 236 237 /** 238 * {@inheritDoc} 239 */ 240 @Override 241 public String getHelpTitle() { 242 return this.getHeader().getHeaderText(); 243 } 244 245 /** 246 * {@inheritDoc} 247 */ 248 @Override 249 @BeanTagAttribute 250 public abstract List<? extends Component> getItems(); 251 252 /** 253 * Setter for the containers list of components 254 * 255 * @param items 256 */ 257 public abstract void setItems(List<? extends Component> items); 258 259 /** 260 * For <code>Component</code> instances in the container's items list that 261 * do not have an order set, a default order number will be assigned using 262 * this property. The first component found in the list without an order 263 * will be assigned the configured initial value, and incremented by one for 264 * each component (without an order) found afterwards 265 * 266 * @return int order sequence 267 */ 268 @BeanTagAttribute 269 public int getDefaultItemPosition() { 270 return this.defaultItemPosition; 271 } 272 273 /** 274 * Setter for the container's item ordering sequence number (initial value) 275 * 276 * @param defaultItemPosition 277 */ 278 public void setDefaultItemPosition(int defaultItemPosition) { 279 this.defaultItemPosition = defaultItemPosition; 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override 286 @BeanTagAttribute(type= BeanTagAttribute.AttributeType.BYTYPE) 287 public LayoutManager getLayoutManager() { 288 return this.layoutManager; 289 } 290 291 /** 292 * {@inheritDoc} 293 */ 294 @Override 295 public void setLayoutManager(LayoutManager layoutManager) { 296 this.layoutManager = layoutManager; 297 } 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override 303 @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE) 304 public Header getHeader() { 305 return this.header; 306 } 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override 312 public void setHeader(Header header) { 313 this.header = header; 314 } 315 316 /** 317 * {@inheritDoc} 318 */ 319 @Override 320 @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECT) 321 public Group getFooter() { 322 return this.footer; 323 } 324 325 /** 326 * {@inheritDoc} 327 */ 328 @Override 329 public void setFooter(Group footer) { 330 this.footer = footer; 331 } 332 333 /** 334 * Convenience setter for configuration to turn rendering of the header 335 * on/off 336 * 337 * <p> 338 * For nested groups (like Field Groups) it is often necessary to only show 339 * the container body (the contained components). This method allows the 340 * header to not be displayed 341 * </p> 342 * 343 * @param renderHeader 344 */ 345 public void setRenderHeader(boolean renderHeader) { 346 if (header != null) { 347 header.setRender(renderHeader); 348 } 349 } 350 351 /** 352 * Convenience getter for the header text 353 * 354 * @return The text that should be displayed on the header 355 */ 356 @BeanTagAttribute 357 public String getHeaderText () { 358 if (header != null && header.getHeaderText() != null) { 359 return header.getHeaderText(); 360 } else { 361 return ""; 362 } 363 } 364 365 /** 366 * Convenience setter for configuration to set the header text 367 * 368 * @param headerText the text that should be displayed on the header. 369 */ 370 public void setHeaderText(String headerText) { 371 if (header != null) { 372 header.setHeaderText(headerText); 373 } 374 } 375 376 /** 377 * Convenience setter for configuration to turn rendering of the footer 378 * on/off 379 * 380 * <p> 381 * For nested groups it is often necessary to only show the container body 382 * (the contained components). This method allows the footer to not be 383 * displayed 384 * </p> 385 * 386 * @param renderFooter 387 */ 388 public void setRenderFooter(boolean renderFooter) { 389 if (footer != null) { 390 footer.setRender(renderFooter); 391 } 392 } 393 394 /** 395 * Text explaining how complete the group inputs, including things like what values should be selected 396 * in certain cases, what fields should be completed and so on (instructions) 397 * 398 * @return instructional message 399 */ 400 @BeanTagAttribute 401 public String getInstructionalText() { 402 return this.instructionalText; 403 } 404 405 /** 406 * Setter for the instructional message 407 * 408 * @param instructionalText 409 */ 410 public void setInstructionalText(String instructionalText) { 411 this.instructionalText = instructionalText; 412 } 413 414 /** 415 * Message field that displays instructional text 416 * 417 * <p> 418 * This message field can be configured to for adjusting how the instructional text will display. Generally 419 * the styleClasses property will be of most interest 420 * </p> 421 * 422 * @return instructional message field 423 */ 424 @BeanTagAttribute 425 public Message getInstructionalMessage() { 426 return this.instructionalMessage; 427 } 428 429 /** 430 * Setter for the instructional text message field 431 * 432 * <p> 433 * Note this is the setter for the field that will render the instructional text. The actual text can be 434 * set on the field but can also be set using {@link #setInstructionalText(String)} 435 * </p> 436 * 437 * @param instructionalMessage 438 */ 439 public void setInstructionalMessage(Message instructionalMessage) { 440 this.instructionalMessage = instructionalMessage; 441 } 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override 447 @BeanTagAttribute 448 public String getEnterKeyAction() { 449 return this.enterKeyAction; 450 } 451 452 /** 453 * {@inheritDoc} 454 */ 455 public void setEnterKeyAction(String enterKeyAction) { 456 this.enterKeyAction = enterKeyAction; 457 } 458 459 /** 460 * {@inheritDoc} 461 */ 462 @Override 463 public void completeValidation(ValidationTrace tracer) { 464 tracer.addBean(this); 465 466 // Checks for over writing of the instructional text or message 467 if (getInstructionalText() != null && getInstructionalMessage() != null) { 468 String currentValues[] = {"instructionalMessage.text = " + getInstructionalMessage().getMessageText(), 469 "instructionalText = " + getInstructionalText()}; 470 tracer.createWarning("InstructionalMessage will override instructioanlText", currentValues); 471 } 472 473 super.completeValidation(tracer.getCopy()); 474 } 475}