001 /** 002 * Copyright 2005-2013 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 */ 016 package org.kuali.rice.krad.uif.container; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.kuali.rice.core.api.exception.RiceRuntimeException; 020 import org.kuali.rice.krad.datadictionary.parse.BeanTag; 021 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 022 import org.kuali.rice.krad.datadictionary.parse.BeanTags; 023 import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 024 import org.kuali.rice.krad.datadictionary.validator.Validator; 025 import org.kuali.rice.krad.uif.UifConstants; 026 import org.kuali.rice.krad.uif.UifParameters; 027 import org.kuali.rice.krad.uif.component.BindingInfo; 028 import org.kuali.rice.krad.uif.component.ClientSideState; 029 import org.kuali.rice.krad.uif.component.Component; 030 import org.kuali.rice.krad.uif.component.ComponentSecurity; 031 import org.kuali.rice.krad.uif.component.DataBinding; 032 import org.kuali.rice.krad.uif.element.Action; 033 import org.kuali.rice.krad.uif.element.Message; 034 import org.kuali.rice.krad.uif.field.DataField; 035 import org.kuali.rice.krad.uif.field.Field; 036 import org.kuali.rice.krad.uif.util.ComponentFactory; 037 import org.kuali.rice.krad.uif.util.ComponentUtils; 038 import org.kuali.rice.krad.uif.view.View; 039 import org.kuali.rice.krad.uif.widget.QuickFinder; 040 import org.kuali.rice.krad.web.form.UifFormBase; 041 042 import java.util.ArrayList; 043 import java.util.Collections; 044 import java.util.List; 045 046 /** 047 * Group that holds a collection of objects and configuration for presenting the 048 * collection in the UI. Supports functionality such as add line, line actions, 049 * and nested collections. 050 * 051 * <p> 052 * Note the standard header/footer can be used to give a header to the 053 * collection as a whole, or to provide actions that apply to the entire 054 * collection 055 * </p> 056 * 057 * <p> 058 * For binding purposes the binding path of each row field is indexed. The name 059 * property inherited from <code>ComponentBase</code> is used as the collection 060 * name. The collectionObjectClass property is used to lookup attributes from 061 * the data dictionary. 062 * </p> 063 * 064 * @author Kuali Rice Team (rice.collab@kuali.org) 065 */ 066 @BeanTags({@BeanTag(name = "collectionGroup-bean", parent = "Uif-CollectionGroupBase"), 067 @BeanTag(name = "stackedCollectionGroup-bean", parent = "Uif-StackedCollectionGroup"), 068 @BeanTag(name = "stackedCollectionSection-bean", parent = "Uif-StackedCollectionSection"), 069 @BeanTag(name = "stackedCollectionSubSection-bean", parent = "Uif-StackedCollectionSubSection"), 070 @BeanTag(name = "stackedSubCollection-withinSection-bean", parent = "Uif-StackedSubCollection-WithinSection"), 071 @BeanTag(name = "stackedSubCollection-withinSubSection-bean", 072 parent = "Uif-StackedSubCollection-WithinSubSection"), 073 @BeanTag(name = "disclosure-stackedCollectionSection-bean", parent = "Uif-Disclosure-StackedCollectionSection"), 074 @BeanTag(name = "disclosure-stackedCollectionSubSection-bean", 075 parent = "Uif-Disclosure-StackedCollectionSubSection"), 076 @BeanTag(name = "disclosure-stackedSubCollection-withinSection-bean", 077 parent = "Uif-Disclosure-StackedSubCollection-WithinSection"), 078 @BeanTag(name = "disclosure-stackedSubCollection-withinSubSection-bean", 079 parent = "Uif-Disclosure-StackedSubCollection-WithinSubSection"), 080 @BeanTag(name = "tableCollectionGroup-bean", parent = "Uif-TableCollectionGroup"), 081 @BeanTag(name = "tableCollectionSection-bean", parent = "Uif-TableCollectionSection"), 082 @BeanTag(name = "tableCollectionSubSection-bean", parent = "Uif-TableCollectionSubSection"), 083 @BeanTag(name = "tableSubCollection-withinSection-bean", parent = "Uif-TableSubCollection-WithinSection"), 084 @BeanTag(name = "tableSubCollection-withinSubSection-bean", parent = "Uif-TableSubCollection-WithinSubSection"), 085 @BeanTag(name = "disclosure-tableCollectionSection-bean", parent = "Uif-Disclosure-TableCollectionSection"), 086 @BeanTag(name = "disclosure-tableCollectionSubSection-bean", 087 parent = "Uif-Disclosure-TableCollectionSubSection"), 088 @BeanTag(name = "disclosure-tableSubCollection-withinSection-bean", 089 parent = "Uif-Disclosure-TableSubCollection-WithinSection"), 090 @BeanTag(name = "disclosure-tableSubCollection-withinSubSection-bean", 091 parent = "Uif-Disclosure-TableSubCollection-WithinSubSection"), 092 @BeanTag(name = "listCollectionGroup-bean", parent = "Uif-ListCollectionGroup"), 093 @BeanTag(name = "listCollectionSection-bean", parent = "Uif-ListCollectionSection"), 094 @BeanTag(name = "listCollectionSubSection-bean", parent = "Uif-ListCollectionSubSection"), 095 @BeanTag(name = "documentNotesSection-bean", parent = "Uif-DocumentNotesSection"), 096 @BeanTag(name = "lookupResultsCollectionSection-bean", parent = "Uif-LookupResultsCollectionSection"), 097 @BeanTag(name = "maintenanceStackedCollectionSection-bean", parent = "Uif-MaintenanceStackedCollectionSection"), 098 @BeanTag(name = "maintenanceStackedSubCollection-withinSection-bean", 099 parent = "Uif-MaintenanceStackedSubCollection-WithinSection"), 100 @BeanTag(name = "maintenanceTableCollectionSection-bean", parent = "Uif-MaintenanceTableCollectionSection"), 101 @BeanTag(name = "maintenanceTableSubCollection-withinSection-bean", 102 parent = "Uif-MaintenanceTableSubCollection-withinSection")}) 103 public class CollectionGroup extends Group implements DataBinding { 104 private static final long serialVersionUID = -6496712566071542452L; 105 106 private Class<?> collectionObjectClass; 107 108 private String propertyName; 109 private BindingInfo bindingInfo; 110 111 private boolean renderAddLine; 112 private String addLinePropertyName; 113 private BindingInfo addLineBindingInfo; 114 115 private Message addLineLabel; 116 private List<? extends Component> addLineItems; 117 private List<Action> addLineActions; 118 119 private boolean renderLineActions; 120 private List<Action> lineActions; 121 122 private boolean includeLineSelectionField; 123 private String lineSelectPropertyName; 124 125 private QuickFinder collectionLookup; 126 127 private boolean renderInactiveToggleButton; 128 @ClientSideState(variableName = "inactive") 129 private boolean showInactiveLines; 130 private CollectionFilter activeCollectionFilter; 131 private List<CollectionFilter> filters; 132 133 private List<CollectionGroup> subCollections; 134 private String subCollectionSuffix; 135 136 private CollectionGroupBuilder collectionGroupBuilder; 137 138 private int displayCollectionSize = -1; 139 140 private boolean highlightNewItems; 141 private boolean highlightAddItem; 142 private String newItemsCssClass; 143 private String addItemCssClass; 144 145 private boolean renderAddBlankLineButton; 146 private Action addBlankLineAction; 147 private String addLinePlacement; 148 149 private boolean renderSaveLineActions; 150 private boolean addViaLightBox; 151 private Action addViaLightBoxAction; 152 153 private boolean useServerPaging = false; 154 private int pageSize; 155 private int displayStart = -1; 156 private int displayLength = -1; 157 private int filteredCollectionSize = -1; 158 159 private List<String> totalColumns; 160 161 public CollectionGroup() { 162 renderAddLine = true; 163 renderLineActions = true; 164 renderInactiveToggleButton = true; 165 highlightNewItems = true; 166 highlightAddItem = true; 167 addLinePlacement = "TOP"; 168 169 filters = Collections.emptyList(); 170 lineActions = Collections.emptyList(); 171 addLineItems = Collections.emptyList(); 172 addLineActions = Collections.emptyList(); 173 subCollections = Collections.emptyList(); 174 } 175 176 /** 177 * The following actions are performed: 178 * 179 * <ul> 180 * <li>Set fieldBindModelPath to the collection model path (since the fields 181 * have to belong to the same model as the collection)</li> 182 * <li>Set defaults for binding</li> 183 * <li>Default add line field list to groups items list</li> 184 * <li>Sets default active collection filter if not set</li> 185 * <li>Sets the dictionary entry (if blank) on each of the items to the 186 * collection class</li> 187 * </ul> 188 * 189 * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View, 190 * java.lang.Object) 191 */ 192 @Override 193 public void performInitialization(View view, Object model) { 194 setFieldBindingObjectPath(getBindingInfo().getBindingObjectPath()); 195 196 super.performInitialization(view, model); 197 198 if (bindingInfo != null) { 199 bindingInfo.setDefaults(view, getPropertyName()); 200 } 201 202 if (addLineBindingInfo != null) { 203 // add line binds to model property 204 if (StringUtils.isNotBlank(addLinePropertyName)) { 205 addLineBindingInfo.setDefaults(view, getPropertyName()); 206 addLineBindingInfo.setBindingName(addLinePropertyName); 207 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) { 208 addLineBindingInfo.setBindByNamePrefix(getFieldBindByNamePrefix()); 209 } 210 } 211 } 212 213 for (Component item : getItems()) { 214 if (item instanceof DataField) { 215 DataField field = (DataField) item; 216 217 if (StringUtils.isBlank(field.getDictionaryObjectEntry())) { 218 field.setDictionaryObjectEntry(collectionObjectClass.getName()); 219 } 220 } 221 } 222 223 if ((addLineItems == null) || addLineItems.isEmpty()) { 224 addLineItems = getItems(); 225 } else { 226 for (Component addLineField : addLineItems) { 227 if (!(addLineField instanceof DataField)) { 228 continue; 229 } 230 231 DataField field = (DataField) addLineField; 232 233 if (StringUtils.isBlank(field.getDictionaryObjectEntry())) { 234 field.setDictionaryObjectEntry(collectionObjectClass.getName()); 235 } 236 } 237 } 238 239 // if active collection filter not set use default 240 if (this.activeCollectionFilter == null) { 241 activeCollectionFilter = new ActiveCollectionFilter(); 242 } 243 244 // set static collection path on items 245 String collectionPath = ""; 246 if (StringUtils.isNotBlank(getBindingInfo().getCollectionPath())) { 247 collectionPath += getBindingInfo().getCollectionPath() + "."; 248 } 249 if (StringUtils.isNotBlank(getBindingInfo().getBindByNamePrefix())) { 250 collectionPath += getBindingInfo().getBindByNamePrefix() + "."; 251 } 252 collectionPath += getBindingInfo().getBindingName(); 253 254 List<DataField> collectionFields = ComponentUtils.getComponentsOfTypeDeep(getItems(), DataField.class); 255 for (DataField collectionField : collectionFields) { 256 collectionField.getBindingInfo().setCollectionPath(collectionPath); 257 } 258 259 List<DataField> addLineCollectionFields = ComponentUtils.getComponentsOfTypeDeep(addLineItems, DataField.class); 260 for (DataField collectionField : addLineCollectionFields) { 261 collectionField.getBindingInfo().setCollectionPath(collectionPath); 262 } 263 264 for (CollectionGroup collectionGroup : getSubCollections()) { 265 collectionGroup.getBindingInfo().setCollectionPath(collectionPath); 266 } 267 268 // add collection entry to abstract classes 269 if (!view.getObjectPathToConcreteClassMapping().containsKey(collectionPath)) { 270 view.getObjectPathToConcreteClassMapping().put(collectionPath, getCollectionObjectClass()); 271 } 272 } 273 274 /** 275 * Calls the configured <code>CollectionGroupBuilder</code> to build the 276 * necessary components based on the collection data 277 * 278 * @see org.kuali.rice.krad.uif.container.ContainerBase#performApplyModel(org.kuali.rice.krad.uif.view.View, 279 * java.lang.Object, org.kuali.rice.krad.uif.component.Component) 280 */ 281 @Override 282 public void performApplyModel(View view, Object model, Component parent) { 283 super.performApplyModel(view, model, parent); 284 285 // If we are using server paging, determine if a displayStart value has been set for this collection 286 // and used that value as the displayStart 287 if (model instanceof UifFormBase && this.isUseServerPaging()) { 288 Object displayStart = ((UifFormBase) model).getExtensionData().get( 289 this.getId() + UifConstants.PageRequest.DISPLAY_START_PROP); 290 291 if (displayStart != null) { 292 this.setDisplayStart(((Integer) displayStart).intValue()); 293 } 294 } 295 296 // adds the script to the add line buttons to keep collection on the same page 297 if (this.renderAddBlankLineButton) { 298 if (this.addBlankLineAction == null) { 299 this.addBlankLineAction = (Action) ComponentFactory.getNewComponentInstance( 300 ComponentFactory.ADD_BLANK_LINE_ACTION); 301 view.assignComponentIds(this.addBlankLineAction); 302 } 303 304 if (addLinePlacement.equals(UifConstants.Position.BOTTOM.name())) { 305 this.addBlankLineAction.setOnClickScript("writeCurrentPageToSession(this, 'last');"); 306 } else { 307 this.addBlankLineAction.setOnClickScript("writeCurrentPageToSession(this, 'first');"); 308 } 309 } else if (this.addViaLightBox) { 310 if (this.addViaLightBoxAction == null) { 311 this.addViaLightBoxAction = (Action) ComponentFactory.getNewComponentInstance( 312 ComponentFactory.ADD_VIA_LIGHTBOX_ACTION); 313 view.assignComponentIds(this.addViaLightBoxAction); 314 } 315 316 if (this.addLinePlacement.equals(UifConstants.Position.BOTTOM.name())) { 317 this.addViaLightBoxAction.setOnClickScript("writeCurrentPageToSession(this, 'last');"); 318 } else { 319 this.addViaLightBoxAction.setOnClickScript("writeCurrentPageToSession(this, 'first');"); 320 } 321 } 322 323 pushCollectionGroupToReference(); 324 325 // if rendering the collection group, build out the lines 326 if (isRender()) { 327 getCollectionGroupBuilder().build(view, model, this); 328 } 329 330 // TODO: is this necessary to call again? 331 // This may be necessary to call in case getCollectionGroupBuilder().build resets the context map 332 pushCollectionGroupToReference(); 333 } 334 335 /** 336 * Sets a reference in the context map for all nested components to the collection group 337 * instance, and sets name as parameter for an action fields in the group 338 */ 339 protected void pushCollectionGroupToReference() { 340 List<Component> components = getComponentsForLifecycle(); 341 components.addAll(getComponentPrototypes()); 342 343 ComponentUtils.pushObjectToContext(components, UifConstants.ContextVariableNames.COLLECTION_GROUP, this); 344 345 List<Action> actions = ComponentUtils.getComponentsOfTypeDeep(components, Action.class); 346 for (Action action : actions) { 347 action.addActionParameter(UifParameters.SELLECTED_COLLECTION_PATH, this.getBindingInfo().getBindingPath()); 348 } 349 } 350 351 /** 352 * New collection lines are handled in the framework by maintaining a map on 353 * the form. The map contains as a key the collection name, and as value an 354 * instance of the collection type. An entry is created here for the 355 * collection represented by the <code>CollectionGroup</code> if an instance 356 * is not available (clearExistingLine will force a new instance). The given 357 * model must be a subclass of <code>UifFormBase</code> in order to find the 358 * Map. 359 * 360 * @param model Model instance that contains the new collection lines Map 361 * @param clearExistingLine boolean that indicates whether the line should be set to a 362 * new instance if it already exists 363 */ 364 public void initializeNewCollectionLine(View view, Object model, CollectionGroup collectionGroup, 365 boolean clearExistingLine) { 366 getCollectionGroupBuilder().initializeNewCollectionLine(view, model, collectionGroup, clearExistingLine); 367 } 368 369 /** 370 * @see org.kuali.rice.krad.uif.container.ContainerBase#getComponentsForLifecycle() 371 */ 372 @Override 373 public List<Component> getComponentsForLifecycle() { 374 List<Component> components = super.getComponentsForLifecycle(); 375 376 components.add(addLineLabel); 377 components.add(collectionLookup); 378 components.add(addBlankLineAction); 379 components.add(addViaLightBoxAction); 380 381 // remove the containers items because we don't want them as children 382 // (they will become children of the layout manager as the rows are created) 383 for (Component item : getItems()) { 384 if (components.contains(item)) { 385 components.remove(item); 386 } 387 } 388 389 return components; 390 } 391 392 /** 393 * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes() 394 */ 395 @Override 396 public List<Component> getComponentPrototypes() { 397 List<Component> components = super.getComponentPrototypes(); 398 399 components.addAll(lineActions); 400 components.addAll(addLineActions); 401 components.addAll(getItems()); 402 components.addAll(getSubCollections()); 403 404 // iterate through addLineItems to make sure we have not already 405 // added them as prototypes (they could have been copied from add lines) 406 if (addLineItems != null) { 407 for (Component addLineItem : addLineItems) { 408 if (!components.contains(addLineItem)) { 409 components.add(addLineItem); 410 } 411 } 412 } 413 414 return components; 415 } 416 417 /** 418 * Object class the collection maintains. Used to get dictionary information 419 * in addition to creating new instances for the collection when necessary 420 * 421 * @return collection object class 422 */ 423 @BeanTagAttribute(name = "collectionObjectClass") 424 public Class<?> getCollectionObjectClass() { 425 return this.collectionObjectClass; 426 } 427 428 /** 429 * Setter for the collection object class 430 * 431 * @param collectionObjectClass 432 */ 433 public void setCollectionObjectClass(Class<?> collectionObjectClass) { 434 this.collectionObjectClass = collectionObjectClass; 435 } 436 437 /** 438 * @see org.kuali.rice.krad.uif.component.DataBinding#getPropertyName() 439 */ 440 @BeanTagAttribute(name = "propertyName") 441 public String getPropertyName() { 442 return this.propertyName; 443 } 444 445 /** 446 * Setter for the collections property name 447 * 448 * @param propertyName 449 */ 450 public void setPropertyName(String propertyName) { 451 this.propertyName = propertyName; 452 } 453 454 /** 455 * Determines the binding path for the collection. Used to get the 456 * collection value from the model in addition to setting the binding path 457 * for the collection attributes 458 * 459 * @see org.kuali.rice.krad.uif.component.DataBinding#getBindingInfo() 460 */ 461 @BeanTagAttribute(name = "bindingInfo", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 462 public BindingInfo getBindingInfo() { 463 return this.bindingInfo; 464 } 465 466 /** 467 * Setter for the binding info instance 468 * 469 * @param bindingInfo 470 */ 471 public void setBindingInfo(BindingInfo bindingInfo) { 472 this.bindingInfo = bindingInfo; 473 } 474 475 /** 476 * Action fields that should be rendered for each collection line. Example 477 * line action is the delete action 478 * 479 * @return line action fields 480 */ 481 @BeanTagAttribute(name = "lineActions", type = BeanTagAttribute.AttributeType.LISTBEAN) 482 public List<Action> getLineActions() { 483 return this.lineActions; 484 } 485 486 /** 487 * Setter for the line action fields list 488 * 489 * @param lineActions 490 */ 491 public void setLineActions(List<Action> lineActions) { 492 this.lineActions = lineActions; 493 } 494 495 /** 496 * Indicates whether the action column for the collection should be rendered 497 * 498 * @return true if the actions should be rendered, false if not 499 * @see #getLineActions() 500 */ 501 @BeanTagAttribute(name = "renderLineActions") 502 public boolean isRenderLineActions() { 503 return this.renderLineActions; 504 } 505 506 /** 507 * Setter for the render line actions indicator 508 * 509 * @param renderLineActions 510 */ 511 public void setRenderLineActions(boolean renderLineActions) { 512 this.renderLineActions = renderLineActions; 513 } 514 515 /** 516 * Indicates whether an add line should be rendered for the collection 517 * 518 * @return true if add line should be rendered, false if it should 519 * not be 520 */ 521 @BeanTagAttribute(name = "renderAddLine") 522 public boolean isRenderAddLine() { 523 return this.renderAddLine; 524 } 525 526 /** 527 * Setter for the render add line indicator 528 * 529 * @param renderAddLine 530 */ 531 public void setRenderAddLine(boolean renderAddLine) { 532 this.renderAddLine = renderAddLine; 533 } 534 535 /** 536 * Convenience getter for the add line label field text. The text is used to 537 * label the add line when rendered and its placement depends on the 538 * <code>LayoutManager</code> 539 * 540 * <p> 541 * For the <code>TableLayoutManager</code> the label appears in the sequence 542 * column to the left of the add line fields. For the 543 * <code>StackedLayoutManager</code> the label is placed into the group 544 * header for the line. 545 * </p> 546 * 547 * @return add line label 548 */ 549 public String getAddLabel() { 550 if (getAddLineLabel() != null) { 551 return getAddLineLabel().getMessageText(); 552 } 553 554 return null; 555 } 556 557 /** 558 * Setter for the add line label text 559 * 560 * @param addLabelText 561 */ 562 public void setAddLabel(String addLabelText) { 563 if (getAddLineLabel() != null) { 564 getAddLineLabel().setMessageText(addLabelText); 565 } 566 } 567 568 /** 569 * <code>Message</code> instance for the add line label 570 * 571 * @return add line Message 572 * @see #getAddLabel 573 */ 574 @BeanTagAttribute(name = "addLineLabel", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 575 public Message getAddLineLabel() { 576 return this.addLineLabel; 577 } 578 579 /** 580 * Setter for the <code>Message</code> instance for the add line label 581 * 582 * @param addLineLabel 583 * @see #getAddLabel 584 */ 585 public void setAddLineLabel(Message addLineLabel) { 586 this.addLineLabel = addLineLabel; 587 } 588 589 /** 590 * Name of the property that contains an instance for the add line. If set 591 * this is used with the binding info to create the path to the add line. 592 * Can be left blank in which case the framework will manage the add line 593 * instance in a generic map. 594 * 595 * @return add line property name 596 */ 597 @BeanTagAttribute(name = "addLinePropertyName") 598 public String getAddLinePropertyName() { 599 return this.addLinePropertyName; 600 } 601 602 /** 603 * Setter for the add line property name 604 * 605 * @param addLinePropertyName 606 */ 607 public void setAddLinePropertyName(String addLinePropertyName) { 608 this.addLinePropertyName = addLinePropertyName; 609 } 610 611 /** 612 * <code>BindingInfo</code> instance for the add line property used to 613 * determine the full binding path. If add line name given 614 * {@link #getAddLabel} then it is set as the binding name on the 615 * binding info. Add line label and binding info are not required, in which 616 * case the framework will manage the new add line instances through a 617 * generic map (model must extend UifFormBase) 618 * 619 * @return BindingInfo add line binding info 620 */ 621 @BeanTagAttribute(name = "addLineBindingInfo", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 622 public BindingInfo getAddLineBindingInfo() { 623 return this.addLineBindingInfo; 624 } 625 626 /** 627 * Setter for the add line binding info 628 * 629 * @param addLineBindingInfo 630 */ 631 public void setAddLineBindingInfo(BindingInfo addLineBindingInfo) { 632 this.addLineBindingInfo = addLineBindingInfo; 633 } 634 635 /** 636 * List of <code>Component</code> instances that should be rendered for the 637 * collection add line (if enabled). If not set, the default group's items 638 * list will be used 639 * 640 * @return add line field list 641 * @see CollectionGroup#performInitialization(org.kuali.rice.krad.uif.view.View, java.lang.Object) 642 */ 643 @BeanTagAttribute(name = "addLineItems", type = BeanTagAttribute.AttributeType.LISTBEAN) 644 public List<? extends Component> getAddLineItems() { 645 return this.addLineItems; 646 } 647 648 /** 649 * Setter for the add line field list 650 * 651 * @param addLineItems 652 */ 653 public void setAddLineItems(List<? extends Component> addLineItems) { 654 this.addLineItems = addLineItems; 655 } 656 657 /** 658 * Action fields that should be rendered for the add line. This is generally 659 * the add action (button) but can be configured to contain additional 660 * actions 661 * 662 * @return add line action fields 663 */ 664 @BeanTagAttribute(name = "addLineActions", type = BeanTagAttribute.AttributeType.LISTBEAN) 665 public List<Action> getAddLineActions() { 666 return this.addLineActions; 667 } 668 669 /** 670 * Setter for the add line action fields 671 * 672 * @param addLineActions 673 */ 674 public void setAddLineActions(List<Action> addLineActions) { 675 this.addLineActions = addLineActions; 676 } 677 678 /** 679 * Indicates whether lines of the collection group should be selected by rendering a 680 * field for each line that will allow selection 681 * 682 * <p> 683 * For example, having the select field enabled could allow selecting multiple lines from a search 684 * to return (multi-value lookup) 685 * </p> 686 * 687 * @return true if select field should be rendered, false if not 688 */ 689 @BeanTagAttribute(name = "includeLineSelectionField") 690 public boolean isIncludeLineSelectionField() { 691 return includeLineSelectionField; 692 } 693 694 /** 695 * Setter for the render selected field indicator 696 * 697 * @param includeLineSelectionField 698 */ 699 public void setIncludeLineSelectionField(boolean includeLineSelectionField) { 700 this.includeLineSelectionField = includeLineSelectionField; 701 } 702 703 /** 704 * When {@link #isIncludeLineSelectionField()} is true, gives the name of the property the select field 705 * should bind to 706 * 707 * <p> 708 * Note if no prefix is given in the property name, such as 'form.', it is assumed the property is 709 * contained on the collection line. In this case the binding path to the collection line will be 710 * appended. In other cases, it is assumed the property is a list or set of String that will hold the 711 * selected identifier strings 712 * </p> 713 * 714 * <p> 715 * This property is not required. If not the set the framework will use a property contained on 716 * <code>UifFormBase</code> 717 * </p> 718 * 719 * @return property name for select field 720 */ 721 @BeanTagAttribute(name = "lineSelectPropertyName") 722 public String getLineSelectPropertyName() { 723 return lineSelectPropertyName; 724 } 725 726 /** 727 * Setter for the property name that will bind to the select field 728 * 729 * @param lineSelectPropertyName 730 */ 731 public void setLineSelectPropertyName(String lineSelectPropertyName) { 732 this.lineSelectPropertyName = lineSelectPropertyName; 733 } 734 735 /** 736 * Instance of the <code>QuickFinder</code> widget that configures a multi-value lookup for the collection 737 * 738 * <p> 739 * If the collection lookup is enabled (by the render property of the quick finder), {@link 740 * #getCollectionObjectClass()} will be used as the data object class for the lookup (if not set). Field 741 * conversions need to be set as usual and will be applied for each line returned 742 * </p> 743 * 744 * @return instance configured for the collection lookup 745 */ 746 @BeanTagAttribute(name = "collectionLookup", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 747 public QuickFinder getCollectionLookup() { 748 return collectionLookup; 749 } 750 751 /** 752 * Setter for the collection lookup quickfinder instance 753 * 754 * @param collectionLookup 755 */ 756 public void setCollectionLookup(QuickFinder collectionLookup) { 757 this.collectionLookup = collectionLookup; 758 } 759 760 /** 761 * Indicates whether inactive collections lines should be displayed 762 * 763 * <p> 764 * Setting only applies when the collection line type implements the 765 * <code>Inactivatable</code> interface. If true and showInactive is 766 * set to false, the collection will be filtered to remove any items 767 * whose active status returns false 768 * </p> 769 * 770 * @return true to show inactive records, false to not render inactive records 771 */ 772 @BeanTagAttribute(name = "showInactiveLines") 773 public boolean isShowInactiveLines() { 774 return showInactiveLines; 775 } 776 777 /** 778 * Setter for the show inactive indicator 779 * 780 * @param showInactiveLines boolean show inactive 781 */ 782 public void setShowInactiveLines(boolean showInactiveLines) { 783 this.showInactiveLines = showInactiveLines; 784 } 785 786 /** 787 * Collection filter instance for filtering the collection data when the 788 * showInactive flag is set to false 789 * 790 * @return CollectionFilter 791 */ 792 @BeanTagAttribute(name = "activeCollectionFilter", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 793 public CollectionFilter getActiveCollectionFilter() { 794 return activeCollectionFilter; 795 } 796 797 /** 798 * Setter for the collection filter to use for filter inactive records from the 799 * collection 800 * 801 * @param activeCollectionFilter CollectionFilter instance 802 */ 803 public void setActiveCollectionFilter(CollectionFilter activeCollectionFilter) { 804 this.activeCollectionFilter = activeCollectionFilter; 805 } 806 807 /** 808 * List of {@link CollectionFilter} instances that should be invoked to filter the collection before 809 * displaying 810 * 811 * @return List<CollectionFilter> 812 */ 813 @BeanTagAttribute(name = "filters", type = BeanTagAttribute.AttributeType.LISTBEAN) 814 public List<CollectionFilter> getFilters() { 815 return filters; 816 } 817 818 /** 819 * Setter for the List of collection filters for which the collection will be filtered against 820 * 821 * @param filters 822 */ 823 public void setFilters(List<CollectionFilter> filters) { 824 this.filters = filters; 825 } 826 827 /** 828 * List of <code>CollectionGroup</code> instances that are sub-collections 829 * of the collection represented by this collection group 830 * 831 * @return sub collections 832 */ 833 @BeanTagAttribute(name = "subCollections", type = BeanTagAttribute.AttributeType.LISTBEAN) 834 public List<CollectionGroup> getSubCollections() { 835 return this.subCollections; 836 } 837 838 /** 839 * Setter for the sub collection list 840 * 841 * @param subCollections 842 */ 843 public void setSubCollections(List<CollectionGroup> subCollections) { 844 this.subCollections = subCollections; 845 } 846 847 /** 848 * Suffix for IDs that identifies the collection line the sub-collection belongs to 849 * 850 * <p> 851 * Built by the framework as the collection lines are being generated 852 * </p> 853 * 854 * @return id suffix for sub-collection 855 */ 856 public String getSubCollectionSuffix() { 857 return subCollectionSuffix; 858 } 859 860 /** 861 * Setter for the sub-collection suffix (used by framework, should not be 862 * set in configuration) 863 * 864 * @param subCollectionSuffix 865 */ 866 public void setSubCollectionSuffix(String subCollectionSuffix) { 867 this.subCollectionSuffix = subCollectionSuffix; 868 } 869 870 /** 871 * Collection Security object that indicates what authorization (permissions) exist for the collection 872 * 873 * @return CollectionGroupSecurity instance 874 */ 875 public CollectionGroupSecurity getCollectionGroupSecurity() { 876 return (CollectionGroupSecurity) super.getComponentSecurity(); 877 } 878 879 /** 880 * Override to assert a {@link CollectionGroupSecurity} instance is set 881 * 882 * @param componentSecurity instance of CollectionGroupSecurity 883 */ 884 @Override 885 public void setComponentSecurity(ComponentSecurity componentSecurity) { 886 if ((componentSecurity != null) && !(componentSecurity instanceof CollectionGroupSecurity)) { 887 throw new RiceRuntimeException( 888 "Component security for CollectionGroup should be instance of CollectionGroupSecurity"); 889 } 890 891 super.setComponentSecurity(componentSecurity); 892 } 893 894 @Override 895 protected Class<? extends ComponentSecurity> getComponentSecurityClass() { 896 return CollectionGroupSecurity.class; 897 } 898 899 /** 900 * <code>CollectionGroupBuilder</code> instance that will build the 901 * components dynamically for the collection instance 902 * 903 * @return CollectionGroupBuilder instance 904 */ 905 @BeanTagAttribute(name = "collectionGroupBuilder", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 906 public CollectionGroupBuilder getCollectionGroupBuilder() { 907 if (this.collectionGroupBuilder == null) { 908 this.collectionGroupBuilder = new CollectionGroupBuilder(); 909 } 910 return this.collectionGroupBuilder; 911 } 912 913 /** 914 * Setter for the collection group building instance 915 * 916 * @param collectionGroupBuilder 917 */ 918 public void setCollectionGroupBuilder(CollectionGroupBuilder collectionGroupBuilder) { 919 this.collectionGroupBuilder = collectionGroupBuilder; 920 } 921 922 /** 923 * @param renderInactiveToggleButton the showHideInactiveButton to set 924 */ 925 public void setRenderInactiveToggleButton(boolean renderInactiveToggleButton) { 926 this.renderInactiveToggleButton = renderInactiveToggleButton; 927 } 928 929 /** 930 * @return the showHideInactiveButton 931 */ 932 @BeanTagAttribute(name = "renderInactiveToggleButton") 933 public boolean isRenderInactiveToggleButton() { 934 return renderInactiveToggleButton; 935 } 936 937 /** 938 * The number of records to display for a collection 939 * 940 * @return int 941 */ 942 @BeanTagAttribute(name = "displayCollectionSize") 943 public int getDisplayCollectionSize() { 944 return this.displayCollectionSize; 945 } 946 947 /** 948 * Setter for the display collection size 949 * 950 * @param displayCollectionSize 951 */ 952 public void setDisplayCollectionSize(int displayCollectionSize) { 953 this.displayCollectionSize = displayCollectionSize; 954 } 955 956 /** 957 * Indicates whether new items should be styled with the #newItemsCssClass 958 * 959 * @return true if new items must be highlighted 960 */ 961 @BeanTagAttribute(name = "highlightNewItems") 962 public boolean isHighlightNewItems() { 963 return highlightNewItems; 964 } 965 966 /** 967 * Setter for the flag that allows for different styling of new items 968 * 969 * @param highlightNewItems 970 */ 971 public void setHighlightNewItems(boolean highlightNewItems) { 972 this.highlightNewItems = highlightNewItems; 973 } 974 975 /** 976 * The css style class that will be added on new items 977 * 978 * @return the new items css style class 979 */ 980 @BeanTagAttribute(name = "newItemsCssClass") 981 public String getNewItemsCssClass() { 982 return newItemsCssClass; 983 } 984 985 /** 986 * Setter for the new items css style class 987 * 988 * @param newItemsCssClass 989 */ 990 public void setNewItemsCssClass(String newItemsCssClass) { 991 this.newItemsCssClass = newItemsCssClass; 992 } 993 994 /** 995 * The css style class that will be added on the add item group or row 996 * 997 * @return the add item group or row css style class 998 */ 999 @BeanTagAttribute(name = "addItemCssClass") 1000 public String getAddItemCssClass() { 1001 return addItemCssClass; 1002 } 1003 1004 /** 1005 * Setter for the add item css style class 1006 * 1007 * @param addItemCssClass 1008 */ 1009 public void setAddItemCssClass(String addItemCssClass) { 1010 this.addItemCssClass = addItemCssClass; 1011 } 1012 1013 /** 1014 * Indicates whether the add item group or row should be styled with the #addItemCssClass 1015 * 1016 * @return true if add item group or row must be highlighted 1017 */ 1018 @BeanTagAttribute(name = "highlightAddItem") 1019 public boolean isHighlightAddItem() { 1020 return highlightAddItem; 1021 } 1022 1023 /** 1024 * Setter for the flag that allows for different styling of the add item group or row 1025 * 1026 * @param highlightAddItem 1027 */ 1028 public void setHighlightAddItem(boolean highlightAddItem) { 1029 this.highlightAddItem = highlightAddItem; 1030 } 1031 1032 /** 1033 * Indicates that a button will be rendered that allows the user to add blank lines to the collection 1034 * 1035 * <p> 1036 * The button will be added separately from the collection items. The default add line wil not be rendered. The 1037 * action of the button will call the controller, add the blank line to the collection and do a component refresh. 1038 * </p> 1039 * 1040 * @return boolean 1041 */ 1042 @BeanTagAttribute(name = "renderAddBlankLineButton") 1043 public boolean isRenderAddBlankLineButton() { 1044 return renderAddBlankLineButton; 1045 } 1046 1047 /** 1048 * Setter for the flag indicating that the add blank line button must be rendered 1049 * 1050 * @param renderAddBlankLineButton 1051 */ 1052 public void setRenderAddBlankLineButton(boolean renderAddBlankLineButton) { 1053 this.renderAddBlankLineButton = renderAddBlankLineButton; 1054 } 1055 1056 /** 1057 * The add blank line {@link Action} field rendered when renderAddBlankLineButton is true 1058 * 1059 * @return boolean 1060 */ 1061 @BeanTagAttribute(name = "addBlankLineAction", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1062 public Action getAddBlankLineAction() { 1063 return addBlankLineAction; 1064 } 1065 1066 /** 1067 * Setter for the add blank line {@link Action} field 1068 * 1069 * @param addBlankLineAction 1070 */ 1071 public void setAddBlankLineAction(Action addBlankLineAction) { 1072 this.addBlankLineAction = addBlankLineAction; 1073 } 1074 1075 /** 1076 * Indicates the add line placement 1077 * 1078 * <p> 1079 * Valid values are 'TOP' or 'BOTTOM'. The default is 'TOP'. When the value is 'BOTTOM' the blank line will be 1080 * added 1081 * to the end of the collection. 1082 * </p> 1083 * 1084 * @return the add blank line action placement 1085 */ 1086 @BeanTagAttribute(name = "addLinePlacement") 1087 public String getAddLinePlacement() { 1088 return addLinePlacement; 1089 } 1090 1091 /** 1092 * Setter for the add line placement 1093 * 1094 * @param addLinePlacement add line placement string 1095 */ 1096 public void setAddLinePlacement(String addLinePlacement) { 1097 this.addLinePlacement = addLinePlacement; 1098 } 1099 1100 /** 1101 * Indicates whether the save line actions should be rendered 1102 * 1103 * @return boolean 1104 */ 1105 @BeanTagAttribute(name = "renderSaveLineActions") 1106 public boolean isRenderSaveLineActions() { 1107 return renderSaveLineActions; 1108 } 1109 1110 /** 1111 * Setter for the flag indicating whether the save actions should be rendered 1112 * 1113 * @param renderSaveLineActions 1114 */ 1115 public void setRenderSaveLineActions(boolean renderSaveLineActions) { 1116 this.renderSaveLineActions = renderSaveLineActions; 1117 } 1118 1119 /** 1120 * Indicates that a add action should be rendered and that the add group be displayed in a lightbox 1121 * 1122 * @return boolean 1123 */ 1124 @BeanTagAttribute(name = "addViaLightBox") 1125 public boolean isAddViaLightBox() { 1126 return addViaLightBox; 1127 } 1128 1129 /** 1130 * Setter for the flag to indicate that add groups should be displayed in a light box 1131 * 1132 * @param addViaLightBox 1133 */ 1134 public void setAddViaLightBox(boolean addViaLightBox) { 1135 this.addViaLightBox = addViaLightBox; 1136 } 1137 1138 /** 1139 * The {@link Action} that will be displayed that will open the add line group in a lightbox 1140 * 1141 * @return Action 1142 */ 1143 @BeanTagAttribute(name = "addViaLightBoxAction", type = BeanTagAttribute.AttributeType.SINGLEBEAN) 1144 public Action getAddViaLightBoxAction() { 1145 return addViaLightBoxAction; 1146 } 1147 1148 /** 1149 * Setter for the add line via lightbox {@link Action} 1150 * 1151 * @param addViaLightBoxAction 1152 */ 1153 public void setAddViaLightBoxAction(Action addViaLightBoxAction) { 1154 this.addViaLightBoxAction = addViaLightBoxAction; 1155 } 1156 1157 /** 1158 * Gets useServerPaging, the flag that indicates whether server side paging is enabled. Defaults to false. 1159 * 1160 * @return true if server side paging is enabled. 1161 */ 1162 @BeanTagAttribute(name = "useServerPaging") 1163 public boolean isUseServerPaging() { 1164 return useServerPaging; 1165 } 1166 1167 /** 1168 * Sets useServerPaging, the flag indicating whether server side paging is enabled. 1169 * 1170 * @param useServerPaging the useServerPaging value to set 1171 */ 1172 public void setUseServerPaging(boolean useServerPaging) { 1173 this.useServerPaging = useServerPaging; 1174 } 1175 1176 public int getPageSize() { 1177 return pageSize; 1178 } 1179 1180 public void setPageSize(int pageSize) { 1181 this.pageSize = pageSize; 1182 } 1183 1184 /** 1185 * Gets the displayStart, the index of the first item to display on the page (assuming useServerPaging is enabled). 1186 * 1187 * <p>if this field has not been set, the returned value will be -1</p> 1188 * 1189 * @return the index of the first item to display, or -1 if unset 1190 */ 1191 public int getDisplayStart() { 1192 return displayStart; 1193 } 1194 1195 /** 1196 * Sets the displayStart, the index of the first item to display on the page (assuming useServerPaging is enabled). 1197 * 1198 * @param displayStart the displayStart to set 1199 */ 1200 public void setDisplayStart(int displayStart) { 1201 this.displayStart = displayStart; 1202 } 1203 1204 /** 1205 * Gets the displayLength, the number of items to display on the page (assuming useServerPaging is enabled). 1206 * 1207 * <p>if this field has not been set, the returned value will be -1</p> 1208 * 1209 * @return the number of items to display on the page, or -1 if unset 1210 */ 1211 public int getDisplayLength() { 1212 return displayLength; 1213 } 1214 1215 /** 1216 * Sets the displayLength, the number of items to display on the page (assuming useServerPaging is enabled). 1217 * 1218 * @param displayLength the displayLength to set 1219 */ 1220 public void setDisplayLength(int displayLength) { 1221 this.displayLength = displayLength; 1222 } 1223 1224 /** 1225 * Gets the number of un-filtered elements from the model collection. 1226 * 1227 * <p>if this field has not been set, the returned value will be -1</p> 1228 * 1229 * @return the filtered collection size, or -1 if unset 1230 */ 1231 public int getFilteredCollectionSize() { 1232 return filteredCollectionSize; 1233 } 1234 1235 /** 1236 * Sets the number of un-filtered elements from the model collection. 1237 * 1238 * <p>This value is used for display and rendering purposes, it has no effect on the model collection</p> 1239 * 1240 * @param filteredCollectionSize the filtered collection size 1241 */ 1242 public void setFilteredCollectionSize(int filteredCollectionSize) { 1243 this.filteredCollectionSize = filteredCollectionSize; 1244 } 1245 1246 /** 1247 * @return list of total columns 1248 */ 1249 @BeanTagAttribute(name = "addTotalColumns") 1250 protected List<String> getTotalColumns() { 1251 return totalColumns; 1252 } 1253 1254 /** 1255 * Setter for the total columns 1256 * 1257 * @param totalColumns 1258 */ 1259 protected void setTotalColumns(List<String> totalColumns) { 1260 this.totalColumns = totalColumns; 1261 } 1262 1263 /** 1264 * @see org.kuali.rice.krad.uif.component.Component#completeValidation 1265 */ 1266 @Override 1267 public void completeValidation(ValidationTrace tracer) { 1268 tracer.addBean(this); 1269 1270 // Checking if collectionObjectClass is set 1271 if (getCollectionObjectClass() == null) { 1272 if (Validator.checkExpressions(this, "collectionObjectClass")) { 1273 String currentValues[] = {"collectionObjectClass = " + getCollectionObjectClass()}; 1274 tracer.createWarning("CollectionObjectClass is not set (disregard if part of an abstract)", 1275 currentValues); 1276 } 1277 } 1278 1279 super.completeValidation(tracer.getCopy()); 1280 } 1281 1282 /** 1283 * @see org.kuali.rice.krad.uif.component.ComponentBase#copy() 1284 */ 1285 @Override 1286 protected <T> void copyProperties(T component) { 1287 super.copyProperties(component); 1288 1289 CollectionGroup collectionGroupCopy = (CollectionGroup) component; 1290 1291 collectionGroupCopy.setDisplayCollectionSize(this.displayCollectionSize); 1292 collectionGroupCopy.setActiveCollectionFilter(this.activeCollectionFilter); 1293 1294 if (this.addBlankLineAction != null) { 1295 collectionGroupCopy.setAddBlankLineAction((Action) this.addBlankLineAction.copy()); 1296 } 1297 1298 collectionGroupCopy.setAddItemCssClass(this.addItemCssClass); 1299 1300 if (addLineItems != null && !addLineItems.isEmpty()) { 1301 List<Component> addLineItemsCopy = new ArrayList<Component>(); 1302 1303 for (Component addLineItem : this.addLineItems) { 1304 addLineItemsCopy.add((Component) addLineItem.copy()); 1305 } 1306 1307 collectionGroupCopy.setAddLineItems(addLineItemsCopy); 1308 } 1309 1310 if (addLineActions != null && !addLineActions.isEmpty()) { 1311 List<Action> addLineActionsCopy = new ArrayList<Action>(); 1312 1313 for (Action addLineAction : this.addLineActions) { 1314 addLineActionsCopy.add((Action) addLineAction.copy()); 1315 } 1316 1317 collectionGroupCopy.setAddLineActions(addLineActionsCopy); 1318 } 1319 1320 if (this.addLineBindingInfo != null) { 1321 collectionGroupCopy.setAddLineBindingInfo((BindingInfo) this.addLineBindingInfo.copy()); 1322 } 1323 1324 if (this.addLineLabel != null) { 1325 collectionGroupCopy.setAddLineLabel((Message) this.addLineLabel.copy()); 1326 } 1327 1328 collectionGroupCopy.setAddLinePlacement(this.addLinePlacement); 1329 collectionGroupCopy.setAddLinePropertyName(this.addLinePropertyName); 1330 collectionGroupCopy.setAddViaLightBox(this.addViaLightBox); 1331 1332 if (this.addViaLightBoxAction != null) { 1333 collectionGroupCopy.setAddViaLightBoxAction((Action) this.addViaLightBoxAction.copy()); 1334 } 1335 1336 if (this.bindingInfo != null) { 1337 collectionGroupCopy.setBindingInfo((BindingInfo) this.bindingInfo.copy()); 1338 } 1339 1340 if (this.collectionLookup != null) { 1341 collectionGroupCopy.setCollectionLookup((QuickFinder) this.collectionLookup.copy()); 1342 } 1343 1344 collectionGroupCopy.setCollectionObjectClass(this.collectionObjectClass); 1345 1346 if (this.filters != null && !this.filters.isEmpty()) { 1347 collectionGroupCopy.setFilters(new ArrayList<CollectionFilter>(this.filters)); 1348 } 1349 1350 collectionGroupCopy.setHighlightAddItem(this.highlightAddItem); 1351 collectionGroupCopy.setHighlightNewItems(this.highlightNewItems); 1352 collectionGroupCopy.setIncludeLineSelectionField(this.includeLineSelectionField); 1353 collectionGroupCopy.setUseServerPaging(this.useServerPaging); 1354 collectionGroupCopy.setPageSize(this.pageSize); 1355 collectionGroupCopy.setDisplayStart(this.displayStart); 1356 collectionGroupCopy.setDisplayLength(this.displayLength); 1357 1358 if (lineActions != null && !lineActions.isEmpty()) { 1359 List<Action> lineActions = new ArrayList<Action>(); 1360 for (Action lineAction : this.lineActions) { 1361 lineActions.add((Action) lineAction.copy()); 1362 } 1363 1364 collectionGroupCopy.setLineActions(lineActions); 1365 } 1366 1367 collectionGroupCopy.setLineSelectPropertyName(this.lineSelectPropertyName); 1368 collectionGroupCopy.setNewItemsCssClass(this.newItemsCssClass); 1369 collectionGroupCopy.setPropertyName(this.propertyName); 1370 collectionGroupCopy.setRenderAddBlankLineButton(this.renderAddBlankLineButton); 1371 collectionGroupCopy.setRenderAddLine(this.renderAddLine); 1372 collectionGroupCopy.setRenderInactiveToggleButton(this.renderInactiveToggleButton); 1373 collectionGroupCopy.setActiveCollectionFilter(this.activeCollectionFilter); 1374 collectionGroupCopy.setFilters(this.filters); 1375 1376 collectionGroupCopy.setRenderLineActions(this.renderLineActions); 1377 collectionGroupCopy.setRenderSaveLineActions(this.renderSaveLineActions); 1378 collectionGroupCopy.setShowInactiveLines(this.showInactiveLines); 1379 1380 if (subCollections != null && !subCollections.isEmpty()) { 1381 List<CollectionGroup> subCollectionsCopy = new ArrayList<CollectionGroup>(); 1382 for (CollectionGroup subCollection : this.subCollections) { 1383 subCollectionsCopy.add((CollectionGroup) subCollection.copy()); 1384 } 1385 1386 collectionGroupCopy.setSubCollections(subCollectionsCopy); 1387 } 1388 collectionGroupCopy.setSubCollectionSuffix(this.subCollectionSuffix); 1389 1390 if (this.totalColumns != null) { 1391 collectionGroupCopy.setTotalColumns(new ArrayList<String>(this.totalColumns)); 1392 } 1393 } 1394 }