View Javadoc

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