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