View Javadoc

1   /**
2    * Copyright 2005-2014 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 com.google.common.collect.Lists;
19  
20  import org.apache.commons.lang.StringUtils;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
22  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
23  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
24  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
25  import org.kuali.rice.krad.datadictionary.validator.Validator;
26  import org.kuali.rice.krad.uif.component.Component;
27  import org.kuali.rice.krad.uif.component.DataBinding;
28  import org.kuali.rice.krad.uif.field.Field;
29  import org.kuali.rice.krad.uif.field.FieldGroup;
30  import org.kuali.rice.krad.uif.view.View;
31  import org.kuali.rice.krad.uif.widget.Disclosure;
32  import org.kuali.rice.krad.uif.widget.Scrollpane;
33  
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Set;
39  
40  /**
41   * Container that holds a list of <code>Field</code> or other <code>Group</code>
42   * instances
43   *
44   * <p>
45   * Groups can exist at different levels of the <code>View</code>, providing
46   * conceptual groupings such as the page, section, and group. In addition, other
47   * group types can be created to add behavior like collection support
48   * </p>
49   *
50   * <p>
51   * <code>Group</code> implementation has properties for defaulting the binding
52   * information (such as the parent object path and a binding prefix) for the
53   * fields it contains. During the phase these properties (if given) are set on
54   * the fields contained in the <code>Group</code> that implement
55   * <code>DataBinding</code>, unless they have already been set on the field.
56   * </p>
57   *
58   * @author Kuali Rice Team (rice.collab@kuali.org)
59   */
60  @BeanTags({@BeanTag(name = "group-bean", parent = "Uif-GroupBase"),
61          @BeanTag(name = "boxGroupBase-bean", parent = "Uif-BoxGroupBase"),
62          @BeanTag(name = "verticalBoxGroup-bean", parent = "Uif-VerticalBoxGroup"),
63          @BeanTag(name = "verticalBoxSection-bean", parent = "Uif-VerticalBoxSection"),
64          @BeanTag(name = "verticalBoxSubSection-bean", parent = "Uif-VerticalBoxSubSection"),
65          @BeanTag(name = "disclosure-verticalBoxSection-bean", parent = "Uif-Disclosure-VerticalBoxSection"),
66          @BeanTag(name = "disclosure-verticalBoxSubSection-bean", parent = "Uif-Disclosure-VerticalBoxSubSection"),
67          @BeanTag(name = "horizontalBoxGroup-bean", parent = "Uif-HorizontalBoxGroup"),
68          @BeanTag(name = "horizontalBoxSection-bean", parent = "Uif-HorizontalBoxSection"),
69          @BeanTag(name = "horizontalBoxSubSection-bean", parent = "Uif-HorizontalBoxSubSection"),
70          @BeanTag(name = "disclosure-horizontalBoxSection-bean", parent = "Uif-Disclosure-HorizontalBoxSection"),
71          @BeanTag(name = "disclosure-horizontalBoxSubSection-bean", parent = "Uif-Disclosure-HorizontalBoxSubSection"),
72          @BeanTag(name = "gridGroup-bean", parent = "Uif-GridGroup"),
73          @BeanTag(name = "gridSection-bean", parent = "Uif-GridSection"),
74          @BeanTag(name = "gridSubSection-bean", parent = "Uif-GridSubSection"),
75          @BeanTag(name = "disclosure-gridSection-bean", parent = "Uif-Disclosure-GridSection"),
76          @BeanTag(name = "fixedCssGridGroup-bean", parent = "Uif-FixedCssGridGroup"),
77          @BeanTag(name = "fixedCssGridSection-bean", parent = "Uif-FixedCssGridSection"),
78          @BeanTag(name = "fixedCssGridSubSection-bean", parent = "Uif-FixedCssGridSubSection"),
79          @BeanTag(name = "fluidCssGridGroup-bean", parent = "Uif-FluidCssGridGroup"),
80          @BeanTag(name = "fluidCssGridSection-bean", parent = "Uif-FluidCssGridSection"),
81          @BeanTag(name = "fluidCssGridSubSection-bean", parent = "Uif-FluidCssGridSubSection"),
82          @BeanTag(name = "listGroup-bean", parent = "Uif-ListGroup"),
83          @BeanTag(name = "listSection-bean", parent = "Uif-ListSection"),
84          @BeanTag(name = "listSubSection-bean", parent = "Uif-ListSubSection"),
85          @BeanTag(name = "disclosure-listSection-bean", parent = "Uif-Disclosure-ListSection"),
86          @BeanTag(name = "disclosure-listSubSection-bean", parent = "Uif-Disclosure-ListSubSection"),
87          @BeanTag(name = "collectionGridItem-bean", parent = "Uif-CollectionGridItem"),
88          @BeanTag(name = "collectionVerticalBoxItem-bean", parent = "Uif-CollectionVerticalBoxItem"),
89          @BeanTag(name = "collectionHorizontalBoxItem-bean", parent = "Uif-CollectionHorizontalBoxItem"),
90          @BeanTag(name = "headerUpperGroup-bean", parent = "Uif-HeaderUpperGroup"),
91          @BeanTag(name = "headerRightGroup-bean", parent = "Uif-HeaderRightGroup"),
92          @BeanTag(name = "headerLowerGroup-bean", parent = "Uif-HeaderLowerGroup"),
93          @BeanTag(name = "footer-bean", parent = "Uif-FooterBase"),
94          @BeanTag(name = "formFooter-bean", parent = "Uif-FormFooter"),
95          @BeanTag(name = "actionsGroup-bean", parent = "Uif-ActionsGroup"),
96          @BeanTag(name = "disclosureActionsGroup-bean", parent = "Uif-DisclosureActionsGroup"),
97          @BeanTag(name = "disclosureActions-reqMessageGroup-bean", parent = "Uif-DisclosureActions-ReqMessageGroup"),
98          @BeanTag(name = "inactiveItemsActionsGroup-bean", parent = "Uif-InactiveItemsActionsGroup"),
99          @BeanTag(name = "documentInfoGroup-bean", parent = "Uif-DocumentInfoGroup"),
100         @BeanTag(name = "documentOverviewSection-bean", parent = "Uif-DocumentOverviewSection"),
101         @BeanTag(name = "documentAdHocRecipientsSection-bean", parent = "Uif-DocumentAdHocRecipientsSection"),
102         @BeanTag(name = "documentRouteLogSection-bean", parent = "Uif-DocumentRouteLogSection"),
103         @BeanTag(name = "documentPageFooter-bean", parent = "Uif-DocumentPageFooter"),
104         @BeanTag(name = "incidentDetailGroup-bean", parent = "Uif-IncidentDetailGroup"),
105         @BeanTag(name = "incidentStackTraceGroup-bean", parent = "Uif-IncidentStackTraceGroup"),
106         @BeanTag(name = "incidentReportFooter-bean", parent = "Uif-IncidentReportFooter"),
107         @BeanTag(name = "initiatedDocumentFooter-bean", parent = "InitiatedDocumentFooter"),
108         @BeanTag(name = "inquiryFooter-bean", parent = "Uif-InquiryFooter"),
109         @BeanTag(name = "lookupCriteriaGroup-bean", parent = "Uif-LookupCriteriaGroup"),
110         @BeanTag(name = "lookupPageHeaderGroup-bean", parent = "Uif-LookupPageHeaderGroup"),
111         @BeanTag(name = "lookupCriteriaFooter-bean", parent = "Uif-LookupCriteriaFooter"),
112         @BeanTag(name = "lookupResultsFooter-bean", parent = "Uif-LookupResultsFooter"),
113         @BeanTag(name = "maintenanceGridGroup-bean", parent = "Uif-MaintenanceGridGroup"),
114         @BeanTag(name = "maintenanceHorizontalBoxGroup-bean", parent = "Uif-MaintenanceHorizontalBoxGroup"),
115         @BeanTag(name = "maintenanceVerticalBoxGroup-bean", parent = "Uif-MaintenanceVerticalBoxGroup"),
116         @BeanTag(name = "maintenanceGridSection-bean", parent = "Uif-MaintenanceGridSection"),
117         @BeanTag(name = "maintenanceGridSubSection-bean", parent = "Uif-MaintenanceGridSubSection"),
118         @BeanTag(name = "maintenanceHorizontalBoxSection-bean", parent = "Uif-MaintenanceHorizontalBoxSection"),
119         @BeanTag(name = "maintenanceVerticalBoxSection-bean", parent = "Uif-MaintenanceVerticalBoxSection"),
120         @BeanTag(name = "maintenanceHorizontalBoxSubSection-bean", parent = "Uif-MaintenanceHorizontalBoxSubSection"),
121         @BeanTag(name = "maintenanceVerticalBoxSubSection-bean", parent = "Uif-MaintenanceVerticalBoxSubSection")
122 })
123 public class Group extends ContainerBase {
124     private static final long serialVersionUID = 7953641325356535509L;
125 
126     private String fieldBindByNamePrefix;
127     private String fieldBindingObjectPath;
128 
129     private Disclosure disclosure;
130     private Scrollpane scrollpane;
131 
132     private List<? extends Component> items;
133 
134     /**
135      * Default Constructor
136      */
137     public Group() {
138         items = Collections.emptyList();
139     }
140 
141     /**
142      * The following actions are performed:
143      *
144      * <ul>
145      * <li>Sets the bindByNamePrefix if blank on any InputField and
146      * FieldGroup instances within the items List</li>
147      * </ul>
148      *
149      * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View,
150      *      java.lang.Object)
151      */
152     @Override
153     public void performInitialization(View view, Object model) {
154         super.performInitialization(view, model);
155 
156         for (Component component : getItems()) {
157             // append group's field bind by name prefix (if set) to each
158             // attribute field's binding prefix
159             if (component instanceof DataBinding) {
160                 DataBinding dataBinding = (DataBinding) component;
161 
162                 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) {
163                     String bindByNamePrefixToSet = getFieldBindByNamePrefix();
164 
165                     if (StringUtils.isNotBlank(dataBinding.getBindingInfo().getBindByNamePrefix())) {
166                         bindByNamePrefixToSet += "." + dataBinding.getBindingInfo().getBindByNamePrefix();
167                     }
168                     dataBinding.getBindingInfo().setBindByNamePrefix(bindByNamePrefixToSet);
169                 }
170 
171                 if (StringUtils.isNotBlank(fieldBindingObjectPath) && StringUtils.isBlank(
172                         dataBinding.getBindingInfo().getBindingObjectPath())) {
173                     dataBinding.getBindingInfo().setBindingObjectPath(fieldBindingObjectPath);
174                 }
175             }
176             // set on FieldGroup's group to recursively set AttributeFields
177             else if (component instanceof FieldGroup) {
178                 FieldGroup fieldGroup = (FieldGroup) component;
179 
180                 if (fieldGroup.getGroup() != null) {
181                     if (StringUtils.isBlank(fieldGroup.getGroup().getFieldBindByNamePrefix())) {
182                         fieldGroup.getGroup().setFieldBindByNamePrefix(fieldBindByNamePrefix);
183                     }
184                     if (StringUtils.isBlank(fieldGroup.getGroup().getFieldBindingObjectPath())) {
185                         fieldGroup.getGroup().setFieldBindingObjectPath(fieldBindingObjectPath);
186                     }
187                 }
188             } else if (component instanceof Group) {
189                 Group subGroup = (Group) component;
190                 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) {
191                     if (StringUtils.isNotBlank(subGroup.getFieldBindByNamePrefix())) {
192                         subGroup.setFieldBindByNamePrefix(
193                                 getFieldBindByNamePrefix() + "." + subGroup.getFieldBindByNamePrefix());
194                     } else {
195                         subGroup.setFieldBindByNamePrefix(getFieldBindByNamePrefix());
196                     }
197                 }
198                 if (StringUtils.isNotBlank(getFieldBindingObjectPath())) {
199                     if (StringUtils.isNotBlank(subGroup.getFieldBindingObjectPath())) {
200                         subGroup.setFieldBindingObjectPath(
201                                 getFieldBindingObjectPath() + "." + subGroup.getFieldBindingObjectPath());
202                     } else {
203                         subGroup.setFieldBindingObjectPath(getFieldBindingObjectPath());
204                     }
205                 }
206             }
207         }
208     }
209 
210     /**
211      * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
212      */
213     @Override
214     public List<Component> getComponentsForLifecycle() {
215         List<Component> components = super.getComponentsForLifecycle();
216 
217         components.add(disclosure);
218         components.add(scrollpane);
219 
220         return components;
221     }
222 
223     /**
224      * @see org.kuali.rice.krad.uif.container.Container#getSupportedComponents()
225      */
226     @Override
227     public Set<Class<? extends Component>> getSupportedComponents() {
228         Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>();
229         supportedComponents.add(Field.class);
230         supportedComponents.add(Group.class);
231 
232         return supportedComponents;
233     }
234 
235     /**
236      * @see org.kuali.rice.krad.uif.component.Component#getComponentTypeName()
237      */
238     @Override
239     public String getComponentTypeName() {
240         return "group";
241     }
242 
243     /**
244      * Binding prefix string to set on each of the groups <code>DataField</code> instances
245      *
246      * <p>
247      * As opposed to setting the bindingPrefix on each attribute field instance,
248      * it can be set here for the group. During initialize the string will then
249      * be set on each attribute field instance if the bindingPrefix is blank and
250      * not a form field
251      * </p>
252      *
253      * @return String binding prefix to set
254      */
255     @BeanTagAttribute(name = "fieldBindByNamePrefix")
256     public String getFieldBindByNamePrefix() {
257         return this.fieldBindByNamePrefix;
258     }
259 
260     /**
261      * Setter for the field binding prefix
262      *
263      * @param fieldBindByNamePrefix
264      */
265     public void setFieldBindByNamePrefix(String fieldBindByNamePrefix) {
266         this.fieldBindByNamePrefix = fieldBindByNamePrefix;
267     }
268 
269     /**
270      * Object binding path to set on each of the group's
271      * <code>InputField</code> instances
272      *
273      * <p>
274      * When the attributes of the group belong to a object whose path is
275      * different from the default then this property can be given to set each of
276      * the attributes instead of setting the model path on each one. The object
277      * path can be overridden at the attribute level. The object path is set to
278      * the fieldBindingObjectPath during the initialize phase.
279      * </p>
280      *
281      * @return String model path to set
282      * @see org.kuali.rice.krad.uif.component.BindingInfo#getBindingObjectPath()
283      */
284     @BeanTagAttribute(name = "fieldBindingObjectPath")
285     public String getFieldBindingObjectPath() {
286         return this.fieldBindingObjectPath;
287     }
288 
289     /**
290      * Setter for the field object binding path
291      *
292      * @param fieldBindingObjectPath
293      */
294     public void setFieldBindingObjectPath(String fieldBindingObjectPath) {
295         this.fieldBindingObjectPath = fieldBindingObjectPath;
296     }
297 
298     /**
299      * Disclosure widget that provides collapse/expand functionality for the
300      * group
301      *
302      * @return Disclosure instance
303      */
304     @BeanTagAttribute(name = "Disclosure", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
305     public Disclosure getDisclosure() {
306         return this.disclosure;
307     }
308 
309     /**
310      * Setter for the group's disclosure instance
311      *
312      * @param disclosure
313      */
314     public void setDisclosure(Disclosure disclosure) {
315         this.disclosure = disclosure;
316     }
317 
318     /**
319      * Scrollpane widget that provides scrolling functionality for the
320      * group
321      *
322      * @return Scrollpane instance
323      */
324     @BeanTagAttribute(name = "scrollpane", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
325     public Scrollpane getScrollpane() {
326         return this.scrollpane;
327     }
328 
329     /**
330      * Setter for the group's scrollpane instance
331      *
332      * @param scrollpane
333      */
334     public void setScrollpane(Scrollpane scrollpane) {
335         this.scrollpane = scrollpane;
336     }
337 
338     /**
339      * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
340      */
341     @Override
342     @BeanTagAttribute(name = "items", type = BeanTagAttribute.AttributeType.LISTBEAN)
343     public List<? extends Component> getItems() {
344         return this.items;
345     }
346 
347     /**
348      * Setter for the Group's list of components
349      *
350      * @param items
351      */
352     @Override
353     public void setItems(List<? extends Component> items) {
354         this.items = items;
355     }
356 
357     /**
358      * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
359      */
360     @Override
361     protected <T> void copyProperties(T component) {
362         super.copyProperties(component);
363         Group groupCopy = (Group) component;
364         groupCopy.setFieldBindByNamePrefix(this.fieldBindByNamePrefix);
365         groupCopy.setFieldBindingObjectPath(this.fieldBindingObjectPath);
366 
367         if (this.disclosure != null) {
368             groupCopy.setDisclosure((Disclosure) this.disclosure.copy());
369         }
370 
371         if (this.scrollpane != null) {
372             groupCopy.setScrollpane((Scrollpane) this.scrollpane.copy());
373         }
374 
375         if (this.items != null) {
376             List<Component> items = new ArrayList<Component>();
377             for (Component itemComponent : this.items) {
378                 items.add((Component) itemComponent.copy());
379                 groupCopy.setItems(items);
380             }
381         }
382     }
383 
384     /**
385      * @see org.kuali.rice.krad.uif.component.Component#completeValidation
386      */
387     @Override
388     public void completeValidation(ValidationTrace tracer) {
389         tracer.addBean(this);
390 
391         // Checks that no invalid items are present
392         for (int i = 0; i < getItems().size(); i++) {
393             if (getItems().get(i).getClass() == PageGroup.class || getItems().get(i).getClass()
394                     == NavigationGroup.class) {
395                 String currentValues[] = {"item(" + i + ").class =" + getItems().get(i).getClass()};
396                 tracer.createError("Items in Group cannot be PageGroup or NaviagtionGroup", currentValues);
397             }
398         }
399 
400         // Checks that the layout manager is set
401         if (getLayoutManager() == null) {
402             if (Validator.checkExpressions(this, "layoutManager")) {
403                 String currentValues[] = {"layoutManager = " + getLayoutManager()};
404                 tracer.createError("LayoutManager must be set", currentValues);
405             }
406         }
407 
408         super.completeValidation(tracer.getCopy());
409     }
410 
411     /**
412      * Determine the group should be rendered on initial load, or if a loading message should be rendered instead.
413      * 
414      * @return True if a loading message should be rendered, false if the group should be rendered now.
415      */
416     public boolean isRenderLoading() {
417         return disclosure != null && disclosure.isAjaxRetrievalWhenOpened()
418                 && (!disclosure.isRender() || !disclosure.isDefaultOpen());
419     }
420 
421 }