View Javadoc
1   /*
2    * Copyright 2007-2008 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.ole.sys;
17  
18  import java.lang.reflect.InvocationTargetException;
19  import java.lang.reflect.Method;
20  import java.math.BigDecimal;
21  import java.sql.Date;
22  import java.sql.Timestamp;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.LinkedHashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Properties;
30  
31  import org.apache.commons.beanutils.DynaClass;
32  import org.apache.commons.beanutils.DynaProperty;
33  import org.apache.commons.beanutils.PropertyUtils;
34  import org.apache.commons.beanutils.WrapDynaClass;
35  import org.apache.commons.lang.ObjectUtils;
36  import org.apache.commons.lang.StringUtils;
37  import org.kuali.rice.core.api.util.type.KualiDecimal;
38  import org.kuali.rice.core.api.util.type.KualiInteger;
39  import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
40  
41  /**
42   * This class provides a set of facilities that can be used to manipulate objects, for example, object population
43   */
44  public class ObjectUtil {
45      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ObjectUtil.class);
46  
47      /**
48       * create an object of the specified type
49       * 
50       * @param clazz the specified type of the object
51       * @return an object of the specified type
52       */
53      public static <T> T createObject(Class<T> clazz) {
54          T object = null;
55  
56          try {
57              object = clazz.newInstance();
58          }
59          catch (InstantiationException ie) {
60              LOG.error(ie);
61              throw new RuntimeException(ie);
62          }
63          catch (IllegalAccessException iae) {
64              LOG.error(iae);
65              throw new RuntimeException(iae);
66          }
67  
68          return object;
69      }
70  
71      /**
72       * Populate the given fields of the target object with the corresponding field values of source object
73       * 
74       * @param targetObject the target object
75       * @param sourceObject the source object
76       * @param keyFields the given fields of the target object that need to be popluated
77       */
78      public static void buildObject(Object targetObject, Object sourceObject, List<String> keyFields) {
79          if (sourceObject.getClass().isArray()) {
80              buildObject(targetObject, sourceObject, keyFields);
81              return;
82          }
83  
84          for (String propertyName : keyFields) {
85              if (PropertyUtils.isReadable(sourceObject, propertyName) && PropertyUtils.isWriteable(targetObject, propertyName)) {
86                  try {
87                      Object propertyValue = PropertyUtils.getProperty(sourceObject, propertyName);
88                      PropertyUtils.setProperty(targetObject, propertyName, propertyValue);
89                  }
90                  catch (Exception e) {
91                      LOG.debug(e);
92                  }
93              }
94          }
95      }
96  
97      /**
98       * Populate the given fields of the target object with the values of an array
99       * 
100      * @param targetObject the target object
101      * @param sourceObject the given array
102      * @param keyFields the given fields of the target object that need to be popluated
103      */
104     public static void buildObject(Object targetObject, Object[] sourceObject, List<String> keyFields) {
105         int indexOfArray = 0;
106         for (String propertyName : keyFields) {
107             if (PropertyUtils.isWriteable(targetObject, propertyName) && indexOfArray < sourceObject.length) {
108                 try {
109                     Object value = sourceObject[indexOfArray];
110                     String propertyValue = value != null ? value.toString() : StringUtils.EMPTY;
111 
112                     String type = getSimpleTypeName(targetObject, propertyName);
113                     Object realPropertyValue = valueOf(type, propertyValue);
114 
115                     if (realPropertyValue != null && !StringUtils.isEmpty(realPropertyValue.toString())) {
116                         PropertyUtils.setProperty(targetObject, propertyName, realPropertyValue);
117                     }
118                     else {
119                         PropertyUtils.setProperty(targetObject, propertyName, null);
120                     }
121                 }
122                 catch (Exception e) {
123                     LOG.debug(e);
124                 }
125             }
126             indexOfArray++;
127         }
128     }
129     
130     public static String getSimpleTypeName(Object targetObject, String propertyName) {
131         String simpleTypeName = StringUtils.EMPTY;
132         try {
133             simpleTypeName = PropertyUtils.getPropertyType(targetObject, propertyName).getSimpleName();
134         }
135         catch (Exception e) {
136             LOG.debug(e);
137         }
138         
139         return simpleTypeName;
140     }
141 
142     /**
143      * Get an object of the given type holding the property value of the specified String.
144      * 
145      * @param type the given type of the returning object
146      * @param propertyValue the property value of the specified string
147      * @return an object of the given type holding the property value of the specified String
148      */
149     public static Object valueOf(String type, String propertyValue) {
150         Object realPropertyValue = null;
151 
152         if (type.equals("Integer")) {
153             realPropertyValue = isInteger(propertyValue) ? Integer.valueOf(propertyValue) : null;
154         }
155         else if (type.equals("KualiInteger")) {
156             realPropertyValue = isInteger(propertyValue) ? new KualiInteger(propertyValue) : null;
157         }
158         else if (type.equalsIgnoreCase("Boolean")) {
159             realPropertyValue = Boolean.valueOf(propertyValue);
160         }
161         else if (type.equals("KualiDecimal")) {
162             realPropertyValue = isDecimal(propertyValue) ? new KualiDecimal(propertyValue) : null;
163         }
164         else if (type.equals("Date")) {
165             realPropertyValue = formatDate(propertyValue);
166         }
167         else if (type.equals("BigDecimal")) {
168             realPropertyValue = isDecimal(propertyValue) ? new BigDecimal(propertyValue) : null;
169         }
170         else if (type.equals("Timestamp")) {
171             realPropertyValue = formatTimeStamp(propertyValue);
172         }
173         else {
174             realPropertyValue = propertyValue;
175         }
176         return realPropertyValue;
177     }
178 
179     /**
180      * determine if the given string can be converted into an Integer
181      * 
182      * @param value the value of the specified string
183      * @return true if the string can be converted into an Integer; otherwise, return false
184      */
185     public static boolean isInteger(String value) {
186         String pattern = "^(\\+|-)?\\d+$";
187         return value != null && value.matches(pattern);
188     }
189 
190     /**
191      * determine if the given string can be converted into a decimal
192      * 
193      * @param value the value of the specified string
194      * @return true if the string can be converted into a decimal; otherwise, return false
195      */
196     public static boolean isDecimal(String value) {
197         String pattern = "^(((\\+|-)?\\d+(\\.\\d*)?)|((\\+|-)?(\\d*\\.)?\\d+))$";
198         return value != null && value.matches(pattern);
199     }
200 
201     /**
202      * convert the given string into a date
203      * 
204      * @param value the given string
205      * @return a date converted from the given string
206      */
207     public static Date formatDate(String value) {
208         Date formattedDate = null;
209 
210         try {
211             formattedDate = Date.valueOf(value);
212         }
213         catch (Exception e) {
214             return formattedDate;
215         }
216         return formattedDate;
217     }
218 
219     /**
220      * convert the given string into a timestamp object if the string is in the valid format of timestamp
221      * 
222      * @param value the given string
223      * @return a timestamp converted from the given string
224      */
225     public static Timestamp formatTimeStamp(String value) {
226         Timestamp formattedTimestamp = null;
227 
228         String pattern = "^(\\d{1,4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d{1,9})?)$";
229         boolean isTimestamp = value != null && value.matches(pattern);
230 
231         try {
232             if (isTimestamp) {
233                 formattedTimestamp = Timestamp.valueOf(value);
234             }
235             else {
236                 formattedTimestamp = new Timestamp(formatDate(value).getTime());
237             }
238         }
239         catch (Exception e) {
240             return formattedTimestamp;
241         }
242         return formattedTimestamp;
243     }
244 
245     /**
246      * Populate the target object with the source object
247      * 
248      * @param targetObject the target object
249      * @param sourceObject the source object
250      */
251     public static void buildObject(Object targetObject, Object sourceObject) {
252         DynaClass dynaClass = WrapDynaClass.createDynaClass(targetObject.getClass());
253         DynaProperty[] properties = dynaClass.getDynaProperties();
254 
255         for (DynaProperty property : properties) {
256             ObjectUtil.setProperty(targetObject, sourceObject, property, false);
257         }
258     }
259 
260     /**
261      * Populate the target object with the source object
262      * 
263      * @param targetObject the target object
264      * @param sourceObject the source object
265      */
266     public static void buildObjectWithoutReferenceFields(Object targetObject, Object sourceObject) {
267         DynaClass dynaClass = WrapDynaClass.createDynaClass(targetObject.getClass());
268         DynaProperty[] properties = dynaClass.getDynaProperties();
269 
270         for (DynaProperty property : properties) {
271             ObjectUtil.setProperty(targetObject, sourceObject, property, true);
272         }
273     }
274 
275     /**
276      * Populate the property of the target object with the counterpart of the source object
277      * 
278      * @param targetObject the target object
279      * @param sourceObject the source object
280      * @param property the specified propety of the target object
281      * @param skipReferenceFields determine whether the referencing fields need to be populated
282      */
283     public static void setProperty(Object targetObject, Object sourceObject, DynaProperty property, boolean skipReferenceFields) {
284         String propertyName = property.getName();
285 
286         try {
287             if (skipReferenceFields) {
288                 @SuppressWarnings("rawtypes")
289                 Class propertyType = property.getType();
290                 if (propertyType == null || PersistableBusinessObjectBase.class.isAssignableFrom(propertyType) || List.class.isAssignableFrom(propertyType)) {
291                     return;
292                 }
293             }
294 
295             if (PropertyUtils.isReadable(sourceObject, propertyName) && PropertyUtils.isWriteable(targetObject, propertyName)) {
296                 Object propertyValue = PropertyUtils.getProperty(sourceObject, propertyName);                        
297                 PropertyUtils.setProperty(targetObject, propertyName, propertyValue);
298             }
299         }
300         catch (IllegalAccessException e) {
301             if (LOG.isDebugEnabled()) {
302                 LOG.debug(e.getMessage() + ":" + propertyName);
303             }
304         }
305         catch (InvocationTargetException e) {
306             if (LOG.isDebugEnabled()) {
307                 LOG.debug(e.getMessage() + ":" + propertyName);
308             }
309         }
310         catch (NoSuchMethodException e) {
311             if (LOG.isDebugEnabled()) {
312                 LOG.debug(e.getMessage() + ":" + propertyName);
313             }
314         }
315         catch (IllegalArgumentException e) {
316             if (LOG.isDebugEnabled()) {
317                 LOG.debug(e.getMessage() + ":" + propertyName);
318             }
319         }
320         catch (Exception e) {
321             if (LOG.isDebugEnabled()) {
322                 LOG.debug(e.getMessage() + ":" + propertyName);
323             }
324         }
325     }
326 
327     /**
328      * Determine if they have the same values in the specified fields
329      * 
330      * @param targetObject the target object
331      * @param sourceObject the source object
332      * @param keyFields the specified fields
333      * @return true if the two objects have the same values in the specified fields; otherwise, false
334      */
335     public static boolean equals(Object targetObject, Object sourceObject, List<String> keyFields) {
336         if (targetObject == sourceObject) {
337             return true;
338         }
339 
340         if (targetObject == null || sourceObject == null) {
341             return false;
342         }
343 
344         for (String propertyName : keyFields) {
345             try {
346                 Object propertyValueOfSource = PropertyUtils.getProperty(sourceObject, propertyName);
347                 Object propertyValueOfTarget = PropertyUtils.getProperty(targetObject, propertyName);
348 
349                 if (!ObjectUtils.equals(propertyValueOfSource, propertyValueOfTarget)) {
350                     return false;
351                 }
352             }
353             catch (Exception e) {
354                 LOG.info(e);
355                 return false;
356             }
357         }
358         return true;
359     }
360 
361     /**
362      * compute the hash code for the given object from the given fields
363      * 
364      * @param object the given object
365      * @param keyFields the specified fields
366      * @return the hash code for the given object from the given fields
367      */
368     public static int generateHashCode(Object object, List<String> keyFields) {
369         if (object == null) {
370             return 0;
371         }
372 
373         final int prime = 31;
374         int result = 1;
375         for (String propertyName : keyFields) {
376             try {
377                 Object propertyValue = PropertyUtils.getProperty(object, propertyName);
378                 result = prime * result + ((propertyValue == null) ? 0 : propertyValue.hashCode());
379             }
380             catch (Exception e) {
381                 LOG.info(e);
382             }
383         }
384         return result;
385     }
386 
387     /**
388      * build a map of business object with its specified property names and corresponding values
389      * 
390      * @param businessObject the given business object
391      * @param the specified fields that need to be included in the return map
392      * @return the map of business object with its property names and values
393      */
394     public static Map<String, Object> buildPropertyMap(Object object, List<String> keyFields) {
395         DynaClass dynaClass = WrapDynaClass.createDynaClass(object.getClass());
396         DynaProperty[] properties = dynaClass.getDynaProperties();
397         Map<String, Object> propertyMap = new LinkedHashMap<String, Object>();
398 
399         for (DynaProperty property : properties) {
400             String propertyName = property.getName();
401 
402             if (PropertyUtils.isReadable(object, propertyName) && keyFields.contains(propertyName)) {
403                 try {
404                     Object propertyValue = PropertyUtils.getProperty(object, propertyName);
405 
406                     if (propertyValue != null && !StringUtils.isEmpty(propertyValue.toString())) {
407                         propertyMap.put(propertyName, propertyValue);
408                     }
409                 }
410                 catch (Exception e) {
411                     LOG.info(e);
412                 }
413             }
414         }
415         return propertyMap;
416     }
417 
418     /**
419      * concat the specified properties of the given object as a string
420      * 
421      * @param object the given object
422      * @param the specified fields that need to be included in the return string
423      * @return the specified properties of the given object as a string
424      */
425     public static String concatPropertyAsString(Object object, List<String> keyFields) {
426         StringBuilder propertyAsString = new StringBuilder();
427         for (String field : keyFields) {
428             if (PropertyUtils.isReadable(object, field)) {
429                 try {
430                     propertyAsString.append(PropertyUtils.getProperty(object, field));
431                 }
432                 catch (Exception e) {
433                     LOG.error(e);
434                 }
435             }
436         }
437 
438         return propertyAsString.toString();
439     }
440 
441     /**
442      * Tokenize the input line with the given deliminator and populate the given object with values of the tokens
443      * 
444      * @param targetObject the target object
445      * @param line the input line
446      * @param delim the deminator that separates the fields in the given line
447      * @param keyFields the specified fields
448      */
449     public static void convertLineToBusinessObject(Object targetObject, String line, String delim, List<String> keyFields) {
450         String[] tokens = StringUtils.split(line, delim);
451         ObjectUtil.buildObject(targetObject, tokens, keyFields);
452     }
453 
454     /**
455      * Tokenize the input line with the given deliminator and populate the given object with values of the tokens
456      * 
457      * @param targetObject the target object
458      * @param line the input line
459      * @param delim the deminator that separates the fields in the given line
460      * @param keyFields the specified fields
461      */
462     public static void convertLineToBusinessObject(Object targetObject, String line, String delim, String fieldNames) {
463         List<String> tokens = split(line, delim);
464         List<String> keyFields = Arrays.asList(StringUtils.split(fieldNames, delim));
465         ObjectUtil.buildObject(targetObject, tokens.toArray(), keyFields);
466     }
467 
468     /**
469      * Tokenize the input line with the given deliminator and store the tokens in a list
470      * 
471      * @param line the input line
472      * @param delim the deminator that separates the fields in the given line
473      * @return a list of tokens
474      */
475     public static List<String> split(String line, String delim) {
476         List<String> tokens = new ArrayList<String>();
477 
478         int currentPosition = 0;
479         for (int step = 0; step < line.length(); step++) {
480             int previousPosition = currentPosition;
481             currentPosition = StringUtils.indexOf(line, delim, currentPosition);
482             currentPosition = currentPosition == -1 ? line.length() - 1 : currentPosition;
483 
484             String sub = line.substring(previousPosition, currentPosition);
485             tokens.add(sub); // don't trim the string
486 
487             currentPosition += delim.length();
488             if (currentPosition >= line.length()) {
489                 break;
490             }
491         }
492         return tokens;
493     }
494 
495     /**
496      * Tokenize the input line with the given deliminator and populate the given object with values of the tokens
497      * 
498      * @param targetObject the target object
499      * @param line the input line
500      * @param delim the deminator that separates the fields in the given line
501      * @param keyFields the specified fields
502      */
503     public static void convertLineToBusinessObject(Object targetObject, String line, int[] fieldLength, List<String> keyFields) {
504         String[] tokens = new String[fieldLength.length];
505 
506         int currentPosition = 0;
507         for (int i = 0; i < fieldLength.length; i++) {
508             currentPosition = i <= 0 ? 0 : fieldLength[i - 1] + currentPosition;
509             tokens[i] = StringUtils.mid(line, currentPosition, fieldLength[i]).trim();
510         }
511         ObjectUtil.buildObject(targetObject, tokens, keyFields);
512     }
513 
514     /**
515      * Populate a business object with the given properities and information
516      * 
517      * @param businessOjbject the business object to be populated
518      * @param properties the given properties
519      * @param propertyKey the property keys in the properties
520      * @param fieldNames the names of the fields to be populated
521      * @param deliminator the deliminator that separates the values to be used in a string
522      */
523     public static void populateBusinessObject(Object businessOjbject, Properties properties, String propertyKey, String fieldNames, String deliminator) {
524         String data = properties.getProperty(propertyKey);
525         ObjectUtil.convertLineToBusinessObject(businessOjbject, data, deliminator, fieldNames);
526     }
527 
528     /**
529      * Populate a business object with the given properities and information
530      * 
531      * @param businessOjbject the business object to be populated
532      * @param properties the given properties
533      * @param propertyKey the property keys in the properties
534      * @param fieldNames the names of the fields to be populated
535      * @param deliminator the deliminator that separates the values to be used in a string
536      */
537     public static void populateBusinessObject(Object businessOjbject, Properties properties, String propertyKey, int[] fieldLength, List<String> keyFields) {
538         String data = properties.getProperty(propertyKey);
539         ObjectUtil.convertLineToBusinessObject(businessOjbject, data, fieldLength, keyFields);
540     }
541 
542     /**
543      * determine if the source object has a field with null as its value
544      * 
545      * @param sourceObject the source object
546      */
547     public static boolean hasNullValueField(Object sourceObject) {
548         DynaClass dynaClass = WrapDynaClass.createDynaClass(sourceObject.getClass());
549         DynaProperty[] properties = dynaClass.getDynaProperties();
550 
551         for (DynaProperty property : properties) {
552             String propertyName = property.getName();
553 
554             if (PropertyUtils.isReadable(sourceObject, propertyName)) {
555                 try {
556                     Object propertyValue = PropertyUtils.getProperty(sourceObject, propertyName);
557                     if (propertyValue == null) {
558                         return true;
559                     }
560                 }
561                 catch (Exception e) {
562                     LOG.info(e);
563                     return false;
564                 }
565             }
566         }
567         return false;
568     }
569 
570     /**
571      * get the types of the nested attributes starting at the given class
572      * 
573      * @param clazz the given class
574      * @param nestedAttribute the nested attributes of the given class
575      * @return a map that contains the types of the nested attributes and the attribute names
576      */
577     public static Map<Class<?>, String> getNestedAttributeTypes(Class<?> clazz, String nestedAttribute) {
578         List<String> attributes = Arrays.asList(StringUtils.split(nestedAttribute, PropertyUtils.NESTED_DELIM));
579         Map<Class<?>, String> nestedAttributes = new HashMap<Class<?>, String>();
580 
581         Class<?> currentClass = clazz;
582         for (String propertyName : attributes) {
583             String methodName = "get" + StringUtils.capitalize(propertyName);
584             try {
585                 Method method = currentClass.getMethod(methodName);
586                 currentClass = method.getReturnType();
587                 nestedAttributes.put(currentClass, propertyName);
588             }
589             catch (Exception e) {
590                 LOG.info(e);
591                 break;
592             }
593         }
594         return nestedAttributes;
595     }
596 }