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