View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.container;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.exception.RiceRuntimeException;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
22  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
23  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
24  import org.kuali.rice.krad.datadictionary.validator.Validator;
25  import org.kuali.rice.krad.uif.UifConstants;
26  import org.kuali.rice.krad.uif.UifParameters;
27  import org.kuali.rice.krad.uif.component.BindingInfo;
28  import org.kuali.rice.krad.uif.component.ClientSideState;
29  import org.kuali.rice.krad.uif.component.Component;
30  import org.kuali.rice.krad.uif.component.ComponentSecurity;
31  import org.kuali.rice.krad.uif.component.DataBinding;
32  import org.kuali.rice.krad.uif.element.Action;
33  import org.kuali.rice.krad.uif.element.Message;
34  import org.kuali.rice.krad.uif.field.DataField;
35  import org.kuali.rice.krad.uif.field.Field;
36  import org.kuali.rice.krad.uif.util.ComponentFactory;
37  import org.kuali.rice.krad.uif.util.ComponentUtils;
38  import org.kuali.rice.krad.uif.view.View;
39  import org.kuali.rice.krad.uif.widget.QuickFinder;
40  import org.kuali.rice.krad.web.form.UifFormBase;
41  
42  import java.util.ArrayList;
43  import java.util.Collections;
44  import java.util.List;
45  
46  /**
47   * Group that holds a collection of objects and configuration for presenting the
48   * collection in the UI. Supports functionality such as add line, line actions,
49   * and nested collections.
50   *
51   * <p>
52   * Note the standard header/footer can be used to give a header to the
53   * collection as a whole, or to provide actions that apply to the entire
54   * collection
55   * </p>
56   *
57   * <p>
58   * For binding purposes the binding path of each row field is indexed. The name
59   * property inherited from <code>ComponentBase</code> is used as the collection
60   * name. The collectionObjectClass property is used to lookup attributes from
61   * the data dictionary.
62   * </p>
63   *
64   * @author Kuali Rice Team (rice.collab@kuali.org)
65   */
66  @BeanTags({@BeanTag(name = "collectionGroup-bean", parent = "Uif-CollectionGroupBase"),
67          @BeanTag(name = "stackedCollectionGroup-bean", parent = "Uif-StackedCollectionGroup"),
68          @BeanTag(name = "stackedCollectionSection-bean", parent = "Uif-StackedCollectionSection"),
69          @BeanTag(name = "stackedCollectionSubSection-bean", parent = "Uif-StackedCollectionSubSection"),
70          @BeanTag(name = "stackedSubCollection-withinSection-bean", parent = "Uif-StackedSubCollection-WithinSection"),
71          @BeanTag(name = "stackedSubCollection-withinSubSection-bean",
72                  parent = "Uif-StackedSubCollection-WithinSubSection"),
73          @BeanTag(name = "disclosure-stackedCollectionSection-bean", parent = "Uif-Disclosure-StackedCollectionSection"),
74          @BeanTag(name = "disclosure-stackedCollectionSubSection-bean",
75                  parent = "Uif-Disclosure-StackedCollectionSubSection"),
76          @BeanTag(name = "disclosure-stackedSubCollection-withinSection-bean",
77                  parent = "Uif-Disclosure-StackedSubCollection-WithinSection"),
78          @BeanTag(name = "disclosure-stackedSubCollection-withinSubSection-bean",
79                  parent = "Uif-Disclosure-StackedSubCollection-WithinSubSection"),
80          @BeanTag(name = "tableCollectionGroup-bean", parent = "Uif-TableCollectionGroup"),
81          @BeanTag(name = "tableCollectionSection-bean", parent = "Uif-TableCollectionSection"),
82          @BeanTag(name = "tableCollectionSubSection-bean", parent = "Uif-TableCollectionSubSection"),
83          @BeanTag(name = "tableSubCollection-withinSection-bean", parent = "Uif-TableSubCollection-WithinSection"),
84          @BeanTag(name = "tableSubCollection-withinSubSection-bean", parent = "Uif-TableSubCollection-WithinSubSection"),
85          @BeanTag(name = "disclosure-tableCollectionSection-bean", parent = "Uif-Disclosure-TableCollectionSection"),
86          @BeanTag(name = "disclosure-tableCollectionSubSection-bean",
87                  parent = "Uif-Disclosure-TableCollectionSubSection"),
88          @BeanTag(name = "disclosure-tableSubCollection-withinSection-bean",
89                  parent = "Uif-Disclosure-TableSubCollection-WithinSection"),
90          @BeanTag(name = "disclosure-tableSubCollection-withinSubSection-bean",
91                  parent = "Uif-Disclosure-TableSubCollection-WithinSubSection"),
92          @BeanTag(name = "listCollectionGroup-bean", parent = "Uif-ListCollectionGroup"),
93          @BeanTag(name = "listCollectionSection-bean", parent = "Uif-ListCollectionSection"),
94          @BeanTag(name = "listCollectionSubSection-bean", parent = "Uif-ListCollectionSubSection"),
95          @BeanTag(name = "documentNotesSection-bean", parent = "Uif-DocumentNotesSection"),
96          @BeanTag(name = "lookupResultsCollectionSection-bean", parent = "Uif-LookupResultsCollectionSection"),
97          @BeanTag(name = "maintenanceStackedCollectionSection-bean", parent = "Uif-MaintenanceStackedCollectionSection"),
98          @BeanTag(name = "maintenanceStackedSubCollection-withinSection-bean",
99                  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 }