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