View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.util;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.util.type.TypeUtils;
20  import org.kuali.rice.krad.uif.UifConstants;
21  import org.kuali.rice.krad.uif.container.CollectionGroup;
22  import org.kuali.rice.krad.uif.container.Container;
23  import org.kuali.rice.krad.uif.component.Component;
24  import org.kuali.rice.krad.uif.component.DataBinding;
25  import org.kuali.rice.krad.uif.component.Ordered;
26  import org.kuali.rice.krad.uif.field.Field;
27  import org.kuali.rice.krad.uif.field.FieldGroup;
28  import org.kuali.rice.krad.uif.layout.LayoutManager;
29  import org.kuali.rice.krad.util.ObjectUtils;
30  import org.springframework.beans.BeanUtils;
31  import org.springframework.core.OrderComparator;
32  
33  import java.beans.PropertyDescriptor;
34  import java.io.Serializable;
35  import java.util.ArrayList;
36  import java.util.Collections;
37  import java.util.HashSet;
38  import java.util.List;
39  import java.util.Set;
40  
41  /**
42   * Utility class providing methods to help create and modify
43   * <code>Component</code> instances
44   * 
45   * @author Kuali Rice Team (rice.collab@kuali.org)
46   */
47  public class ComponentUtils {
48      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ComponentUtils.class);
49  
50  
51      public static <T extends Component> T copy(T component) {
52          return copy(component, null);
53      }
54  
55      public static <T extends Component> T copy(T component, String idSuffix) {
56          T copy = copyObject(component);
57  
58          if (StringUtils.isNotBlank(idSuffix)) {
59              updateIdsWithSuffixNested(copy, idSuffix);
60          }
61  
62          return copy;
63      }
64  
65      public static <T extends Object> T copyObject(T object) {
66          if (object == null) {
67              return null;
68          }
69  
70          T copy = null;
71          try {
72              copy = CloneUtils.deepClone(object);
73          }
74          catch (Exception e) {
75              throw new RuntimeException(e);
76          }
77  
78          return copy;
79      }
80  
81      protected static Object getCopyPropertyValue(Set<String> propertiesForReferenceCopy, String propertyName,
82              Object propertyValue) {
83          if (propertyValue == null) {
84              return null;
85          }
86  
87          Object copyValue = propertyValue;
88  
89          Class<?> valuePropertyType = propertyValue.getClass();
90          if (propertiesForReferenceCopy.contains(propertyName) || TypeUtils.isSimpleType(valuePropertyType)
91                  || TypeUtils.isClassClass(valuePropertyType)) {
92              return copyValue;
93          }
94  
95          if (Component.class.isAssignableFrom(valuePropertyType)
96                  || LayoutManager.class.isAssignableFrom(valuePropertyType)) {
97              copyValue = copyObject(propertyValue);
98          }
99          else {
100             copyValue = ObjectUtils.deepCopy((Serializable) propertyValue);
101         }
102 
103         return copyValue;
104     }
105 
106     @SuppressWarnings("unchecked")
107     protected static <T extends Object> T getNewInstance(T object) {
108         T copy = null;
109         try {
110             copy = (T) object.getClass().newInstance();
111         }
112         catch (Exception e) {
113             throw new RuntimeException("Unable to create new instance of class: " + object.getClass());
114         }
115 
116         return copy;
117     }
118 
119     public static <T extends Field> List<T> copyFieldList(List<T> fields, String addBindingPrefix, String idSuffix) {
120         List<T> copiedFieldList = copyFieldList(fields, idSuffix);
121 
122         prefixBindingPath(copiedFieldList, addBindingPrefix);
123 
124         return copiedFieldList;
125     }
126 
127     public static <T extends Field> List<T> copyFieldList(List<T> fields, String idSuffix) {
128         List<T> copiedFieldList = new ArrayList<T>();
129 
130         for (T field : fields) {
131             T copiedField = copy(field, idSuffix);
132             copiedFieldList.add(copiedField);
133         }
134 
135         return copiedFieldList;
136     }
137 
138     public static <T extends Component> T copyComponent(T component, String addBindingPrefix, String idSuffix) {
139         T copy = copy(component, idSuffix);
140 
141         prefixBindingPathNested(copy, addBindingPrefix);
142 
143         return copy;
144     }
145 
146     public static <T extends Component> List<T> copyComponentList(List<T> components, String idSuffix) {
147         List<T> copiedComponentList = new ArrayList<T>();
148 
149         for (T field : components) {
150             T copiedComponent = copy(field, idSuffix);
151             copiedComponentList.add(copiedComponent);
152         }
153 
154         return copiedComponentList;
155     }
156 
157     @SuppressWarnings("unchecked")
158     public static <T extends Component> List<T> getComponentsOfType(List<? extends Component> items,
159             Class<T> componentType) {
160         List<T> typeComponents = new ArrayList<T>();
161 
162         for (Component component : items) {
163             if (componentType.isAssignableFrom(component.getClass())) {
164                 typeComponents.add((T) component);
165             }
166         }
167 
168         return typeComponents;
169     }
170     
171     public static <T extends Component> List<T> getComponentsOfTypeDeep(List<? extends Component> items,
172             Class<T> componentType) {
173         List<T> typeComponents = new ArrayList<T>();
174 
175         for (Component component : items) {
176             typeComponents.addAll(getComponentsOfTypeDeep(component, componentType));
177         }
178 
179         return typeComponents;
180     }
181     
182     @SuppressWarnings("unchecked")
183     public static <T extends Component> List<T> getComponentsOfTypeDeep(Component component, Class<T> componentType) {
184         List<T> typeComponents = new ArrayList<T>();
185 
186         if (component == null) {
187             return typeComponents;
188         }
189 
190         if (componentType.isAssignableFrom(component.getClass())) {
191             typeComponents.add((T) component);
192         }
193 
194         for (Component nested : component.getComponentsForLifecycle()) {
195             typeComponents.addAll(getComponentsOfTypeDeep(nested, componentType));
196         }
197 
198         return typeComponents;
199     }
200 
201     public static List<Component> getAllNestedComponents(Component component) {
202         List<Component> components = new ArrayList<Component>();
203 
204         if (component == null) {
205             return components;
206         }
207 
208         for (Component nested : component.getComponentsForLifecycle()) {
209             components.add(nested);
210             components.addAll(getAllNestedComponents(nested));
211         }
212 
213         return components;
214     }
215 
216     public static void prefixBindingPath(List<? extends Field> fields, String addBindingPrefix) {
217         for (Field field : fields) {
218             if (field instanceof DataBinding) {
219                 prefixBindingPath((DataBinding) field, addBindingPrefix);
220             }
221             else if ((field instanceof FieldGroup) && (((FieldGroup) field).getItems() != null) ) {
222                 List<Field> groupFields = getComponentsOfType(((FieldGroup) field).getItems(), Field.class);
223                 prefixBindingPath(groupFields, addBindingPrefix);
224             }
225         }
226     }
227 
228     public static void prefixBindingPathNested(Component component, String addBindingPrefix) {
229         if (component instanceof DataBinding) {
230             if (LOG.isDebugEnabled()) {
231                 LOG.info("setting nested binding prefix '"+ addBindingPrefix  +"' on " + component);
232             }
233             prefixBindingPath((DataBinding) component, addBindingPrefix);
234         }
235 
236         for (Component nested : component.getComponentsForLifecycle()) {
237            if (nested != null) {
238               prefixBindingPathNested(nested, addBindingPrefix);
239            }
240         }
241     }
242 
243     public static void prefixBindingPath(DataBinding field, String addBindingPrefix) {
244         String bindingPrefix = addBindingPrefix;
245         if (StringUtils.isNotBlank(field.getBindingInfo().getBindByNamePrefix())) {
246             bindingPrefix += "." + field.getBindingInfo().getBindByNamePrefix();
247         }
248         field.getBindingInfo().setBindByNamePrefix(bindingPrefix);
249     }
250 
251     public static void updateIdsWithSuffixNested(List<? extends Component> components, String idSuffix) {
252         for (Component component : components) {
253             updateIdsWithSuffixNested(component, idSuffix);
254         }
255     }
256 
257     public static void updateIdsWithSuffixNested(Component component, String idSuffix) {
258         updateIdsWithSuffix(component, idSuffix);
259 
260         if (Container.class.isAssignableFrom(component.getClass())) {
261             LayoutManager layoutManager = ((Container) component).getLayoutManager();
262             layoutManager.setId(layoutManager.getId() + idSuffix);
263         }
264 
265         for (Component nested : component.getComponentsForLifecycle()) {
266             if (nested != null) {
267                 updateIdsWithSuffixNested(nested, idSuffix);
268             }
269         }
270         
271         for (Component nested : component.getPropertyReplacerComponents()) {
272             if (nested != null) {
273                 updateIdsWithSuffixNested(nested, idSuffix);
274             }
275         }        
276     }
277 
278     public static void updateIdsWithSuffix(Component component, String idSuffix) {
279         component.setId(component.getId() + idSuffix);
280     }
281 
282     public static void setComponentsPropertyDeep(List<? extends Component> components, String propertyPath,
283             Object propertyValue) {
284         for (Component component : components) {
285             setComponentPropertyDeep(component, propertyPath, propertyValue);
286         }
287     }
288 
289     public static void setComponentPropertyDeep(Component component, String propertyPath, Object propertyValue) {
290         ObjectPropertyUtils.setPropertyValue(component, propertyPath, propertyValue, true);
291 
292         for (Component nested : component.getComponentsForLifecycle()) {
293             if (nested != null) {
294                 setComponentPropertyDeep(nested, propertyPath, propertyValue);
295             }
296         }
297     }
298 
299     public static List<String> getComponentPropertyNames(Class<? extends Component> componentClass) {
300         List<String> componentProperties = new ArrayList<String>();
301 
302         PropertyDescriptor[] properties = BeanUtils.getPropertyDescriptors(componentClass);
303         for (int i = 0; i < properties.length; i++) {
304             PropertyDescriptor descriptor = properties[i];
305             if (descriptor.getReadMethod() != null) {
306                 componentProperties.add(descriptor.getName());
307             }
308         }
309 
310         return componentProperties;
311     }
312 
313     public static void pushObjectToContext(List<? extends Component> components, String contextName, Object contextValue) {
314         for (Component component : components) {
315             pushObjectToContext(component, contextName, contextValue);
316         }
317     }
318 
319     public static void pushObjectToContext(Component component, String contextName, Object contextValue) {
320         if (component == null) {
321             return;
322         }
323 
324         component.pushObjectToContext(contextName, contextValue);
325 
326         // special container check so we pick up the layout manager
327         if (Container.class.isAssignableFrom(component.getClass())) {
328             LayoutManager layoutManager = ((Container) component).getLayoutManager();
329             if (layoutManager != null) {
330                 layoutManager.pushObjectToContext(contextName, contextValue);
331 
332                 for (Component nestedComponent : layoutManager.getComponentsForLifecycle()) {
333                     pushObjectToContext(nestedComponent, contextName, contextValue);
334                 }
335             }
336         }
337 
338         for (Component nestedComponent : component.getComponentsForLifecycle()) {
339             pushObjectToContext(nestedComponent, contextName, contextValue);
340         }
341     }
342 
343     public static void updateContextsForLine(List<? extends Component> components, Object collectionLine,
344             int lineIndex) {
345         for (Component component : components) {
346             updateContextForLine(component, collectionLine, lineIndex);
347         }
348     }
349 
350     public static void updateContextForLine(Component component, Object collectionLine, int lineIndex) {
351         pushObjectToContext(component, UifConstants.ContextVariableNames.LINE, collectionLine);
352         pushObjectToContext(component, UifConstants.ContextVariableNames.INDEX, new Integer(lineIndex));
353         
354         boolean isAddLine = (lineIndex == -1);
355         pushObjectToContext(component, UifConstants.ContextVariableNames.IS_ADD_LINE, isAddLine);
356     }
357 
358     /**
359      * Performs sorting logic of the given list of <code>Ordered</code>
360      * instances by its order property
361      *
362      * <p>
363      * Items list is sorted based on its order property. Lower order values are
364      * placed higher in the list. If a item does not have a value assigned for
365      * the order (or is equal to the default order of 0), it will be assigned
366      * the a value based on the given order sequence integer. If two or more
367      * items share the same order value, all but the last item found in the list
368      * will be removed.
369      * </p>
370      * 
371      * @param items
372      * @param defaultOrderSequence
373      * @return List<Ordered> sorted items
374      * @see org.kuali.rice.krad.uif.component.Component#getOrder()
375      * @see @see org.springframework.core.Ordered
376      */
377     public static List<? extends Ordered> sort(List<? extends Ordered> items, int defaultOrderSequence) {
378         List<Ordered> orderedItems = new ArrayList<Ordered>();
379 
380         // do replacement for items with the same order property value
381         Set<Integer> foundOrders = new HashSet<Integer>();
382 
383         // reverse the list, so items later in the list win
384         Collections.reverse(items);
385         for (Ordered component : items) {
386             int order = component.getOrder();
387 
388             // if order not set just add to list
389             if (order == 0) {
390                 orderedItems.add(component);
391             }
392             // check if the order value has been used already
393             else if (!foundOrders.contains(new Integer(order))) {
394                 orderedItems.add(component);
395                 foundOrders.add(new Integer(order));
396             }
397         }
398 
399         // now reverse the list back so we can assign defaults for items without
400         // an order value
401         Collections.reverse(items);
402         for (Ordered component : items) {
403             int order = component.getOrder();
404 
405             // if order property not set assign default
406             if (order == 0) {
407                 defaultOrderSequence++;
408                 while (foundOrders.contains(new Integer(defaultOrderSequence))) {
409                     defaultOrderSequence++;
410                 }
411                 component.setOrder(defaultOrderSequence);
412             }
413         }
414 
415         // now sort the list by its order property
416         Collections.sort(orderedItems, new OrderComparator());
417 
418         return orderedItems;
419     }
420 
421     public static String getLinePathValue(Component component) {
422         CollectionGroup collectionGroup =
423                 (CollectionGroup) (component.getContext().get(UifConstants.ContextVariableNames.COLLECTION_GROUP));
424         String linePath = "";
425         if (collectionGroup != null) {
426             Object indexObj = component.getContext().get(UifConstants.ContextVariableNames.INDEX);
427             if (indexObj != null) {
428                 int index = (Integer) indexObj;
429                 boolean addLine = false;
430                 Object addLineObj = component.getContext().get(UifConstants.ContextVariableNames.IS_ADD_LINE);
431 
432                 if (addLineObj != null) {
433                     addLine = (Boolean) addLineObj;
434                 }
435                 if (addLine) {
436                     linePath = collectionGroup.getAddLineBindingInfo().getBindingPath();
437                 } else {
438                     linePath = collectionGroup.getBindingInfo().getBindingPath() + "[" + index + "]";
439                 }
440             }
441         }
442         return linePath;
443     }
444     
445     public static String replaceLineAttr(String statement, String replacement){
446         if (statement.contains("#line") && StringUtils.isNotEmpty(replacement)) {
447             statement = statement.replace("#line?", replacement);
448             statement = statement.replace("#line", replacement);
449         }
450         return statement;
451     }
452 }