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