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     
018    package org.apache.commons.beanutils.locale;
019    
020    
021    import org.apache.commons.beanutils.BeanUtilsBean;
022    import org.apache.commons.beanutils.ConvertUtils;
023    import org.apache.commons.beanutils.ConvertUtilsBean;
024    import org.apache.commons.beanutils.DynaBean;
025    import org.apache.commons.beanutils.DynaClass;
026    import org.apache.commons.beanutils.DynaProperty;
027    import org.apache.commons.beanutils.MappedPropertyDescriptor;
028    import org.apache.commons.beanutils.PropertyUtilsBean;
029    import org.apache.commons.beanutils.ContextClassLoaderLocal;
030    import org.apache.commons.beanutils.expression.Resolver;
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    
034    import java.beans.IndexedPropertyDescriptor;
035    import java.beans.PropertyDescriptor;
036    import java.lang.reflect.InvocationTargetException;
037    import 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    
053    public 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