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