    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 *      https://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.beanutils2.locale;
    019
    020import java.beans.IndexedPropertyDescriptor;
    021import java.beans.PropertyDescriptor;
    022import java.lang.reflect.InvocationTargetException;
    023import java.util.Locale;
    024
    025import org.apache.commons.beanutils2.BeanUtilsBean;
    026import org.apache.commons.beanutils2.ContextClassLoaderLocal;
    027import org.apache.commons.beanutils2.ConvertUtils;
    028import org.apache.commons.beanutils2.ConvertUtilsBean;
    029import org.apache.commons.beanutils2.DynaBean;
    030import org.apache.commons.beanutils2.DynaClass;
    031import org.apache.commons.beanutils2.DynaProperty;
    032import org.apache.commons.beanutils2.MappedPropertyDescriptor;
    033import org.apache.commons.beanutils2.PropertyUtilsBean;
    034import org.apache.commons.beanutils2.expression.Resolver;
    035import org.apache.commons.logging.Log;
    036import org.apache.commons.logging.LogFactory;
    037
    038/**
    039 * <p>
    040 * Utility methods for populating JavaBeans properties via reflection in a locale-dependent manner.
    041 * </p>
    042 *
    043 * @since 1.7
    044 */
    045public class LocaleBeanUtilsBean extends BeanUtilsBean {
    046
    047    /**
    048     * Contains {@code LocaleBeanUtilsBean} instances indexed by context classloader.
    049     */
    050    private static final ContextClassLoaderLocal<LocaleBeanUtilsBean> LOCALE_BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal<LocaleBeanUtilsBean>() {
    051        // Creates the default instance used when the context classloader is unavailable
    052        @Override
    053        protected LocaleBeanUtilsBean initialValue() {
    054            return new LocaleBeanUtilsBean();
    055        }
    056    };
    057
    058    /** All logging goes through this logger */
    059    private static final Log LOG = LogFactory.getLog(LocaleBeanUtilsBean.class);
    060
    061    /**
    062     * Gets singleton instance
    063     *
    064     * @return the singleton instance
    065     */
    066    public static LocaleBeanUtilsBean getLocaleBeanUtilsInstance() {
    067        return LOCALE_BEANS_BY_CLASSLOADER.get();
    068    }
    069
    070    /**
    071     * Sets the instance which provides the functionality for {@link LocaleBeanUtils}. This is a pseudo-singleton - an single instance is provided per (thread)
    072     * context classloader. This mechanism provides isolation for web apps deployed in the same container.
    073     *
    074     * @param newInstance a new singleton instance
    075     */
    076    public static void setInstance(final LocaleBeanUtilsBean newInstance) {
    077        LOCALE_BEANS_BY_CLASSLOADER.set(newInstance);
    078    }
    079
    080    /** Convertor used by this class */
    081    private final LocaleConvertUtilsBean localeConvertUtils;
    082
    083    /** Constructs instance with standard conversion bean */
    084    public LocaleBeanUtilsBean() {
    085        this.localeConvertUtils = new LocaleConvertUtilsBean();
    086    }
    087
    088    /**
    089     * Constructs instance that uses given locale conversion
    090     *
    091     * @param localeConvertUtils use this {@code localeConvertUtils} to perform conversions
    092     */
    093    public LocaleBeanUtilsBean(final LocaleConvertUtilsBean localeConvertUtils) {
    094        this.localeConvertUtils = localeConvertUtils;
    095    }
    096
    097    /**
    098     * Constructs instance that uses given locale conversion
    099     *
    100     * @param localeConvertUtils use this {@code localeConvertUtils} to perform conversions
    101     * @param convertUtilsBean   use this for standard conversions
    102     * @param propertyUtilsBean  use this for property conversions
    103     */
    104    public LocaleBeanUtilsBean(final LocaleConvertUtilsBean localeConvertUtils, final ConvertUtilsBean convertUtilsBean,
    105            final PropertyUtilsBean propertyUtilsBean) {
    106        super(convertUtilsBean, propertyUtilsBean);
    107        this.localeConvertUtils = localeConvertUtils;
    108    }
    109
    110    /**
    111     * Convert the specified value to the required type.
    112     *
    113     * @param type  The Java type of target property
    114     * @param index The indexed subscript value (if any)
    115     * @param value The value to be converted
    116     * @return The converted value
    117     */
    118    protected Object convert(final Class<?> type, final int index, final Object value) {
    119        Object newValue = null;
    120
    121        if (type.isArray() && index < 0) { // Scalar value into array
    122            if (value instanceof String) {
    123                final String[] values = new String[1];
    124                values[0] = (String) value;
    125                newValue = ConvertUtils.convert(values, type);
    126            } else if (value instanceof String[]) {
    127                newValue = ConvertUtils.convert((String[]) value, type);
    128            } else {
    129                newValue = value;
    130            }
    131        } else if (type.isArray()) { // Indexed value into array
    132            if (value instanceof String) {
    133                newValue = ConvertUtils.convert((String) value, type.getComponentType());
    134            } else if (value instanceof String[]) {
    135                newValue = ConvertUtils.convert(((String[]) value)[0], type.getComponentType());
    136            } else {
    137                newValue = value;
    138            }
    139        } else if (value instanceof String) {
    140            newValue = ConvertUtils.convert((String) value, type);
    141        } else if (value instanceof String[]) {
    142            newValue = ConvertUtils.convert(((String[]) value)[0], type);
    143        } else {
    144            newValue = value;
    145        }
    146        return newValue;
    147    }
    148
    149    /**
    150     * Convert the specified value to the required type using the specified conversion pattern.
    151     *
    152     * @param type    The Java type of target property
    153     * @param index   The indexed subscript value (if any)
    154     * @param value   The value to be converted
    155     * @param pattern The conversion pattern
    156     * @return The converted value
    157     */
    158    protected Object convert(final Class<?> type, final int index, final Object value, final String pattern) {
    159        if (LOG.isTraceEnabled()) {
    160            LOG.trace("Converting value '" + value + "' to type:" + type);
    161        }
    162
    163        Object newValue = null;
    164
    165        if (type.isArray() && index < 0) { // Scalar value into array
    166            if (value instanceof String) {
    167                final String[] values = new String[1];
    168                values[0] = (String) value;
    169                newValue = getLocaleConvertUtils().convert(values, type, pattern);
    170            } else if (value instanceof String[]) {
    171                newValue = getLocaleConvertUtils().convert((String[]) value, type, pattern);
    172            } else {
    173                newValue = value;
    174            }
    175        } else if (type.isArray()) { // Indexed value into array
    176            if (value instanceof String) {
    177                newValue = getLocaleConvertUtils().convert((String) value, type.getComponentType(), pattern);
    178            } else if (value instanceof String[]) {
    179                newValue = getLocaleConvertUtils().convert(((String[]) value)[0], type.getComponentType(), pattern);
    180            } else {
    181                newValue = value;
    182            }
    183        } else if (value instanceof String) {
    184            newValue = getLocaleConvertUtils().convert((String) value, type, pattern);
    185        } else if (value instanceof String[]) {
    186            newValue = getLocaleConvertUtils().convert(((String[]) value)[0], type, pattern);
    187        } else {
    188            newValue = value;
    189        }
    190        return newValue;
    191    }
    192
    193    /**
    194     * Calculate the property type.
    195     *
    196     * @param target   The bean
    197     * @param name     The property name
    198     * @param propName The Simple name of target property
    199     * @return The property's type
    200     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    201     * @throws InvocationTargetException if the property accessor method throws an exception
    202     */
    203    protected Class<?> definePropertyType(final Object target, final String name, final String propName)
    204            throws IllegalAccessException, InvocationTargetException {
    205        Class<?> type = null; // Java type of target property
    206
    207        if (target instanceof DynaBean) {
    208            final DynaClass dynaClass = ((DynaBean) target).getDynaClass();
    209            final DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
    210            if (dynaProperty == null) {
    211                return null; // Skip this property setter
    212            }
    213            type = dynaProperty.getType();
    214        } else {
    215            PropertyDescriptor descriptor = null;
    216            try {
    217                descriptor = getPropertyUtils().getPropertyDescriptor(target, name);
    218                if (descriptor == null) {
    219                    return null; // Skip this property setter
    220                }
    221            } catch (final NoSuchMethodException e) {
    222                return null; // Skip this property setter
    223            }
    224            if (descriptor instanceof MappedPropertyDescriptor) {
    225                type = ((MappedPropertyDescriptor) descriptor).getMappedPropertyType();
    226            } else if (descriptor instanceof IndexedPropertyDescriptor) {
    227                type = ((IndexedPropertyDescriptor) descriptor).getIndexedPropertyType();
    228            } else {
    229                type = descriptor.getPropertyType();
    230            }
    231        }
    232        return type;
    233    }
    234
    235    /**
    236     * Is the pattern to be applied localized (Indicate whether the pattern is localized or not)
    237     *
    238     * @return {@code true} if pattern is localized, otherwise {@code false}
    239     */
    240    public boolean getApplyLocalized() {
    241        return getLocaleConvertUtils().getApplyLocalized();
    242    }
    243
    244    /**
    245     * Gets the default Locale
    246     *
    247     * @return the default locale
    248     */
    249    public Locale getDefaultLocale() {
    250        return getLocaleConvertUtils().getDefaultLocale();
    251    }
    252
    253    /**
    254     * Gets the value of the specified locale-sensitive indexed property of the specified bean, as a String using the default conversion pattern of the
    255     * corresponding {@link LocaleConverter}. The zero-relative index of the required value must be included (in square brackets) as a suffix to the property
    256     * name, or {@code IllegalArgumentException} will be thrown.
    257     *
    258     * @param bean Bean whose property is to be extracted
    259     * @param name {@code propertyname[index]} of the property value to be extracted
    260     * @return The indexed property's value, converted to a String
    261     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    262     * @throws InvocationTargetException if the property accessor method throws an exception
    263     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    264     */
    265    @Override
    266    public String getIndexedProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    267        return getIndexedProperty(bean, name, null);
    268    }
    269
    270    /**
    271     * Gets the value of the specified locale-sensetive indexed property of the specified bean, as a String using the default conversion pattern of the
    272     * corresponding {@link LocaleConverter}. The index is specified as a method parameter and must *not* be included in the property name expression
    273     *
    274     * @param bean  Bean whose property is to be extracted
    275     * @param name  Simple property name of the property value to be extracted
    276     * @param index Index of the property value to be extracted
    277     * @return The indexed property's value, converted to a String
    278     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    279     * @throws InvocationTargetException if the property accessor method throws an exception
    280     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    281     */
    282    @Override
    283    public String getIndexedProperty(final Object bean, final String name, final int index)
    284            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    285        return getIndexedProperty(bean, name, index, null);
    286    }
    287
    288    /**
    289     * Gets the value of the specified locale-sensetive indexed property of the specified bean, as a String using the specified conversion pattern. The index is
    290     * specified as a method parameter and must *not* be included in the property name expression
    291     *
    292     * @param bean    Bean whose property is to be extracted
    293     * @param name    Simple property name of the property value to be extracted
    294     * @param index   Index of the property value to be extracted
    295     * @param pattern The conversion pattern
    296     * @return The indexed property's value, converted to a String
    297     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    298     * @throws InvocationTargetException if the property accessor method throws an exception
    299     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    300     */
    301    public String getIndexedProperty(final Object bean, final String name, final int index, final String pattern)
    302            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    303        final Object value = getPropertyUtils().getIndexedProperty(bean, name, index);
    304        return getLocaleConvertUtils().convert(value, pattern);
    305    }
    306
    307    /**
    308     * Gets the value of the specified locale-sensitive indexed property of the specified bean, as a String. The zero-relative index of the required value must
    309     * be included (in square brackets) as a suffix to the property name, or {@code IllegalArgumentException} will be thrown.
    310     *
    311     * @param bean    Bean whose property is to be extracted
    312     * @param name    {@code propertyname[index]} of the property value to be extracted
    313     * @param pattern The conversion pattern
    314     * @return The indexed property's value, converted to a String
    315     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    316     * @throws InvocationTargetException if the property accessor method throws an exception
    317     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    318     */
    319    public String getIndexedProperty(final Object bean, final String name, final String pattern)
    320            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    321        final Object value = getPropertyUtils().getIndexedProperty(bean, name);
    322        return getLocaleConvertUtils().convert(value, pattern);
    323    }
    324
    325    /**
    326     * Gets the bean instance used for conversions
    327     *
    328     * @return the locale converter bean instance
    329     */
    330    public LocaleConvertUtilsBean getLocaleConvertUtils() {
    331        return localeConvertUtils;
    332    }
    333
    334    /**
    335     * Gets the value of the specified locale-sensitive mapped property of the specified bean, as a String using the default conversion pattern of the
    336     * corresponding {@link LocaleConverter}. The String-valued key of the required value must be included (in parentheses) as a suffix to the property name, or
    337     * {@code IllegalArgumentException} will be thrown.
    338     *
    339     * @param bean Bean whose property is to be extracted
    340     * @param name {@code propertyname(index)} of the property value to be extracted
    341     * @return The mapped property's value, converted to a String
    342     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    343     * @throws InvocationTargetException if the property accessor method throws an exception
    344     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    345     */
    346    @Override
    347    public String getMappedProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    348        return getMappedPropertyLocale(bean, name, null);
    349    }
    350
    351    /**
    352     * Gets the value of the specified mapped locale-sensitive property of the specified bean, as a String The key is specified as a method parameter and must
    353     * *not* be included in 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     * @return The mapped property's value, converted to a String
    359     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    360     * @throws InvocationTargetException if the property accessor method throws an exception
    361     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    362     */
    363    @Override
    364    public String getMappedProperty(final Object bean, final String name, final String key)
    365            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    366        return getMappedProperty(bean, name, key, null);
    367    }
    368
    369    /**
    370     * Gets the value of the specified mapped locale-sensitive property of the specified bean, as a String using the specified conversion pattern. The key is
    371     * specified as a method parameter and must *not* be included in the property name expression.
    372     *
    373     * @param bean    Bean whose property is to be extracted
    374     * @param name    Simple property name of the property value to be extracted
    375     * @param key     Lookup key of the property value to be extracted
    376     * @param pattern The conversion pattern
    377     * @return The mapped property's value, converted to a String
    378     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    379     * @throws InvocationTargetException if the property accessor method throws an exception
    380     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    381     */
    382    public String getMappedProperty(final Object bean, final String name, final String key, final String pattern)
    383            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    384        final Object value = getPropertyUtils().getMappedProperty(bean, name, key);
    385        return getLocaleConvertUtils().convert(value, pattern);
    386    }
    387
    388    /**
    389     * Gets the value of the specified locale-sensitive mapped property of the specified bean, as a String using the specified pattern. The String-valued key of
    390     * the required value must be included (in parentheses) as a suffix to the property name, or {@code IllegalArgumentException} will be thrown.
    391     *
    392     * @param bean    Bean whose property is to be extracted
    393     * @param name    {@code propertyname(index)} of the property value to be extracted
    394     * @param pattern The conversion pattern
    395     * @return The mapped property's value, converted to a String
    396     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    397     * @throws InvocationTargetException if the property accessor method throws an exception
    398     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    399     */
    400    public String getMappedPropertyLocale(final Object bean, final String name, final String pattern)
    401            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    402        final Object value = getPropertyUtils().getMappedProperty(bean, name);
    403        return getLocaleConvertUtils().convert(value, pattern);
    404    }
    405
    406    /**
    407     * Gets the value of the (possibly nested) locale-sensitive property of the specified name, for the specified bean, as a String using the default conversion
    408     * pattern of the corresponding {@link LocaleConverter}.
    409     *
    410     * @param bean Bean whose property is to be extracted
    411     * @param name Possibly nested name of the property to be extracted
    412     * @return The nested property's value, converted to a String
    413     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    414     * @throws IllegalArgumentException  if a nested reference to a property returns null
    415     * @throws InvocationTargetException if the property accessor method throws an exception
    416     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    417     */
    418    @Override
    419    public String getNestedProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    420        return getNestedProperty(bean, name, null);
    421    }
    422
    423    /**
    424     * Gets the value of the (possibly nested) locale-sensitive property of the specified name, for the specified bean, as a String using the specified pattern.
    425     *
    426     * @param bean    Bean whose property is to be extracted
    427     * @param name    Possibly nested name of the property to be extracted
    428     * @param pattern The conversion pattern
    429     * @return The nested property's value, converted to a String
    430     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    431     * @throws IllegalArgumentException  if a nested reference to a property returns null
    432     * @throws InvocationTargetException if the property accessor method throws an exception
    433     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    434     */
    435    public String getNestedProperty(final Object bean, final String name, final String pattern)
    436            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    437        final Object value = getPropertyUtils().getNestedProperty(bean, name);
    438        return getLocaleConvertUtils().convert(value, pattern);
    439    }
    440
    441    /**
    442     * Gets the value of the specified locale-sensitive property of the specified bean, no matter which property reference format is used, as a String using the
    443     * default conversion pattern of the corresponding {@link LocaleConverter}.
    444     *
    445     * @param bean Bean whose property is to be extracted
    446     * @param name Possibly indexed and/or nested name of the property to be extracted
    447     * @return The property's value, converted to a String
    448     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    449     * @throws InvocationTargetException if the property accessor method throws an exception
    450     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    451     */
    452    @Override
    453    public String getProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    454        return getNestedProperty(bean, name);
    455    }
    456
    457    /**
    458     * Gets the value of the specified locale-sensitive property of the specified bean, no matter which property reference format is used, as a String using the
    459     * specified conversion pattern.
    460     *
    461     * @param bean    Bean whose property is to be extracted
    462     * @param name    Possibly indexed and/or nested name of the property to be extracted
    463     * @param pattern The conversion pattern
    464     * @return The nested property's value, converted to a String
    465     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    466     * @throws InvocationTargetException if the property accessor method throws an exception
    467     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    468     */
    469    public String getProperty(final Object bean, final String name, final String pattern)
    470            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    471        return getNestedProperty(bean, name, pattern);
    472    }
    473
    474    /**
    475     * Gets the value of the specified simple locale-sensitive property of the specified bean, converted to a String using the default conversion pattern of the
    476     * corresponding {@link LocaleConverter}.
    477     *
    478     * @param bean Bean whose property is to be extracted
    479     * @param name Name of the property to be extracted
    480     * @return The property's value, converted to a String
    481     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    482     * @throws InvocationTargetException if the property accessor method throws an exception
    483     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    484     */
    485    @Override
    486    public String getSimpleProperty(final Object bean, final String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    487        return getSimpleProperty(bean, name, null);
    488    }
    489
    490    /**
    491     * Gets the value of the specified simple locale-sensitive property of the specified bean, converted to a String using the specified conversion pattern.
    492     *
    493     * @param bean    Bean whose property is to be extracted
    494     * @param name    Name of the property to be extracted
    495     * @param pattern The conversion pattern
    496     * @return The property's value, converted to a String
    497     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    498     * @throws InvocationTargetException if the property accessor method throws an exception
    499     * @throws NoSuchMethodException     if an accessor method for this property cannot be found
    500     */
    501    public String getSimpleProperty(final Object bean, final String name, final String pattern)
    502            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    503        final Object value = getPropertyUtils().getSimpleProperty(bean, name);
    504        return getLocaleConvertUtils().convert(value, pattern);
    505    }
    506
    507    /**
    508     * Invoke the setter method.
    509     *
    510     * @param target   The bean
    511     * @param propName The Simple name of target property
    512     * @param key      The Mapped key value (if any)
    513     * @param index    The indexed subscript value (if any)
    514     * @param newValue The value to be set
    515     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    516     * @throws InvocationTargetException if the property accessor method throws an exception
    517     */
    518    protected void invokeSetter(final Object target, final String propName, final String key, final int index, final Object newValue)
    519            throws IllegalAccessException, InvocationTargetException {
    520
    521        try {
    522            if (index >= 0) {
    523                getPropertyUtils().setIndexedProperty(target, propName, index, newValue);
    524            } else if (key != null) {
    525                getPropertyUtils().setMappedProperty(target, propName, key, newValue);
    526            } else {
    527                getPropertyUtils().setProperty(target, propName, newValue);
    528            }
    529        } catch (final NoSuchMethodException e) {
    530            throw new InvocationTargetException(e, "Cannot set " + propName);
    531        }
    532    }
    533
    534    /**
    535     * Sets whether the pattern is applied localized (Indicate whether the pattern is localized or not)
    536     *
    537     * @param newApplyLocalized {@code true} if pattern is localized, otherwise {@code false}
    538     */
    539    public void setApplyLocalized(final boolean newApplyLocalized) {
    540        getLocaleConvertUtils().setApplyLocalized(newApplyLocalized);
    541    }
    542
    543    /**
    544     * Sets the default Locale.
    545     *
    546     * @param locale the default locale
    547     */
    548    public void setDefaultLocale(final Locale locale) {
    549        getLocaleConvertUtils().setDefaultLocale(locale);
    550    }
    551
    552    /**
    553     * Sets the specified locale-sensitive property value, performing type conversions as required to conform to the type of the destination property using the
    554     * default conversion pattern of the corresponding {@link LocaleConverter}.
    555     *
    556     * @param bean  Bean on which setting is to be performed
    557     * @param name  Property name (can be nested/indexed/mapped/combo)
    558     * @param value Value to be set
    559     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    560     * @throws InvocationTargetException if the property accessor method throws an exception
    561     */
    562    @Override
    563    public void setProperty(final Object bean, final String name, final Object value) throws IllegalAccessException, InvocationTargetException {
    564        setProperty(bean, name, value, null);
    565    }
    566
    567    /**
    568     * Sets the specified locale-sensitive property value, performing type conversions as required to conform to the type of the destination property using the
    569     * specified conversion pattern.
    570     *
    571     * @param bean    Bean on which setting is to be performed
    572     * @param name    Property name (can be nested/indexed/mapped/combo)
    573     * @param value   Value to be set
    574     * @param pattern The conversion pattern
    575     * @throws IllegalAccessException    if the caller does not have access to the property accessor method
    576     * @throws InvocationTargetException if the property accessor method throws an exception
    577     */
    578    public void setProperty(final Object bean, String name, final Object value, final String pattern) throws IllegalAccessException, InvocationTargetException {
    579        // Trace logging (if enabled)
    580        if (LOG.isTraceEnabled()) {
    581            final StringBuilder sb = new StringBuilder("  setProperty(");
    582            sb.append(bean);
    583            sb.append(", ");
    584            sb.append(name);
    585            sb.append(", ");
    586            if (value == null) {
    587                sb.append("<NULL>");
    588            } else if (value instanceof String) {
    589                sb.append((String) value);
    590            } else if (value instanceof String[]) {
    591                final String[] values = (String[]) value;
    592                sb.append('[');
    593                for (int i = 0; i < values.length; i++) {
    594                    if (i > 0) {
    595                        sb.append(',');
    596                    }
    597                    sb.append(values[i]);
    598                }
    599                sb.append(']');
    600            } else {
    601                sb.append(value.toString());
    602            }
    603            sb.append(')');
    604            LOG.trace(sb.toString());
    605        }
    606
    607        // Resolve any nested expression to get the actual target bean
    608        Object target = bean;
    609        final Resolver resolver = getPropertyUtils().getResolver();
    610        while (resolver.hasNested(name)) {
    611            try {
    612                target = getPropertyUtils().getProperty(target, resolver.next(name));
    613                name = resolver.remove(name);
    614            } catch (final NoSuchMethodException e) {
    615                return; // Skip this property setter
    616            }
    617        }
    618        if (LOG.isTraceEnabled()) {
    619            LOG.trace("    Target bean = " + target);
    620            LOG.trace("    Target name = " + name);
    621        }
    622
    623        // Declare local variables we will require
    624        final String propName = resolver.getProperty(name); // Simple name of target property
    625        final int index = resolver.getIndex(name); // Indexed subscript value (if any)
    626        final String key = resolver.getKey(name); // Mapped key value (if any)
    627
    628        final Class<?> type = definePropertyType(target, name, propName);
    629        if (type != null) {
    630            final Object newValue = convert(type, index, value, pattern);
    631            invokeSetter(target, propName, key, index, newValue);
    632        }
    633    }
    634}