Coverage Report - org.kuali.rice.krad.uif.layout.StackedLayoutManager
 
Classes in this File Line Coverage Branch Coverage Complexity
StackedLayoutManager
0%
0/82
0%
0/28
1.667
 
 1  
 /**
 2  
  * Copyright 2005-2011 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.layout;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 20  
 import org.kuali.rice.krad.uif.component.KeepExpression;
 21  
 import org.kuali.rice.krad.uif.container.CollectionGroup;
 22  
 import org.kuali.rice.krad.uif.container.Container;
 23  
 import org.kuali.rice.krad.uif.container.Group;
 24  
 import org.kuali.rice.krad.uif.field.FieldGroup;
 25  
 import org.kuali.rice.krad.uif.view.View;
 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.ActionField;
 29  
 import org.kuali.rice.krad.uif.field.Field;
 30  
 import org.kuali.rice.krad.uif.util.ComponentUtils;
 31  
 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
 32  
 
 33  
 import java.util.ArrayList;
 34  
 import java.util.List;
 35  
 
 36  
 /**
 37  
  * Layout manager that works with <code>CollectionGroup</code> containers and
 38  
  * renders the collection lines in a vertical row
 39  
  *
 40  
  * <p>
 41  
  * For each line of the collection, a <code>Group</code> instance is created.
 42  
  * The group header contains a label for the line (summary information), the
 43  
  * group fields are the collection line fields, and the group footer contains
 44  
  * the line actions. All the groups are rendered using the
 45  
  * <code>BoxLayoutManager</code> with vertical orientation.
 46  
  * </p>
 47  
  *
 48  
  * <p>
 49  
  * Modify the lineGroupPrototype to change header/footer styles or any other
 50  
  * customization for the line groups
 51  
  * </p>
 52  
  *
 53  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 54  
  */
 55  
 public class StackedLayoutManager extends LayoutManagerBase implements CollectionLayoutManager {
 56  
     private static final long serialVersionUID = 4602368505430238846L;
 57  
 
 58  
     @KeepExpression
 59  
     private String summaryTitle;
 60  
     private List<String> summaryFields;
 61  
 
 62  
     private Group addLineGroup;
 63  
     private Group lineGroupPrototype;
 64  
     private FieldGroup subCollectionFieldGroupPrototype;
 65  
     private Field selectFieldPrototype;
 66  
 
 67  
     private List<Group> stackedGroups;
 68  
 
 69  
     public StackedLayoutManager() {
 70  0
         super();
 71  
 
 72  0
         summaryFields = new ArrayList<String>();
 73  0
         stackedGroups = new ArrayList<Group>();
 74  0
     }
 75  
 
 76  
     /**
 77  
      * The following actions are performed:
 78  
      *
 79  
      * <ul>
 80  
      * <li>Initializes the prototypes</li>
 81  
      * </ul>
 82  
      *
 83  
      * @see org.kuali.rice.krad.uif.layout.BoxLayoutManager#performInitialization(org.kuali.rice.krad.uif.view.View,
 84  
      *      java.lang.Object, org.kuali.rice.krad.uif.container.Container)
 85  
      */
 86  
     @Override
 87  
     public void performInitialization(View view, Object model, Container container) {
 88  0
         super.performInitialization(view, model, container);
 89  
 
 90  0
         if (addLineGroup != null) {
 91  0
             view.getViewHelperService().performComponentInitialization(view, model, addLineGroup);
 92  
         }
 93  0
         view.getViewHelperService().performComponentInitialization(view, model, lineGroupPrototype);
 94  0
         view.getViewHelperService().performComponentInitialization(view, model, subCollectionFieldGroupPrototype);
 95  0
         view.getViewHelperService().performComponentInitialization(view, model, selectFieldPrototype);
 96  0
     }
 97  
 
 98  
     /**
 99  
      * Builds a <code>Group</code> instance for a collection line. The group is
 100  
      * built by first creating a copy of the configured prototype. Then the
 101  
      * header for the group is created using the configured summary fields on
 102  
      * the <code>CollectionGroup</code>. The line fields passed in are set as
 103  
      * the items for the group, and finally the actions are placed into the
 104  
      * group footer
 105  
      *
 106  
      * @see org.kuali.rice.krad.uif.layout.CollectionLayoutManager#buildLine(org.kuali.rice.krad.uif.view.View,
 107  
      *      java.lang.Object, org.kuali.rice.krad.uif.container.CollectionGroup,
 108  
      *      java.util.List, java.util.List, java.lang.String, java.util.List,
 109  
      *      java.lang.String, java.lang.Object, int)
 110  
      */
 111  
     public void buildLine(View view, Object model, CollectionGroup collectionGroup, List<Field> lineFields,
 112  
             List<FieldGroup> subCollectionFields, String bindingPath, List<ActionField> actions, String idSuffix,
 113  
             Object currentLine, int lineIndex) {
 114  0
         boolean isAddLine = lineIndex == -1;
 115  
 
 116  
         // construct new group
 117  0
         Group lineGroup = null;
 118  0
         if (isAddLine) {
 119  0
             stackedGroups = new ArrayList<Group>();
 120  
 
 121  0
             if (addLineGroup == null) {
 122  0
                 lineGroup = ComponentUtils.copy(lineGroupPrototype, idSuffix);
 123  
             } else {
 124  0
                 lineGroup = ComponentUtils.copy(getAddLineGroup(), idSuffix);
 125  
             }
 126  
         } else {
 127  0
             lineGroup = ComponentUtils.copy(lineGroupPrototype, idSuffix);
 128  
         }
 129  
 
 130  0
         ComponentUtils.updateContextForLine(lineGroup, currentLine, lineIndex);
 131  
 
 132  
         // build header text for group
 133  0
         String headerText = "";
 134  0
         if (isAddLine) {
 135  0
             headerText = collectionGroup.getAddLineLabel();
 136  
         } else {
 137  
             // get the collection for this group from the model
 138  0
             List<Object> modelCollection = ObjectPropertyUtils.getPropertyValue(model,
 139  
                     ((DataBinding) collectionGroup).getBindingInfo().getBindingPath());
 140  
 
 141  0
             headerText = buildLineHeaderText(modelCollection.get(lineIndex), lineGroup);
 142  
         }
 143  
 
 144  
         // don't set header if text is blank (could already be set by other means)
 145  0
         if (StringUtils.isNotBlank(headerText)) {
 146  0
             lineGroup.getHeader().setHeaderText(headerText);
 147  
         }
 148  
 
 149  
         // stack all fields (including sub-collections) for the group
 150  0
         List<Field> groupFields = new ArrayList<Field>();
 151  0
         groupFields.addAll(lineFields);
 152  0
         groupFields.addAll(subCollectionFields);
 153  
 
 154  0
         lineGroup.setItems(groupFields);
 155  
 
 156  
         // set line actions on group footer
 157  0
         if (collectionGroup.isRenderLineActions() && !collectionGroup.isReadOnly() && (lineGroup.getFooter() != null)) {
 158  0
             lineGroup.getFooter().setItems(actions);
 159  
         }
 160  
 
 161  0
         stackedGroups.add(lineGroup);
 162  0
     }
 163  
 
 164  
     /**
 165  
      * Builds the header text for the collection line
 166  
      *
 167  
      * <p>
 168  
      * Header text is built up by first the collection label, either specified
 169  
      * on the collection definition or retrieved from the dictionary. Then for
 170  
      * each summary field defined, the value from the model is retrieved and
 171  
      * added to the header.
 172  
      * </p>
 173  
      *
 174  
      * <p>
 175  
      * Note the {@link #getSummaryTitle()} field may have expressions defined, in which cause it will be copied to the
 176  
      * property expressions map to set the title for the line group (which will have the item context variable set)
 177  
      * </p>
 178  
      *
 179  
      * @param line - Collection line containing data
 180  
      * @param lineGroup - Group instance for rendering the line and whose title should be built
 181  
      * @return String header text for line
 182  
      */
 183  
     protected String buildLineHeaderText(Object line, Group lineGroup) {
 184  
         // check for expression on summary title
 185  0
         if (KRADServiceLocatorWeb.getExpressionEvaluatorService().containsElPlaceholder(summaryTitle)) {
 186  0
             lineGroup.getPropertyExpressions().put("title", summaryTitle);
 187  0
             return null;
 188  
         }
 189  
 
 190  
         // build up line summary from declared field values and fixed title
 191  0
         String summaryFieldString = "";
 192  0
         for (String summaryField : summaryFields) {
 193  0
             Object summaryFieldValue = ObjectPropertyUtils.getPropertyValue(line, summaryField);
 194  0
             if (StringUtils.isNotBlank(summaryFieldString)) {
 195  0
                 summaryFieldString += " - ";
 196  
             }
 197  
 
 198  0
             if (summaryFieldValue != null) {
 199  0
                 summaryFieldString += summaryFieldValue;
 200  
             } else {
 201  0
                 summaryFieldString += "Null";
 202  
             }
 203  0
         }
 204  
 
 205  0
         String headerText = summaryTitle;
 206  0
         if (StringUtils.isNotBlank(summaryFieldString)) {
 207  0
             headerText += " ( " + summaryFieldString + " )";
 208  
         }
 209  
 
 210  0
         return headerText;
 211  
     }
 212  
 
 213  
     /**
 214  
      * @see org.kuali.rice.krad.uif.layout.ContainerAware#getSupportedContainer()
 215  
      */
 216  
     @Override
 217  
     public Class<? extends Container> getSupportedContainer() {
 218  0
         return CollectionGroup.class;
 219  
     }
 220  
 
 221  
     /**
 222  
      * @see org.kuali.rice.krad.uif.layout.LayoutManagerBase#getComponentsForLifecycle()
 223  
      */
 224  
     @Override
 225  
     public List<Component> getComponentsForLifecycle() {
 226  0
         List<Component> components = super.getComponentsForLifecycle();
 227  
 
 228  0
         components.addAll(stackedGroups);
 229  
 
 230  0
         return components;
 231  
     }
 232  
 
 233  
     /**
 234  
      * @see org.kuali.rice.krad.uif.layout.LayoutManager#getComponentPrototypes()
 235  
      */
 236  
     @Override
 237  
     public List<Component> getComponentPrototypes() {
 238  0
         List<Component> components = super.getComponentPrototypes();
 239  
 
 240  0
         components.add(addLineGroup);
 241  0
         components.add(lineGroupPrototype);
 242  0
         components.add(subCollectionFieldGroupPrototype);
 243  0
         components.add(selectFieldPrototype);
 244  
 
 245  0
         return components;
 246  
     }
 247  
 
 248  
     /**
 249  
      * Text to appears in the header for each collection lines Group. Used in
 250  
      * conjunction with {@link #getSummaryFields()} to build up the final header
 251  
      * text
 252  
      *
 253  
      * @return String summary title text
 254  
      */
 255  
     public String getSummaryTitle() {
 256  0
         return this.summaryTitle;
 257  
     }
 258  
 
 259  
     /**
 260  
      * Setter for the summary title text
 261  
      *
 262  
      * @param summaryTitle
 263  
      */
 264  
     public void setSummaryTitle(String summaryTitle) {
 265  0
         this.summaryTitle = summaryTitle;
 266  0
     }
 267  
 
 268  
     /**
 269  
      * List of attribute names from the collection line class that should be
 270  
      * used to build the line summary. To build the summary the value for each
 271  
      * attribute is retrieved from the line instance. All the values are then
 272  
      * placed together with a separator.
 273  
      *
 274  
      * @return List<String> summary field names
 275  
      * @see #buildLineHeaderText(java.lang.Object)
 276  
      */
 277  
     public List<String> getSummaryFields() {
 278  0
         return this.summaryFields;
 279  
     }
 280  
 
 281  
     /**
 282  
      * Setter for the summary field name list
 283  
      *
 284  
      * @param summaryFields
 285  
      */
 286  
     public void setSummaryFields(List<String> summaryFields) {
 287  0
         this.summaryFields = summaryFields;
 288  0
     }
 289  
 
 290  
     /**
 291  
      * Group instance that will be used for the add line
 292  
      *
 293  
      * <p>
 294  
      * Add line fields and actions configured on the
 295  
      * <code>CollectionGroup</code> will be set onto the add line group (if add
 296  
      * line is enabled). If the add line group is not configured, a new instance
 297  
      * of the line group prototype will be used for the add line.
 298  
      * </p>
 299  
      *
 300  
      * @return Group add line group instance
 301  
      * @see #getAddLineGroup()
 302  
      */
 303  
     public Group getAddLineGroup() {
 304  0
         return this.addLineGroup;
 305  
     }
 306  
 
 307  
     /**
 308  
      * Setter for the add line group
 309  
      *
 310  
      * @param addLineGroup
 311  
      */
 312  
     public void setAddLineGroup(Group addLineGroup) {
 313  0
         this.addLineGroup = addLineGroup;
 314  0
     }
 315  
 
 316  
     /**
 317  
      * Group instance that is used as a prototype for creating the collection
 318  
      * line groups. For each line a copy of the prototype is made and then
 319  
      * adjusted as necessary
 320  
      *
 321  
      * @return Group instance to use as prototype
 322  
      */
 323  
     public Group getLineGroupPrototype() {
 324  0
         return this.lineGroupPrototype;
 325  
     }
 326  
 
 327  
     /**
 328  
      * Setter for the line group prototype
 329  
      *
 330  
      * @param lineGroupPrototype
 331  
      */
 332  
     public void setLineGroupPrototype(Group lineGroupPrototype) {
 333  0
         this.lineGroupPrototype = lineGroupPrototype;
 334  0
     }
 335  
 
 336  
     /**
 337  
      * @see org.kuali.rice.krad.uif.layout.CollectionLayoutManager#getSubCollectionFieldGroupPrototype()
 338  
      */
 339  
     public FieldGroup getSubCollectionFieldGroupPrototype() {
 340  0
         return this.subCollectionFieldGroupPrototype;
 341  
     }
 342  
 
 343  
     /**
 344  
      * Setter for the sub-collection field group prototype
 345  
      *
 346  
      * @param subCollectionFieldGroupPrototype
 347  
      */
 348  
     public void setSubCollectionFieldGroupPrototype(FieldGroup subCollectionFieldGroupPrototype) {
 349  0
         this.subCollectionFieldGroupPrototype = subCollectionFieldGroupPrototype;
 350  0
     }
 351  
 
 352  
     /**
 353  
      * Field instance that serves as a prototype for creating the select field on each line when
 354  
      * {@link org.kuali.rice.krad.uif.container.CollectionGroup#isRenderSelectField()} is true
 355  
      *
 356  
      * <p>
 357  
      * This prototype can be used to set the control used for the select field (generally will be a checkbox control)
 358  
      * in addition to styling and other setting. The binding path will be formed with using the
 359  
      * {@link org.kuali.rice.krad.uif.container.CollectionGroup#getSelectPropertyName()} or if not set the framework
 360  
      * will use {@link org.kuali.rice.krad.web.form.UifFormBase#getSelectedCollectionLines()}
 361  
      * </p>
 362  
      *
 363  
      * @return Field select field prototype instance
 364  
      */
 365  
     public Field getSelectFieldPrototype() {
 366  0
         return selectFieldPrototype;
 367  
     }
 368  
 
 369  
     /**
 370  
      * Setter for the prototype instance for select fields
 371  
      *
 372  
      * @param selectFieldPrototype
 373  
      */
 374  
     public void setSelectFieldPrototype(Field selectFieldPrototype) {
 375  0
         this.selectFieldPrototype = selectFieldPrototype;
 376  0
     }
 377  
 
 378  
     /**
 379  
      * Final <code>List</code> of Groups to render for the collection
 380  
      *
 381  
      * @return List<Group> collection groups
 382  
      */
 383  
     public List<Group> getStackedGroups() {
 384  0
         return this.stackedGroups;
 385  
     }
 386  
 
 387  
     /**
 388  
      * Setter for the collection groups
 389  
      *
 390  
      * @param stackedGroups
 391  
      */
 392  
     public void setStackedGroups(List<Group> stackedGroups) {
 393  0
         this.stackedGroups = stackedGroups;
 394  0
     }
 395  
 
 396  
 }