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