Coverage Report - org.kuali.rice.kns.web.struts.pojo.PojoPropertyUtilsBean
 
Classes in this File Line Coverage Branch Coverage Complexity
PojoPropertyUtilsBean
0%
0/197
0%
0/116
11.667
 
 1  
 /*
 2  
  * Copyright 2004 Jonathan M. Lehr
 3  
  *
 4  
  * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 12  
  * governing permissions and limitations under the License.
 13  
  * 
 14  
  * MODIFIED BY THE KUALI FOUNDATION
 15  
  */
 16  
  
 17  
 // begin Kuali Foundation modification
 18  
 package org.kuali.rice.kns.web.struts.pojo;
 19  
 
 20  
 import java.beans.IntrospectionException;
 21  
 import java.beans.PropertyDescriptor;
 22  
 import java.lang.reflect.InvocationTargetException;
 23  
 import java.lang.reflect.Method;
 24  
 import java.util.ArrayList;
 25  
 import java.util.HashMap;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 
 29  
 import org.apache.commons.beanutils.MappedPropertyDescriptor;
 30  
 import org.apache.commons.beanutils.NestedNullException;
 31  
 import org.apache.commons.beanutils.PropertyUtils;
 32  
 import org.apache.commons.beanutils.PropertyUtilsBean;
 33  
 import org.apache.commons.collections.FastHashMap;
 34  
 import org.apache.log4j.Logger;
 35  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 36  
 import org.kuali.rice.kns.util.ExternalizableBusinessObjectUtils;
 37  
 import org.kuali.rice.kns.util.ObjectUtils;
 38  
 import org.kuali.rice.kns.web.format.Formatter;
 39  
 
 40  
 
 41  
 /**
 42  
  * begin Kuali Foundation modification
 43  
  * This class is used to access the properties of a Pojo bean.
 44  
  * deleted author tag
 45  
  * end Kuali Foundation modification
 46  
  */
 47  
 // Kuali Foundation modification: class originally SLPropertyUtilsBean
 48  
 public class PojoPropertyUtilsBean extends PropertyUtilsBean {
 49  
 
 50  0
     public static final Logger LOG = Logger.getLogger(PojoPropertyUtilsBean.class.getName());
 51  
 
 52  
         // begin Kuali Foundation modification
 53  
     public PojoPropertyUtilsBean() {
 54  0
         super();
 55  0
     }
 56  
     // end Kuali Foundation modification
 57  
 
 58  
     public Object getProperty(Object bean, String key) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 59  
         // begin Kuali Foundation modification
 60  0
         if (!(bean instanceof PojoForm))
 61  0
             return super.getProperty(bean, key);
 62  
 
 63  0
         PojoForm form = (PojoForm) bean;
 64  0
         Map unconvertedValues = form.getUnconvertedValues();
 65  
 
 66  0
         if (unconvertedValues.containsKey(key))
 67  0
             return unconvertedValues.get(key);
 68  
 
 69  0
         Object val = getNestedProperty(bean, key);
 70  0
         Class type = (val!=null)?val.getClass():null;
 71  0
         if ( type == null ) {
 72  
             try {
 73  0
                 type = getPropertyType(bean, key);
 74  0
             } catch ( Exception ex ) {
 75  0
                 type = String.class;
 76  0
                 LOG.warn( "Unable to get property type for Class: " + bean.getClass().getName() + "/Property: " + key );
 77  0
             }
 78  
         }
 79  0
         return (Formatter.isSupportedType(type) ? form.formatValue(val, key, type) : val);
 80  
         // end Kuali Foundation modification
 81  
     }
 82  
 
 83  
         // begin Kuali Foundation modification
 84  0
     private Map<String,List<Method>> cache = new HashMap<String,List<Method>>();
 85  0
     private static Map<String,Method> readMethodCache = new HashMap<String, Method>();
 86  0
     private IntrospectionException introspectionException = new IntrospectionException( "" );
 87  
     
 88  
     public Object fastGetNestedProperty(Object obj, String propertyName) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
 89  
         //logger.debug("entering fastGetNestedProperty");
 90  
 
 91  0
         List<Method> methods = (List<Method>) cache.get(propertyName + obj.getClass().getName());
 92  0
         if (methods == null) {
 93  0
             methods = new ArrayList<Method>();
 94  0
             Object currentObj = obj;
 95  0
             Class<?> currentObjClass = currentObj.getClass();
 96  
 
 97  0
             for (String currentPropertyName : propertyName.split("\\.") ) {
 98  0
                 String cacheKey = currentObjClass.getName() + currentPropertyName;
 99  0
                 Method readMethod = readMethodCache.get( cacheKey );
 100  0
                 if ( readMethod == null ) {
 101  0
                         synchronized (readMethodCache) {
 102  
                             // if the read method was resolved to an error, repeat the exception
 103  
                             // rather than performing the reflection calls below
 104  0
                             if ( readMethodCache.containsKey(cacheKey) ) {
 105  0
                                 throw introspectionException;
 106  
                             }
 107  
                             try {
 108  
                                 try {
 109  0
                                     readMethod = currentObjClass.getMethod("get" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null);
 110  0
                                 } catch (NoSuchMethodException e) {
 111  0
                                     readMethod = currentObjClass.getMethod("is" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null);
 112  0
                                 }
 113  0
                             } catch ( NoSuchMethodException ex ) {
 114  
                                 // cache failures to prevent re-checking of the parameter
 115  0
                                 readMethodCache.put( cacheKey, null );
 116  0
                                 throw introspectionException;
 117  
         //                        throw new IntrospectionException( currentPropertyName );
 118  
         //                        try {
 119  
         //                        System.out.println( "using PropertyDescriptor" ); 
 120  
         //                        PropertyDescriptor pd = new PropertyDescriptor( currentPropertyName, currentObjClass, "get" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), null );
 121  
         //                        readMethod = pd.getReadMethod();
 122  
         //                        } catch ( Exception ex2 ) {
 123  
         //                            LOG.error( ex2.getMessage() );
 124  
         //                        }
 125  
         //                        System.out.println( "used PropertyDescriptor to get readMethod for " + currentObjClass.getName() + "." + currentPropertyName + " : " + readMethod );
 126  
                                 //LOG.error( "Unable to determine readMethod for " + currentObjClass.getName() + "." + currentPropertyName, ex);
 127  
                                 //return null;
 128  0
                             }
 129  0
                             readMethodCache.put(cacheKey, readMethod );
 130  0
                                         }
 131  
                 }
 132  0
                 methods.add(readMethod);
 133  0
                 currentObj = readMethod.invoke(currentObj, (Object[])null);
 134  0
                 currentObjClass = currentObj.getClass();
 135  
             }
 136  0
             synchronized (cache) {
 137  0
                 cache.put(propertyName + obj.getClass().getName(), methods);
 138  0
                         }
 139  
         }
 140  
 
 141  0
         for ( Method method : methods ) {
 142  0
             obj = method.invoke(obj, (Object[])null);
 143  
         }
 144  
 
 145  
         //logger.debug("exiting fastGetNestedProperty");
 146  
 
 147  0
         return obj;
 148  
     }
 149  
         // end Kuali Foundation modification
 150  
 
 151  
 
 152  
     /**
 153  
      * begin Kuali Foundation modification
 154  
      * removed comments and @<no space>since javadoc attribute
 155  
      * end Kuali Foundation modification
 156  
      * @see org.apache.commons.beanutils.PropertyUtilsBean#getNestedProperty(java.lang.Object, java.lang.String)
 157  
      */
 158  
     public Object getNestedProperty(Object arg0, String arg1) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 159  
                 // begin Kuali Foundation modification
 160  
         try {
 161  
             try {
 162  0
                 return fastGetNestedProperty(arg0, arg1);
 163  
             }
 164  0
             catch (Exception e) {
 165  0
                 return super.getNestedProperty(arg0, arg1);
 166  
             }
 167  
         }
 168  0
         catch (NestedNullException e) {
 169  0
             return "";
 170  
         }
 171  0
         catch (InvocationTargetException e1) {
 172  0
             return "";
 173  
         }
 174  
         // removed commented code
 175  
         // end Kuali Foundation modification
 176  
     }
 177  
 
 178  
 
 179  
     // begin Kuali Foundation modification 
 180  
     /**
 181  
      * begin Kuali Foundation modification
 182  
      * Set the value of the (possibly nested) property of the specified name, for the specified bean, with no type conversions.
 183  
      *
 184  
      * @param bean Bean whose property is to be modified
 185  
      * @param name Possibly nested name of the property to be modified
 186  
      * @param value Value to which the property is to be set
 187  
      *
 188  
      * @exception IllegalAccessException if the caller does not have access to the property accessor method
 189  
      * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null
 190  
      * @exception IllegalArgumentException if a nested reference to a property returns null
 191  
      * @exception InvocationTargetException if the property accessor method throws an exception
 192  
      * @exception NoSuchMethodException if an accessor method for this propety cannot be found
 193  
      * end Kuali Foundation modification
 194  
      */
 195  
     public void setNestedProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 196  
 
 197  0
         if (bean == null) {
 198  0
                 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value);
 199  0
                 return;
 200  
         }
 201  0
         if (name == null) {
 202  0
             throw new IllegalArgumentException("No name specified");
 203  
         }
 204  
 
 205  0
         Object propBean = null;
 206  0
         int indexOfINDEXED_DELIM = -1;
 207  0
         int indexOfMAPPED_DELIM = -1;
 208  
         while (true) {
 209  0
             int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
 210  0
             if (delim < 0) {
 211  0
                 break;
 212  
             }
 213  0
             String next = name.substring(0, delim);
 214  0
             indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
 215  0
             indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
 216  0
             if (bean instanceof Map) {
 217  0
                 propBean = ((Map) bean).get(next);
 218  
             }
 219  0
             else if (indexOfMAPPED_DELIM >= 0) {
 220  0
                 propBean = getMappedProperty(bean, next);
 221  
             }
 222  0
             else if (indexOfINDEXED_DELIM >= 0) {
 223  0
                 propBean = getIndexedProperty(bean, next);
 224  
             }
 225  
             else {
 226  0
                 propBean = getSimpleProperty(bean, next);
 227  
             }
 228  0
             if (ObjectUtils.isNull(propBean)) {
 229  0
                 Class propertyType = getPropertyType(bean, next);
 230  0
                 if (propertyType != null) {
 231  0
                         Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType);
 232  0
                     setSimpleProperty(bean, next, newInstance);
 233  0
                     propBean = getSimpleProperty(bean, next);
 234  
                 }
 235  
             }
 236  0
             bean = propBean;
 237  0
             name = name.substring(delim + 1);
 238  0
         }
 239  
 
 240  0
         indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM);
 241  0
         indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
 242  
 
 243  0
         if (bean instanceof Map) {
 244  
             // check to see if the class has a standard property
 245  0
             PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
 246  0
             if (descriptor == null) {
 247  
                 // no - then put the value into the map
 248  0
                 ((Map) bean).put(name, value);
 249  
             }
 250  
             else {
 251  
                 // yes - use that instead
 252  0
                 setSimpleProperty(bean, name, value);
 253  
             }
 254  0
         }
 255  0
         else if (indexOfMAPPED_DELIM >= 0) {
 256  0
             setMappedProperty(bean, name, value);
 257  
         }
 258  0
         else if (indexOfINDEXED_DELIM >= 0) {
 259  0
             setIndexedProperty(bean, name, value);
 260  
         }
 261  
         else {
 262  0
             setSimpleProperty(bean, name, value);
 263  
         }
 264  0
     }
 265  
     // end Kuali Foundation modification
 266  
 
 267  
         // begin Kuali Foundation modification
 268  
     /**
 269  
      * <p>
 270  
      * Retrieve the property descriptor for the specified property of the specified bean, or return <code>null</code> if there is
 271  
      * no such descriptor. This method resolves indexed and nested property references in the same manner as other methods in this
 272  
      * class, except that if the last (or only) name element is indexed, the descriptor for the last resolved property itself is
 273  
      * returned.
 274  
      * </p>
 275  
      *
 276  
      * <p>
 277  
      * <strong>FIXME </strong>- Does not work with DynaBeans.
 278  
      * </p>
 279  
      *
 280  
      * @param bean Bean for which a property descriptor is requested
 281  
      * @param name Possibly indexed and/or nested name of the property for which a property descriptor is requested
 282  
      *
 283  
      * @exception IllegalAccessException if the caller does not have access to the property accessor method
 284  
      * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null
 285  
      * @exception IllegalArgumentException if a nested reference to a property returns null
 286  
      * @exception InvocationTargetException if the property accessor method throws an exception
 287  
      * @exception NoSuchMethodException if an accessor method for this propety cannot be found
 288  
      */
 289  
     public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
 290  0
         if (bean == null) {
 291  0
                 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name);
 292  0
                 return null;
 293  
         }
 294  0
         if (name == null) {
 295  0
             throw new IllegalArgumentException("No name specified");
 296  
         }
 297  
         try {
 298  
             // Resolve nested references
 299  0
             Object propBean = null;
 300  
             while (true) {
 301  0
                 int delim = findNextNestedIndex(name);
 302  
                 //int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
 303  0
                 if (delim < 0) {
 304  0
                     break;
 305  
                 }
 306  0
                 String next = name.substring(0, delim);
 307  0
                 int indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
 308  0
                 int indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
 309  0
                 if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM < 0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) {
 310  0
                     propBean = getMappedProperty(bean, next);
 311  
                 }
 312  
                 else {
 313  0
                     if (indexOfINDEXED_DELIM >= 0) {
 314  0
                         propBean = getIndexedProperty(bean, next);
 315  
                     }
 316  
                     else {
 317  0
                         propBean = getSimpleProperty(bean, next);
 318  
                     }
 319  
                 }
 320  0
                 if (ObjectUtils.isNull(propBean)) {
 321  0
                     Class propertyType = getPropertyType(bean, next);
 322  0
                     if (propertyType != null) {
 323  0
                             Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType);
 324  0
                         setSimpleProperty(bean, next, newInstance);
 325  0
                         propBean = getSimpleProperty(bean, next);
 326  
                     }
 327  
                 }
 328  0
                 bean = propBean;
 329  0
                 name = name.substring(delim + 1);
 330  0
             }
 331  
     
 332  
             // Remove any subscript from the final name value
 333  0
             int left = name.indexOf(PropertyUtils.INDEXED_DELIM);
 334  0
             if (left >= 0) {
 335  0
                 name = name.substring(0, left);
 336  
             }
 337  0
             left = name.indexOf(PropertyUtils.MAPPED_DELIM);
 338  0
             if (left >= 0) {
 339  0
                 name = name.substring(0, left);
 340  
             }
 341  
     
 342  
             // Look up and return this property from our cache
 343  
             // creating and adding it to the cache if not found.
 344  0
             if ((bean == null) || (name == null)) {
 345  0
                 return (null);
 346  
             }
 347  
     
 348  0
             PropertyDescriptor descriptors[] = getPropertyDescriptors(bean);
 349  0
             if (descriptors != null) {
 350  
     
 351  0
                 for (int i = 0; i < descriptors.length; i++) {
 352  0
                     if (name.equals(descriptors[i].getName()))
 353  0
                         return (descriptors[i]);
 354  
                 }
 355  
             }
 356  
     
 357  0
             PropertyDescriptor result = null;
 358  0
             FastHashMap mappedDescriptors = getMappedPropertyDescriptors(bean);
 359  0
             if (mappedDescriptors == null) {
 360  0
                 mappedDescriptors = new FastHashMap();
 361  0
                 mappedDescriptors.setFast(true);
 362  
             }
 363  0
             result = (PropertyDescriptor) mappedDescriptors.get(name);
 364  0
             if (result == null) {
 365  
                 // not found, try to create it
 366  
                 try {
 367  0
                     result = new MappedPropertyDescriptor(name, bean.getClass());
 368  
                 }
 369  0
                 catch (IntrospectionException ie) {
 370  0
                 }
 371  0
                 if (result != null) {
 372  0
                     mappedDescriptors.put(name, result);
 373  
                 }
 374  
             }
 375  
     
 376  0
             return result;
 377  0
         } catch ( RuntimeException ex ) {
 378  0
             LOG.error( "Unable to get property descriptor for " + bean.getClass().getName() + " . " + name
 379  
                     + "\n" + ex.getClass().getName() + ": " + ex.getMessage() );
 380  0
             throw ex;
 381  
         }
 382  
     }
 383  
     // end Kuali Foundation modification
 384  
 
 385  
     private int findNextNestedIndex(String expression)
 386  
     {
 387  
         // walk back from the end to the start
 388  
         // and find the first index that
 389  0
         int bracketCount = 0;
 390  0
         for (int i=0, size=expression.length(); i<size ; i++) {
 391  0
             char at = expression.charAt(i);
 392  0
             switch (at) {
 393  
                 case PropertyUtils.NESTED_DELIM:
 394  0
                     if (bracketCount < 1) {
 395  0
                         return i;
 396  
                     }
 397  
                     break;
 398  
 
 399  
                 case PropertyUtils.MAPPED_DELIM:
 400  
                 case PropertyUtils.INDEXED_DELIM:
 401  
                     // not bothered which
 402  0
                     ++bracketCount;
 403  0
                     break;
 404  
 
 405  
                 case PropertyUtils.MAPPED_DELIM2:
 406  
                 case PropertyUtils.INDEXED_DELIM2:
 407  
                     // not bothered which
 408  0
                     --bracketCount;
 409  
                     break;
 410  
             }
 411  
         }
 412  
         // can't find any
 413  0
         return -1;
 414  
     }
 415  
 
 416  
     /**
 417  
      * Set the value of the specified simple property of the specified bean,
 418  
      * with no type conversions.
 419  
      *
 420  
      * @param bean Bean whose property is to be modified
 421  
      * @param name Name of the property to be modified
 422  
      * @param value Value to which the property should be set
 423  
      *
 424  
      * @exception IllegalAccessException if the caller does not have
 425  
      *  access to the property accessor method
 426  
      * @exception IllegalArgumentException if <code>bean</code> or
 427  
      *  <code>name</code> is null
 428  
      * @exception IllegalArgumentException if the property name is
 429  
      *  nested or indexed
 430  
      * @exception InvocationTargetException if the property accessor method
 431  
      *  throws an exception
 432  
      * @exception NoSuchMethodException if an accessor method for this
 433  
      *  propety cannot be found
 434  
      */
 435  
     public void setSimpleProperty(Object bean,
 436  
                                          String name, Object value)
 437  
             throws IllegalAccessException, InvocationTargetException,
 438  
             NoSuchMethodException {
 439  
 
 440  0
         if (bean == null) {
 441  0
                 if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value);
 442  0
                 return;
 443  
         }
 444  0
         if (name == null) {
 445  0
             throw new IllegalArgumentException("No name specified");
 446  
         }
 447  
 
 448  
         // Validate the syntax of the property name
 449  0
         if (name.indexOf(PropertyUtils.NESTED_DELIM) >= 0) {
 450  0
             throw new IllegalArgumentException
 451  
                     ("Nested property names are not allowed");
 452  0
         } else if (name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0) {
 453  0
             throw new IllegalArgumentException
 454  
                     ("Indexed property names are not allowed");
 455  0
         } else if (name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0) {
 456  0
             throw new IllegalArgumentException
 457  
                     ("Mapped property names are not allowed");
 458  
         }
 459  
 
 460  
         // Retrieve the property setter method for the specified property
 461  0
         PropertyDescriptor descriptor =
 462  
                 getPropertyDescriptor(bean, name);
 463  0
         if (descriptor == null) {
 464  0
             throw new NoSuchMethodException("Unknown property '" +
 465  
                     name + "'");
 466  
         }
 467  0
         Method writeMethod = getWriteMethod(descriptor);
 468  0
         if (writeMethod == null) {
 469  
             //throw new NoSuchMethodException("Property '" + name + "' has no setter method");
 470  0
                 LOG.warn("Bean: " + bean.getClass().getName() + ", Property '" + name + "' has no setter method");
 471  0
                 return;
 472  
         }
 473  
 
 474  
         // Call the property setter method
 475  0
         Object values[] = new Object[1];
 476  0
         values[0] = value;
 477  0
         if (LOG.isDebugEnabled()) {
 478  0
             String valueClassName =
 479  
                 value == null ? "<null>" : value.getClass().getName();
 480  0
             LOG.debug("setSimpleProperty: Invoking method " + writeMethod
 481  
                       + " with value " + value + " (class " + valueClassName + ")");
 482  
         }
 483  
         
 484  
         
 485  0
         invokeMethod(writeMethod, bean, values);
 486  
 
 487  0
     }
 488  
     
 489  
     /** This just catches and wraps IllegalArgumentException. */
 490  
     private Object invokeMethod(
 491  
                         Method method, 
 492  
                         Object bean, 
 493  
                         Object[] values) 
 494  
                             throws
 495  
                                 IllegalAccessException,
 496  
                                 InvocationTargetException {
 497  
         try {
 498  
             
 499  0
             return method.invoke(bean, values);
 500  
         
 501  0
         } catch (IllegalArgumentException e) {
 502  
             
 503  0
             LOG.error("Method invocation failed.", e);
 504  0
             throw new IllegalArgumentException(
 505  
                 "Cannot invoke " + method.getDeclaringClass().getName() + "." 
 506  
                 + method.getName() + " - " + e.getMessage());
 507  
             
 508  
         }
 509  
     }
 510  
 
 511  
 }