Coverage Report - org.kuali.rice.kns.util.ObjectUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
ObjectUtils
0%
0/371
0%
0/264
5.897
 
 1  
 /*
 2  
  * Copyright 2005-2007 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.kns.util;
 17  
 
 18  
 import java.beans.PropertyDescriptor;
 19  
 import java.io.ByteArrayInputStream;
 20  
 import java.io.ByteArrayOutputStream;
 21  
 import java.io.ObjectInputStream;
 22  
 import java.io.ObjectOutputStream;
 23  
 import java.io.Serializable;
 24  
 import java.lang.reflect.Field;
 25  
 import java.lang.reflect.InvocationTargetException;
 26  
 import java.security.MessageDigest;
 27  
 import java.util.Collection;
 28  
 import java.util.Iterator;
 29  
 import java.util.List;
 30  
 import java.util.Map;
 31  
 
 32  
 import org.apache.commons.beanutils.NestedNullException;
 33  
 import org.apache.commons.beanutils.PropertyUtils;
 34  
 import org.apache.commons.lang.StringUtils;
 35  
 import org.apache.log4j.Logger;
 36  
 import org.apache.ojb.broker.core.proxy.ProxyHelper;
 37  
 import org.hibernate.collection.PersistentBag;
 38  
 import org.kuali.rice.kns.bo.BusinessObject;
 39  
 import org.kuali.rice.kns.bo.ExternalizableBusinessObject;
 40  
 import org.kuali.rice.kns.bo.PersistableBusinessObject;
 41  
 import org.kuali.rice.kns.bo.PersistableBusinessObjectExtension;
 42  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 43  
 import org.kuali.rice.kns.service.ModuleService;
 44  
 import org.kuali.rice.kns.service.PersistenceStructureService;
 45  
 import org.kuali.rice.kns.util.cache.CopiedObject;
 46  
 import org.kuali.rice.kns.web.format.CollectionFormatter;
 47  
 import org.kuali.rice.kns.web.format.FormatException;
 48  
 import org.kuali.rice.kns.web.format.Formatter;
 49  
 
 50  
 /**
 51  
  * This class contains various Object, Proxy, and serialization utilities.
 52  
  */
 53  
 public class ObjectUtils {
 54  0
     private static final Logger LOG = Logger.getLogger(ObjectUtils.class);
 55  
 
 56  0
     private ObjectUtils() {
 57  0
     }
 58  
 
 59  
     /**
 60  
      * Uses Serialization mechanism to create a deep copy of the given Object. As a special case, deepCopy of null returns null,
 61  
      * just to make using this method simpler. For a detailed discussion see:
 62  
      * http://www.javaworld.com/javaworld/javatips/jw-javatip76.html
 63  
      * 
 64  
      * @param src
 65  
      * @return deep copy of the given Serializable
 66  
      */
 67  
     public static Serializable deepCopy(Serializable src) {
 68  0
         CopiedObject co = deepCopyForCaching(src);
 69  0
         return co.getContent();
 70  
     }
 71  
 
 72  
 
 73  
     /**
 74  
      * Uses Serialization mechanism to create a deep copy of the given Object, and returns a CacheableObject instance containing the
 75  
      * deepCopy and its size in bytes. As a special case, deepCopy of null returns a cacheableObject containing null and a size of
 76  
      * 0, to make using this method simpler. For a detailed discussion see:
 77  
      * http://www.javaworld.com/javaworld/javatips/jw-javatip76.html
 78  
      * 
 79  
      * @param src
 80  
      * @return CopiedObject containing a deep copy of the given Serializable and its size in bytes
 81  
      */
 82  
     public static CopiedObject deepCopyForCaching(Serializable src) {
 83  0
         CopiedObject co = new CopiedObject();
 84  
 
 85  0
         co.setContent( src );
 86  
         
 87  0
         return co;
 88  
     }
 89  
 
 90  
 
 91  
     /**
 92  
      * Converts the object to a byte array using the output stream.
 93  
      * 
 94  
      * @param object
 95  
      * @return byte array of the object
 96  
      */
 97  
     public static byte[] toByteArray(Object object) throws Exception {
 98  0
         ObjectOutputStream oos = null;
 99  
         try {
 100  0
             ByteArrayOutputStream bos = new ByteArrayOutputStream(); // A
 101  0
             oos = new ObjectOutputStream(bos); // B
 102  
             // serialize and pass the object
 103  0
             oos.writeObject(object); // C
 104  
             // oos.flush(); // D
 105  0
             return bos.toByteArray();
 106  
         }
 107  0
         catch (Exception e) {
 108  0
             LOG.warn("Exception in ObjectUtil = " + e);
 109  0
             throw (e);
 110  
         }
 111  
         finally {
 112  0
             if (oos != null) {
 113  0
                 oos.close();
 114  
             }
 115  
         }
 116  
     }
 117  
 
 118  
     /**
 119  
      * reconsitiutes the object that was converted into a byte array by toByteArray
 120  
      * @param bytes
 121  
      * @return
 122  
      * @throws Exception
 123  
      */
 124  
     public static Object fromByteArray(byte[] bytes) throws Exception {
 125  0
         ObjectInputStream ois = null;
 126  
         try {
 127  0
             ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
 128  0
             ois = new ObjectInputStream(bis);
 129  0
             Object obj = ois.readObject();
 130  0
             return obj;
 131  
         }
 132  0
         catch (Exception e) {
 133  0
             LOG.warn("Exception in ObjectUtil = " + e);
 134  0
             throw (e);
 135  
         }
 136  
         finally {
 137  0
             if (ois != null) {
 138  0
                 ois.close();
 139  
             }
 140  
         }
 141  
     }
 142  
     
 143  
     /**
 144  
      * use MD5 to create a one way hash of an object
 145  
      * 
 146  
      * @param object
 147  
      * @return
 148  
      */
 149  
     public static String getMD5Hash(Object object) throws Exception {
 150  
         try {
 151  0
             MessageDigest md = MessageDigest.getInstance("MD5");
 152  0
             md.update(toByteArray(object));
 153  0
             return new String(md.digest());
 154  
         }
 155  0
         catch (Exception e) {
 156  0
             LOG.warn(e);
 157  0
             throw e;
 158  
         }
 159  
     }
 160  
 
 161  
     /**
 162  
      * Creates a new instance of a given BusinessObject, copying fields specified in template from the given source BO. For example,
 163  
      * this can be used to create an AccountChangeDetail based on a particular Account.
 164  
      * 
 165  
      * @param template a map defining the relationships between the fields of the newly created BO, and the source BO.  For each K (key), V (value)
 166  
      * entry, the value of property V on the source BO will be assigned to the K property of the newly created BO
 167  
      * 
 168  
      * @see org.kuali.rice.kns.maintenance.util.MaintenanceUtils
 169  
      * 
 170  
      * @throws NoSuchMethodException
 171  
      * @throws InvocationTargetException
 172  
      * @throws IllegalAccessException
 173  
      * @throws FormatException
 174  
      */
 175  
 
 176  
     public static BusinessObject createHybridBusinessObject(Class businessObjectClass, BusinessObject source, Map<String, String> template) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 177  0
         BusinessObject obj = null;
 178  
         try {
 179  0
                     ModuleService moduleService = KNSServiceLocator.getKualiModuleService().getResponsibleModuleService(businessObjectClass);
 180  0
                     if (moduleService != null && moduleService.isExternalizable(businessObjectClass))
 181  0
                             obj = (BusinessObject)moduleService.createNewObjectFromExternalizableClass(businessObjectClass);
 182  
                     else
 183  0
                             obj = (BusinessObject) businessObjectClass.newInstance();
 184  
         }
 185  0
         catch (Exception e) {
 186  0
             throw new RuntimeException("Cannot instantiate " + businessObjectClass.getName(), e);
 187  0
         }
 188  
 
 189  0
         createHybridBusinessObject(obj, source, template);
 190  
 
 191  0
         return obj;
 192  
     }
 193  
     
 194  
     public static void createHybridBusinessObject(BusinessObject businessObject, BusinessObject source, Map<String, String> template) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 195  0
         for (String name : template.keySet()) {
 196  0
             String sourcePropertyName = template.get(name);
 197  0
             setObjectProperty(businessObject, name, easyGetPropertyType(source, sourcePropertyName), getPropertyValue(source, sourcePropertyName));
 198  0
         }
 199  0
     }
 200  
 
 201  
 
 202  
     /**
 203  
      * This method simply uses PojoPropertyUtilsBean logic to get the Class of a Class property.
 204  
      * This method does not have any of the logic needed to obtain the Class of an element of a Collection specified in the DataDictionary.
 205  
      * 
 206  
      * @param object An instance of the Class of which we're trying to get the property Class.
 207  
      * @param propertyName The name of the property.
 208  
 
 209  
      * @return
 210  
      * 
 211  
      * @throws IllegalAccessException
 212  
      * @throws NoSuchMethodException
 213  
      * @throws InvocationTargetException
 214  
      */
 215  
     static public Class easyGetPropertyType(Object object, String propertyName) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
 216  
 
 217  
         // FIXME (laran) This dependence should be inverted. Instead of having a core class
 218  
         // depend on PojoPropertyUtilsBean, which is in the web layer, the web layer
 219  
         // should depend downward to the core.
 220  0
         return PropertyUtils.getPropertyType(object, propertyName);
 221  
 
 222  
     }
 223  
 
 224  
     /**
 225  
      * Returns the type of the property in the object. This implementation is not smart enough to look through a Collection to get the property type
 226  
      * of an attribute of an element in the collection.
 227  
      * 
 228  
      * NOTE: A patch file attached to https://test.kuali.org/jira/browse/KULRNE-4435 contains a modified version of this method which IS smart enough
 229  
      * to look through Collections. This patch is currently under review.
 230  
      * 
 231  
      * @param object An instance of the Class for which we're trying to get the property type.
 232  
      * @param propertyName The name of the property of the Class the Class of which we're trying to get. Dot notation is used to separate properties.
 233  
      *                     TODO: The rules about this dot notation needs to be explained in Confluence using examples. 
 234  
      * @param persistenceStructureService Needed to get the type of elements in a Collection from OJB. 
 235  
      * 
 236  
      * @return Object will be null if any parent property for the given property is null.
 237  
      */
 238  
     public static Class getPropertyType(Object object, String propertyName, PersistenceStructureService persistenceStructureService) {
 239  0
                 if (object == null || propertyName == null) {
 240  0
                         throw new RuntimeException("Business object and property name can not be null");
 241  
                 }
 242  
 
 243  0
                 Class propertyType = null;
 244  
                 try {
 245  
                         try {
 246  
                                 // Try to simply use the default or simple way of getting the property type.
 247  0
                                 propertyType = PropertyUtils.getPropertyType(object, propertyName);
 248  0
                         } catch (IllegalArgumentException ex) {
 249  
                                 // swallow the exception, propertyType stays null
 250  0
                         } catch (NoSuchMethodException nsme) {
 251  
                                 // swallow the exception, propertyType stays null
 252  0
                         }
 253  
 
 254  
                         // if the property type as determined from the object is PersistableBusinessObject,
 255  
                         // then this must be an extension attribute -- attempt to get the property type from the
 256  
                         // persistence structure service
 257  0
                         if (propertyType != null && propertyType.equals(PersistableBusinessObjectExtension.class)) {
 258  0
                                 propertyType = persistenceStructureService.getBusinessObjectAttributeClass(
 259  
                                                 ProxyHelper.getRealClass(object), propertyName);
 260  
                         }
 261  
 
 262  
                         // If the easy way didn't work ...
 263  0
                         if (null == propertyType && -1 != propertyName.indexOf('.')) {
 264  0
                                 if (null == persistenceStructureService) {
 265  0
                                         LOG.info("PropertyType couldn't be determined simply and no PersistenceStructureService was given. If you pass in a PersistenceStructureService I can look in other places to try to determine the type of the property.");
 266  
                                 } else {
 267  0
                                         String prePeriod = StringUtils.substringBefore(propertyName, ".");
 268  0
                                         String postPeriod = StringUtils.substringAfter(propertyName, ".");
 269  
 
 270  0
                                         Class prePeriodClass = getPropertyType(object, prePeriod, persistenceStructureService);
 271  0
                                         Object prePeriodClassInstance = prePeriodClass.newInstance();
 272  0
                                         propertyType = getPropertyType(prePeriodClassInstance, postPeriod, persistenceStructureService);
 273  0
                                 }
 274  
 
 275  0
                         } else if (Collection.class.isAssignableFrom(propertyType)) {
 276  0
                                 Map<String, Class> map = persistenceStructureService.listCollectionObjectTypes(object.getClass());
 277  0
                                 propertyType = map.get(propertyName);
 278  
                         }
 279  
 
 280  0
                 } catch (Exception e) {
 281  0
                         LOG.debug("unable to get property type for " + propertyName + " " + e.getMessage());
 282  
                         // continue and return null for propertyType
 283  0
                 }
 284  
 
 285  0
                 return propertyType;
 286  
         }
 287  
 
 288  
     /**
 289  
      * Returns the value of the property in the object.
 290  
      * 
 291  
      * @param businessObject
 292  
      * @param propertyName
 293  
      * 
 294  
      * @return Object will be null if any parent property for the given property is null.
 295  
      */
 296  
     public static Object getPropertyValue(Object businessObject, String propertyName) {
 297  0
         if (businessObject == null || propertyName == null) {
 298  0
             throw new RuntimeException("Business object and property name can not be null");
 299  
         }
 300  
 
 301  0
         Object propertyValue = null;
 302  
         try {
 303  0
             propertyValue = PropertyUtils.getProperty(businessObject, propertyName);
 304  0
         } catch (NestedNullException e) {
 305  
             // continue and return null for propertyValue
 306  0
         } catch (IllegalAccessException e1) {
 307  0
             LOG.error("error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage());
 308  0
             throw new RuntimeException("error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage(), e1);
 309  0
         } catch (InvocationTargetException e1) {
 310  
             // continue and return null for propertyValue
 311  0
         } catch (NoSuchMethodException e1) {
 312  0
             LOG.error("error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage());
 313  0
             throw new RuntimeException("error getting property value for  " + businessObject.getClass() + "." + propertyName + " " + e1.getMessage(),e1);
 314  0
         }
 315  
 
 316  0
         return propertyValue;
 317  
     }
 318  
     
 319  
         /**
 320  
          * Gets the property value from the business object, then based on the value
 321  
          * type select a formatter and format the value
 322  
          * 
 323  
          * @param element
 324  
          *            BusinessObject instance that contains the property
 325  
          * @param propertyName
 326  
          *            Name of property in BusinessObject to get value for
 327  
          * @param formatter
 328  
          *            Default formatter to use (or null)
 329  
          * @return Formatted property value as String, or empty string if value is null
 330  
          */
 331  
         public static String getFormattedPropertyValue(BusinessObject businessObject, String propertyName, Formatter formatter) {
 332  0
                 String propValue = KNSConstants.EMPTY_STRING;
 333  
 
 334  0
                 Object prop = ObjectUtils.getPropertyValue(businessObject, propertyName);
 335  0
                 if (formatter == null) {
 336  0
                         propValue = formatPropertyValue(prop);
 337  
                 } else {
 338  0
                         final Object formattedValue = formatter.format(prop);
 339  0
                         if (formattedValue != null) {
 340  0
                                 propValue = String.valueOf(formattedValue);
 341  
                         }
 342  
                 }
 343  
 
 344  0
                 return propValue;
 345  
         }
 346  
         
 347  
         /**
 348  
          * References the data dictionary to find any registered formatter class then if not found checks for associated formatter for the
 349  
          * property type. Value is then formatted using the found Formatter
 350  
          * 
 351  
          * @param element
 352  
          *            BusinessObject instance that contains the property
 353  
          * @param propertyName
 354  
          *            Name of property in BusinessObject to get value for
 355  
          * @return Formatted property value as String, or empty string if value is null
 356  
          */
 357  
         public static String getFormattedPropertyValueUsingDataDictionary(BusinessObject businessObject, String propertyName) {
 358  0
                 Formatter formatter = getFormatterWithDataDictionary(businessObject, propertyName);
 359  
 
 360  0
                 return getFormattedPropertyValue(businessObject, propertyName, formatter);
 361  
         }
 362  
 
 363  
         /**
 364  
          * Based on the value type selects a formatter and returns the formatted
 365  
          * value as a string
 366  
          * 
 367  
          * @param propertyValue
 368  
          *            Object value to be formatted
 369  
          * @return formatted value as a String
 370  
          */
 371  
         public static String formatPropertyValue(Object propertyValue) {
 372  0
                 Object propValue = KNSConstants.EMPTY_STRING;
 373  
 
 374  0
                 Formatter formatter = null;
 375  0
                 if (propertyValue != null) {
 376  0
                         if (propertyValue instanceof Collection) {
 377  0
                                 formatter = new CollectionFormatter();
 378  
                         } else {
 379  0
                                 formatter = Formatter.getFormatter(propertyValue.getClass());
 380  
                         }
 381  
 
 382  0
                         propValue = formatter != null ? formatter.format(propertyValue) : propertyValue;
 383  
                 }
 384  
 
 385  0
                 return propValue != null ? String.valueOf(propValue) : KNSConstants.EMPTY_STRING;
 386  
         }
 387  
     
 388  
     /**
 389  
      * Sets the property of an object with the given value. Converts using the formatter of the type for the property.
 390  
      * Note: propertyType does not need passed, is found by util method.
 391  
      */
 392  
     public static void setObjectProperty(Object bo, String propertyName, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 393  0
         Class propertyType = easyGetPropertyType(bo, propertyName);
 394  0
         setObjectProperty(bo, propertyName, propertyType, propertyValue);
 395  0
     }
 396  
 
 397  
 
 398  
         /**
 399  
          * Sets the property of an object with the given value. Converts using the formatter of the given type if one is found.
 400  
          * 
 401  
          * @param bo
 402  
          * @param propertyName
 403  
          * @param propertyType
 404  
          * @param propertyValue
 405  
          * 
 406  
          * @throws NoSuchMethodException
 407  
          * @throws InvocationTargetException
 408  
          * @throws IllegalAccessException
 409  
          */
 410  
         public static void setObjectProperty(Object bo, String propertyName, Class propertyType, Object propertyValue)
 411  
                         throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 412  
                 // reformat propertyValue, if necessary
 413  0
                 boolean reformat = false;
 414  0
                 if (propertyType != null) {
 415  0
                         if (propertyValue != null && propertyType.isAssignableFrom(String.class)) {
 416  
                                 // always reformat if the destination is a String
 417  0
                                 reformat = true;
 418  0
                         } else if (propertyValue != null && !propertyType.isAssignableFrom(propertyValue.getClass())) {
 419  
                                 // otherwise, only reformat if the propertyValue can't be assigned into the property
 420  0
                                 reformat = true;
 421  
                         }
 422  
 
 423  
                         // attempting to set boolean fields to null throws an exception, set to false instead
 424  0
                         if (boolean.class.isAssignableFrom(propertyType) && propertyValue == null) {
 425  0
                                 propertyValue = false;
 426  
                         }
 427  
                 }
 428  
 
 429  0
                 Formatter formatter = getFormatterWithDataDictionary(bo, propertyName);
 430  0
                 if (reformat && formatter != null) {
 431  0
                         LOG.debug("reformatting propertyValue using Formatter " + formatter.getClass().getName());
 432  0
                         propertyValue = formatter.convertFromPresentationFormat(propertyValue);
 433  
                 }
 434  
 
 435  
                 // set property in the object
 436  0
                 PropertyUtils.setNestedProperty(bo, propertyName, propertyValue);
 437  0
         }
 438  
 
 439  
 
 440  
     /**
 441  
      * Sets the property of an object with the given value. Converts using the given formatter, if it isn't null.
 442  
      * 
 443  
      * @param formatter
 444  
      * @param bo
 445  
      * @param propertyName
 446  
      * @param type
 447  
      * @param propertyValue
 448  
      * 
 449  
      * @throws NoSuchMethodException
 450  
      * @throws InvocationTargetException
 451  
      * @throws IllegalAccessException
 452  
      */
 453  
     public static void setObjectProperty(Formatter formatter, Object bo, String propertyName, Class type, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 454  
 
 455  
         // convert value using formatter for type
 456  0
         if (formatter != null) {
 457  0
             propertyValue = formatter.convertFromPresentationFormat(propertyValue);
 458  
         }
 459  
 
 460  
         // set property in the object
 461  0
         PropertyUtils.setNestedProperty(bo, propertyName, propertyValue);
 462  0
     }
 463  
 
 464  
         /**
 465  
          * Returns a Formatter instance for the given property name in the given given business object. First
 466  
          * checks if a formatter is defined for the attribute in the data dictionary, is not found then returns
 467  
          * the registered formatter for the property type in Formatter
 468  
          * 
 469  
          * @param bo
 470  
          *            - business object instance with property to get formatter for
 471  
          * @param propertyName
 472  
          *            - name of property to get formatter for
 473  
          * @return Formatter instance
 474  
          */
 475  
         public static Formatter getFormatterWithDataDictionary(Object bo, String propertyName) {
 476  0
                 Formatter formatter = null;
 477  
 
 478  0
                 Class boClass = bo.getClass();
 479  0
                 String boPropertyName = propertyName;
 480  
 
 481  
                 // for collections, formatter should come from property on the collection type
 482  0
                 if (StringUtils.contains(propertyName, "]")) {
 483  0
                         Object collectionParent = getNestedValue(bo, StringUtils.substringBeforeLast(propertyName, "].") + "]");
 484  0
                         if (collectionParent != null) {
 485  0
                                 boClass = collectionParent.getClass();
 486  0
                                 boPropertyName = StringUtils.substringAfterLast(propertyName, "].");
 487  
                         }
 488  
                 }
 489  
 
 490  0
                 Class<? extends Formatter> formatterClass = KNSServiceLocator.getDataDictionaryService().getAttributeFormatter(
 491  
                                 boClass, boPropertyName);
 492  0
                 if (formatterClass == null) {
 493  
                         try {
 494  0
                                 formatterClass = Formatter.findFormatter(getPropertyType(boClass.newInstance(), boPropertyName,
 495  
                                                 KNSServiceLocator.getPersistenceStructureService()));
 496  0
                         } catch (InstantiationException e) {
 497  0
                                 LOG.warn("Unable to find a formater for bo class " + boClass + " and property " + boPropertyName);
 498  
                                 // just swallow the exception and let formatter be null
 499  0
                         } catch (IllegalAccessException e) {
 500  0
                                 LOG.warn("Unable to find a formater for bo class " + boClass + " and property " + boPropertyName);
 501  
                                 // just swallow the exception and let formatter be null
 502  0
                         }
 503  
                 }
 504  
 
 505  0
                 if (formatterClass != null) {
 506  
                         try {
 507  0
                                 formatter = formatterClass.newInstance();
 508  0
                         } catch (Exception e) {
 509  0
                                 throw new RuntimeException(
 510  
                                                 "cannot create new instance of formatter class " + formatterClass.toString(), e);
 511  0
                         }
 512  
                 }
 513  
 
 514  0
                 return formatter;
 515  
         }
 516  
 
 517  
     /**
 518  
      * Recursive; sets all occurences of the property in the object, its nested objects and its object lists with the given value.
 519  
      * 
 520  
      * @param bo
 521  
      * @param propertyName
 522  
      * @param type
 523  
      * @param propertyValue
 524  
      * 
 525  
      * @throws NoSuchMethodException
 526  
      * @throws InvocationTargetException
 527  
      * @throws IllegalAccessException
 528  
      */
 529  
     public static void setObjectPropertyDeep(Object bo, String propertyName, Class type, Object propertyValue) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 530  
 
 531  
         // Base return cases to avoid null pointers & infinite loops
 532  0
         if (isNull(bo) || !PropertyUtils.isReadable(bo, propertyName) || (propertyValue != null && propertyValue.equals(getPropertyValue(bo, propertyName))) || (type != null && !type.equals(easyGetPropertyType(bo, propertyName)))) {
 533  0
             return;
 534  
         }
 535  
         
 536  
         // need to materialize the updateable collections before resetting the property, because it may be used in the retrieval
 537  0
         materializeUpdateableCollections(bo);
 538  
         
 539  
         // Set the property in the BO
 540  0
         setObjectProperty(bo, propertyName, type, propertyValue);
 541  
 
 542  
         // Now drill down and check nested BOs and BO lists
 543  0
         PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass());
 544  0
         for (int i = 0; i < propertyDescriptors.length; i++) {
 545  
 
 546  0
             PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
 547  
 
 548  
             // Business Objects
 549  0
             if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo, propertyDescriptor.getName())) {
 550  0
                 Object nestedBo = getPropertyValue(bo, propertyDescriptor.getName());
 551  0
                 if ( nestedBo instanceof BusinessObject ) {
 552  0
                     setObjectPropertyDeep((BusinessObject)nestedBo, propertyName, type, propertyValue);
 553  
                 }
 554  0
             }
 555  
 
 556  
             // Lists
 557  0
             else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && getPropertyValue(bo, propertyDescriptor.getName()) != null) {
 558  
 
 559  0
                 List propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName());
 560  0
                 for(Object listedBo: propertyList) {
 561  0
                     if (listedBo != null && listedBo instanceof BusinessObject) {
 562  0
                         setObjectPropertyDeep(listedBo, propertyName, type, propertyValue);
 563  
                     }
 564  
                 } // end for
 565  
             }
 566  
         } // end for
 567  0
     }
 568  
     /*
 569  
     * Recursive up to a given depth; sets all occurences of the property in the object, its nested objects and its object lists with the given value.
 570  
     */  
 571  
     public static void setObjectPropertyDeep(Object bo, String propertyName, Class type, Object propertyValue, int depth) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 572  
         // Base return cases to avoid null pointers & infinite loops
 573  0
         if (depth == 0 || isNull(bo) || !PropertyUtils.isReadable(bo, propertyName)) {
 574  0
             return;
 575  
         }
 576  
         
 577  
         // need to materialize the updateable collections before resetting the property, because it may be used in the retrieval
 578  0
         materializeUpdateableCollections(bo);
 579  
         
 580  
         // Set the property in the BO
 581  0
         setObjectProperty(bo, propertyName, type, propertyValue);
 582  
 
 583  
         // Now drill down and check nested BOs and BO lists
 584  0
         PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass());
 585  0
         for (int i = 0; i < propertyDescriptors.length; i++) {
 586  0
             PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
 587  
 
 588  
             // Business Objects
 589  0
             if (propertyDescriptor.getPropertyType() != null && (BusinessObject.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && PropertyUtils.isReadable(bo, propertyDescriptor.getName())) {
 590  0
                 Object nestedBo = getPropertyValue(bo, propertyDescriptor.getName());
 591  0
                 if ( nestedBo instanceof BusinessObject ) {
 592  0
                     setObjectPropertyDeep((BusinessObject)nestedBo, propertyName, type, propertyValue, depth - 1);
 593  
                 }
 594  0
             }
 595  
 
 596  
             // Lists
 597  0
             else if (propertyDescriptor.getPropertyType() != null && (List.class).isAssignableFrom(propertyDescriptor.getPropertyType()) && getPropertyValue(bo, propertyDescriptor.getName()) != null) {
 598  
 
 599  0
                 List propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName());
 600  
                 
 601  
                 // Complete Hibernate Hack - fetches the proxied List into the PersistenceContext and sets it on the BO Copy.
 602  0
                 if (propertyList instanceof PersistentBag) {
 603  
                         try {
 604  0
                                 PersistentBag bag = (PersistentBag) propertyList;
 605  0
                                 PersistableBusinessObject pbo = (PersistableBusinessObject) KNSServiceLocator.getEntityManagerFactory().createEntityManager().find(bo.getClass(), bag.getKey());
 606  0
                                         Field field1 = pbo.getClass().getDeclaredField(propertyDescriptor.getName());
 607  0
                                         Field field2 = bo.getClass().getDeclaredField(propertyDescriptor.getName());
 608  0
                                         field1.setAccessible(true);
 609  0
                                         field2.setAccessible(true);
 610  0
                                         field2.set(bo, field1.get(pbo));
 611  0
                                         propertyList = (List) getPropertyValue(bo, propertyDescriptor.getName());;
 612  0
                         } catch (Exception e) {
 613  0
                                 LOG.error(e.getMessage(), e);
 614  0
                         }
 615  
                 }
 616  
                 // End Complete Hibernate Hack
 617  
                 
 618  0
                 for(Object listedBo: propertyList) {
 619  0
                     if (listedBo != null && listedBo instanceof BusinessObject) {
 620  0
                         setObjectPropertyDeep(listedBo, propertyName, type, propertyValue, depth - 1);
 621  
                     }
 622  
                 } // end for
 623  
             }
 624  
         } // end for
 625  0
     }
 626  
     
 627  
     /**
 628  
      * 
 629  
      * This method checks for updateable collections on the business object provided and materializes the corresponding collection proxies
 630  
      * 
 631  
      * @param bo The business object for which you want unpdateable, proxied collections materialized
 632  
      * @throws FormatException
 633  
      * @throws IllegalAccessException
 634  
      * @throws InvocationTargetException
 635  
      * @throws NoSuchMethodException
 636  
      */
 637  
     public static void materializeUpdateableCollections(Object bo) throws FormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 638  0
         if (isNotNull(bo)) {
 639  0
             PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(bo.getClass());
 640  0
             for (int i = 0; i < propertyDescriptors.length; i++) {
 641  0
                 if (KNSServiceLocator.getPersistenceStructureService().hasCollection(bo.getClass(), propertyDescriptors[i].getName()) && KNSServiceLocator.getPersistenceStructureService().isCollectionUpdatable(bo.getClass(), propertyDescriptors[i].getName())) {
 642  0
                     Collection updateableCollection = (Collection) getPropertyValue(bo, propertyDescriptors[i].getName());
 643  0
                     if ((updateableCollection != null) && ProxyHelper.isCollectionProxy(updateableCollection)) {
 644  0
                         materializeObjects(updateableCollection);
 645  
                     }
 646  
                 }
 647  
             }
 648  
         }
 649  0
     }
 650  
 
 651  
 
 652  
     /**
 653  
      * Removes all query characters from a string.
 654  
      * 
 655  
      * @param string
 656  
      * 
 657  
      * @return Cleaned string
 658  
      */
 659  
     public static String clean(String string) {
 660  0
         for (int i = 0; i < KNSConstants.QUERY_CHARACTERS.length; i++) {
 661  0
             string = StringUtils.replace(string, KNSConstants.QUERY_CHARACTERS[i], KNSConstants.EMPTY_STRING);
 662  
         }
 663  0
         return string;
 664  
     }
 665  
 
 666  
 
 667  
     /**
 668  
      * Compares two {@link PersistableBusinessObject} instances for equality of type and key values.
 669  
      * 
 670  
      * @param bo1
 671  
      * @param bo2
 672  
      *  
 673  
      * @return boolean indicating whether the two objects are equal.
 674  
      */
 675  
     public static boolean equalByKeys(PersistableBusinessObject bo1, PersistableBusinessObject bo2) {
 676  0
         boolean equal = true;
 677  
 
 678  0
         if (bo1 == null && bo2 == null) {
 679  0
             equal = true;
 680  
         }
 681  0
         else if (bo1 == null || bo2 == null) {
 682  0
             equal = false;
 683  
         }
 684  0
         else if (!bo1.getClass().getName().equals(bo2.getClass().getName())) {
 685  0
             equal = false;
 686  
         }
 687  
         else {
 688  0
             Map bo1Keys = KNSServiceLocator.getPersistenceService().getPrimaryKeyFieldValues(bo1);
 689  0
             Map bo2Keys = KNSServiceLocator.getPersistenceService().getPrimaryKeyFieldValues(bo2);
 690  0
             for (Iterator iter = bo1Keys.keySet().iterator(); iter.hasNext();) {
 691  0
                 String keyName = (String) iter.next();
 692  0
                 if (bo1Keys.get(keyName) != null && bo2Keys.get(keyName) != null) {
 693  0
                     if (!bo1Keys.get(keyName).toString().equals(bo2Keys.get(keyName).toString())) {
 694  0
                         equal = false;
 695  
                     }
 696  
                 } else {
 697  0
                     equal = false;
 698  
                 }
 699  0
             }
 700  
         }  
 701  
 
 702  
 
 703  0
         return equal;
 704  
     }
 705  
 
 706  
     /**
 707  
      * Compares a business object with a List of {@link PersistableBusinessObject}s to determine if an object with the same key as the BO exists in the list.
 708  
      * 
 709  
      * @param controlList - The list of items to check
 710  
      * @param bo - The BO whose keys we are looking for in the controlList
 711  
      * 
 712  
      * @return boolean
 713  
      */
 714  
     public static boolean collectionContainsObjectWithIdentitcalKey(Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) {
 715  0
         boolean objectExistsInList = false;
 716  
 
 717  0
         for (Iterator i = controlList.iterator(); i.hasNext();) {
 718  0
             if (equalByKeys((PersistableBusinessObject) i.next(), bo)) {
 719  0
                 return true;
 720  
             }
 721  
         }
 722  
 
 723  0
         return objectExistsInList;
 724  
     }
 725  
 
 726  
     /**
 727  
      * Compares a business object with a Collection of {@link PersistableBusinessObject}s to count how many have the same key as the BO.
 728  
      * 
 729  
      * @param collection - The collection of items to check
 730  
      * @param bo - The BO whose keys we are looking for in the collection
 731  
      * 
 732  
      * @return how many have the same keys
 733  
      */
 734  
     public static int countObjectsWithIdentitcalKey(Collection<? extends PersistableBusinessObject> collection, PersistableBusinessObject bo) {
 735  
         // todo: genericize collectionContainsObjectWithIdentitcalKey() to leverage this method?
 736  0
         int n = 0;
 737  0
         for (PersistableBusinessObject item : collection) {
 738  0
             if (equalByKeys(item, bo)) {
 739  0
                 n++;
 740  
             }
 741  
         }
 742  0
         return n;
 743  
     }
 744  
 
 745  
     /**
 746  
      * Compares a business object with a List of {@link PersistableBusinessObject}s to determine if an object with the same key as the BO exists in the list. If it
 747  
      * does, the item is removed from the List. This is functionally similar to List.remove() that operates only on Key values.
 748  
      * 
 749  
      * @param controlList - The list of items to check
 750  
      * @param bo - The BO whose keys we are looking for in the controlList
 751  
      */
 752  
 
 753  
     public static void removeObjectWithIdentitcalKey(Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) {
 754  0
         for (Iterator<? extends PersistableBusinessObject> i = controlList.iterator(); i.hasNext();) {
 755  0
                 PersistableBusinessObject listBo = i.next();
 756  0
             if (equalByKeys(listBo, bo)) {
 757  0
                 i.remove();
 758  
             }
 759  0
         }
 760  0
     }
 761  
 
 762  
     /**
 763  
      * Compares a business object with a List of BOs to determine if an object with the same key as the BO exists in the list. If it
 764  
      * does, the item is returned.
 765  
      * 
 766  
      * @param controlList - The list of items to check
 767  
      * @param bo - The BO whose keys we are looking for in the controlList
 768  
      */
 769  
 
 770  
     public static BusinessObject retrieveObjectWithIdentitcalKey(Collection<? extends PersistableBusinessObject> controlList, PersistableBusinessObject bo) {
 771  0
         BusinessObject returnBo = null;
 772  
 
 773  0
         for (Iterator<? extends PersistableBusinessObject> i = controlList.iterator(); i.hasNext();) {
 774  0
                 PersistableBusinessObject listBo = i.next();
 775  0
             if (equalByKeys(listBo, bo)) {
 776  0
                 returnBo = listBo;
 777  
             }
 778  0
         }
 779  
 
 780  0
         return returnBo;
 781  
     }
 782  
 
 783  
     /**
 784  
      * Determines if a given string could represent a nested attribute of an object.
 785  
      * 
 786  
      * @param attributeName
 787  
      * 
 788  
      * @return true if the attribute is nested
 789  
      */
 790  
     public static boolean isNestedAttribute(String attributeName) {
 791  0
         boolean isNested = false;
 792  
 
 793  0
         if (StringUtils.contains(attributeName, ".")) {
 794  0
             isNested = true;
 795  
         }
 796  
 
 797  0
         return isNested;
 798  
     }
 799  
 
 800  
     /**
 801  
      * Returns the prefix of a nested attribute name, or the empty string if the attribute name is not nested.
 802  
      * 
 803  
      * @param attributeName
 804  
      * 
 805  
      * @return everything BEFORE the last "." character in attributeName
 806  
      */
 807  
     public static String getNestedAttributePrefix(String attributeName) {
 808  0
         String prefix = "";
 809  
 
 810  0
         if (StringUtils.contains(attributeName, ".")) {
 811  0
             prefix = StringUtils.substringBeforeLast(attributeName, ".");
 812  
         }
 813  
 
 814  0
         return prefix;
 815  
     }
 816  
 
 817  
     /**
 818  
      * Returns the primitive part of an attribute name string.
 819  
      * 
 820  
      * @param attributeName
 821  
      * 
 822  
      * @return everything AFTER the last "." character in attributeName
 823  
      */
 824  
     public static String getNestedAttributePrimitive(String attributeName) {
 825  0
         String primitive = attributeName;
 826  
 
 827  0
         if (StringUtils.contains(attributeName, ".")) {
 828  0
             primitive = StringUtils.substringAfterLast(attributeName, ".");
 829  
         }
 830  
 
 831  0
         return primitive;
 832  
     }
 833  
 
 834  
     /**
 835  
      * This method is a OJB Proxy-safe way to test for null on a proxied object that may or may not be materialized yet. It is safe
 836  
      * to use on a proxy (materialized or non-materialized) or on a non-proxy (ie, regular object). Note that this will force a
 837  
      * materialization of the proxy if the object is a proxy and unmaterialized.
 838  
      * 
 839  
      * @param object - any object, proxied or not, materialized or not
 840  
      * 
 841  
      * @return true if the object (or underlying materialized object) is null, false otherwise
 842  
      */
 843  
     public static boolean isNull(Object object) {
 844  
 
 845  
         // regardless, if its null, then its null
 846  0
         if (object == null) {
 847  0
             return true;
 848  
         }
 849  
 
 850  
         // only try to materialize the object to see if its null if this is a
 851  
         // proxy object
 852  0
         if (ProxyHelper.isProxy(object) || ProxyHelper.isCollectionProxy(object)) {
 853  0
             if (ProxyHelper.getRealObject(object) == null) {
 854  0
                 return true;
 855  
             }
 856  
         }
 857  
 
 858  0
         return false;
 859  
     }
 860  
 
 861  
     /**
 862  
      * This method is a OJB Proxy-safe way to test for notNull on a proxied object that may or may not be materialized yet. It is
 863  
      * safe to use on a proxy (materialized or non-materialized) or on a non-proxy (ie, regular object). Note that this will force a
 864  
      * materialization of the proxy if the object is a proxy and unmaterialized.
 865  
      * 
 866  
      * @param object - any object, proxied or not, materialized or not
 867  
      * 
 868  
      * @return true if the object (or underlying materialized object) is not null, true if its null
 869  
      */
 870  
     public static boolean isNotNull(Object object) {
 871  0
         return !ObjectUtils.isNull(object);
 872  
     }
 873  
 
 874  
     /**
 875  
      * This method runs the ObjectUtils.isNotNull() method for each item in a list of BOs. ObjectUtils.isNotNull() will materialize
 876  
      * the objects if they are currently OJB proxies.
 877  
      * 
 878  
      * @param possiblyProxiedObjects - a Collection of objects that may be proxies
 879  
      */
 880  
     public static void materializeObjects(Collection possiblyProxiedObjects) {
 881  0
         for (Iterator i = possiblyProxiedObjects.iterator(); i.hasNext();) {
 882  0
             ObjectUtils.isNotNull(i.next());
 883  
         }
 884  0
     }
 885  
 
 886  
     /**
 887  
      * This method attempts to materialize all of the proxied reference objects (ie, sub-objects) hanging off the passed-in BO
 888  
      * object. It will do it down to the specified depth. An IllegalArgumentException will be thrown if the bo object passed in is
 889  
      * itself a non-materialized proxy object. If the bo passed in has no proxied sub-objects, then the object will not be modified,
 890  
      * and no errors will be thrown. WARNING: Be careful using depth any greater than 2. The number of DB hits, time, and memory
 891  
      * consumed grows exponentially with each additional increment to depth. Make sure you really need that depth before doing so.
 892  
      * 
 893  
      * @param bo A valid, populated BusinessObject containing (possibly) proxied sub-objects. This object will be modified in place.
 894  
      * @param depth int Value 0-5 indicating how deep to recurse the materialization. If a zero (0) is passed in, then no work will
 895  
      *        be done.
 896  
      */
 897  
     public static void materializeSubObjectsToDepth(PersistableBusinessObject bo, int depth) {
 898  0
         if (bo == null) {
 899  0
             throw new IllegalArgumentException("The bo passed in was null.");
 900  
         }
 901  0
         if (depth < 0 || depth > 5) {
 902  0
             throw new IllegalArgumentException("The depth passed in was out of bounds.  Only values " + "between 0 and 5, inclusively, are allowed.");
 903  
         }
 904  
 
 905  
         // if depth is zero, then we're done recursing and can just exit
 906  0
         if (depth == 0) {
 907  0
             return;
 908  
         }
 909  
 
 910  
         // deal with the possibility that the bo passed in (ie, the parent object) is an un-materialized proxy
 911  0
         if (ProxyHelper.isProxy(bo)) {
 912  0
             if (!ProxyHelper.isMaterialized(bo)) {
 913  0
                 throw new IllegalArgumentException("The bo passed in is an un-materialized proxy, and cannot be used.");
 914  
             }
 915  
         }
 916  
 
 917  
         // get the list of reference objects hanging off the parent BO
 918  0
         if ( KNSServiceLocator.getPersistenceStructureService().isPersistable( bo.getClass() ) ) {
 919  0
                 Map<String, Class> references = KNSServiceLocator.getPersistenceStructureService().listReferenceObjectFields(bo);
 920  
         
 921  
                 // initialize our in-loop objects
 922  0
                 String referenceName = "";
 923  0
                 Class referenceClass = null;
 924  0
                 Object referenceValue = null;
 925  0
                 Object realReferenceValue = null;
 926  
         
 927  
                 // for each reference object on the parent bo
 928  0
                 for (Iterator iter = references.keySet().iterator(); iter.hasNext();) {
 929  0
                     referenceName = (String) iter.next();
 930  0
                     referenceClass = references.get(referenceName);
 931  
         
 932  
                     // if its a proxy, replace it with a non-proxy
 933  0
                     referenceValue = getPropertyValue(bo, referenceName);
 934  0
                     if (referenceValue != null) {
 935  0
                         if (ProxyHelper.isProxy(referenceValue)) {
 936  0
                             realReferenceValue = ProxyHelper.getRealObject(referenceValue);
 937  0
                             if (realReferenceValue != null) {
 938  
                                 try {
 939  0
                                     setObjectProperty(bo, referenceName, referenceClass, realReferenceValue);
 940  
                                 }
 941  0
                                 catch (FormatException e) {
 942  0
                                     throw new RuntimeException("FormatException: could not set the property '" + referenceName + "'.", e);
 943  
                                 }
 944  0
                                 catch (IllegalAccessException e) {
 945  0
                                     throw new RuntimeException("IllegalAccessException: could not set the property '" + referenceName + "'.", e);
 946  
                                 }
 947  0
                                 catch (InvocationTargetException e) {
 948  0
                                     throw new RuntimeException("InvocationTargetException: could not set the property '" + referenceName + "'.", e);
 949  
                                 }
 950  0
                                 catch (NoSuchMethodException e) {
 951  0
                                     throw new RuntimeException("NoSuchMethodException: could not set the property '" + referenceName + "'.", e);
 952  0
                                 }
 953  
                             }
 954  
                         }
 955  
         
 956  
                         // recurse down through this reference object
 957  0
                         if (realReferenceValue instanceof PersistableBusinessObject && depth > 1) {
 958  0
                             materializeSubObjectsToDepth((PersistableBusinessObject) realReferenceValue, depth - 1);
 959  
                         }
 960  
                     }
 961  
         
 962  
                 }
 963  
         }
 964  0
     }
 965  
 
 966  
     /**
 967  
      * This method attempts to materialize all of the proxied reference objects (ie, sub-objects) hanging off the passed-in BO
 968  
      * object. It will do it just three levels down. In other words, it will only materialize the objects that are direct members of
 969  
      * the bo, objects that are direct members of those bos, that one more time, and no further down. An IllegalArgumentException 
 970  
      * will be thrown if the bo object passed in is itself a non-materialized proxy object. If the bo passed in has no proxied 
 971  
      * sub-objects, then the object will not be modified, and no errors will be thrown.
 972  
      * 
 973  
      * @param bo A valid, populated BusinessObject containing (possibly) proxied sub-objects. This object will be modified in place.
 974  
      */
 975  
     public static void materializeAllSubObjects(PersistableBusinessObject bo) {
 976  0
         materializeSubObjectsToDepth(bo, 3);
 977  0
     }
 978  
 
 979  
     /**
 980  
      * This method safely extracts either simple values OR nested values. For example, if the bo is SubAccount, and the fieldName is
 981  
      * a21SubAccount.subAccountTypeCode, this thing makes sure it gets the value off the very end attribute, no matter how deeply
 982  
      * nested it is. The code would be slightly simpler if this was done recursively, but this is safer, and consumes a constant
 983  
      * amount of memory, no matter how deeply nested it goes.
 984  
      * 
 985  
      * @param bo
 986  
      * @param fieldName
 987  
      * 
 988  
      * @return The field value if it exists. If it doesnt, and the name is invalid, and
 989  
      */
 990  
     public static Object getNestedValue(Object bo, String fieldName) {
 991  
 
 992  0
         if (bo == null) {
 993  0
             throw new IllegalArgumentException("The bo passed in was null.");
 994  
         }
 995  0
         if (StringUtils.isBlank(fieldName)) {
 996  0
             throw new IllegalArgumentException("The fieldName passed in was blank.");
 997  
         }
 998  
 
 999  
         // okay, this section of code is to handle sub-object values, like
 1000  
         // SubAccount.a21SubAccount.subAccountTypeCode. it basically walks
 1001  
         // through the period-delimited list of names, and ends up with the
 1002  
         // final value.
 1003  0
         String[] fieldNameParts = fieldName.split("\\.");
 1004  0
         Object currentObject = null;
 1005  0
         Object priorObject = bo;
 1006  0
         for (int i = 0; i < fieldNameParts.length; i++) {
 1007  0
             String fieldNamePart = fieldNameParts[i];
 1008  
 
 1009  
             try {
 1010  0
                 if (fieldNamePart.indexOf("]") > 0) {
 1011  0
                     currentObject = PropertyUtils.getIndexedProperty(priorObject, fieldNamePart);
 1012  
                 }
 1013  
                 else {
 1014  0
                     currentObject = PropertyUtils.getSimpleProperty(priorObject, fieldNamePart);
 1015  
                 }
 1016  
             }
 1017  0
             catch (IllegalAccessException e) {
 1018  0
                 throw new RuntimeException("Caller does not have access to the property accessor method.", e);
 1019  
             }
 1020  0
             catch (InvocationTargetException e) {
 1021  0
                 throw new RuntimeException("Property accessor method threw an exception.", e);
 1022  
             }
 1023  0
             catch (NoSuchMethodException e) {
 1024  0
                 throw new RuntimeException("The accessor method requested for this property cannot be found.", e);
 1025  0
             }
 1026  
 
 1027  
             // materialize the proxy, if it is a proxy
 1028  0
             if (ProxyHelper.isProxy(currentObject)) {
 1029  0
                 currentObject = ProxyHelper.getRealObject(currentObject);
 1030  
             }
 1031  
 
 1032  
             // if a node or the leaf is null, then we're done, there's no need to
 1033  
             // continue accessing null things
 1034  0
             if (currentObject == null) {
 1035  0
                 return currentObject;
 1036  
             }
 1037  
 
 1038  0
             priorObject = currentObject;
 1039  
         }
 1040  0
         return currentObject;
 1041  
     }
 1042  
 
 1043  
     /**
 1044  
      * Returns the equality of the two given objects, automatically handling when one or both of the objects is null.
 1045  
      * 
 1046  
      * @param obj1
 1047  
      * @param obj2
 1048  
      * 
 1049  
      * @return true if both objects are null or both are equal
 1050  
      */
 1051  
     public static boolean nullSafeEquals(Object obj1, Object obj2) {
 1052  0
         if (obj1 != null && obj2 != null) {
 1053  0
             return obj1.equals(obj2);
 1054  
         }
 1055  
         else {
 1056  0
             return (obj1 == obj2);
 1057  
         }
 1058  
     }
 1059  
     
 1060  
     /**
 1061  
      * This method safely creates a object from a class
 1062  
      * Convenience method to create new object and throw a runtime exception if it cannot
 1063  
      * If the class is an {@link ExternalizableBusinessObject}, this method will determine the interface for the EBO and query the
 1064  
          * appropriate module service to create a new instance.
 1065  
      * 
 1066  
      * @param boClass
 1067  
      * 
 1068  
      * @return a newInstance() of clazz
 1069  
      */
 1070  
     public static Object createNewObjectFromClass(Class clazz) {
 1071  0
                 if (clazz == null) {
 1072  0
                         throw new RuntimeException("BO class was passed in as null");
 1073  
                 }
 1074  
                 try {
 1075  0
                         if (ExternalizableBusinessObject.class.isAssignableFrom(clazz)) {
 1076  0
                                 Class eboInterface = ExternalizableBusinessObjectUtils.determineExternalizableBusinessObjectSubInterface(clazz);
 1077  0
                                 ModuleService moduleService = KNSServiceLocator.getKualiModuleService().getResponsibleModuleService(eboInterface);
 1078  0
                                 return moduleService.createNewObjectFromExternalizableClass(eboInterface);
 1079  
                         }
 1080  
                         else {
 1081  0
                                 return clazz.newInstance();
 1082  
                         }
 1083  0
                 } catch (Exception e) {
 1084  0
                         throw new RuntimeException("Error occured while trying to create a new instance for class " + clazz,e);
 1085  
                 }
 1086  
     }
 1087  
 
 1088  
         /**
 1089  
          * Return whether or not an attribute is writeable. This method is aware that that Collections may be involved and handles them
 1090  
          * consistently with the way in which OJB handles specifying the attributes of elements of a Collection.
 1091  
          * 
 1092  
          * @param o
 1093  
          * @param p
 1094  
          * @return
 1095  
          * @throws IllegalArgumentException
 1096  
          */
 1097  
         public static boolean isWriteable(Object o, String p, PersistenceStructureService persistenceStructureService)
 1098  
                         throws IllegalArgumentException {
 1099  0
                 if (null == o || null == p) {
 1100  0
                         throw new IllegalArgumentException("Cannot check writeable status with null arguments.");
 1101  
                 }
 1102  
 
 1103  0
                 boolean b = false;
 1104  
 
 1105  
                 // Try the easy way.
 1106  0
                 if (!(PropertyUtils.isWriteable(o, p))) {
 1107  
                         // If that fails lets try to be a bit smarter, understanding that Collections may be involved.
 1108  0
                         if (-1 != p.indexOf('.')) {
 1109  0
                                 String[] parts = p.split("\\.");
 1110  
 
 1111  
                                 // Get the type of the attribute.
 1112  0
                                 Class c = ObjectUtils.getPropertyType(o, parts[0], persistenceStructureService);
 1113  
 
 1114  0
                                 if (c != null) {
 1115  0
                                         Object i = null;
 1116  
 
 1117  
                                         // If the next level is a Collection, look into the collection, to find out what type its elements are.
 1118  0
                                         if (Collection.class.isAssignableFrom(c)) {
 1119  0
                                                 Map<String, Class> m = persistenceStructureService.listCollectionObjectTypes(o.getClass());
 1120  0
                                                 c = m.get(parts[0]);
 1121  
                                         }
 1122  
 
 1123  
                                         // Look into the attribute class to see if it is writeable.
 1124  
                                         try {
 1125  0
                                                 i = c.newInstance();
 1126  
 
 1127  0
                                                 StringBuffer sb = new StringBuffer();
 1128  0
                                                 for (int x = 1; x < parts.length; x++) {
 1129  0
                                                         sb.append(1 == x ? "" : ".").append(parts[x]);
 1130  
                                                 }
 1131  0
                                                 b = isWriteable(i, sb.toString(), persistenceStructureService);
 1132  
 
 1133  0
                                         } catch (Exception ex) {
 1134  0
                                                 LOG.error("Skipping Criteria: " + p + " - Unable to instantiate class : " + c.getName(), ex);
 1135  0
                                         }
 1136  0
                                 } else {
 1137  0
                                         LOG.error("Skipping Criteria: " + p + " - Unable to determine class for object: "
 1138  
                                                         + o.getClass().getName() + " - " + parts[0]);
 1139  
                                 }
 1140  0
                         }
 1141  
 
 1142  
                 } else {
 1143  0
                         b = true;
 1144  
                 }
 1145  
 
 1146  0
                 return b;
 1147  
         }
 1148  
 }