Coverage Report - org.kuali.rice.krad.uif.container.CollectionGroup
 
Classes in this File Line Coverage Branch Coverage Complexity
CollectionGroup
0%
0/123
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 java.util.ArrayList;
 14  
 import java.util.List;
 15  
 
 16  
 import org.apache.commons.lang.StringUtils;
 17  
 import org.kuali.rice.core.api.mo.common.active.Inactivatable;
 18  
 import org.kuali.rice.krad.uif.UifConstants;
 19  
 import org.kuali.rice.krad.uif.UifParameters;
 20  
 import org.kuali.rice.krad.uif.core.ActiveCollectionFilter;
 21  
 import org.kuali.rice.krad.uif.core.BindingInfo;
 22  
 import org.kuali.rice.krad.uif.core.CollectionFilter;
 23  
 import org.kuali.rice.krad.uif.core.Component;
 24  
 import org.kuali.rice.krad.uif.core.DataBinding;
 25  
 import org.kuali.rice.krad.uif.field.ActionField;
 26  
 import org.kuali.rice.krad.uif.field.AttributeField;
 27  
 import org.kuali.rice.krad.uif.field.Field;
 28  
 import org.kuali.rice.krad.uif.field.LabelField;
 29  
 import org.kuali.rice.krad.uif.util.ComponentUtils;
 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) {
 183  0
         super.performApplyModel(view, model);
 184  
 
 185  0
         pushCollectionGroupToReference();
 186  
 
 187  0
         performCollectionFiltering(view, model);
 188  
 
 189  0
         getCollectionGroupBuilder().build(view, model, this);
 190  0
     }
 191  
 
 192  
     /**
 193  
      * Sets a reference in the context map for all nested components to the collection group
 194  
      * instance, and sets name as parameter for an action fields in the group
 195  
      */
 196  
     protected void pushCollectionGroupToReference() {
 197  0
         ComponentUtils
 198  
                 .pushObjectToContext(this.getNestedComponents(), UifConstants.ContextVariableNames.COLLECTION_GROUP,
 199  
                         this);
 200  
 
 201  0
         List<ActionField> actionFields =
 202  
                 ComponentUtils.getComponentsOfTypeDeep(this.getNestedComponents(), ActionField.class);
 203  0
         for (ActionField actionField : actionFields) {
 204  0
             actionField.addActionParameter(UifParameters.SELLECTED_COLLECTION_PATH,
 205  
                     this.getBindingInfo().getBindingPath());
 206  
         }
 207  0
     }
 208  
 
 209  
     /**
 210  
      * Performs any filtering necessary on the collection before building the collection fields
 211  
      *
 212  
      * <p>
 213  
      * If showInactive is set to false and the collection line type implements <code>Inactivatable</code>,
 214  
      * invokes the active collection filer
 215  
      * </p>
 216  
      *
 217  
      * @param model - object containing the views data, from which the collection will be pulled
 218  
      */
 219  
     protected void performCollectionFiltering(View view, Object model) {
 220  0
         if (Inactivatable.class.isAssignableFrom(this.collectionObjectClass) && !showInactive) {
 221  0
             this.activeCollectionFilter.filter(view, model, this);
 222  
         }
 223  0
     }
 224  
 
 225  
     /**
 226  
      * New collection lines are handled in the framework by maintaining a map on
 227  
      * the form. The map contains as a key the collection name, and as value an
 228  
      * instance of the collection type. An entry is created here for the
 229  
      * collection represented by the <code>CollectionGroup</code> if an instance
 230  
      * is not available (clearExistingLine will force a new instance). The given
 231  
      * model must be a subclass of <code>UifFormBase</code> in order to find the
 232  
      * Map.
 233  
      * 
 234  
      * @param model
 235  
      *            - Model instance that contains the new collection lines Map
 236  
      * @param clearExistingLine
 237  
      *            - boolean that indicates whether the line should be set to a
 238  
      *            new instance if it already exists
 239  
      */
 240  
     public void initializeNewCollectionLine(View view, Object model, CollectionGroup collectionGroup,
 241  
             boolean clearExistingLine) {
 242  0
         getCollectionGroupBuilder().initializeNewCollectionLine(view, model, collectionGroup, clearExistingLine);
 243  0
     }
 244  
 
 245  
     /**
 246  
      * @see org.kuali.rice.krad.uif.container.ContainerBase#getNestedComponents()
 247  
      */
 248  
     @Override
 249  
     public List<Component> getNestedComponents() {
 250  0
         List<Component> components = super.getNestedComponents();
 251  
 
 252  0
         components.add(addLineLabelField);
 253  0
         components.addAll(actionFields);
 254  0
         components.addAll(addLineActionFields);
 255  
 
 256  
         // remove the containers items because we don't want them as children
 257  
         // (they will become children of the layout manager as the rows are
 258  
         // created)
 259  
         // TODO: is this necessary?
 260  0
         for (Component item : getItems()) {
 261  0
             if (components.contains(item)) {
 262  0
                 components.remove(item);
 263  
             }
 264  
         }
 265  
 
 266  0
         return components;
 267  
     }
 268  
 
 269  
     /**
 270  
      * Object class the collection maintains. Used to get dictionary information
 271  
      * in addition to creating new instances for the collection when necessary
 272  
      * 
 273  
      * @return Class<?> collection object class
 274  
      */
 275  
     public Class<?> getCollectionObjectClass() {
 276  0
         return this.collectionObjectClass;
 277  
     }
 278  
 
 279  
     /**
 280  
      * Setter for the collection object class
 281  
      * 
 282  
      * @param collectionObjectClass
 283  
      */
 284  
     public void setCollectionObjectClass(Class<?> collectionObjectClass) {
 285  0
         this.collectionObjectClass = collectionObjectClass;
 286  0
     }
 287  
 
 288  
     /**
 289  
      * @see org.kuali.rice.krad.uif.core.DataBinding#getPropertyName()
 290  
      */
 291  
     public String getPropertyName() {
 292  0
         return this.propertyName;
 293  
     }
 294  
 
 295  
     /**
 296  
      * Setter for the collections property name
 297  
      * 
 298  
      * @param propertyName
 299  
      */
 300  
     public void setPropertyName(String propertyName) {
 301  0
         this.propertyName = propertyName;
 302  0
     }
 303  
 
 304  
     /**
 305  
      * Determines the binding path for the collection. Used to get the
 306  
      * collection value from the model in addition to setting the binding path
 307  
      * for the collection attributes
 308  
      * 
 309  
      * @see org.kuali.rice.krad.uif.core.DataBinding#getBindingInfo()
 310  
      */
 311  
     public BindingInfo getBindingInfo() {
 312  0
         return this.bindingInfo;
 313  
     }
 314  
 
 315  
     /**
 316  
      * Setter for the binding info instance
 317  
      * 
 318  
      * @param bindingInfo
 319  
      */
 320  
     public void setBindingInfo(BindingInfo bindingInfo) {
 321  0
         this.bindingInfo = bindingInfo;
 322  0
     }
 323  
 
 324  
     /**
 325  
      * Action fields that should be rendered for each collection line. Example
 326  
      * line action is the delete action
 327  
      * 
 328  
      * @return List<ActionField> line action fields
 329  
      */
 330  
     public List<ActionField> getActionFields() {
 331  0
         return this.actionFields;
 332  
     }
 333  
 
 334  
     /**
 335  
      * Setter for the line action fields list
 336  
      * 
 337  
      * @param actionFields
 338  
      */
 339  
     public void setActionFields(List<ActionField> actionFields) {
 340  0
         this.actionFields = actionFields;
 341  0
     }
 342  
 
 343  
     /**
 344  
      * Indicates whether the action column for the collection should be rendered
 345  
      * 
 346  
      * @return boolean true if the actions should be rendered, false if not
 347  
      * @see #getActionFields()
 348  
      */
 349  
     public boolean isRenderLineActions() {
 350  0
         return this.renderLineActions;
 351  
     }
 352  
 
 353  
     /**
 354  
      * Setter for the render line actions indicator
 355  
      * 
 356  
      * @param renderLineActions
 357  
      */
 358  
     public void setRenderLineActions(boolean renderLineActions) {
 359  0
         this.renderLineActions = renderLineActions;
 360  0
     }
 361  
 
 362  
     /**
 363  
      * Indicates whether an add line should be rendered for the collection
 364  
      * 
 365  
      * @return boolean true if add line should be rendered, false if it should
 366  
      *         not be
 367  
      */
 368  
     public boolean isRenderAddLine() {
 369  0
         return this.renderAddLine;
 370  
     }
 371  
 
 372  
     /**
 373  
      * Setter for the render add line indicator
 374  
      * 
 375  
      * @param renderAddLine
 376  
      */
 377  
     public void setRenderAddLine(boolean renderAddLine) {
 378  0
         this.renderAddLine = renderAddLine;
 379  0
     }
 380  
 
 381  
     /**
 382  
      * Convenience getter for the add line label field text. The text is used to
 383  
      * label the add line when rendered and its placement depends on the
 384  
      * <code>LayoutManager</code>.
 385  
      * <p>
 386  
      * For the <code>TableLayoutManager</code> the label appears in the sequence
 387  
      * column to the left of the add line fields. For the
 388  
      * <code>StackedLayoutManager</code> the label is placed into the group
 389  
      * header for the line.
 390  
      * </p>
 391  
      * 
 392  
      * @return String add line label
 393  
      */
 394  
     public String getAddLineLabel() {
 395  0
         if (getAddLineLabelField() != null) {
 396  0
             return getAddLineLabelField().getLabelText();
 397  
         }
 398  
 
 399  0
         return null;
 400  
     }
 401  
 
 402  
     /**
 403  
      * Setter for the add line label text
 404  
      * 
 405  
      * @param addLineLabel
 406  
      */
 407  
     public void setAddLineLabel(String addLineLabel) {
 408  0
         if (getAddLineLabelField() != null) {
 409  0
             getAddLineLabelField().setLabelText(addLineLabel);
 410  
         }
 411  0
     }
 412  
 
 413  
     /**
 414  
      * <code>LabelField</code> instance for the add line label
 415  
      * 
 416  
      * @return LabelField add line label field
 417  
      * @see #getAddLineLabel()
 418  
      */
 419  
     public LabelField getAddLineLabelField() {
 420  0
         return this.addLineLabelField;
 421  
     }
 422  
 
 423  
     /**
 424  
      * Setter for the <code>LabelField</code> instance for the add line label
 425  
      * 
 426  
      * @param addLineLabelField
 427  
      * @see #getAddLineLabel()
 428  
      */
 429  
     public void setAddLineLabelField(LabelField addLineLabelField) {
 430  0
         this.addLineLabelField = addLineLabelField;
 431  0
     }
 432  
 
 433  
     /**
 434  
      * Name of the property that contains an instance for the add line. If set
 435  
      * this is used with the binding info to create the path to the add line.
 436  
      * Can be left blank in which case the framework will manage the add line
 437  
      * instance in a generic map.
 438  
      * 
 439  
      * @return String add line property name
 440  
      */
 441  
     public String getAddLinePropertyName() {
 442  0
         return this.addLinePropertyName;
 443  
     }
 444  
 
 445  
     /**
 446  
      * Setter for the add line property name
 447  
      * 
 448  
      * @param addLinePropertyName
 449  
      */
 450  
     public void setAddLinePropertyName(String addLinePropertyName) {
 451  0
         this.addLinePropertyName = addLinePropertyName;
 452  0
     }
 453  
 
 454  
     /**
 455  
      * <code>BindingInfo</code> instance for the add line property used to
 456  
      * determine the full binding path. If add line name given
 457  
      * {@link #getAddLineLabel()} then it is set as the binding name on the
 458  
      * binding info. Add line label and binding info are not required, in which
 459  
      * case the framework will manage the new add line instances through a
 460  
      * generic map (model must extend UifFormBase)
 461  
      * 
 462  
      * @return BindingInfo add line binding info
 463  
      */
 464  
     public BindingInfo getAddLineBindingInfo() {
 465  0
         return this.addLineBindingInfo;
 466  
     }
 467  
 
 468  
     /**
 469  
      * Setter for the add line binding info
 470  
      * 
 471  
      * @param addLineBindingInfo
 472  
      */
 473  
     public void setAddLineBindingInfo(BindingInfo addLineBindingInfo) {
 474  0
         this.addLineBindingInfo = addLineBindingInfo;
 475  0
     }
 476  
 
 477  
     /**
 478  
      * List of <code>Field</code> instances that should be rendered for the
 479  
      * collection add line (if enabled). If not set, the default group's items
 480  
      * list will be used
 481  
      * 
 482  
      * @return List<? extends Field> add line field list
 483  
      */
 484  
     public List<? extends Field> getAddLineFields() {
 485  0
         return this.addLineFields;
 486  
     }
 487  
 
 488  
     /**
 489  
      * Setter for the add line field list
 490  
      * 
 491  
      * @param addLineFields
 492  
      */
 493  
     public void setAddLineFields(List<? extends Field> addLineFields) {
 494  0
         this.addLineFields = addLineFields;
 495  0
     }
 496  
 
 497  
     /**
 498  
      * Action fields that should be rendered for the add line. This is generally
 499  
      * the add action (button) but can be configured to contain additional
 500  
      * actions
 501  
      * 
 502  
      * @return List<ActionField> add line action fields
 503  
      */
 504  
     public List<ActionField> getAddLineActionFields() {
 505  0
         return this.addLineActionFields;
 506  
     }
 507  
 
 508  
     /**
 509  
      * Setter for the add line action fields
 510  
      * 
 511  
      * @param addLineActionFields
 512  
      */
 513  
     public void setAddLineActionFields(List<ActionField> addLineActionFields) {
 514  0
         this.addLineActionFields = addLineActionFields;
 515  0
     }
 516  
 
 517  
     /**
 518  
      * Indicates whether inactive collections lines should be displayed
 519  
      *
 520  
      * <p>
 521  
      * Setting only applies when the collection line type implements the
 522  
      * <code>Inactivatable</code> interface. If true and showInactive is
 523  
      * set to false, the collection will be filtered to remove any items
 524  
      * whose active status returns false
 525  
      * </p>
 526  
      *
 527  
      * @return boolean true to show inactive records, false to not render inactive records
 528  
      */
 529  
     public boolean isShowInactive() {
 530  0
         return showInactive;
 531  
     }
 532  
 
 533  
     /**
 534  
      * Setter for the show inactive indicator
 535  
      *
 536  
      * @param boolean show inactive
 537  
      */
 538  
     public void setShowInactive(boolean showInactive) {
 539  0
         this.showInactive = showInactive;
 540  0
     }
 541  
 
 542  
     /**
 543  
      * Collection filter instance for filtering the collection data when the
 544  
      * showInactive flag is set to false
 545  
      *
 546  
      * @return CollectionFilter
 547  
      */
 548  
     public CollectionFilter getActiveCollectionFilter() {
 549  0
         return activeCollectionFilter;
 550  
     }
 551  
 
 552  
     /**
 553  
      * Setter for the collection filter to use for filter inactive records from the
 554  
      * collection
 555  
      *
 556  
      * @param activeCollectionFilter - CollectionFilter instance
 557  
      */
 558  
     public void setActiveCollectionFilter(CollectionFilter activeCollectionFilter) {
 559  0
         this.activeCollectionFilter = activeCollectionFilter;
 560  0
     }
 561  
 
 562  
     /**
 563  
      * List of <code>CollectionGroup</code> instances that are sub-collections
 564  
      * of the collection represented by this collection group
 565  
      * 
 566  
      * @return List<CollectionGroup> sub collections
 567  
      */
 568  
     public List<CollectionGroup> getSubCollections() {
 569  0
         return this.subCollections;
 570  
     }
 571  
 
 572  
     /**
 573  
      * Setter for the sub collection list
 574  
      * 
 575  
      * @param subCollections
 576  
      */
 577  
     public void setSubCollections(List<CollectionGroup> subCollections) {
 578  0
         this.subCollections = subCollections;
 579  0
     }
 580  
 
 581  
     /**
 582  
      * <code>CollectionGroupBuilder</code> instance that will build the
 583  
      * components dynamically for the collection instance
 584  
      * 
 585  
      * @return CollectionGroupBuilder instance
 586  
      */
 587  
     public CollectionGroupBuilder getCollectionGroupBuilder() {
 588  0
         if (this.collectionGroupBuilder == null) {
 589  0
             this.collectionGroupBuilder = new CollectionGroupBuilder();
 590  
         }
 591  0
         return this.collectionGroupBuilder;
 592  
     }
 593  
 
 594  
     /**
 595  
      * Setter for the collection group building instance
 596  
      * 
 597  
      * @param collectionGroupBuilder
 598  
      */
 599  
     public void setCollectionGroupBuilder(CollectionGroupBuilder collectionGroupBuilder) {
 600  0
         this.collectionGroupBuilder = collectionGroupBuilder;
 601  0
     }
 602  
 
 603  
     /**
 604  
      * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
 605  
      */
 606  
     @SuppressWarnings("unchecked")
 607  
     @Override
 608  
     public List<? extends Field> getItems() {
 609  0
         return (List<? extends Field>) super.getItems();
 610  
     }
 611  
 
 612  
 }