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.BeanTag; 020import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 021import org.kuali.rice.krad.datadictionary.parse.BeanTags; 022import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 023import org.kuali.rice.krad.datadictionary.validator.Validator; 024import org.kuali.rice.krad.uif.UifConstants; 025import org.kuali.rice.krad.uif.UifPropertyPaths; 026import org.kuali.rice.krad.uif.component.Component; 027import org.kuali.rice.krad.uif.component.DataBinding; 028import org.kuali.rice.krad.uif.component.DelayedCopy; 029import org.kuali.rice.krad.uif.element.Action; 030import org.kuali.rice.krad.uif.field.Field; 031import org.kuali.rice.krad.uif.field.FieldGroup; 032import org.kuali.rice.krad.uif.field.InputField; 033import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 034import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleUtils; 035import org.kuali.rice.krad.uif.util.ExpressionUtils; 036import org.kuali.rice.krad.uif.util.LifecycleElement; 037import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 038import org.kuali.rice.krad.uif.util.ScriptUtils; 039import org.kuali.rice.krad.uif.view.ExpressionEvaluator; 040import org.kuali.rice.krad.uif.view.View; 041import org.kuali.rice.krad.uif.view.ViewModel; 042import org.kuali.rice.krad.uif.widget.Disclosure; 043import org.kuali.rice.krad.uif.widget.Inquiry; 044import org.kuali.rice.krad.uif.widget.QuickFinder; 045import org.kuali.rice.krad.uif.widget.Scrollpane; 046import org.kuali.rice.krad.util.KRADUtils; 047 048import java.util.ArrayList; 049import java.util.Arrays; 050import java.util.Collections; 051import java.util.HashSet; 052import java.util.Iterator; 053import java.util.List; 054import java.util.Set; 055 056/** 057 * Container that holds a list of <code>Field</code> or other <code>Group</code> 058 * instances 059 * 060 * <p> 061 * Groups can exist at different levels of the <code>View</code>, providing 062 * conceptual groupings such as the page, section, and group. In addition, other 063 * group types can be created to add behavior like collection support 064 * </p> 065 * 066 * <p> 067 * <code>Group</code> implementation has properties for defaulting the binding 068 * information (such as the parent object path and a binding prefix) for the 069 * fields it contains. During the phase these properties (if given) are set on 070 * the fields contained in the <code>Group</code> that implement 071 * <code>DataBinding</code>, unless they have already been set on the field. 072 * </p> 073 * 074 * @author Kuali Rice Team (rice.collab@kuali.org) 075 */ 076@BeanTags({@BeanTag(name = "group", parent = "Uif-GroupBase"), 077 @BeanTag(name = "boxGroup", parent = "Uif-BoxGroupBase"), 078 @BeanTag(name = "verticalGroup", parent = "Uif-VerticalBoxGroup"), 079 @BeanTag(name = "verticalSection", parent = "Uif-VerticalBoxSection"), 080 @BeanTag(name = "verticalSubSection", parent = "Uif-VerticalBoxSubSection"), 081 @BeanTag(name = "disclosureVerticalSection", parent = "Uif-Disclosure-VerticalBoxSection"), 082 @BeanTag(name = "disclosureVerticalSubSection", parent = "Uif-Disclosure-VerticalBoxSubSection"), 083 @BeanTag(name = "horizontalGroup", parent = "Uif-HorizontalBoxGroup"), 084 @BeanTag(name = "horizontalSection", parent = "Uif-HorizontalBoxSection"), 085 @BeanTag(name = "horizontalSubSection", parent = "Uif-HorizontalBoxSubSection"), 086 @BeanTag(name = "disclosureHorizontalSection", parent = "Uif-Disclosure-HorizontalBoxSection"), 087 @BeanTag(name = "disclosureHorizontalSubSection", parent = "Uif-Disclosure-HorizontalBoxSubSection"), 088 @BeanTag(name = "grid", parent = "Uif-GridGroup"), 089 @BeanTag(name = "gridSection", parent = "Uif-GridSection"), 090 @BeanTag(name = "gridSubSection", parent = "Uif-GridSubSection"), 091 @BeanTag(name = "disclosureGridSection", parent = "Uif-Disclosure-GridSection"), 092 @BeanTag(name = "cssGrid", parent = "Uif-CssGridGroup"), 093 @BeanTag(name = "section", parent = "Uif-CssGridSection"), 094 @BeanTag(name = "subSection", parent = "Uif-CssGridSubSection"), 095 @BeanTag(name = "section1Col", parent = "Uif-CssGridSection-1FieldLabelColumn"), 096 @BeanTag(name = "section2Col", parent = "Uif-CssGridSection-2FieldLabelColumn"), 097 @BeanTag(name = "section3Col", parent = "Uif-CssGridSection-3FieldLabelColumn"), 098 @BeanTag(name = "subSection1Col", parent = "Uif-CssGridSubSection-1FieldLabelColumn"), 099 @BeanTag(name = "subSection2Col", parent = "Uif-CssGridSubSection-2FieldLabelColumn"), 100 @BeanTag(name = "subSection3Col", parent = "Uif-CssGridSubSection-3FieldLabelColumn"), 101 @BeanTag(name = "list", parent = "Uif-ListGroup"), 102 @BeanTag(name = "listSection", parent = "Uif-ListSection"), 103 @BeanTag(name = "listSubSection", parent = "Uif-ListSubSection"), 104 @BeanTag(name = "disclosureListSection", parent = "Uif-Disclosure-ListSection"), 105 @BeanTag(name = "disclosureListSubSection", parent = "Uif-Disclosure-ListSubSection"), 106 @BeanTag(name = "collectionGridItem", parent = "Uif-CollectionGridItem"), 107 @BeanTag(name = "collectionVerticalBoxItem", parent = "Uif-CollectionVerticalBoxItem"), 108 @BeanTag(name = "collectionHorizontalBoxItem", parent = "Uif-CollectionHorizontalBoxItem"), 109 @BeanTag(name = "headerUpperGroup", parent = "Uif-HeaderUpperGroup"), 110 @BeanTag(name = "headerRightGroup", parent = "Uif-HeaderRightGroup"), 111 @BeanTag(name = "headerLowerGroup", parent = "Uif-HeaderLowerGroup"), 112 @BeanTag(name = "footer", parent = "Uif-FooterBase"), 113 @BeanTag(name = "formFooter", parent = "Uif-FormFooter"), 114 @BeanTag(name = "maintenanceGrid", parent = "Uif-MaintenanceGridGroup"), 115 @BeanTag(name = "maintenanceHorizontalGroup", parent = "Uif-MaintenanceHorizontalBoxGroup"), 116 @BeanTag(name = "maintenanceVerticalGroup", parent = "Uif-MaintenanceVerticalBoxGroup"), 117 @BeanTag(name = "maintenanceGridSection", parent = "Uif-MaintenanceGridSection"), 118 @BeanTag(name = "maintenanceGridSubSection", parent = "Uif-MaintenanceGridSubSection"), 119 @BeanTag(name = "maintenanceHorizontalSection", parent = "Uif-MaintenanceHorizontalBoxSection"), 120 @BeanTag(name = "maintenanceVerticalSection", parent = "Uif-MaintenanceVerticalBoxSection"), 121 @BeanTag(name = "maintenanceHorizontalSubSection", parent = "Uif-MaintenanceHorizontalBoxSubSection"), 122 @BeanTag(name = "maintenanceVerticalSubSection", parent = "Uif-MaintenanceVerticalBoxSubSection")}) 123public class GroupBase extends ContainerBase implements Group { 124 private static final long serialVersionUID = 7953641325356535509L; 125 126 public static enum ACTION_VALIDATION_COMPONENTS {QUICKFINDER, INQUIRY, COLLECTION} 127 128 private String fieldBindByNamePrefix; 129 private String fieldBindingObjectPath; 130 131 @DelayedCopy 132 private Disclosure disclosure; 133 private Scrollpane scrollpane; 134 135 private List<? extends Component> items; 136 137 private String wrapperTag; 138 139 /** 140 * Default Constructor 141 */ 142 public GroupBase() { 143 items = Collections.emptyList(); 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 public void performInitialization(Object model) { 151 if (isAjaxDisclosureGroup()) { 152 this.setItems(new ArrayList<Component>()); 153 } 154 155 super.performInitialization(model); 156 157 Iterator<? extends Component> itemIterator = getItems().iterator(); 158 while (itemIterator.hasNext()) { 159 Component component = itemIterator.next(); 160 161 if (component == null) { 162 continue; 163 } 164 165 String excludeUnless = component.getExcludeUnless(); 166 if (StringUtils.isNotBlank(excludeUnless) && 167 !Boolean.TRUE.equals(ObjectPropertyUtils.getPropertyValue(model, excludeUnless))) { 168 itemIterator.remove(); 169 continue; 170 } 171 172 String excludeIf = component.getExcludeIf(); 173 if (StringUtils.isNotBlank(excludeIf) && 174 Boolean.TRUE.equals(ObjectPropertyUtils.getPropertyValue(model, excludeIf))) { 175 itemIterator.remove(); 176 continue; 177 } 178 179 // append group's field bind by name prefix (if set) to each 180 // attribute field's binding prefix 181 if (component instanceof DataBinding) { 182 DataBinding dataBinding = (DataBinding) component; 183 184 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) { 185 String bindByNamePrefixToSet = getFieldBindByNamePrefix(); 186 187 if (StringUtils.isNotBlank(dataBinding.getBindingInfo().getBindByNamePrefix())) { 188 bindByNamePrefixToSet += "." + dataBinding.getBindingInfo().getBindByNamePrefix(); 189 } 190 dataBinding.getBindingInfo().setBindByNamePrefix(bindByNamePrefixToSet); 191 } 192 193 if (StringUtils.isNotBlank(fieldBindingObjectPath) && StringUtils.isBlank( 194 dataBinding.getBindingInfo().getBindingObjectPath())) { 195 dataBinding.getBindingInfo().setBindingObjectPath(fieldBindingObjectPath); 196 } 197 } 198 // set on FieldGroup's group to recursively set AttributeFields 199 else if (component instanceof FieldGroup) { 200 FieldGroup fieldGroup = (FieldGroup) component; 201 202 if (fieldGroup.getGroup() != null) { 203 if (StringUtils.isBlank(fieldGroup.getGroup().getFieldBindByNamePrefix())) { 204 fieldGroup.getGroup().setFieldBindByNamePrefix(fieldBindByNamePrefix); 205 } 206 if (StringUtils.isBlank(fieldGroup.getGroup().getFieldBindingObjectPath())) { 207 fieldGroup.getGroup().setFieldBindingObjectPath(fieldBindingObjectPath); 208 } 209 } 210 } else if (component instanceof Group) { 211 Group subGroup = (Group) component; 212 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) { 213 if (StringUtils.isNotBlank(subGroup.getFieldBindByNamePrefix())) { 214 subGroup.setFieldBindByNamePrefix( 215 getFieldBindByNamePrefix() + "." + subGroup.getFieldBindByNamePrefix()); 216 } else { 217 subGroup.setFieldBindByNamePrefix(getFieldBindByNamePrefix()); 218 } 219 } 220 if (StringUtils.isNotBlank(getFieldBindingObjectPath())) { 221 if (StringUtils.isNotBlank(subGroup.getFieldBindingObjectPath())) { 222 subGroup.setFieldBindingObjectPath( 223 getFieldBindingObjectPath() + "." + subGroup.getFieldBindingObjectPath()); 224 } else { 225 subGroup.setFieldBindingObjectPath(getFieldBindingObjectPath()); 226 } 227 } 228 } 229 } 230 } 231 232 /** 233 * {@inheritDoc} 234 */ 235 @Override 236 public void afterEvaluateExpression() { 237 super.afterEvaluateExpression(); 238 239 if (getReadOnly() == null) { 240 Component parent = ViewLifecycle.getPhase().getParent(); 241 setReadOnly(parent == null ? null : parent.getReadOnly()); 242 } 243 } 244 245 /** 246 * Sets the section boolean to true if this group has a rendering header with text 247 * 248 * {@inheritDoc} 249 */ 250 @Override 251 public void performFinalize(Object model, LifecycleElement parent) { 252 super.performFinalize(model, parent); 253 254 if (StringUtils.isBlank(wrapperTag) && StringUtils.isNotBlank(this.getHeaderText()) && this.getHeader() 255 .isRender()) { 256 wrapperTag = UifConstants.WrapperTags.SECTION; 257 } else if (StringUtils.isBlank(wrapperTag)) { 258 wrapperTag = UifConstants.WrapperTags.DIV; 259 } 260 261 setNestedComponentId(getInstructionalMessage(), this.getId() + UifConstants.IdSuffixes.INSTRUCTIONAL); 262 setNestedComponentId(getHeader(), this.getId() + UifConstants.IdSuffixes.HEADER_WRAPPER); 263 setNestedComponentId(getHelp(), this.getId() + UifConstants.IdSuffixes.HELP_WRAPPER); 264 265 if (getHelp() != null && getHelp().getHelpAction() != null) { 266 setNestedComponentId(getHelp().getHelpAction(), this.getId() + UifConstants.IdSuffixes.HELP); 267 } 268 269 // set up action validation scripts to avoid bind errors 270 setupValidationScripts(ACTION_VALIDATION_COMPONENTS.QUICKFINDER, ACTION_VALIDATION_COMPONENTS.INQUIRY); 271 272 // set the fields in the group to omit their data on form post according to its own value 273 List<Field> fields = ViewLifecycleUtils.getElementsOfTypeDeep(getItems(), Field.class); 274 for (Field field : fields) { 275 field.setOmitFromFormPost(isOmitFromFormPost()); 276 } 277 278 if (isOmitFromFormPost()) { 279 this.addDataAttribute(UifConstants.DataAttributes.OMIT_GROUP_FROM_POST, "true"); 280 } 281 } 282 283 /** 284 * Helper method to set the validation action scripts for widgets (at the very least, quickfinders, and currently, 285 * at the most, inquiries also). 286 * 287 * @param componentsToValidate the list of components with actions to validate 288 */ 289 protected void setupValidationScripts(ACTION_VALIDATION_COMPONENTS... componentsToValidate) { 290 List<ACTION_VALIDATION_COMPONENTS> componentsList = Arrays.asList(componentsToValidate); 291 List<InputField> inputFieldsWithActionsToValidate = new ArrayList<InputField>(); 292 List<InputField> allInputFields = new ArrayList<InputField>(); 293 294 // get all the components with quickfinder and inquiry (Note: for now only these 2 widgets are required 295 // to be supported but in future this could include possibly others if needed, and therefore the enum) 296 for (Component component : getItems()) { 297 if (component instanceof InputField) { 298 InputField inputField = (InputField) component; 299 QuickFinder quickFinder = inputField.getQuickfinder(); 300 Inquiry inquiry = inputField.getInquiry(); 301 if ((quickFinder != null && componentsList.contains(ACTION_VALIDATION_COMPONENTS.QUICKFINDER)) || ( 302 inquiry != null && componentsList.contains(ACTION_VALIDATION_COMPONENTS.INQUIRY))) { 303 inputFieldsWithActionsToValidate.add(inputField); 304 } 305 allInputFields.add(inputField); 306 } 307 } 308 309 // for all input fields with widgets we want to set their action script to validate the other 310 // fields in this group that the widgets are effected by 311 for (InputField widgetInputField : inputFieldsWithActionsToValidate) { 312 QuickFinder quickFinder = widgetInputField.getQuickfinder(); 313 Inquiry inquiry = widgetInputField.getInquiry(); 314 String script = buildInputFieldValidationActionScript(allInputFields, Arrays.asList( 315 widgetInputField.getId())); 316 317 // quickfinders 318 if (componentsList.contains(ACTION_VALIDATION_COMPONENTS.QUICKFINDER) && quickFinder != null) { 319 Action quickFinderAction = quickFinder.getQuickfinderAction(); 320 script = ScriptUtils.appendScript(script, quickFinderAction.getActionScript()); 321 quickFinderAction.setActionScript(script); 322 } 323 324 // inquiries 325 if (componentsList.contains(ACTION_VALIDATION_COMPONENTS.INQUIRY) && inquiry != null) { 326 Action directInquiryAction = inquiry.getDirectInquiryAction(); 327 script = ScriptUtils.appendScript(script, directInquiryAction.getActionScript()); 328 directInquiryAction.setActionScript(script); 329 } 330 } 331 } 332 333 /** 334 * Helper method to build action script for input fields with actions that depend on the validation of 335 * other input fields in the same group. 336 * 337 * @param allInputFields all other input fields that might have validation constraints 338 * @param excludedFields fields that shouldn't be part of the validation script 339 * @return the validation action script 340 */ 341 protected String buildInputFieldValidationActionScript(List<InputField> allInputFields, 342 List<String> excludedFields) { 343 List<String> controlsToValidate = new ArrayList<String>(); 344 345 for (InputField inputField : allInputFields) { 346 if ((excludedFields == null || !excludedFields.contains(inputField.getId())) && validateInputField( 347 inputField)) { 348 controlsToValidate.add(inputField.getId() + UifConstants.IdSuffixes.CONTROL); 349 350 // in cases of collection groups we also want to account for add line 351 if (this instanceof CollectionGroup) { 352 controlsToValidate.add( 353 inputField.getId() + UifConstants.IdSuffixes.ADD_LINE + UifConstants.IdSuffixes.CONTROL); 354 } 355 } 356 } 357 358 // set the action scripts and set them on quickfinders and/or inquiries 359 String script = ""; 360 if (!controlsToValidate.isEmpty()) { 361 script = "var control;var allValid=true;"; 362 for (String controlToValidate : controlsToValidate) { 363 script += "control=jQuery('#" + controlToValidate 364 + "');if(jQuery(control).val()){allValid=allValid&&validateFieldValue(control);}"; 365 } 366 script += "if(allValid == 0){return;}control = null;"; 367 } 368 return script; 369 } 370 371 /** 372 * Helper method to determine whether the given input field needs validated or not. 373 * 374 * @param inputField the field to check for validation 375 * @return whether validation should be done 376 */ 377 protected boolean validateInputField(InputField inputField) { 378 if (inputField.getValidCharactersConstraint() != null 379 && inputField.getValidCharactersConstraint().getApplyClientSide() != null 380 && inputField.getValidCharactersConstraint().getApplyClientSide() == Boolean.TRUE) { 381 return true; 382 } 383 return false; 384 } 385 386 /** 387 * Helper method for setting a new ID for the nested components 388 * 389 * @param component component to adjust ID for 390 * @param newId 391 */ 392 protected void setNestedComponentId(Component component, String newId) { 393 if (component != null) { 394 component.setId(newId); 395 } 396 } 397 398 /** 399 * {@inheritDoc} 400 */ 401 @Override 402 public Set<Class<? extends Component>> getSupportedComponents() { 403 Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>(); 404 supportedComponents.add(Field.class); 405 supportedComponents.add(Group.class); 406 407 return supportedComponents; 408 } 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override 414 public String getComponentTypeName() { 415 return "group"; 416 } 417 418 /** 419 * {@inheritDoc} 420 */ 421 @Override 422 @BeanTagAttribute 423 public String getFieldBindByNamePrefix() { 424 return this.fieldBindByNamePrefix; 425 } 426 427 /** 428 * {@inheritDoc} 429 */ 430 @Override 431 public void setFieldBindByNamePrefix(String fieldBindByNamePrefix) { 432 this.fieldBindByNamePrefix = fieldBindByNamePrefix; 433 } 434 435 /** 436 * {@inheritDoc} 437 */ 438 @BeanTagAttribute 439 public String getFieldBindingObjectPath() { 440 return this.fieldBindingObjectPath; 441 } 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override 447 public void setFieldBindingObjectPath(String fieldBindingObjectPath) { 448 this.fieldBindingObjectPath = fieldBindingObjectPath; 449 } 450 451 /** 452 * {@inheritDoc} 453 */ 454 @BeanTagAttribute 455 public Disclosure getDisclosure() { 456 return this.disclosure; 457 } 458 459 /** 460 * {@inheritDoc} 461 */ 462 @Override 463 public void setDisclosure(Disclosure disclosure) { 464 this.disclosure = disclosure; 465 } 466 467 /** 468 * {@inheritDoc} 469 */ 470 @BeanTagAttribute 471 public Scrollpane getScrollpane() { 472 return this.scrollpane; 473 } 474 475 /** 476 * {@inheritDoc} 477 */ 478 @Override 479 public void setScrollpane(Scrollpane scrollpane) { 480 this.scrollpane = scrollpane; 481 } 482 483 /** 484 * {@inheritDoc} 485 */ 486 @Override 487 @BeanTagAttribute 488 public List<? extends Component> getItems() { 489 if (items == Collections.EMPTY_LIST && isMutable(true)) { 490 items = new ArrayList<Component>(); 491 } 492 493 return this.items; 494 } 495 496 /** 497 * {@inheritDoc} 498 */ 499 @Override 500 public void setItems(List<? extends Component> items) { 501 if (items == null) { 502 this.items = Collections.emptyList(); 503 } else if (items.contains(this)) { 504 throw new IllegalArgumentException("Attempted to add group to itself"); 505 } else { 506 this.items = items; 507 } 508 } 509 510 /** 511 * Defines the html tag that will wrap this group, if left blank, this will automatically be set 512 * by the framework to the appropriate tag (in most cases section or div) 513 * 514 * @return the html tag used to wrap this group 515 */ 516 @BeanTagAttribute 517 public String getWrapperTag() { 518 return wrapperTag; 519 } 520 521 /** 522 * @see org.kuali.rice.krad.uif.container.GroupBase#getWrapperTag() 523 */ 524 public void setWrapperTag(String wrapperTag) { 525 this.wrapperTag = wrapperTag; 526 } 527 528 /** 529 * Returns true if this group has a Disclosure widget that is currently closed and using ajax disclosure 530 * 531 * @return true if this group has a Disclosure widget that is currently closed and using ajax disclosure 532 */ 533 protected boolean isAjaxDisclosureGroup() { 534 ViewModel model = (ViewModel) ViewLifecycle.getModel(); 535 View view = ViewLifecycle.getView(); 536 537 ExpressionUtils.populatePropertyExpressionsFromGraph(this); 538 // Evaluate the disclosure.defaultOpen expression early so that ajax disclosure mechanisms 539 // can take its state into account when replacing items with Placeholders in ContainerBase#performInitialization 540 if (this.getDisclosure() != null && StringUtils.isNotBlank(this.getDisclosure().getPropertyExpression( 541 UifPropertyPaths.DEFAULT_OPEN))){ 542 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 543 544 String expression = this.getDisclosure().getPropertyExpression(UifPropertyPaths.DEFAULT_OPEN); 545 expression = expressionEvaluator.replaceBindingPrefixes(view, this, expression); 546 547 expression = expressionEvaluator.evaluateExpressionTemplate(this.getDisclosure().getContext(), expression); 548 ObjectPropertyUtils.setPropertyValue(this.getDisclosure(), UifPropertyPaths.DEFAULT_OPEN, expression); 549 } 550 551 // Ensure that the disclosure has the correct state before evaluate ajax-based placeholder replacement 552 if (this.getDisclosure() != null) { 553 KRADUtils.syncClientSideStateForComponent(this.getDisclosure(), model.getClientStateForSyncing()); 554 } 555 556 // This this will be replaced with a PlaceholderDisclosure group if it is not opened and the 557 // ajaxRetrievalWhenOpened option is set 558 return !this.isRetrieveViaAjax() && this.getDisclosure() != null && this.getDisclosure() 559 .isAjaxRetrievalWhenOpened() && !this.getDisclosure().isDefaultOpen(); 560 } 561 562 /** 563 * {@inheritDoc} 564 */ 565 @Override 566 public void completeValidation(ValidationTrace tracer) { 567 tracer.addBean(this); 568 569 // Checks that no invalid items are present 570 for (int i = 0; i < getItems().size(); i++) { 571 if (getItems().get(i).getClass() == PageGroup.class || getItems().get(i).getClass() 572 == TabNavigationGroup.class) { 573 String currentValues[] = {"item(" + i + ").class =" + getItems().get(i).getClass()}; 574 tracer.createError("Items in Group cannot be PageGroup or NaviagtionGroup", currentValues); 575 } 576 } 577 578 // Checks that the layout manager is set 579 if (getLayoutManager() == null) { 580 if (Validator.checkExpressions(this, "layoutManager")) { 581 String currentValues[] = {"layoutManager = " + getLayoutManager()}; 582 tracer.createError("LayoutManager must be set", currentValues); 583 } 584 } 585 586 super.completeValidation(tracer.getCopy()); 587 } 588 589 /** 590 * {@inheritDoc} 591 */ 592 @Override 593 public boolean isRenderLoading() { 594 return disclosure != null && disclosure.isAjaxRetrievalWhenOpened() && (!disclosure.isRender() || !disclosure 595 .isDefaultOpen()); 596 } 597}