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