1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.beanutils2.locale;
19
20 import java.beans.IndexedPropertyDescriptor;
21 import java.beans.PropertyDescriptor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.util.Locale;
24
25 import org.apache.commons.beanutils2.BeanUtilsBean;
26 import org.apache.commons.beanutils2.ContextClassLoaderLocal;
27 import org.apache.commons.beanutils2.ConvertUtils;
28 import org.apache.commons.beanutils2.ConvertUtilsBean;
29 import org.apache.commons.beanutils2.DynaBean;
30 import org.apache.commons.beanutils2.DynaClass;
31 import org.apache.commons.beanutils2.DynaProperty;
32 import org.apache.commons.beanutils2.MappedPropertyDescriptor;
33 import org.apache.commons.beanutils2.PropertyUtilsBean;
34 import org.apache.commons.beanutils2.expression.Resolver;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 /**
39 * <p>
40 * Utility methods for populating JavaBeans properties via reflection in a locale-dependent manner.
41 * </p>
42 *
43 * @since 1.7
44 */
45 public class LocaleBeanUtilsBean extends BeanUtilsBean {
46
47 /**
48 * Contains {@code LocaleBeanUtilsBean} instances indexed by context classloader.
49 */
50 private static final ContextClassLoaderLocal<LocaleBeanUtilsBean> LOCALE_BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal<LocaleBeanUtilsBean>() {
51 // Creates the default instance used when the context classloader is unavailable
52 @Override
53 protected LocaleBeanUtilsBean initialValue() {
54 return new LocaleBeanUtilsBean();
55 }
56 };
57
58 /** All logging goes through this logger */
59 private static final Log LOG = LogFactory.getLog(LocaleBeanUtilsBean.class);
60
61 /**
62 * Gets singleton instance
63 *
64 * @return the singleton instance
65 */
66 public static LocaleBeanUtilsBean getLocaleBeanUtilsInstance() {
67 return LOCALE_BEANS_BY_CLASSLOADER.get();
68 }
69
70 /**
71 * Sets the instance which provides the functionality for {@link LocaleBeanUtils}. This is a pseudo-singleton - an single instance is provided per (thread)
72 * context classloader. This mechanism provides isolation for web apps deployed in the same container.
73 *
74 * @param newInstance a new singleton instance
75 */
76 public static void setInstance(final LocaleBeanUtilsBean newInstance) {
77 LOCALE_BEANS_BY_CLASSLOADER.set(newInstance);
78 }
79
80 /** Convertor used by this class */
81 private final LocaleConvertUtilsBean localeConvertUtils;
82
83 /** Constructs instance with standard conversion bean */
84 public LocaleBeanUtilsBean() {
85 this.localeConvertUtils = new LocaleConvertUtilsBean();
86 }
87
88 /**
89 * Constructs instance that uses given locale conversion
90 *
91 * @param localeConvertUtils use this {@code localeConvertUtils} to perform conversions
92 */
93 public LocaleBeanUtilsBean(final LocaleConvertUtilsBean localeConvertUtils) {
94 this.localeConvertUtils = localeConvertUtils;
95 }
96
97 /**
98 * Constructs instance that uses given locale conversion
99 *
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 }