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.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}