Coverage Report - org.kuali.rice.krad.uif.container.CollectionGroup
 
Classes in this File Line Coverage Branch Coverage Complexity
CollectionGroup
0%
0/125
0%
0/48
1.625
 
 1  
 /*
 2  
  * Copyright 2007 The Kuali Foundation Licensed under the Educational Community
 3  
  * License, Version 1.0 (the "License"); you may not use this file except in
 4  
  * compliance with the License. You may obtain a copy of the License at
 5  
  * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
 6  
  * or agreed to in writing, software distributed under the License is
 7  
  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 8  
  * KIND, either express or implied. See the License for the specific language
 9  
  * governing permissions and limitations under the License.
 10  
  */
 11  
 package org.kuali.rice.krad.uif.container;
 12  
 
 13  
 import org.apache.commons.lang.StringUtils;
 14  
 import org.kuali.rice.core.api.mo.common.active.ImmutableInactivatable;
 15  
 import org.kuali.rice.krad.uif.UifConstants;
 16  
 import org.kuali.rice.krad.uif.UifParameters;
 17  
 import org.kuali.rice.krad.uif.core.ActiveCollectionFilter;
 18  
 import org.kuali.rice.krad.uif.core.BindingInfo;
 19  
 import org.kuali.rice.krad.uif.core.CollectionFilter;
 20  
 import org.kuali.rice.krad.uif.core.Component;
 21  
 import org.kuali.rice.krad.uif.core.DataBinding;
 22  
 import org.kuali.rice.krad.uif.field.ActionField;
 23  
 import org.kuali.rice.krad.uif.field.AttributeField;
 24  
 import org.kuali.rice.krad.uif.field.Field;
 25  
 import org.kuali.rice.krad.uif.field.LabelField;
 26  
 import org.kuali.rice.krad.uif.util.ComponentUtils;
 27  
 
 28  
 import java.util.ArrayList;
 29  
 import java.util.List;
 30  
 
 31  
 /**
 32  
  * Group that holds a collection of objects and configuration for presenting the
 33  
  * collection in the UI. Supports functionality such as add line, line actions,
 34  
  * and nested collections.
 35  
  * 
 36  
  * <p>
 37  
  * Note the standard header/footer can be used to give a header to the
 38  
  * collection as a whole, or to provide actions that apply to the entire
 39  
  * collection
 40  
  * </p>
 41  
  * 
 42  
  * <p>
 43  
  * For binding purposes the binding path of each row field is indexed. The name
 44  
  * property inherited from <code>ComponentBase</code> is used as the collection
 45  
  * name. The collectionObjectClass property is used to lookup attributes from
 46  
  * the data dictionary.
 47  
  * </p>
 48  
  * 
 49  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 50  
  */
 51  
 public class CollectionGroup extends Group implements DataBinding {
 52  
     private static final long serialVersionUID = -6496712566071542452L;
 53  
 
 54  
     private Class<?> collectionObjectClass;
 55  
 
 56  
     private String propertyName;
 57  
     private BindingInfo bindingInfo;
 58  
 
 59  
     private boolean renderAddLine;
 60  
     private String addLinePropertyName;
 61  
     private BindingInfo addLineBindingInfo;
 62  
     private LabelField addLineLabelField;
 63  
     private List<? extends Field> addLineFields;
 64  
     private List<ActionField> addLineActionFields;
 65  
 
 66  
     private boolean renderLineActions;
 67  
     private List<ActionField> actionFields;
 68  
 
 69  
     private boolean showInactive;
 70  
     private CollectionFilter activeCollectionFilter;
 71  
 
 72  
     private List<CollectionGroup> subCollections;
 73  
 
 74  
     private CollectionGroupBuilder collectionGroupBuilder;
 75  
 
 76  0
     public CollectionGroup() {
 77  0
         renderAddLine = true;
 78  0
         renderLineActions = true;
 79  0
         showInactive = false;
 80  
 
 81  0
         actionFields = new ArrayList<ActionField>();
 82  0
         addLineFields = new ArrayList<Field>();
 83  0
         addLineActionFields = new ArrayList<ActionField>();
 84  0
         subCollections = new ArrayList<CollectionGroup>();
 85  0
     }
 86  
 
 87  
     /**
 88  
      * The following actions are performed:
 89  
      *
 90  
      * <ul>
 91  
      * <li>Set fieldBindModelPath to the collection model path (since the fields
 92  
      * have to belong to the same model as the collection)</li>
 93  
      * <li>Set defaults for binding</li>
 94  
      * <li>Default add line field list to groups items list</li>
 95  
      * <li>Sets default active collection filter if not set</li>
 96  
      * <li>Sets the dictionary entry (if blank) on each of the items to the
 97  
      * collection class</li>
 98  
      * </ul>
 99  
      *
 100  
      * @see org.kuali.rice.krad.uif.core.ComponentBase#performInitialization(org.kuali.rice.krad.uif.container.View)
 101  
      */
 102  
     @Override
 103  
     public void performInitialization(View view) {
 104  0
         setFieldBindingObjectPath(getBindingInfo().getBindingObjectPath());
 105  
 
 106  0
         super.performInitialization(view);
 107  
 
 108  0
         if (bindingInfo != null) {
 109  0
             bindingInfo.setDefaults(view, getPropertyName());
 110  
         }
 111  
 
 112  0
         if (addLineBindingInfo != null) {
 113  
             // add line binds to model property
 114  0
             if (StringUtils.isNotBlank(addLinePropertyName)) {
 115  0
                 addLineBindingInfo.setDefaults(view, getPropertyName());
 116  0
                 addLineBindingInfo.setBindingName(addLinePropertyName);
 117  0
                 if (StringUtils.isNotBlank(getFieldBindByNamePrefix())) {
 118  0
                     addLineBindingInfo.setBindByNamePrefix(getFieldBindByNamePrefix());
 119  
                 }
 120  
             }
 121  
         }
 122  
 
 123  0
         for (Component item : getItems()) {
 124  0
             if (item instanceof AttributeField) {
 125  0
                 AttributeField field = (AttributeField) item;
 126  
 
 127  0
                 if (StringUtils.isBlank(field.getDictionaryObjectEntry())) {
 128  0
                     field.setDictionaryObjectEntry(collectionObjectClass.getName());
 129  
                 }
 130  0
             }
 131  
         }
 132  
         
 133  0
         if ((addLineFields == null) || addLineFields.isEmpty()) {
 134  0
             addLineFields = getItems();
 135  
         }
 136  
 
 137  
         // if active collection filter not set use default
 138  0
         if (this.activeCollectionFilter == null) {
 139  0
             activeCollectionFilter = new ActiveCollectionFilter();
 140  
         }
 141  
         
 142  
         // set static collection path on items
 143  0
         String collectionPath = "";
 144  0
         if (StringUtils.isNotBlank(getBindingInfo().getCollectionPath())) {
 145  0
             collectionPath += getBindingInfo().getCollectionPath() + ".";
 146  
         }
 147  0
         if (StringUtils.isNotBlank(getBindingInfo().getBindByNamePrefix())) {
 148  0
             collectionPath += getBindingInfo().getBindByNamePrefix() + ".";
 149  
         }
 150  0
         collectionPath += getBindingInfo().getBindingName();
 151  
         
 152  0
         List<AttributeField> collectionFields = ComponentUtils.getComponentsOfTypeDeep(getItems(), AttributeField.class);
 153  0
         for (AttributeField collectionField : collectionFields) {
 154  0
             collectionField.getBindingInfo().setCollectionPath(collectionPath);
 155  
         }
 156  
         
 157  0
         for (CollectionGroup collectionGroup : getSubCollections()) {
 158  0
             collectionGroup.getBindingInfo().setCollectionPath(collectionPath);
 159  0
             view.getViewHelperService().performComponentInitialization(view, collectionGroup);
 160  
         }
 161  
         
 162  
         // add collection entry to abstract classes
 163  0
         if (!view.getAbstractTypeClasses().containsKey(collectionPath)) {
 164  0
             view.getAbstractTypeClasses().put(collectionPath, getCollectionObjectClass());
 165  
         }
 166  
 
 167  
         // initialize container items and sub-collections (since they are not in
 168  
         // child list)
 169  0
         for (Component item : getItems()) {
 170  0
             view.getViewHelperService().performComponentInitialization(view, item);
 171  
         }
 172  0
     }
 173  
 
 174  
     /**
 175  
      * Calls the configured <code>CollectionGroupBuilder</code> to build the
 176  
      * necessary components based on the collection data
 177  
      * 
 178  
      * @see org.kuali.rice.krad.uif.container.ContainerBase#performApplyModel(org.kuali.rice.krad.uif.container.View,
 179  
      *      java.lang.Object)
 180  
      */
 181  
     @Override
 182  
     public void performApplyModel(View view, Object model, Component parent) {
 183  0
         super.performApplyModel(view, model, parent);
 184  
 
 185  0
         pushCollectionGroupToReference();
 186  
 
 187  0
         performCollectionFiltering(view, model);
 188  
 
 189  0
         getCollectionGroupBuilder().build(view, model, this);
 190  
         
 191  0
         pushCollectionGroupToReference();
 192  0
     }
 193  
 
 194  
     /**
 195  
      * Sets a reference in the context map for all nested components to the collection group
 196  
      * instance, and sets name as parameter for an action fields in the group
 197  
      */
 198  
     protected void pushCollectionGroupToReference() {
 199  0
         List<Component> components = this.getNestedComponents();
 200  
         
 201  0
         ComponentUtils
 202  
                 .pushObjectToContext(components, UifConstants.ContextVariableNames.COLLECTION_GROUP,
 203  
                         this);
 204  
 
 205  0
         List<ActionField> actionFields =
 206  
                 ComponentUtils.getComponentsOfTypeDeep(components, ActionField.class);
 207  0
         for (ActionField actionField : actionFields) {
 208  0
             actionField.addActionParameter(UifParameters.SELLECTED_COLLECTION_PATH,
 209  
                     this.getBindingInfo().getBindingPath());
 210  
         }
 211  0
     }
 212  
 
 213  
     /**
 214  
      * Performs any filtering necessary on the collection before building the collection fields
 215  
      *
 216  
      * <p>
 217  
      * If showInactive is set to false and the collection line type implements <code>Inactivatable</code>,
 218  
      * invokes the active collection filer
 219  
      * </p>
 220  
      *
 221  
      * @param model - object containing the views data, from which the collection will be pulled
 222  
      */
 223  
     protected void performCollectionFiltering(View view, Object model) {
 224  0
         if (ImmutableInactivatable.class.isAssignableFrom(this.collectionObjectClass) && !showInactive) {
 225  0
             this.activeCollectionFilter.filter(view, model, this);
 226  
         }
 227  0
     }
 228  
 
 229  
     /**
 230  
      * New collection lines are handled in the framework by maintaining a map on
 231  
      * the form. The map contains as a key the collection name, and as value an
 232  
      * instance of the collection type. An entry is created here for the
 233  
      * collection represented by the <code>CollectionGroup</code> if an instance
 234  
      * is not available (clearExistingLine will force a new instance). The given
 235  
      * model must be a subclass of <code>UifFormBase</code> in order to find the
 236  
      * Map.
 237  
      * 
 238  
      * @param model
 239  
      *            - Model instance that contains the new collection lines Map
 240  
      * @param clearExistingLine
 241  
      *            - boolean that indicates whether the line should be set to a
 242  
      *            new instance if it already exists
 243  
      */
 244  
     public void initializeNewCollectionLine(View view, Object model, CollectionGroup collectionGroup,
 245  
             boolean clearExistingLine) {
 246  0
         getCollectionGroupBuilder().initializeNewCollectionLine(view, model, collectionGroup, clearExistingLine);
 247  0
     }
 248  
 
 249  
     /**
 250  
      * @see org.kuali.rice.krad.uif.container.ContainerBase#getNestedComponents()
 251  
      */
 252  
     @Override
 253  
     public List<Component> getNestedComponents() {
 254  0
         List<Component> components = super.getNestedComponents();
 255  
 
 256  0
         components.add(addLineLabelField);
 257  0
         components.addAll(actionFields);
 258  0
         components.addAll(addLineActionFields);
 259  
 
 260  
         // remove the containers items because we don't want them as children
 261  
         // (they will become children of the layout manager as the rows are
 262  
         // created)
 263  
         // TODO: is this necessary?
 264  0
         for (Component item : getItems()) {
 265  0
             if (components.contains(item)) {
 266  0
                 components.remove(item);
 267  
             }
 268  
         }
 269  
 
 270  0
         return components;
 271  
     }
 272  
 
 273  
     /**
 274  
      * Object class the collection maintains. Used to get dictionary information
 275  
      * in addition to creating new instances for the collection when necessary
 276  
      * 
 277  
      * @return Class<?> collection object class
 278  
      */
 279  
     public Class<?> getCollectionObjectClass() {
 280  0
         return this.collectionObjectClass;
 281  
     }
 282  
 
 283  
     /**
 284  
      * Setter for the collection object class
 285  
      * 
 286  
      * @param collectionObjectClass
 287  
      */
 288  
     public void setCollectionObjectClass(Class<?> collectionObjectClass) {
 289  0
         this.collectionObjectClass = collectionObjectClass;
 290  0
     }
 291  
 
 292  
     /**
 293  
      * @see org.kuali.rice.krad.uif.core.DataBinding#getPropertyName()
 294  
      */
 295  
     public String getPropertyName() {
 296  0
         return this.propertyName;
 297  
     }
 298  
 
 299  
     /**
 300  
      * Setter for the collections property name
 301  
      * 
 302  
      * @param propertyName
 303  
      */
 304  
     public void setPropertyName(String propertyName) {
 305  0
         this.propertyName = propertyName;
 306  0
     }
 307  
 
 308  
     /**
 309  
      * Determines the binding path for the collection. Used to get the
 310  
      * collection value from the model in addition to setting the binding path
 311  
      * for the collection attributes
 312  
      * 
 313  
      * @see org.kuali.rice.krad.uif.core.DataBinding#getBindingInfo()
 314  
      */
 315  
     public BindingInfo getBindingInfo() {
 316  0
         return this.bindingInfo;
 317  
     }
 318  
 
 319  
     /**
 320  
      * Setter for the binding info instance
 321  
      * 
 322  
      * @param bindingInfo
 323  
      */
 324  
     public void setBindingInfo(BindingInfo bindingInfo) {
 325  0
         this.bindingInfo = bindingInfo;
 326  0
     }
 327  
 
 328  
     /**
 329  
      * Action fields that should be rendered for each collection line. Example
 330  
      * line action is the delete action
 331  
      * 
 332  
      * @return List<ActionField> line action fields
 333  
      */
 334  
     public List<ActionField> getActionFields() {
 335  0
         return this.actionFields;
 336  
     }
 337  
 
 338  
     /**
 339  
      * Setter for the line action fields list
 340  
      * 
 341  
      * @param actionFields
 342  
      */
 343  
     public void setActionFields(List<ActionField> actionFields) {
 344  0
         this.actionFields = actionFields;
 345  0
     }
 346  
 
 347  
     /**
 348  
      * Indicates whether the action column for the collection should be rendered
 349  
      * 
 350  
      * @return boolean true if the actions should be rendered, false if not
 351  
      * @see #getActionFields()
 352  
      */
 353  
     public boolean isRenderLineActions() {
 354  0
         return this.renderLineActions;
 355  
     }
 356  
 
 357  
     /**
 358  
      * Setter for the render line actions indicator
 359  
      * 
 360  
      * @param renderLineActions
 361  
      */
 362  
     public void setRenderLineActions(boolean renderLineActions) {
 363  0
         this.renderLineActions = renderLineActions;
 364  0
     }
 365  
 
 366  
     /**
 367  
      * Indicates whether an add line should be rendered for the collection
 368  
      * 
 369  
      * @return boolean true if add line should be rendered, false if it should
 370  
      *         not be
 371  
      */
 372  
     public boolean isRenderAddLine() {
 373  0
         return this.renderAddLine;
 374  
     }
 375  
 
 376  
     /**
 377  
      * Setter for the render add line indicator
 378  
      * 
 379  
      * @param renderAddLine
 380  
      */
 381  
     public void setRenderAddLine(boolean renderAddLine) {
 382  0
         this.renderAddLine = renderAddLine;
 383  0
     }
 384  
 
 385  
     /**
 386  
      * Convenience getter for the add line label field text. The text is used to
 387  
      * label the add line when rendered and its placement depends on the
 388  
      * <code>LayoutManager</code>.
 389  
      * <p>
 390  
      * For the <code>TableLayoutManager</code> the label appears in the sequence
 391  
      * column to the left of the add line fields. For the
 392  
      * <code>StackedLayoutManager</code> the label is placed into the group
 393  
      * header for the line.
 394  
      * </p>
 395  
      * 
 396  
      * @return String add line label
 397  
      */
 398  
     public String getAddLineLabel() {
 399  0
         if (getAddLineLabelField() != null) {
 400  0
             return getAddLineLabelField().getLabelText();
 401  
         }
 402  
 
 403  0
         return null;
 404  
     }
 405  
 
 406  
     /**
 407  
      * Setter for the add line label text
 408  
      * 
 409  
      * @param addLineLabel
 410  
      */
 411  
     public void setAddLineLabel(String addLineLabel) {
 412  0
         if (getAddLineLabelField() != null) {
 413  0
             getAddLineLabelField().setLabelText(addLineLabel);
 414  
         }
 415  0
     }
 416  
 
 417  
     /**
 418  
      * <code>LabelField</code> instance for the add line label
 419  
      * 
 420  
      * @return LabelField add line label field
 421  
      * @see #getAddLineLabel()
 422  
      */
 423  
     public LabelField getAddLineLabelField() {
 424  0
         return this.addLineLabelField;
 425  
     }
 426  
 
 427  
     /**
 428  
      * Setter for the <code>LabelField</code> instance for the add line label
 429  
      * 
 430  
      * @param addLineLabelField
 431  
      * @see #getAddLineLabel()
 432  
      */
 433  
     public void setAddLineLabelField(LabelField addLineLabelField) {
 434  0
         this.addLineLabelField = addLineLabelField;
 435  0
     }
 436  
 
 437  
     /**
 438  
      * Name of the property that contains an instance for the add line. If set
 439  
      * this is used with the binding info to create the path to the add line.
 440  
      * Can be left blank in which case the framework will manage the add line
 441  
      * instance in a generic map.
 442  
      * 
 443  
      * @return String add line property name
 444  
      */
 445  
     public String getAddLinePropertyName() {
 446  0
         return this.addLinePropertyName;
 447  
     }
 448  
 
 449  
     /**
 450  
      * Setter for the add line property name
 451  
      * 
 452  
      * @param addLinePropertyName
 453  
      */
 454  
     public void setAddLinePropertyName(String addLinePropertyName) {
 455  0
         this.addLinePropertyName = addLinePropertyName;
 456  0
     }
 457  
 
 458  
     /**
 459  
      * <code>BindingInfo</code> instance for the add line property used to
 460  
      * determine the full binding path. If add line name given
 461  
      * {@link #getAddLineLabel()} then it is set as the binding name on the
 462  
      * binding info. Add line label and binding info are not required, in which
 463  
      * case the framework will manage the new add line instances through a
 464  
      * generic map (model must extend UifFormBase)
 465  
      * 
 466  
      * @return BindingInfo add line binding info
 467  
      */
 468  
     public BindingInfo getAddLineBindingInfo() {
 469  0
         return this.addLineBindingInfo;
 470  
     }
 471  
 
 472  
     /**
 473  
      * Setter for the add line binding info
 474  
      * 
 475  
      * @param addLineBindingInfo
 476  
      */
 477  
     public void setAddLineBindingInfo(BindingInfo addLineBindingInfo) {
 478  0
         this.addLineBindingInfo = addLineBindingInfo;
 479  0
     }
 480  
 
 481  
     /**
 482  
      * List of <code>Field</code> instances that should be rendered for the
 483  
      * collection add line (if enabled). If not set, the default group's items
 484  
      * list will be used
 485  
      * 
 486  
      * @return List<? extends Field> add line field list
 487  
      */
 488  
     public List<? extends Field> getAddLineFields() {
 489  0
         return this.addLineFields;
 490  
     }
 491  
 
 492  
     /**
 493  
      * Setter for the add line field list
 494  
      * 
 495  
      * @param addLineFields
 496  
      */
 497  
     public void setAddLineFields(List<? extends Field> addLineFields) {
 498  0
         this.addLineFields = addLineFields;
 499  0
     }
 500  
 
 501  
     /**
 502  
      * Action fields that should be rendered for the add line. This is generally
 503  
      * the add action (button) but can be configured to contain additional
 504  
      * actions
 505  
      * 
 506  
      * @return List<ActionField> add line action fields
 507  
      */
 508  
     public List<ActionField> getAddLineActionFields() {
 509  0
         return this.addLineActionFields;
 510  
     }
 511  
 
 512  
     /**
 513  
      * Setter for the add line action fields
 514  
      * 
 515  
      * @param addLineActionFields
 516  
      */
 517  
     public void setAddLineActionFields(List<ActionField> addLineActionFields) {
 518  0
         this.addLineActionFields = addLineActionFields;
 519  0
     }
 520  
 
 521  
     /**
 522  
      * Indicates whether inactive collections lines should be displayed
 523  
      *
 524  
      * <p>
 525  
      * Setting only applies when the collection line type implements the
 526  
      * <code>Inactivatable</code> interface. If true and showInactive is
 527  
      * set to false, the collection will be filtered to remove any items
 528  
      * whose active status returns false
 529  
      * </p>
 530  
      *
 531  
      * @return boolean true to show inactive records, false to not render inactive records
 532  
      */
 533  
     public boolean isShowInactive() {
 534  0
         return showInactive;
 535  
     }
 536  
 
 537  
     /**
 538  
      * Setter for the show inactive indicator
 539  
      *
 540  
      * @param boolean show inactive
 541  
      */
 542  
     public void setShowInactive(boolean showInactive) {
 543  0
         this.showInactive = showInactive;
 544  0
     }
 545  
 
 546  
     /**
 547  
      * Collection filter instance for filtering the collection data when the
 548  
      * showInactive flag is set to false
 549  
      *
 550  
      * @return CollectionFilter
 551  
      */
 552  
     public CollectionFilter getActiveCollectionFilter() {
 553  0
         return activeCollectionFilter;
 554  
     }
 555  
 
 556  
     /**
 557  
      * Setter for the collection filter to use for filter inactive records from the
 558  
      * collection
 559  
      *
 560  
      * @param activeCollectionFilter - CollectionFilter instance
 561  
      */
 562  
     public void setActiveCollectionFilter(CollectionFilter activeCollectionFilter) {
 563  0
         this.activeCollectionFilter = activeCollectionFilter;
 564  0
     }
 565  
 
 566  
     /**
 567  
      * List of <code>CollectionGroup</code> instances that are sub-collections
 568  
      * of the collection represented by this collection group
 569  
      * 
 570  
      * @return List<CollectionGroup> sub collections
 571  
      */
 572  
     public List<CollectionGroup> getSubCollections() {
 573  0
         return this.subCollections;
 574  
     }
 575  
 
 576  
     /**
 577  
      * Setter for the sub collection list
 578  
      * 
 579  
      * @param subCollections
 580  
      */
 581  
     public void setSubCollections(List<CollectionGroup> subCollections) {
 582  0
         this.subCollections = subCollections;
 583  0
     }
 584  
 
 585  
     /**
 586  
      * <code>CollectionGroupBuilder</code> instance that will build the
 587  
      * components dynamically for the collection instance
 588  
      * 
 589  
      * @return CollectionGroupBuilder instance
 590  
      */
 591  
     public CollectionGroupBuilder getCollectionGroupBuilder() {
 592  0
         if (this.collectionGroupBuilder == null) {
 593  0
             this.collectionGroupBuilder = new CollectionGroupBuilder();
 594  
         }
 595  0
         return this.collectionGroupBuilder;
 596  
     }
 597  
 
 598  
     /**
 599  
      * Setter for the collection group building instance
 600  
      * 
 601  
      * @param collectionGroupBuilder
 602  
      */
 603  
     public void setCollectionGroupBuilder(CollectionGroupBuilder collectionGroupBuilder) {
 604  0
         this.collectionGroupBuilder = collectionGroupBuilder;
 605  0
     }
 606  
 
 607  
     /**
 608  
      * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
 609  
      */
 610  
     @SuppressWarnings("unchecked")
 611  
     @Override
 612  
     public List<? extends Field> getItems() {
 613  0
         return (List<? extends Field>) super.getItems();
 614  
     }
 615  
 
 616  
 }