    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