View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.beanutils.locale;
19  
20  
21  import java.beans.IndexedPropertyDescriptor;
22  import java.beans.PropertyDescriptor;
23  import java.lang.reflect.InvocationTargetException;
24  import java.util.Locale;
25  
26  import org.apache.commons.beanutils.BeanUtilsBean;
27  import org.apache.commons.beanutils.ContextClassLoaderLocal;
28  import org.apache.commons.beanutils.ConvertUtils;
29  import org.apache.commons.beanutils.ConvertUtilsBean;
30  import org.apache.commons.beanutils.DynaBean;
31  import org.apache.commons.beanutils.DynaClass;
32  import org.apache.commons.beanutils.DynaProperty;
33  import org.apache.commons.beanutils.MappedPropertyDescriptor;
34  import org.apache.commons.beanutils.PropertyUtilsBean;
35  import org.apache.commons.beanutils.expression.Resolver;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  
40  /**
41   * <p>Utility methods for populating JavaBeans properties
42   * via reflection in a locale-dependent manner.</p>
43   *
44   * @since 1.7
45   * @version $Id$
46   */
47  
48  public class LocaleBeanUtilsBean extends BeanUtilsBean {
49  
50      /**
51       * Contains <code>LocaleBeanUtilsBean</code> instances indexed by context classloader.
52       */
53      private static final ContextClassLoaderLocal<LocaleBeanUtilsBean>
54              LOCALE_BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal<LocaleBeanUtilsBean>() {
55                          // Creates the default instance used when the context classloader is unavailable
56                          @Override
57                          protected LocaleBeanUtilsBean initialValue() {
58                              return new LocaleBeanUtilsBean();
59                          }
60                      };
61  
62       /**
63        * Gets singleton instance
64        *
65        * @return the singleton instance
66        */
67       public static LocaleBeanUtilsBean getLocaleBeanUtilsInstance() {
68          return LOCALE_BEANS_BY_CLASSLOADER.get();
69       }
70  
71      /**
72       * Sets the instance which provides the functionality for {@link LocaleBeanUtils}.
73       * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
74       * This mechanism provides isolation for web apps deployed in the same container.
75       *
76       * @param newInstance a new singleton instance
77       */
78      public static void setInstance(final LocaleBeanUtilsBean newInstance) {
79          LOCALE_BEANS_BY_CLASSLOADER.set(newInstance);
80      }
81  
82      /** All logging goes through this logger */
83      private final Log log = LogFactory.getLog(LocaleBeanUtilsBean.class);
84  
85      // ----------------------------------------------------- Instance Variables
86  
87      /** Convertor used by this class */
88      private final LocaleConvertUtilsBean localeConvertUtils;
89  
90      // --------------------------------------------------------- Constructors
91  
92      /** Construct instance with standard conversion bean */
93      public LocaleBeanUtilsBean() {
94          this.localeConvertUtils = new LocaleConvertUtilsBean();
95      }
96  
97      /**
98       * Construct instance that uses given locale conversion
99       *
100      * @param localeConvertUtils use this <code>localeConvertUtils</code> to perform
101      * conversions
102      * @param convertUtilsBean use this for standard conversions
103      * @param propertyUtilsBean use this for property conversions
104      */
105     public LocaleBeanUtilsBean(
106                             final LocaleConvertUtilsBean localeConvertUtils,
107                             final ConvertUtilsBean convertUtilsBean,
108                             final PropertyUtilsBean propertyUtilsBean) {
109         super(convertUtilsBean, propertyUtilsBean);
110         this.localeConvertUtils = localeConvertUtils;
111     }
112 
113     /**
114      * Construct instance that uses given locale conversion
115      *
116      * @param localeConvertUtils use this <code>localeConvertUtils</code> to perform
117      * conversions
118      */
119     public LocaleBeanUtilsBean(final LocaleConvertUtilsBean localeConvertUtils) {
120         this.localeConvertUtils = localeConvertUtils;
121     }
122 
123     // --------------------------------------------------------- Public Methods
124 
125     /**
126      * Gets the bean instance used for conversions
127      *
128      * @return the locale converter bean instance
129      */
130     public LocaleConvertUtilsBean getLocaleConvertUtils() {
131         return localeConvertUtils;
132     }
133 
134     /**
135      * Gets the default Locale
136      * @return the default locale
137      */
138     public Locale getDefaultLocale() {
139 
140         return getLocaleConvertUtils().getDefaultLocale();
141     }
142 
143 
144     /**
145      * Sets the default Locale.
146      *
147      * @param locale the default locale
148      */
149     public void setDefaultLocale(final Locale locale) {
150 
151         getLocaleConvertUtils().setDefaultLocale(locale);
152     }
153 
154     /**
155      * Is the pattern to be applied localized
156      * (Indicate whether the pattern is localized or not)
157      *
158      * @return <code>true</code> if pattern is localized,
159      * otherwise <code>false</code>
160      */
161     public boolean getApplyLocalized() {
162 
163         return getLocaleConvertUtils().getApplyLocalized();
164     }
165 
166     /**
167      * Sets whether the pattern is applied localized
168      * (Indicate whether the pattern is localized or not)
169      *
170      * @param newApplyLocalized <code>true</code> if pattern is localized,
171      * otherwise <code>false</code>
172      */
173     public void setApplyLocalized(final boolean newApplyLocalized) {
174 
175         getLocaleConvertUtils().setApplyLocalized(newApplyLocalized);
176     }
177 
178 
179     // --------------------------------------------------------- Public Methods
180 
181     /**
182      * Return the value of the specified locale-sensitive indexed property
183      * of the specified bean, as a String. The zero-relative index of the
184      * required value must be included (in square brackets) as a suffix to
185      * the property name, or <code>IllegalArgumentException</code> will be
186      * thrown.
187      *
188      * @param bean Bean whose property is to be extracted
189      * @param name <code>propertyname[index]</code> of the property value
190      *  to be extracted
191      * @param pattern The conversion pattern
192      * @return The indexed property's value, converted to a String
193      *
194      * @throws IllegalAccessException if the caller does not have
195      *  access to the property accessor method
196      * @throws InvocationTargetException if the property accessor method
197      *  throws an exception
198      * @throws NoSuchMethodException if an accessor method for this
199      *  propety cannot be found
200      */
201     public String getIndexedProperty(
202                                     final Object bean,
203                                     final String name,
204                                     final String pattern)
205                                         throws
206                                             IllegalAccessException,
207                                             InvocationTargetException,
208                                             NoSuchMethodException {
209 
210         final Object value = getPropertyUtils().getIndexedProperty(bean, name);
211         return getLocaleConvertUtils().convert(value, pattern);
212     }
213 
214     /**
215      * Return the value of the specified locale-sensitive indexed property
216      * of the specified bean, as a String using the default conversion pattern of
217      * the corresponding {@link LocaleConverter}. The zero-relative index
218      * of the required value must be included (in square brackets) as a suffix
219      * to the property name, or <code>IllegalArgumentException</code> will be thrown.
220      *
221      * @param bean Bean whose property is to be extracted
222      * @param name <code>propertyname[index]</code> of the property value
223      *  to be extracted
224      * @return The indexed property's value, converted to a String
225      *
226      * @throws IllegalAccessException if the caller does not have
227      *  access to the property accessor method
228      * @throws InvocationTargetException if the property accessor method
229      *  throws an exception
230      * @throws NoSuchMethodException if an accessor method for this
231      *  propety cannot be found
232      */
233     @Override
234     public String getIndexedProperty(
235                                     final Object bean,
236                                     final String name)
237                                         throws
238                                             IllegalAccessException,
239                                             InvocationTargetException,
240                                             NoSuchMethodException {
241 
242         return getIndexedProperty(bean, name, null);
243     }
244 
245     /**
246      * Return the value of the specified locale-sensetive indexed property
247      * of the specified bean, as a String using the specified conversion pattern.
248      * The index is specified as a method parameter and
249      * must *not* be included in the property name expression
250      *
251      * @param bean Bean whose property is to be extracted
252      * @param name Simple property name of the property value to be extracted
253      * @param index Index of the property value to be extracted
254      * @param pattern The conversion pattern
255      * @return The indexed property's value, converted to a String
256      *
257      * @throws IllegalAccessException if the caller does not have
258      *  access to the property accessor method
259      * @throws InvocationTargetException if the property accessor method
260      *  throws an exception
261      * @throws NoSuchMethodException if an accessor method for this
262      *  propety cannot be found
263      */
264     public String getIndexedProperty(final Object bean,
265                                             final String name, final int index, final String pattern)
266             throws IllegalAccessException, InvocationTargetException,
267             NoSuchMethodException {
268 
269         final Object value = getPropertyUtils().getIndexedProperty(bean, name, index);
270         return getLocaleConvertUtils().convert(value, pattern);
271     }
272 
273     /**
274      * Return the value of the specified locale-sensetive indexed property
275      * of the specified bean, as a String using the default conversion pattern of
276      * the corresponding {@link LocaleConverter}.
277      * The index is specified as a method parameter and
278      * must *not* be included in the property name expression
279      *
280      * @param bean Bean whose property is to be extracted
281      * @param name Simple property name of the property value to be extracted
282      * @param index Index of the property value to be extracted
283      * @return The indexed property's value, converted to a String
284      *
285      * @throws IllegalAccessException if the caller does not have
286      *  access to the property accessor method
287      * @throws InvocationTargetException if the property accessor method
288      *  throws an exception
289      * @throws NoSuchMethodException if an accessor method for this
290      *  propety cannot be found
291      */
292     @Override
293     public String getIndexedProperty(final Object bean,
294                                             final String name, final int index)
295             throws IllegalAccessException, InvocationTargetException,
296             NoSuchMethodException {
297         return getIndexedProperty(bean, name, index, null);
298     }
299 
300     /**
301      * Return the value of the specified simple locale-sensitive property
302      * of the specified bean, converted to a String using the specified
303      * conversion pattern.
304      *
305      * @param bean Bean whose property is to be extracted
306      * @param name Name of the property to be extracted
307      * @param pattern The conversion pattern
308      * @return The property's value, converted to a String
309      *
310      * @throws IllegalAccessException if the caller does not have
311      *  access to the property accessor method
312      * @throws InvocationTargetException if the property accessor method
313      *  throws an exception
314      * @throws NoSuchMethodException if an accessor method for this
315      *  property cannot be found
316      */
317     public String getSimpleProperty(final Object bean, final String name, final String pattern)
318             throws IllegalAccessException, InvocationTargetException,
319             NoSuchMethodException {
320 
321         final Object value = getPropertyUtils().getSimpleProperty(bean, name);
322         return getLocaleConvertUtils().convert(value, pattern);
323     }
324 
325     /**
326      * Return the value of the specified simple locale-sensitive property
327      * of the specified bean, converted to a String using the default
328      * conversion pattern of the corresponding {@link LocaleConverter}.
329      *
330      * @param bean Bean whose property is to be extracted
331      * @param name Name of the property to be extracted
332      * @return The property's value, converted to a String
333      *
334      * @throws IllegalAccessException if the caller does not have
335      *  access to the property accessor method
336      * @throws InvocationTargetException if the property accessor method
337      *  throws an exception
338      * @throws NoSuchMethodException if an accessor method for this
339      *  property cannot be found
340      */
341     @Override
342     public String getSimpleProperty(final Object bean, final String name)
343             throws IllegalAccessException, InvocationTargetException,
344             NoSuchMethodException {
345 
346         return getSimpleProperty(bean, name, null);
347     }
348 
349     /**
350      * Return the value of the specified mapped locale-sensitive property
351      * of the specified bean, as a String using the specified conversion pattern.
352      * The key is specified as a method parameter and must *not* be included in
353      * the property name expression.
354      *
355      * @param bean Bean whose property is to be extracted
356      * @param name Simple property name of the property value to be extracted
357      * @param key Lookup key of the property value to be extracted
358      * @param pattern The conversion pattern
359      * @return The mapped property's value, converted to a String
360      *
361      * @throws IllegalAccessException if the caller does not have
362      *  access to the property accessor method
363      * @throws InvocationTargetException if the property accessor method
364      *  throws an exception
365      * @throws NoSuchMethodException if an accessor method for this
366      *  property cannot be found
367      */
368     public String getMappedProperty(
369                                     final Object bean,
370                                     final String name,
371                                     final String key,
372                                     final String pattern)
373                                         throws
374                                             IllegalAccessException,
375                                             InvocationTargetException,
376                                             NoSuchMethodException {
377 
378         final Object value = getPropertyUtils().getMappedProperty(bean, name, key);
379         return getLocaleConvertUtils().convert(value, pattern);
380     }
381 
382     /**
383      * Return the value of the specified mapped locale-sensitive property
384      * of the specified bean, as a String
385      * The key is specified as a method parameter and must *not* be included
386      * in the property name expression
387      *
388      * @param bean Bean whose property is to be extracted
389      * @param name Simple property name of the property value to be extracted
390      * @param key Lookup key of the property value to be extracted
391      * @return The mapped property's value, converted to a String
392      *
393      * @throws IllegalAccessException if the caller does not have
394      *  access to the property accessor method
395      * @throws InvocationTargetException if the property accessor method
396      *  throws an exception
397      * @throws NoSuchMethodException if an accessor method for this
398      *  property cannot be found
399      */
400     @Override
401     public String getMappedProperty(final Object bean,
402                                            final String name, final String key)
403             throws IllegalAccessException, InvocationTargetException,
404             NoSuchMethodException {
405 
406         return getMappedProperty(bean, name, key, null);
407     }
408 
409 
410     /**
411      * Return the value of the specified locale-sensitive mapped property
412      * of the specified bean, as a String using the specified pattern.
413      * The String-valued key of the required value
414      * must be included (in parentheses) as a suffix to
415      * the property name, or <code>IllegalArgumentException</code> will be
416      * thrown.
417      *
418      * @param bean Bean whose property is to be extracted
419      * @param name <code>propertyname(index)</code> of the property value
420      *  to be extracted
421      * @param pattern The conversion pattern
422      * @return The mapped property's value, converted to a String
423      *
424      * @throws IllegalAccessException if the caller does not have
425      *  access to the property accessor method
426      * @throws InvocationTargetException if the property accessor method
427      *  throws an exception
428      * @throws NoSuchMethodException if an accessor method for this
429      *  property cannot be found
430      */
431     public String getMappedPropertyLocale(
432                                         final Object bean,
433                                         final String name,
434                                         final String pattern)
435                                             throws
436                                                 IllegalAccessException,
437                                                 InvocationTargetException,
438                                                 NoSuchMethodException {
439 
440         final Object value = getPropertyUtils().getMappedProperty(bean, name);
441         return getLocaleConvertUtils().convert(value, pattern);
442     }
443 
444 
445     /**
446      * Return the value of the specified locale-sensitive mapped property
447      * of the specified bean, as a String using the default
448      * conversion pattern of the corresponding {@link LocaleConverter}.
449      * The String-valued key of the required value
450      * must be included (in parentheses) as a suffix to
451      * the property name, or <code>IllegalArgumentException</code> will be
452      * thrown.
453      *
454      * @param bean Bean whose property is to be extracted
455      * @param name <code>propertyname(index)</code> of the property value
456      *  to be extracted
457      * @return The mapped property's value, converted to a String
458      *
459      * @throws IllegalAccessException if the caller does not have
460      *  access to the property accessor method
461      * @throws InvocationTargetException if the property accessor method
462      *  throws an exception
463      * @throws NoSuchMethodException if an accessor method for this
464      *  property cannot be found
465      */
466     @Override
467     public String getMappedProperty(final Object bean, final String name)
468                                     throws
469                                         IllegalAccessException,
470                                         InvocationTargetException,
471                                         NoSuchMethodException {
472 
473         return getMappedPropertyLocale(bean, name, null);
474     }
475 
476     /**
477      * Return the value of the (possibly nested) locale-sensitive property
478      * of the specified name, for the specified bean,
479      * as a String using the specified pattern.
480      *
481      * @param bean Bean whose property is to be extracted
482      * @param name Possibly nested name of the property to be extracted
483      * @param pattern The conversion pattern
484      * @return The nested property's value, converted to a String
485      *
486      * @throws IllegalAccessException if the caller does not have
487      *  access to the property accessor method
488      * @throws IllegalArgumentException if a nested reference to a
489      *  property returns null
490      * @throws InvocationTargetException if the property accessor method
491      *  throws an exception
492      * @throws NoSuchMethodException if an accessor method for this
493      *  property cannot be found
494      */
495     public String getNestedProperty(
496                                     final Object bean,
497                                     final String name,
498                                     final String pattern)
499                                         throws
500                                             IllegalAccessException,
501                                             InvocationTargetException,
502                                             NoSuchMethodException {
503 
504         final Object value = getPropertyUtils().getNestedProperty(bean, name);
505         return getLocaleConvertUtils().convert(value, pattern);
506     }
507 
508     /**
509      * Return the value of the (possibly nested) locale-sensitive property
510      * of the specified name, for the specified bean, as a String using the default
511      * conversion pattern of the corresponding {@link LocaleConverter}.
512      *
513      * @param bean Bean whose property is to be extracted
514      * @param name Possibly nested name of the property to be extracted
515      * @return The nested property's value, converted to a String
516      *
517      * @throws IllegalAccessException if the caller does not have
518      *  access to the property accessor method
519      * @throws IllegalArgumentException if a nested reference to a
520      *  property returns null
521      * @throws InvocationTargetException if the property accessor method
522      *  throws an exception
523      * @throws NoSuchMethodException if an accessor method for this
524      *  property cannot be found
525      */
526     @Override
527     public String getNestedProperty(final Object bean, final String name)
528                                     throws
529                                         IllegalAccessException,
530                                         InvocationTargetException,
531                                         NoSuchMethodException {
532 
533         return getNestedProperty(bean, name, null);
534     }
535 
536     /**
537      * Return the value of the specified locale-sensitive property
538      * of the specified bean, no matter which property reference
539      * format is used, as a String using the specified conversion pattern.
540      *
541      * @param bean Bean whose property is to be extracted
542      * @param name Possibly indexed and/or nested name of the property
543      *  to be extracted
544      * @param pattern The conversion pattern
545      * @return The nested property's value, converted to a String
546      *
547      * @throws IllegalAccessException if the caller does not have
548      *  access to the property accessor method
549      * @throws InvocationTargetException if the property accessor method
550      *  throws an exception
551      * @throws NoSuchMethodException if an accessor method for this
552      *  property cannot be found
553      */
554     public String getProperty(final Object bean, final String name, final String pattern)
555                                 throws
556                                     IllegalAccessException,
557                                     InvocationTargetException,
558                                     NoSuchMethodException {
559 
560         return getNestedProperty(bean, name, pattern);
561     }
562 
563     /**
564      * Return the value of the specified locale-sensitive property
565      * of the specified bean, no matter which property reference
566      * format is used, as a String using the default
567      * conversion pattern of the corresponding {@link LocaleConverter}.
568      *
569      * @param bean Bean whose property is to be extracted
570      * @param name Possibly indexed and/or nested name of the property
571      *  to be extracted
572      * @return The property's value, converted to a String
573      *
574      * @throws IllegalAccessException if the caller does not have
575      *  access to the property accessor method
576      * @throws InvocationTargetException if the property accessor method
577      *  throws an exception
578      * @throws NoSuchMethodException if an accessor method for this
579      *  property cannot be found
580      */
581     @Override
582     public String getProperty(final Object bean, final String name)
583                                 throws
584                                     IllegalAccessException,
585                                     InvocationTargetException,
586                                     NoSuchMethodException {
587 
588         return getNestedProperty(bean, name);
589     }
590 
591     /**
592      * Set the specified locale-sensitive property value, performing type
593      * conversions as required to conform to the type of the destination property
594      * using the default conversion pattern of the corresponding {@link LocaleConverter}.
595      *
596      * @param bean Bean on which setting is to be performed
597      * @param name Property name (can be nested/indexed/mapped/combo)
598      * @param value Value to be set
599      *
600      * @throws IllegalAccessException if the caller does not have
601      *  access to the property accessor method
602      * @throws InvocationTargetException if the property accessor method
603      *  throws an exception
604      */
605     @Override
606     public void setProperty(final Object bean, final String name, final Object value)
607                                 throws
608                                     IllegalAccessException,
609                                     InvocationTargetException {
610 
611         setProperty(bean, name, value, null);
612     }
613 
614     /**
615      * Set the specified locale-sensitive property value, performing type
616      * conversions as required to conform to the type of the destination
617      * property using the specified conversion pattern.
618      *
619      * @param bean Bean on which setting is to be performed
620      * @param name Property name (can be nested/indexed/mapped/combo)
621      * @param value Value to be set
622      * @param pattern The conversion pattern
623      *
624      * @throws IllegalAccessException if the caller does not have
625      *  access to the property accessor method
626      * @throws InvocationTargetException if the property accessor method
627      *  throws an exception
628      */
629     public void setProperty(
630                             final Object bean,
631                             String name,
632                             final Object value,
633                             final String pattern)
634                                 throws
635                                     IllegalAccessException,
636                                     InvocationTargetException {
637 
638         // Trace logging (if enabled)
639         if (log.isTraceEnabled()) {
640             final StringBuilder sb = new StringBuilder("  setProperty(");
641             sb.append(bean);
642             sb.append(", ");
643             sb.append(name);
644             sb.append(", ");
645             if (value == null) {
646                 sb.append("<NULL>");
647             }
648             else if (value instanceof String) {
649                 sb.append((String) value);
650             }
651             else if (value instanceof String[]) {
652                 final String[] values = (String[]) value;
653                 sb.append('[');
654                 for (int i = 0; i < values.length; i++) {
655                     if (i > 0) {
656                         sb.append(',');
657                     }
658                     sb.append(values[i]);
659                 }
660                 sb.append(']');
661             }
662             else {
663                 sb.append(value.toString());
664             }
665             sb.append(')');
666             log.trace(sb.toString());
667         }
668 
669         // Resolve any nested expression to get the actual target bean
670         Object target = bean;
671         final Resolver resolver = getPropertyUtils().getResolver();
672         while (resolver.hasNested(name)) {
673             try {
674                 target = getPropertyUtils().getProperty(target, resolver.next(name));
675                 name = resolver.remove(name);
676             } catch (final NoSuchMethodException e) {
677                 return; // Skip this property setter
678             }
679         }
680         if (log.isTraceEnabled()) {
681             log.trace("    Target bean = " + target);
682             log.trace("    Target name = " + name);
683         }
684 
685         // Declare local variables we will require
686         final String propName = resolver.getProperty(name); // Simple name of target property
687         final int index  = resolver.getIndex(name);         // Indexed subscript value (if any)
688         final String key = resolver.getKey(name);           // Mapped key value (if any)
689 
690         final Class<?> type = definePropertyType(target, name, propName);
691         if (type != null) {
692             final Object newValue = convert(type, index, value, pattern);
693             invokeSetter(target, propName, key, index, newValue);
694         }
695     }
696 
697     /**
698      * Calculate the property type.
699      *
700      * @param target The bean
701      * @param name The property name
702      * @param propName The Simple name of target property
703      * @return The property's type
704      *
705      * @throws IllegalAccessException if the caller does not have
706      *  access to the property accessor method
707      * @throws InvocationTargetException if the property accessor method
708      *  throws an exception
709      */
710     protected Class<?> definePropertyType(final Object target, final String name, final String propName)
711             throws IllegalAccessException, InvocationTargetException {
712 
713         Class<?> type = null;               // Java type of target property
714 
715         if (target instanceof DynaBean) {
716             final DynaClass dynaClass = ((DynaBean) target).getDynaClass();
717             final DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
718             if (dynaProperty == null) {
719                 return null; // Skip this property setter
720             }
721             type = dynaProperty.getType();
722         }
723         else {
724             PropertyDescriptor descriptor = null;
725             try {
726                 descriptor =
727                         getPropertyUtils().getPropertyDescriptor(target, name);
728                 if (descriptor == null) {
729                     return null; // Skip this property setter
730                 }
731             }
732             catch (final NoSuchMethodException e) {
733                 return null; // Skip this property setter
734             }
735             if (descriptor instanceof MappedPropertyDescriptor) {
736                 type = ((MappedPropertyDescriptor) descriptor).
737                         getMappedPropertyType();
738             }
739             else if (descriptor instanceof IndexedPropertyDescriptor) {
740                 type = ((IndexedPropertyDescriptor) descriptor).
741                         getIndexedPropertyType();
742             }
743             else {
744                 type = descriptor.getPropertyType();
745             }
746         }
747         return type;
748     }
749 
750     /**
751      * Convert the specified value to the required type using the
752      * specified conversion pattern.
753      *
754      * @param type The Java type of target property
755      * @param index The indexed subscript value (if any)
756      * @param value The value to be converted
757      * @param pattern The conversion pattern
758      * @return The converted value
759      */
760     protected Object convert(final Class<?> type, final int index, final Object value, final String pattern) {
761 
762         if (log.isTraceEnabled()) {
763             log.trace("Converting value '" + value + "' to type:" + type);
764         }
765 
766         Object newValue = null;
767 
768         if (type.isArray() && (index < 0)) { // Scalar value into array
769             if (value instanceof String) {
770                 final String[] values = new String[1];
771                 values[0] = (String) value;
772                 newValue = getLocaleConvertUtils().convert(values, type, pattern);
773             }
774             else if (value instanceof String[]) {
775                 newValue = getLocaleConvertUtils().convert((String[]) value, type, pattern);
776             }
777             else {
778                 newValue = value;
779             }
780         }
781         else if (type.isArray()) {         // Indexed value into array
782             if (value instanceof String) {
783                 newValue = getLocaleConvertUtils().convert((String) value,
784                         type.getComponentType(), pattern);
785             }
786             else if (value instanceof String[]) {
787                 newValue = getLocaleConvertUtils().convert(((String[]) value)[0],
788                         type.getComponentType(), pattern);
789             }
790             else {
791                 newValue = value;
792             }
793         }
794         else {                             // Value into scalar
795             if (value instanceof String) {
796                 newValue = getLocaleConvertUtils().convert((String) value, type, pattern);
797             }
798             else if (value instanceof String[]) {
799                 newValue = getLocaleConvertUtils().convert(((String[]) value)[0],
800                         type, pattern);
801             }
802             else {
803                 newValue = value;
804             }
805         }
806         return newValue;
807     }
808 
809     /**
810      *  Convert the specified value to the required type.
811      *
812      * @param type The Java type of target property
813      * @param index The indexed subscript value (if any)
814      * @param value The value to be converted
815      * @return The converted value
816      */
817     protected Object convert(final Class<?> type, final int index, final Object value) {
818 
819         Object newValue = null;
820 
821         if (type.isArray() && (index < 0)) { // Scalar value into array
822             if (value instanceof String) {
823                 final String[] values = new String[1];
824                 values[0] = (String) value;
825                 newValue = ConvertUtils.convert(values, type);
826             }
827             else if (value instanceof String[]) {
828                 newValue = ConvertUtils.convert((String[]) value, type);
829             }
830             else {
831                 newValue = value;
832             }
833         }
834         else if (type.isArray()) {         // Indexed value into array
835             if (value instanceof String) {
836                 newValue = ConvertUtils.convert((String) value,
837                         type.getComponentType());
838             }
839             else if (value instanceof String[]) {
840                 newValue = ConvertUtils.convert(((String[]) value)[0],
841                         type.getComponentType());
842             }
843             else {
844                 newValue = value;
845             }
846         }
847         else {                             // Value into scalar
848             if (value instanceof String) {
849                 newValue = ConvertUtils.convert((String) value, type);
850             }
851             else if (value instanceof String[]) {
852                 newValue = ConvertUtils.convert(((String[]) value)[0],
853                         type);
854             }
855             else {
856                 newValue = value;
857             }
858         }
859         return newValue;
860     }
861 
862     /**
863      * Invoke the setter method.
864      *
865      * @param target The bean
866      * @param propName The Simple name of target property
867      * @param key The Mapped key value (if any)
868      * @param index The indexed subscript value (if any)
869      * @param newValue The value to be set
870      *
871      * @throws IllegalAccessException if the caller does not have
872      *  access to the property accessor method
873      * @throws InvocationTargetException if the property accessor method
874      *  throws an exception
875      */
876     protected void invokeSetter(final Object target, final String propName, final String key, final int index, final Object newValue)
877             throws IllegalAccessException, InvocationTargetException {
878 
879         try {
880             if (index >= 0) {
881                 getPropertyUtils().setIndexedProperty(target, propName,
882                         index, newValue);
883             }
884             else if (key != null) {
885                 getPropertyUtils().setMappedProperty(target, propName,
886                         key, newValue);
887             }
888             else {
889                 getPropertyUtils().setProperty(target, propName, newValue);
890             }
891         }
892         catch (final NoSuchMethodException e) {
893             throw new InvocationTargetException
894                     (e, "Cannot set " + propName);
895         }
896     }
897 
898     /**
899      * Resolve any nested expression to get the actual target property.
900      *
901      * @param bean The bean
902      * @param name The property name
903      * @return The property's descriptor
904      *
905      * @throws IllegalAccessException if the caller does not have
906      *  access to the property accessor method
907      * @throws InvocationTargetException if the property accessor method
908      *  throws an exception
909      * @deprecated Property name expressions are now processed by
910      * the configured {@link Resolver} implementation and this method
911      * is no longer used by BeanUtils.
912      */
913     @Deprecated
914     protected Descriptor calculate(final Object bean, String name)
915             throws IllegalAccessException, InvocationTargetException {
916 
917         // Resolve any nested expression to get the actual target bean
918         Object target = bean;
919         final Resolver resolver = getPropertyUtils().getResolver();
920         while (resolver.hasNested(name)) {
921             try {
922                 target = getPropertyUtils().getProperty(target, resolver.next(name));
923                 name = resolver.remove(name);
924             } catch (final NoSuchMethodException e) {
925                 return null; // Skip this property setter
926             }
927         }
928         if (log.isTraceEnabled()) {
929             log.trace("    Target bean = " + target);
930             log.trace("    Target name = " + name);
931         }
932 
933         // Declare local variables we will require
934         final String propName = resolver.getProperty(name); // Simple name of target property
935         final int index  = resolver.getIndex(name);         // Indexed subscript value (if any)
936         final String key = resolver.getKey(name);           // Mapped key value (if any)
937 
938         return new Descriptor(target, name, propName, key, index);
939     }
940 
941     /**
942      * @deprecated Property name expressions are now processed by
943      * the configured {@link Resolver} implementation and this class
944      * is no longer used by BeanUtils.
945      */
946     @Deprecated
947     protected class Descriptor {
948 
949         private int index = -1;    // Indexed subscript value (if any)
950         private String name;
951         private String propName;   // Simple name of target property
952         private String key;        // Mapped key value (if any)
953         private Object target;
954 
955         /**
956          * Construct a descriptor instance for the target bean and property.
957          *
958          * @param target The target bean
959          * @param name The property name (includes indexed/mapped expr)
960          * @param propName The property name
961          * @param key The mapped property key (if any)
962          * @param index The indexed property index (if any)
963          */
964         public Descriptor(final Object target, final String name, final String propName, final String key, final int index) {
965 
966             setTarget(target);
967             setName(name);
968             setPropName(propName);
969             setKey(key);
970             setIndex(index);
971         }
972 
973         /**
974          * Return the target bean.
975          *
976          * @return The descriptors target bean
977          */
978         public Object getTarget() {
979             return target;
980         }
981 
982         /**
983          * Set the target bean.
984          *
985          * @param target The target bean
986          */
987         public void setTarget(final Object target) {
988             this.target = target;
989         }
990 
991         /**
992          * Return the mapped property key.
993          *
994          * @return the mapped property key (if any)
995          */
996         public String getKey() {
997             return key;
998         }
999 
1000         /**
1001          * Set the mapped property key.
1002          *
1003          * @param key The mapped property key (if any)
1004          */
1005         public void setKey(final String key) {
1006             this.key = key;
1007         }
1008 
1009         /**
1010          * Return indexed property index.
1011          *
1012          * @return indexed property index (if any)
1013          */
1014         public int getIndex() {
1015             return index;
1016         }
1017 
1018         /**
1019          * Set the indexed property index.
1020          *
1021          * @param index The indexed property index (if any)
1022          */
1023         public void setIndex(final int index) {
1024             this.index = index;
1025         }
1026 
1027         /**
1028          * Return property name (includes indexed/mapped expr).
1029          *
1030          * @return The property name (includes indexed/mapped expr)
1031          */
1032         public String getName() {
1033             return name;
1034         }
1035 
1036         /**
1037          * Set the property name (includes indexed/mapped expr).
1038          *
1039          * @param name The property name (includes indexed/mapped expr)
1040          */
1041         public void setName(final String name) {
1042             this.name = name;
1043         }
1044 
1045         /**
1046          * Return the property name.
1047          *
1048          * @return The property name
1049          */
1050         public String getPropName() {
1051             return propName;
1052         }
1053 
1054         /**
1055          * Set the property name.
1056          *
1057          * @param propName The property name
1058          */
1059         public void setPropName(final String propName) {
1060             this.propName = propName;
1061         }
1062     }
1063 }
1064 
1065