View Javadoc

1   /**
2    * Copyright 2005-2013 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.DynaBean;
20  import org.apache.commons.beanutils.DynaProperty;
21  import org.apache.commons.beanutils.MappedPropertyDescriptor;
22  import org.apache.commons.beanutils.MethodUtils;
23  import org.apache.commons.beanutils.NestedNullException;
24  import org.apache.commons.beanutils.PropertyUtils;
25  import org.apache.commons.beanutils.PropertyUtilsBean;
26  import org.apache.commons.beanutils.WrapDynaBean;
27  import org.apache.commons.collections.FastHashMap;
28  import org.apache.log4j.Logger;
29  import org.apache.ojb.broker.metadata.ClassDescriptor;
30  import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
31  import org.apache.ojb.broker.metadata.CollectionDescriptor;
32  import org.apache.ojb.broker.metadata.DescriptorRepository;
33  import org.apache.ojb.broker.metadata.MetadataManager;
34  import org.kuali.rice.core.web.format.Formatter;
35  import org.kuali.rice.krad.bo.PersistableBusinessObject;
36  import org.kuali.rice.krad.service.KRADServiceLocator;
37  import org.kuali.rice.krad.service.PersistenceStructureService;
38  import org.kuali.rice.krad.util.ObjectUtils;
39  
40  import java.beans.IndexedPropertyDescriptor;
41  import java.beans.IntrospectionException;
42  import java.beans.PropertyDescriptor;
43  import java.lang.reflect.InvocationTargetException;
44  import java.lang.reflect.Method;
45  import java.util.ArrayList;
46  import java.util.Collection;
47  import java.util.HashMap;
48  import java.util.List;
49  import java.util.Map;
50  
51  /**
52   * begin Kuali Foundation modification
53   * This class is used to access the properties of a Pojo bean.
54   * deleted author tag
55   * end Kuali Foundation modification
56   */
57  // Kuali Foundation modification: class originally SLPropertyUtilsBean
58  public class PojoPropertyUtilsBean extends PropertyUtilsBean {
59  
60      public static final Logger LOG = Logger.getLogger(PojoPropertyUtilsBean.class.getName());
61  
62      /**
63       * Thin interface for determining the appropriate item class for a collection property
64       */
65      public static interface CollectionItemClassProvider {
66          public Class getCollectionItemClass(Object bean, String property);
67      }
68  
69      /**
70       * CollectionItemClassProvider backed by OJB metadata
71       */
72      public static class PersistenceStructureServiceProvider implements CollectionItemClassProvider {
73          protected static PersistenceStructureService persistenceStructureService = null;
74          protected static PersistenceStructureService getPersistenceStructureService() {
75              if (persistenceStructureService == null) {
76                  persistenceStructureService = KRADServiceLocator.getPersistenceStructureService();
77              }
78              return persistenceStructureService;
79          }
80  
81          @Override
82          public Class getCollectionItemClass(Object bean, String property) {
83              Map<String, Class> collectionObjectTypes = getPersistenceStructureService().listCollectionObjectTypes(bean.getClass());
84              return collectionObjectTypes.get(property);
85          }
86      }
87  
88      // default is to consult OJB
89      protected static CollectionItemClassProvider collectionItemClassProvider = new PersistenceStructureServiceProvider();
90  
91  	// begin Kuali Foundation modification
92      public PojoPropertyUtilsBean() {
93          super();
94      }
95      // end Kuali Foundation modification
96  
97      public Object getProperty(Object bean, String key) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
98          // begin Kuali Foundation modification
99          if (!(bean instanceof PojoForm))
100             return super.getProperty(bean, key);
101 
102         PojoForm form = (PojoForm) bean;
103         Map unconvertedValues = form.getUnconvertedValues();
104 
105         if (unconvertedValues.containsKey(key))
106             return unconvertedValues.get(key);
107 
108         Object val = getNestedProperty(bean, key);
109         Class type = (val!=null)?val.getClass():null;
110         if ( type == null ) {
111             try {
112                 type = getPropertyType(bean, key);
113             } catch ( Exception ex ) {
114                 type = String.class;
115                 LOG.warn( "Unable to get property type for Class: " + bean.getClass().getName() + "/Property: " + key );
116             }
117         }
118         return (Formatter.isSupportedType(type) ? form.formatValue(val, key, type) : val);
119         // end Kuali Foundation modification
120     }
121 
122     	// begin Kuali Foundation modification
123     private Map<String,List<Method>> cache = new HashMap<String, List<Method>>();
124     private static Map<String,Method> readMethodCache = new HashMap<String, Method>();
125     private IntrospectionException introspectionException = new IntrospectionException( "" );
126 
127     public Object fastGetNestedProperty(Object obj, String propertyName) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
128         //logger.debug("entering fastGetNestedProperty");
129 
130         List<Method> methods = (List<Method>) cache.get(propertyName + obj.getClass().getName());
131         if (methods == null) {
132             methods = new ArrayList<Method>();
133             Object currentObj = obj;
134             Class<?> currentObjClass = currentObj.getClass();
135 
136             for (String currentPropertyName : propertyName.split("\\.") ) {
137                 String cacheKey = currentObjClass.getName() + currentPropertyName;
138                 Method readMethod = readMethodCache.get( cacheKey );
139                 if ( readMethod == null ) {
140                 	synchronized (readMethodCache) {
141 	                    // if the read method was resolved to an error, repeat the exception
142 	                    // rather than performing the reflection calls below
143 	                    if ( readMethodCache.containsKey(cacheKey) ) {
144 	                        throw introspectionException;
145 	                    }
146 	                    try {
147 	                        try {
148 	                            readMethod = currentObjClass.getMethod("get" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null);
149 	                        } catch (NoSuchMethodException e) {
150 	                            readMethod = currentObjClass.getMethod("is" + currentPropertyName.substring(0, 1).toUpperCase() + currentPropertyName.substring(1), (Class[])null);
151 	                        }
152 	                    } catch ( NoSuchMethodException ex ) {
153 	                        // cache failures to prevent re-checking of the parameter
154 	                        readMethodCache.put( cacheKey, null );
155 	                        throw introspectionException;
156 	                    }
157 	                    readMethodCache.put(cacheKey, readMethod );
158 					}
159                 }
160                 methods.add(readMethod);
161                 currentObjClass = readMethod.getReturnType();
162             }
163             synchronized (cache) {
164                 cache.put(propertyName + obj.getClass().getName(), methods);
165 			}
166         }
167 
168         for ( Method method : methods ) {
169             obj = method.invoke(obj, (Object[])null);
170         }
171 
172         //logger.debug("exiting fastGetNestedProperty");
173 
174         return obj;
175     }
176 	// end Kuali Foundation modification
177 
178     /*
179      *  Kuali modification to make isWriteable work like it did in beanUtils 1.7.
180      *  Checking for nested nulls caused exceptions in rice 2.0.
181      */
182     @Override
183     public boolean isWriteable(Object bean, String name) {
184         // Validate method parameters
185         if (bean == null) {
186             throw new IllegalArgumentException("No bean specified");
187         }
188         if (name == null) {
189             throw new IllegalArgumentException("No name specified for bean class '" +
190                     bean.getClass() + "'");
191         }
192 
193         // Remove any subscript from the final name value
194         name = getResolver().getProperty(name);
195 
196         // Treat WrapDynaBean as special case - may be a read-only property
197         // (see Jira issue# BEANUTILS-61)
198         if (bean instanceof WrapDynaBean) {
199             bean = ((WrapDynaBean)bean).getInstance();
200         }
201 
202         // Return the requested result
203         if (bean instanceof DynaBean) {
204             // All DynaBean properties are writeable
205             return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
206         } else {
207             try {
208                 PropertyDescriptor desc =
209                         getPropertyDescriptor(bean, name);
210                 if (desc != null) {
211                     Method writeMethod = desc.getWriteMethod();
212                     if (writeMethod == null) {
213                         if (desc instanceof IndexedPropertyDescriptor) {
214                             writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod();
215                         } else if (desc instanceof MappedPropertyDescriptor) {
216                             writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod();
217                         }
218                         writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
219                     }
220                     return (writeMethod != null);
221                 } else {
222                     return (false);
223                 }
224             } catch (IllegalAccessException e) {
225                 return (false);
226             } catch (InvocationTargetException e) {
227                 return (false);
228             } catch (NoSuchMethodException e) {
229                 return (false);
230             }
231         }
232 
233     }
234 
235     /**
236      * begin Kuali Foundation modification
237      * removed comments and @<no space>since javadoc attribute
238      * end Kuali Foundation modification
239      * @see org.apache.commons.beanutils.PropertyUtilsBean#getNestedProperty(java.lang.Object, java.lang.String)
240      */
241     public Object getNestedProperty(Object arg0, String arg1) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
242 		// begin Kuali Foundation modification
243         try {
244             try {
245                 return fastGetNestedProperty(arg0, arg1);
246             }
247             catch (Exception e) {
248                 return super.getNestedProperty(arg0, arg1);
249             }
250         }
251         catch (NestedNullException e) {
252             return getUnreachableNestedProperty(arg0, arg1);
253         }
254         catch (InvocationTargetException e1) {
255             return getUnreachableNestedProperty(arg0, arg1);
256         }
257         // removed commented code
258         // end Kuali Foundation modification
259     }
260 
261     /**
262      * Customization of superclass getNestedProperty which transparently creates indexed property items
263      * {@inheritDoc}
264      */
265     public Object getIndexedProperty(Object bean, String name, int index) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
266         try {
267             return super.getIndexedProperty(bean, name, index);
268         } catch (IndexOutOfBoundsException ioobe) {
269             return generateIndexedProperty(bean, name, index, ioobe);
270         }
271     }
272 
273     protected Object generateIndexedProperty(Object nestedBean, String property, int index, IndexOutOfBoundsException ioobe)  throws IllegalAccessException, InvocationTargetException,
274             NoSuchMethodException {
275 
276         if (!(nestedBean instanceof PersistableBusinessObject)) throw ioobe;
277 
278         // we can only grow lists
279         if (!List.class.isAssignableFrom(getPropertyType(nestedBean, property))) throw ioobe;
280 
281         List list= (List) getProperty(nestedBean, property);
282 
283         Class c = collectionItemClassProvider.getCollectionItemClass(nestedBean, property);
284 
285         if (c == null) {
286             throw new RuntimeException("Unable to determined item class for collection '" + property + "' on bean of type '" + nestedBean.getClass() + "'");
287         }
288 
289         Object value;
290         try {
291             value = c.newInstance();
292         } catch (InstantiationException ie) {
293             throw new RuntimeException("Error instantiating item class: " + c);
294         }
295 
296         // fill any missing indices
297         while (list.size() <= index) {
298             list.add(null);
299         }
300         list.set(index, value);
301 
302         return super.getIndexedProperty(nestedBean, property, index);
303     }
304 
305     // begin Kuali Foundation modification
306     /**
307      * helper method makes sure we don't return "" for collections
308      */
309     private Object getUnreachableNestedProperty(Object arg0, String arg1) {
310         try {
311             PropertyDescriptor propertyDescriptor  = getPropertyDescriptor(arg0, arg1);
312             if (propertyDescriptor == null || Collection.class.isAssignableFrom(propertyDescriptor.getPropertyType())) {
313                 return null;
314             }
315         } catch (IllegalAccessException e) {
316             // ignore
317         } catch (InvocationTargetException e) {
318             // ignore
319         } catch (NoSuchMethodException e) {
320             // ignore
321         }
322 
323         return "";
324     }
325     // end Kuali Foundation modification
326 
327 
328     // begin Kuali Foundation modification 
329     /**
330      * begin Kuali Foundation modification
331      * Set the value of the (possibly nested) property of the specified name, for the specified bean, with no type conversions.
332      *
333      * @param bean Bean whose property is to be modified
334      * @param name Possibly nested name of the property to be modified
335      * @param value Value to which the property is to be set
336      *
337      * @exception IllegalAccessException if the caller does not have access to the property accessor method
338      * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null
339      * @exception IllegalArgumentException if a nested reference to a property returns null
340      * @exception InvocationTargetException if the property accessor method throws an exception
341      * @exception NoSuchMethodException if an accessor method for this propety cannot be found
342      * end Kuali Foundation modification
343      */
344     public void setNestedProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
345 
346         if (bean == null) {
347         	if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value);
348         	return;
349         }
350         if (name == null) {
351             throw new IllegalArgumentException("No name specified");
352         }
353 
354         Object propBean = null;
355         int indexOfINDEXED_DELIM = -1;
356         int indexOfMAPPED_DELIM = -1;
357         while (true) {
358             int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
359             if (delim < 0) {
360                 break;
361             }
362             String next = name.substring(0, delim);
363             indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
364             indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
365             if (bean instanceof Map) {
366                 propBean = ((Map) bean).get(next);
367             }
368             else if (indexOfMAPPED_DELIM >= 0) {
369                 propBean = getMappedProperty(bean, next);
370             }
371             else if (indexOfINDEXED_DELIM >= 0) {
372                 propBean = getIndexedProperty(bean, next);
373             }
374             else {
375                 propBean = getSimpleProperty(bean, next);
376             }
377             if (ObjectUtils.isNull(propBean)) {
378                 Class propertyType = getPropertyType(bean, next);
379                 if (propertyType != null) {
380                 	Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType);
381                     setSimpleProperty(bean, next, newInstance);
382                     propBean = getSimpleProperty(bean, next);
383                 }
384             }
385             bean = propBean;
386             name = name.substring(delim + 1);
387         }
388 
389         indexOfINDEXED_DELIM = name.indexOf(PropertyUtils.INDEXED_DELIM);
390         indexOfMAPPED_DELIM = name.indexOf(PropertyUtils.MAPPED_DELIM);
391 
392         if (bean instanceof Map) {
393             // check to see if the class has a standard property
394             PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
395             if (descriptor == null) {
396                 // no - then put the value into the map
397                 ((Map) bean).put(name, value);
398             }
399             else {
400                 // yes - use that instead
401                 setSimpleProperty(bean, name, value);
402             }
403         }
404         else if (indexOfMAPPED_DELIM >= 0) {
405             setMappedProperty(bean, name, value);
406         }
407         else if (indexOfINDEXED_DELIM >= 0) {
408             setIndexedProperty(bean, name, value);
409         }
410         else {
411             setSimpleProperty(bean, name, value);
412         }
413     }
414     // end Kuali Foundation modification
415 
416 	// begin Kuali Foundation modification
417     /**
418      * <p>
419      * Retrieve the property descriptor for the specified property of the specified bean, or return <code>null</code> if there is
420      * no such descriptor. This method resolves indexed and nested property references in the same manner as other methods in this
421      * class, except that if the last (or only) name element is indexed, the descriptor for the last resolved property itself is
422      * returned.
423      * </p>
424      *
425      * <p>
426      * <strong>FIXME </strong>- Does not work with DynaBeans.
427      * </p>
428      *
429      * @param bean Bean for which a property descriptor is requested
430      * @param name Possibly indexed and/or nested name of the property for which a property descriptor is requested
431      *
432      * @exception IllegalAccessException if the caller does not have access to the property accessor method
433      * @exception IllegalArgumentException if <code>bean</code> or <code>name</code> is null
434      * @exception IllegalArgumentException if a nested reference to a property returns null
435      * @exception InvocationTargetException if the property accessor method throws an exception
436      * @exception NoSuchMethodException if an accessor method for this propety cannot be found
437      */
438     public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
439         if (bean == null) {
440         	if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name);
441         	return null;
442         }
443         if (name == null) {
444             throw new IllegalArgumentException("No name specified");
445         }
446         try {
447             // Resolve nested references
448             Object propBean = null;
449             while (true) {
450                 int delim = findNextNestedIndex(name);
451                 //int delim = name.indexOf(PropertyUtils.NESTED_DELIM);
452                 if (delim < 0) {
453                     break;
454                 }
455                 String next = name.substring(0, delim);
456                 int indexOfINDEXED_DELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
457                 int indexOfMAPPED_DELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
458                 if (indexOfMAPPED_DELIM >= 0 && (indexOfINDEXED_DELIM < 0 || indexOfMAPPED_DELIM < indexOfINDEXED_DELIM)) {
459                     propBean = getMappedProperty(bean, next);
460                 }
461                 else {
462                     if (indexOfINDEXED_DELIM >= 0) {
463                         propBean = getIndexedProperty(bean, next);
464                     }
465                     else {
466                         propBean = getSimpleProperty(bean, next);
467                     }
468                 }
469                 if (ObjectUtils.isNull(propBean)) {
470                     Class propertyType = getPropertyType(bean, next);
471                     if (propertyType != null) {
472                     	Object newInstance = ObjectUtils.createNewObjectFromClass(propertyType);
473                         setSimpleProperty(bean, next, newInstance);
474                         propBean = getSimpleProperty(bean, next);
475                     }
476                 }
477                 bean = propBean;
478                 name = name.substring(delim + 1);
479             }
480     
481             // Remove any subscript from the final name value
482             int left = name.indexOf(PropertyUtils.INDEXED_DELIM);
483             if (left >= 0) {
484                 name = name.substring(0, left);
485             }
486             left = name.indexOf(PropertyUtils.MAPPED_DELIM);
487             if (left >= 0) {
488                 name = name.substring(0, left);
489             }
490     
491             // Look up and return this property from our cache
492             // creating and adding it to the cache if not found.
493             if ((bean == null) || (name == null)) {
494                 return (null);
495             }
496     
497             PropertyDescriptor descriptors[] = getPropertyDescriptors(bean);
498             if (descriptors != null) {
499     
500                 for (int i = 0; i < descriptors.length; i++) {
501                     if (name.equals(descriptors[i].getName()))
502                         return (descriptors[i]);
503                 }
504             }
505     
506             PropertyDescriptor result = null;
507             FastHashMap mappedDescriptors = getMappedPropertyDescriptors(bean);
508             if (mappedDescriptors == null) {
509                 mappedDescriptors = new FastHashMap();
510                 mappedDescriptors.setFast(true);
511             }
512             result = (PropertyDescriptor) mappedDescriptors.get(name);
513             if (result == null) {
514                 // not found, try to create it
515                 try {
516                     result = new MappedPropertyDescriptor(name, bean.getClass());
517                 }
518                 catch (IntrospectionException ie) {
519                 }
520                 if (result != null) {
521                     mappedDescriptors.put(name, result);
522                 }
523             }
524     
525             return result;
526         } catch ( RuntimeException ex ) {
527             LOG.error( "Unable to get property descriptor for " + bean.getClass().getName() + " . " + name
528                     + "\n" + ex.getClass().getName() + ": " + ex.getMessage() );
529             throw ex;
530         }
531     }
532     // end Kuali Foundation modification
533 
534     private int findNextNestedIndex(String expression)
535     {
536         // walk back from the end to the start
537         // and find the first index that
538         int bracketCount = 0;
539         for (int i=0, size=expression.length(); i<size ; i++) {
540             char at = expression.charAt(i);
541             switch (at) {
542                 case PropertyUtils.NESTED_DELIM:
543                     if (bracketCount < 1) {
544                         return i;
545                     }
546                     break;
547 
548                 case PropertyUtils.MAPPED_DELIM:
549                 case PropertyUtils.INDEXED_DELIM:
550                     // not bothered which
551                     ++bracketCount;
552                     break;
553 
554                 case PropertyUtils.MAPPED_DELIM2:
555                 case PropertyUtils.INDEXED_DELIM2:
556                     // not bothered which
557                     --bracketCount;
558                     break;
559             }
560         }
561         // can't find any
562         return -1;
563     }
564 
565     /**
566      * Set the value of the specified simple property of the specified bean,
567      * with no type conversions.
568      *
569      * @param bean Bean whose property is to be modified
570      * @param name Name of the property to be modified
571      * @param value Value to which the property should be set
572      *
573      * @exception IllegalAccessException if the caller does not have
574      *  access to the property accessor method
575      * @exception IllegalArgumentException if <code>bean</code> or
576      *  <code>name</code> is null
577      * @exception IllegalArgumentException if the property name is
578      *  nested or indexed
579      * @exception InvocationTargetException if the property accessor method
580      *  throws an exception
581      * @exception NoSuchMethodException if an accessor method for this
582      *  propety cannot be found
583      */
584     public void setSimpleProperty(Object bean,
585                                          String name, Object value)
586             throws IllegalAccessException, InvocationTargetException,
587             NoSuchMethodException {
588 
589         if (bean == null) {
590         	if (LOG.isDebugEnabled()) LOG.debug("No bean specified, name = " + name + ", value = " + value);
591         	return;
592         }
593         if (name == null) {
594             throw new IllegalArgumentException("No name specified");
595         }
596 
597         // Validate the syntax of the property name
598         if (name.indexOf(PropertyUtils.NESTED_DELIM) >= 0) {
599             throw new IllegalArgumentException
600                     ("Nested property names are not allowed");
601         } else if (name.indexOf(PropertyUtils.INDEXED_DELIM) >= 0) {
602             throw new IllegalArgumentException
603                     ("Indexed property names are not allowed");
604         } else if (name.indexOf(PropertyUtils.MAPPED_DELIM) >= 0) {
605             throw new IllegalArgumentException
606                     ("Mapped property names are not allowed");
607         }
608 
609         // Retrieve the property setter method for the specified property
610         PropertyDescriptor descriptor =
611                 getPropertyDescriptor(bean, name);
612         if (descriptor == null) {
613             throw new NoSuchMethodException("Unknown property '" +
614                     name + "'");
615         }
616         Method writeMethod = getWriteMethod(descriptor);
617         if (writeMethod == null) {
618             //throw new NoSuchMethodException("Property '" + name + "' has no setter method");
619         	LOG.warn("Bean: " + bean.getClass().getName() + ", Property '" + name + "' has no setter method");
620         	return;
621         }
622 
623         // Call the property setter method
624         Object values[] = new Object[1];
625         values[0] = value;
626         if (LOG.isDebugEnabled()) {
627             String valueClassName =
628                 value == null ? "<null>" : value.getClass().getName();
629             LOG.debug("setSimpleProperty: Invoking method " + writeMethod
630                       + " with value " + value + " (class " + valueClassName + ")");
631         }
632         
633         
634         invokeMethod(writeMethod, bean, values);
635 
636     }
637     
638     /** This just catches and wraps IllegalArgumentException. */
639     private Object invokeMethod(
640                         Method method, 
641                         Object bean, 
642                         Object[] values) 
643                             throws
644                                 IllegalAccessException,
645                                 InvocationTargetException {
646         try {
647             
648             return method.invoke(bean, values);
649         
650         } catch (IllegalArgumentException e) {
651             
652             LOG.error("Method invocation failed.", e);
653             throw new IllegalArgumentException(
654                 "Cannot invoke " + method.getDeclaringClass().getName() + "." 
655                 + method.getName() + " - " + e.getMessage());
656             
657         }
658     }
659     
660     public Class getPropertyType(Object bean, String name)
661             throws IllegalAccessException, InvocationTargetException,
662             NoSuchMethodException {
663 
664         if (bean == null) {
665             throw new IllegalArgumentException("No bean specified");
666         }
667         if (name == null) {
668             throw new IllegalArgumentException("No name specified for bean class '" +
669                     bean.getClass() + "'");
670         }
671 
672         // Resolve nested references
673         while (getResolver().hasNested(name)) {
674             String next = getResolver().next(name);
675             Object nestedBean = getProperty(bean, next);
676             if (nestedBean == null) {
677             	Class<?>[] paramTypes = {};
678             	Method method = null;
679             	try {
680                     method = bean.getClass().getMethod("get" + next.substring(0, 1).toUpperCase() + next.substring(1), (Class[])null);
681                 } catch (NoSuchMethodException e) {
682                     method = bean.getClass().getMethod("is" + next.substring(0, 1).toUpperCase() + next.substring(1), (Class[])null);
683                 }
684             	try {
685                     nestedBean = ObjectUtils.createNewObjectFromClass(method.getReturnType());
686 				} catch (RuntimeException e) {
687 					NestedNullException nne = new NestedNullException
688                     ("Null property value for '" + next +
689                     "' on bean class '" + bean.getClass() + "'");
690                     nne.initCause(e);
691                     throw nne;
692 				}
693             }
694             bean = nestedBean;
695             name = getResolver().remove(name);
696         }
697 
698         // Remove any subscript from the final name value
699         name = getResolver().getProperty(name);
700 
701         // Special handling for DynaBeans
702         if (bean instanceof DynaBean) {
703             DynaProperty descriptor =
704                     ((DynaBean) bean).getDynaClass().getDynaProperty(name);
705             if (descriptor == null) {
706                 return (null);
707             }
708             Class type = descriptor.getType();
709             if (type == null) {
710                 return (null);
711             } else if (type.isArray()) {
712                 return (type.getComponentType());
713             } else {
714                 return (type);
715             }
716         }
717 
718         PropertyDescriptor descriptor =
719                 getPropertyDescriptor(bean, name);
720         if (descriptor == null) {
721             return (null);
722         } else if (descriptor instanceof IndexedPropertyDescriptor) {
723             return (((IndexedPropertyDescriptor) descriptor).
724                     getIndexedPropertyType());
725         } else if (descriptor instanceof MappedPropertyDescriptor) {
726             return (((MappedPropertyDescriptor) descriptor).
727                     getMappedPropertyType());
728         } else {
729             return (descriptor.getPropertyType());
730         }
731 
732     }
733 }