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