Coverage Report - org.kuali.rice.krad.uif.modifier.CompareFieldCreateModifier
 
Classes in this File Line Coverage Branch Coverage Complexity
CompareFieldCreateModifier
0%
0/81
0%
0/44
2.769
 
 1  
 /*
 2  
  * Copyright 2011 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.modifier;
 12  
 
 13  
 import org.apache.commons.lang.StringUtils;
 14  
 import org.apache.log4j.Logger;
 15  
 import org.kuali.rice.krad.uif.UifPropertyPaths;
 16  
 import org.kuali.rice.krad.uif.container.Group;
 17  
 import org.kuali.rice.krad.uif.container.View;
 18  
 import org.kuali.rice.krad.uif.core.Component;
 19  
 import org.kuali.rice.krad.uif.field.AttributeField;
 20  
 import org.kuali.rice.krad.uif.field.HeaderField;
 21  
 import org.kuali.rice.krad.uif.util.ComponentUtils;
 22  
 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
 23  
 
 24  
 import java.util.ArrayList;
 25  
 import java.util.HashSet;
 26  
 import java.util.List;
 27  
 import java.util.Set;
 28  
 
 29  
 /**
 30  
  * Generates <code>Field</code> instances to produce a comparison view among
 31  
  * objects of the same type
 32  
  *
 33  
  * <p>
 34  
  * Modifier is initialized with a List of <code>ComparableInfo</code> instances.
 35  
  * For each comparable info, a copy of the configured group field is made and
 36  
  * adjusted to the binding object path for the comparable. The comparison fields
 37  
  * are ordered based on the configured order property of the comparable. In
 38  
  * addition, a <code>HeaderField<code> can be generated to label each group
 39  
  * of comparison fields.
 40  
  * </p>
 41  
  *
 42  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 43  
  */
 44  
 public class CompareFieldCreateModifier extends ComponentModifierBase {
 45  0
     private static final Logger LOG = Logger.getLogger(CompareFieldCreateModifier.class);
 46  
 
 47  
     private static final long serialVersionUID = -6285531580512330188L;
 48  
 
 49  
     private int defaultOrderSequence;
 50  
     private boolean generateCompareHeaders;
 51  
 
 52  
     private HeaderField headerFieldPrototype;
 53  
     private List<ComparableInfo> comparables;
 54  
 
 55  0
     public CompareFieldCreateModifier() {
 56  0
         defaultOrderSequence = 1;
 57  0
         generateCompareHeaders = true;
 58  
 
 59  0
         comparables = new ArrayList<ComparableInfo>();
 60  0
     }
 61  
 
 62  
     /**
 63  
      * Generates the comparison fields
 64  
      *
 65  
      * <p>
 66  
      * First the configured List of <code>ComparableInfo</code> instances are
 67  
      * sorted based on their order property. Then if generateCompareHeaders is
 68  
      * set to true, a <code>HeaderField</code> is created for each comparable
 69  
      * using the headerFieldPrototype and the headerText given by the
 70  
      * comparable. Finally for each field configured on the <code>Group</code>,
 71  
      * a corresponding comparison field is generated for each comparable and
 72  
      * adjusted to the binding object path given by the comparable in addition
 73  
      * to suffixing the id and setting the readOnly property
 74  
      * </p>
 75  
      *
 76  
      * @see org.kuali.rice.krad.uif.modifier.ComponentModifier#performModification(org.kuali.rice.krad.uif.container.View,
 77  
      *      java.lang.Object, org.kuali.rice.krad.uif.core.Component)
 78  
      */
 79  
     @SuppressWarnings("unchecked")
 80  
     @Override
 81  
     public void performModification(View view, Object model, Component component) {
 82  0
         if ((component != null) && !(component instanceof Group)) {
 83  0
             throw new IllegalArgumentException("Compare field initializer only support Group components, found type: "
 84  
                     + component.getClass());
 85  
         }
 86  
 
 87  0
         if (component == null) {
 88  0
             return;
 89  
         }
 90  
 
 91  
         // list to hold the generated compare items
 92  0
         List<Component> comparisonItems = new ArrayList<Component>();
 93  
 
 94  
         // sort comparables by their order property
 95  0
         List<ComparableInfo> groupComparables =
 96  
                 (List<ComparableInfo>) ComponentUtils.sort(comparables, defaultOrderSequence);
 97  
 
 98  
         // generate compare header
 99  0
         if (isGenerateCompareHeaders()) {
 100  0
             for (ComparableInfo comparable : groupComparables) {
 101  0
                 HeaderField compareHeaderField = ComponentUtils.copy(headerFieldPrototype, comparable.getIdSuffix());
 102  0
                 compareHeaderField.setHeaderText(comparable.getHeaderText());
 103  
 
 104  0
                 comparisonItems.add(compareHeaderField);
 105  0
             }
 106  
         }
 107  
 
 108  
         // find the comparable to use for comparing value changes (if
 109  
         // configured)
 110  0
         boolean performValueChangeComparison = false;
 111  0
         String compareValueObjectBindingPath = null;
 112  0
         for (ComparableInfo comparable : groupComparables) {
 113  0
             if (comparable.isCompareToForValueChange()) {
 114  0
                 performValueChangeComparison = true;
 115  0
                 compareValueObjectBindingPath = comparable.getBindingObjectPath();
 116  
             }
 117  
         }
 118  
 
 119  
         // generate the compare items from the configured group
 120  0
         Group group = (Group) component;
 121  0
         for (Component item : group.getItems()) {
 122  0
             int defaultSuffix = 0;
 123  0
             for (ComparableInfo comparable : groupComparables) {
 124  0
                 String idSuffix = comparable.getIdSuffix();
 125  0
                 if (StringUtils.isBlank(idSuffix)) {
 126  0
                     idSuffix = "_c" + defaultSuffix;
 127  
                 }
 128  
 
 129  0
                 Component compareItem = ComponentUtils.copy(item, idSuffix);
 130  
 
 131  0
                 ComponentUtils.setComponentPropertyDeep(compareItem, UifPropertyPaths.BIND_OBJECT_PATH,
 132  
                         comparable.getBindingObjectPath());
 133  0
                 if (comparable.isReadOnly()) {
 134  0
                     compareItem.setReadOnly(true);
 135  0
                     compareItem.setConditionalReadOnly("");
 136  
                 }
 137  
 
 138  
                 // do value comparison
 139  0
                 if (performValueChangeComparison && comparable.isHighlightValueChange() &&
 140  
                         !comparable.isCompareToForValueChange()) {
 141  0
                     performValueComparison(group, compareItem, model, compareValueObjectBindingPath);
 142  
                 }
 143  
 
 144  0
                 comparisonItems.add(compareItem);
 145  0
                 defaultSuffix++;
 146  0
             }
 147  0
         }
 148  
 
 149  
         // update the group's list of components
 150  0
         group.setItems(comparisonItems);
 151  0
     }
 152  
 
 153  
     /**
 154  
      * For each attribute field in the compare item, retrieves the field value and compares against the value for the
 155  
      * main comparable. If the value is different, adds script to the field on ready event to add the change icon to
 156  
      * the field and the containing group header
 157  
      *
 158  
      * @param group - group that contains the item and whose header will be highlighted for changes
 159  
      * @param compareItem - the compare item being generated and to pull attribute fields from
 160  
      * @param model - object containing the data
 161  
      * @param compareValueObjectBindingPath - object path for the comparison item
 162  
      */
 163  
     protected void performValueComparison(Group group, Component compareItem, Object model,
 164  
                                           String compareValueObjectBindingPath) {
 165  
         // get any attribute fields for the item so we can compare the values
 166  0
         List<AttributeField> itemFields = ComponentUtils.getComponentsOfTypeDeep(compareItem, AttributeField.class);
 167  0
         for (AttributeField field : itemFields) {
 168  0
             String fieldBindingPath = field.getBindingInfo().getBindingPath();
 169  0
             Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, fieldBindingPath);
 170  
 
 171  0
             String compareBindingPath = StringUtils
 172  
                     .replaceOnce(fieldBindingPath, field.getBindingInfo().getBindingObjectPath(),
 173  
                             compareValueObjectBindingPath);
 174  0
             Object compareValue = ObjectPropertyUtils.getPropertyValue(model, compareBindingPath);
 175  
 
 176  0
             boolean valueChanged = false;
 177  0
             if (!((fieldValue == null) && (compareValue == null))) {
 178  
                 // if one is null then value changed
 179  0
                 if ((fieldValue == null) || (compareValue == null)) {
 180  0
                     valueChanged = true;
 181  
                 } else {
 182  
                     // both not null, compare values
 183  0
                     valueChanged = !fieldValue.equals(compareValue);
 184  
                 }
 185  
             }
 186  
 
 187  
             // add script to show change icon
 188  0
             if (valueChanged) {
 189  0
                 String onReadyScript = "showChangeIcon('" + field.getId() + "');";
 190  
 
 191  
                 // add icon to group header
 192  0
                 Component headerField = group.getHeader();
 193  0
                 onReadyScript += "showChangeIconOnHeader('" + headerField.getId() + "');";
 194  
 
 195  0
                 field.setOnDocumentReadyScript(onReadyScript);
 196  
             }
 197  
 
 198  
             // TODO: add script for value changed?
 199  0
         }
 200  0
     }
 201  
 
 202  
 
 203  
     /**
 204  
      * Generates an id suffix for the comparable item
 205  
      *
 206  
      * <p>
 207  
      * If the idSuffix to use if configured on the <code>ComparableInfo</code>
 208  
      * it will be used, else the given integer index will be used with an
 209  
      * underscore
 210  
      * </p>
 211  
      *
 212  
      * @param comparable
 213  
      *            - comparable info to check for id suffix
 214  
      * @param index
 215  
      *            - sequence integer
 216  
      * @return String id suffix
 217  
      * @see org.kuali.rice.krad.uif.modifier.ComparableInfo.getIdSuffix()
 218  
      */
 219  
     protected String getIdSuffix(ComparableInfo comparable, int index) {
 220  0
         String idSuffix = comparable.getIdSuffix();
 221  0
         if (StringUtils.isBlank(idSuffix)) {
 222  0
             idSuffix = "_" + index;
 223  
         }
 224  
 
 225  0
         return idSuffix;
 226  
     }
 227  
 
 228  
     /**
 229  
      * @see org.kuali.rice.krad.uif.modifier.ComponentModifier#getSupportedComponents()
 230  
      */
 231  
     @Override
 232  
     public Set<Class<? extends Component>> getSupportedComponents() {
 233  0
         Set<Class<? extends Component>> components = new HashSet<Class<? extends Component>>();
 234  0
         components.add(Group.class);
 235  
 
 236  0
         return components;
 237  
     }
 238  
 
 239  
     /**
 240  
      * Indicates the starting integer sequence value to use for
 241  
      * <code>ComparableInfo</code> instances that do not have the order property
 242  
      * set
 243  
      *
 244  
      * @return int default sequence starting value
 245  
      */
 246  
     public int getDefaultOrderSequence() {
 247  0
         return this.defaultOrderSequence;
 248  
     }
 249  
 
 250  
     /**
 251  
      * Setter for the default sequence starting value
 252  
      *
 253  
      * @param defaultOrderSequence
 254  
      */
 255  
     public void setDefaultOrderSequence(int defaultOrderSequence) {
 256  0
         this.defaultOrderSequence = defaultOrderSequence;
 257  0
     }
 258  
 
 259  
     /**
 260  
      * Indicates whether a <code>HeaderField</code> should be created for each
 261  
      * group of comparison fields
 262  
      *
 263  
      * <p>
 264  
      * If set to true, for each group of comparison fields a header field will
 265  
      * be created using the headerFieldPrototype configured on the modifier with
 266  
      * the headerText property of the comparable
 267  
      * </p>
 268  
      *
 269  
      * @return boolean true if the headers should be created, false if no
 270  
      *         headers should be created
 271  
      */
 272  
     public boolean isGenerateCompareHeaders() {
 273  0
         return this.generateCompareHeaders;
 274  
     }
 275  
 
 276  
     /**
 277  
      * Setter for the generate comparison headers indicator
 278  
      *
 279  
      * @param generateCompareHeaders
 280  
      */
 281  
     public void setGenerateCompareHeaders(boolean generateCompareHeaders) {
 282  0
         this.generateCompareHeaders = generateCompareHeaders;
 283  0
     }
 284  
 
 285  
     /**
 286  
      * Prototype instance to use for creating the <code>HeaderField</code> for
 287  
      * each group of comparison fields (if generateCompareHeaders is true)
 288  
      *
 289  
      * @return HeaderField header field prototype
 290  
      */
 291  
     public HeaderField getHeaderFieldPrototype() {
 292  0
         return this.headerFieldPrototype;
 293  
     }
 294  
 
 295  
     /**
 296  
      * Setter for the header field prototype
 297  
      *
 298  
      * @param headerFieldPrototype
 299  
      */
 300  
     public void setHeaderFieldPrototype(HeaderField headerFieldPrototype) {
 301  0
         this.headerFieldPrototype = headerFieldPrototype;
 302  0
     }
 303  
 
 304  
     /**
 305  
      * List of <code>ComparableInfo</code> instances the compare fields should
 306  
      * be generated for
 307  
      *
 308  
      * <p>
 309  
      * For each comparable, a copy of the fields configured for the
 310  
      * <code>Group</code> will be created for the comparison view
 311  
      * </p>
 312  
      *
 313  
      * @return List<ComparableInfo> comparables to generate fields for
 314  
      */
 315  
     public List<ComparableInfo> getComparables() {
 316  0
         return this.comparables;
 317  
     }
 318  
 
 319  
     /**
 320  
      * Setter for the list of comparable info instances
 321  
      *
 322  
      * @param comparables
 323  
      */
 324  
     public void setComparables(List<ComparableInfo> comparables) {
 325  0
         this.comparables = comparables;
 326  0
     }
 327  
 
 328  
 }