View Javadoc

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