1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.beanutils.locale;
  17. import org.apache.commons.beanutils.*;
  18. import org.apache.commons.beanutils.ContextClassLoaderLocal;
  19. import org.apache.commons.logging.Log;
  20. import org.apache.commons.logging.LogFactory;
  21. import java.beans.IndexedPropertyDescriptor;
  22. import java.beans.PropertyDescriptor;
  23. import java.lang.reflect.InvocationTargetException;
  24. import java.util.Locale;
  25. /**
  26. * <p>Utility methods for populating JavaBeans properties
  27. * via reflection in a locale-dependent manner.</p>
  28. *
  29. * @author Craig R. McClanahan
  30. * @author Ralph Schaer
  31. * @author Chris Audley
  32. * @author Rey François
  33. * @author Gregor Raıman
  34. * @author Yauheny Mikulski
  35. * @since 1.7
  36. */
  37. public class LocaleBeanUtilsBean extends BeanUtilsBean {
  38. /**
  39. * Contains <code>LocaleBeanUtilsBean</code> instances indexed by context classloader.
  40. */
  41. private static final ContextClassLoaderLocal
  42. localeBeansByClassLoader = new ContextClassLoaderLocal() {
  43. // Creates the default instance used when the context classloader is unavailable
  44. protected Object initialValue() {
  45. return new LocaleBeanUtilsBean();
  46. }
  47. };
  48. /** Gets singleton instance */
  49. public synchronized static LocaleBeanUtilsBean getLocaleBeanUtilsInstance() {
  50. return (LocaleBeanUtilsBean)localeBeansByClassLoader.get();
  51. }
  52. /**
  53. * Sets the instance which provides the functionality for {@link LocaleBeanUtils}.
  54. * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
  55. * This mechanism provides isolation for web apps deployed in the same container.
  56. */
  57. public synchronized static void setInstance(LocaleBeanUtilsBean newInstance) {
  58. localeBeansByClassLoader.set(newInstance);
  59. }
  60. /** All logging goes through this logger */
  61. private static Log log = LogFactory.getLog(LocaleBeanUtilsBean.class);
  62. // ----------------------------------------------------- Instance Variables
  63. /** Convertor used by this class */
  64. private LocaleConvertUtilsBean localeConvertUtils;
  65. // --------------------------------------------------------- Constructors
  66. /** Construct instance with standard conversion bean */
  67. public LocaleBeanUtilsBean() {
  68. this.localeConvertUtils = new LocaleConvertUtilsBean();
  69. }
  70. /**
  71. * Construct instance that uses given locale conversion
  72. *
  73. * @param localeConvertUtils use this <code>localeConvertUtils</code> to perform
  74. * conversions
  75. * @param convertUtilsBean use this for standard conversions
  76. * @param propertyUtilsBean use this for property conversions
  77. */
  78. public LocaleBeanUtilsBean(
  79. LocaleConvertUtilsBean localeConvertUtils,
  80. ConvertUtilsBean convertUtilsBean,
  81. PropertyUtilsBean propertyUtilsBean) {
  82. super(convertUtilsBean, propertyUtilsBean);
  83. this.localeConvertUtils = localeConvertUtils;
  84. }
  85. /**
  86. * Construct instance that uses given locale conversion
  87. *
  88. * @param localeConvertUtils use this <code>localeConvertUtils</code> to perform
  89. * conversions
  90. */
  91. public LocaleBeanUtilsBean(LocaleConvertUtilsBean localeConvertUtils) {
  92. this.localeConvertUtils = localeConvertUtils;
  93. }
  94. // --------------------------------------------------------- Public Methods
  95. /** Gets the bean instance used for conversions */
  96. public LocaleConvertUtilsBean getLocaleConvertUtils() {
  97. return localeConvertUtils;
  98. }
  99. /**
  100. * Gets the default Locale
  101. */
  102. public Locale getDefaultLocale() {
  103. return getLocaleConvertUtils().getDefaultLocale();
  104. }
  105. /**
  106. * Sets the default Locale
  107. */
  108. public void setDefaultLocale(Locale locale) {
  109. getLocaleConvertUtils().setDefaultLocale(locale);
  110. }
  111. /**
  112. * Is the pattern to be applied localized
  113. * (Indicate whether the pattern is localized or not)
  114. */
  115. public boolean getApplyLocalized() {
  116. return getLocaleConvertUtils().getApplyLocalized();
  117. }
  118. /**
  119. * Sets whether the pattern is applied localized
  120. * (Indicate whether the pattern is localized or not)
  121. */
  122. public void setApplyLocalized(boolean newApplyLocalized) {
  123. getLocaleConvertUtils().setApplyLocalized(newApplyLocalized);
  124. }
  125. // --------------------------------------------------------- Public Methods
  126. /**
  127. * Return the value of the specified locale-sensitive indexed property
  128. * of the specified bean, as a String. The zero-relative index of the
  129. * required value must be included (in square brackets) as a suffix to
  130. * the property name, or <code>IllegalArgumentException</code> will be
  131. * thrown.
  132. *
  133. * @param bean Bean whose property is to be extracted
  134. * @param name <code>propertyname[index]</code> of the property value
  135. * to be extracted
  136. * @param pattern The convertion pattern
  137. *
  138. * @exception IllegalAccessException if the caller does not have
  139. * access to the property accessor method
  140. * @exception InvocationTargetException if the property accessor method
  141. * throws an exception
  142. * @exception NoSuchMethodException if an accessor method for this
  143. * propety cannot be found
  144. */
  145. public String getIndexedProperty(
  146. Object bean,
  147. String name,
  148. String pattern)
  149. throws
  150. IllegalAccessException,
  151. InvocationTargetException,
  152. NoSuchMethodException {
  153. Object value = getPropertyUtils().getIndexedProperty(bean, name);
  154. return getLocaleConvertUtils().convert(value, pattern);
  155. }
  156. /**
  157. * Return the value of the specified locale-sensitive indexed property
  158. * of the specified bean, as a String using the default convertion pattern of
  159. * the corresponding {@link LocaleConverter}. The zero-relative index
  160. * of the required value must be included (in square brackets) as a suffix
  161. * to the property name, or <code>IllegalArgumentException</code> will be thrown.
  162. *
  163. * @param bean Bean whose property is to be extracted
  164. * @param name <code>propertyname[index]</code> of the property value
  165. * to be extracted
  166. *
  167. * @exception IllegalAccessException if the caller does not have
  168. * access to the property accessor method
  169. * @exception InvocationTargetException if the property accessor method
  170. * throws an exception
  171. * @exception NoSuchMethodException if an accessor method for this
  172. * propety cannot be found
  173. */
  174. public String getIndexedProperty(
  175. Object bean,
  176. String name)
  177. throws
  178. IllegalAccessException,
  179. InvocationTargetException,
  180. NoSuchMethodException {
  181. return getIndexedProperty(bean, name, null);
  182. }
  183. /**
  184. * Return the value of the specified locale-sensetive indexed property
  185. * of the specified bean, as a String using the specified convertion pattern.
  186. * The index is specified as a method parameter and
  187. * must *not* be included in the property name expression
  188. *
  189. * @param bean Bean whose property is to be extracted
  190. * @param name Simple property name of the property value to be extracted
  191. * @param index Index of the property value to be extracted
  192. * @param pattern The convertion pattern
  193. *
  194. * @exception IllegalAccessException if the caller does not have
  195. * access to the property accessor method
  196. * @exception InvocationTargetException if the property accessor method
  197. * throws an exception
  198. * @exception NoSuchMethodException if an accessor method for this
  199. * propety cannot be found
  200. */
  201. public String getIndexedProperty(Object bean,
  202. String name, int index, String pattern)
  203. throws IllegalAccessException, InvocationTargetException,
  204. NoSuchMethodException {
  205. Object value = getPropertyUtils().getIndexedProperty(bean, name, index);
  206. return getLocaleConvertUtils().convert(value, pattern);
  207. }
  208. /**
  209. * Return the value of the specified locale-sensetive indexed property
  210. * of the specified bean, as a String using the default convertion pattern of
  211. * the corresponding {@link LocaleConverter}.
  212. * The index is specified as a method parameter and
  213. * must *not* be included in the property name expression
  214. *
  215. * @param bean Bean whose property is to be extracted
  216. * @param name Simple property name of the property value to be extracted
  217. * @param index Index of the property value to be extracted
  218. *
  219. * @exception IllegalAccessException if the caller does not have
  220. * access to the property accessor method
  221. * @exception InvocationTargetException if the property accessor method
  222. * throws an exception
  223. * @exception NoSuchMethodException if an accessor method for this
  224. * propety cannot be found
  225. */
  226. public String getIndexedProperty(Object bean,
  227. String name, int index)
  228. throws IllegalAccessException, InvocationTargetException,
  229. NoSuchMethodException {
  230. return getIndexedProperty(bean, name, index, null);
  231. }
  232. /**
  233. * Return the value of the specified simple locale-sensitive property
  234. * of the specified bean, converted to a String using the specified
  235. * convertion pattern.
  236. *
  237. * @param bean Bean whose property is to be extracted
  238. * @param name Name of the property to be extracted
  239. * @param pattern The convertion pattern
  240. *
  241. * @exception IllegalAccessException if the caller does not have
  242. * access to the property accessor method
  243. * @exception InvocationTargetException if the property accessor method
  244. * throws an exception
  245. * @exception NoSuchMethodException if an accessor method for this
  246. * propety cannot be found
  247. */
  248. public String getSimpleProperty(Object bean, String name, String pattern)
  249. throws IllegalAccessException, InvocationTargetException,
  250. NoSuchMethodException {
  251. Object value = getPropertyUtils().getSimpleProperty(bean, name);
  252. return getLocaleConvertUtils().convert(value, pattern);
  253. }
  254. /**
  255. * Return the value of the specified simple locale-sensitive property
  256. * of the specified bean, converted to a String using the default
  257. * convertion pattern of the corresponding {@link LocaleConverter}.
  258. *
  259. * @param bean Bean whose property is to be extracted
  260. * @param name Name of the property to be extracted
  261. *
  262. * @exception IllegalAccessException if the caller does not have
  263. * access to the property accessor method
  264. * @exception InvocationTargetException if the property accessor method
  265. * throws an exception
  266. * @exception NoSuchMethodException if an accessor method for this
  267. * propety cannot be found
  268. */
  269. public String getSimpleProperty(Object bean, String name)
  270. throws IllegalAccessException, InvocationTargetException,
  271. NoSuchMethodException {
  272. return getSimpleProperty(bean, name, null);
  273. }
  274. /**
  275. * Return the value of the specified mapped locale-sensitive property
  276. * of the specified bean, as a String using the specified convertion pattern.
  277. * The key is specified as a method parameter and must *not* be included in
  278. * the property name expression.
  279. *
  280. * @param bean Bean whose property is to be extracted
  281. * @param name Simple property name of the property value to be extracted
  282. * @param key Lookup key of the property value to be extracted
  283. * @param pattern The convertion pattern
  284. *
  285. * @exception IllegalAccessException if the caller does not have
  286. * access to the property accessor method
  287. * @exception InvocationTargetException if the property accessor method
  288. * throws an exception
  289. * @exception NoSuchMethodException if an accessor method for this
  290. * propety cannot be found
  291. */
  292. public String getMappedProperty(
  293. Object bean,
  294. String name,
  295. String key,
  296. String pattern)
  297. throws
  298. IllegalAccessException,
  299. InvocationTargetException,
  300. NoSuchMethodException {
  301. Object value = getPropertyUtils().getMappedProperty(bean, name, key);
  302. return getLocaleConvertUtils().convert(value, pattern);
  303. }
  304. /**
  305. * Return the value of the specified mapped locale-sensitive property
  306. * of the specified bean, as a String
  307. * The key is specified as a method parameter and must *not* be included
  308. * in the property name expression
  309. *
  310. * @param bean Bean whose property is to be extracted
  311. * @param name Simple property name of the property value to be extracted
  312. * @param key Lookup key of the property value to be extracted
  313. *
  314. * @exception IllegalAccessException if the caller does not have
  315. * access to the property accessor method
  316. * @exception InvocationTargetException if the property accessor method
  317. * throws an exception
  318. * @exception NoSuchMethodException if an accessor method for this
  319. * propety cannot be found
  320. */
  321. public String getMappedProperty(Object bean,
  322. String name, String key)
  323. throws IllegalAccessException, InvocationTargetException,
  324. NoSuchMethodException {
  325. return getMappedProperty(bean, name, key, null);
  326. }
  327. /**
  328. * Return the value of the specified locale-sensitive mapped property
  329. * of the specified bean, as a String using the specified pattern.
  330. * The String-valued key of the required value
  331. * must be included (in parentheses) as a suffix to
  332. * the property name, or <code>IllegalArgumentException</code> will be
  333. * thrown.
  334. *
  335. * @param bean Bean whose property is to be extracted
  336. * @param name <code>propertyname(index)</code> of the property value
  337. * to be extracted
  338. * @param pattern The convertion pattern
  339. *
  340. * @exception IllegalAccessException if the caller does not have
  341. * access to the property accessor method
  342. * @exception InvocationTargetException if the property accessor method
  343. * throws an exception
  344. * @exception NoSuchMethodException if an accessor method for this
  345. * propety cannot be found
  346. */
  347. public String getMappedPropertyLocale(
  348. Object bean,
  349. String name,
  350. String pattern)
  351. throws
  352. IllegalAccessException,
  353. InvocationTargetException,
  354. NoSuchMethodException {
  355. Object value = getPropertyUtils().getMappedProperty(bean, name);
  356. return getLocaleConvertUtils().convert(value, pattern);
  357. }
  358. /**
  359. * Return the value of the specified locale-sensitive mapped property
  360. * of the specified bean, as a String using the default
  361. * convertion pattern of the corresponding {@link LocaleConverter}.
  362. * The String-valued key of the required value
  363. * must be included (in parentheses) as a suffix to
  364. * the property name, or <code>IllegalArgumentException</code> will be
  365. * thrown.
  366. *
  367. * @param bean Bean whose property is to be extracted
  368. * @param name <code>propertyname(index)</code> of the property value
  369. * to be extracted
  370. *
  371. * @exception IllegalAccessException if the caller does not have
  372. * access to the property accessor method
  373. * @exception InvocationTargetException if the property accessor method
  374. * throws an exception
  375. * @exception NoSuchMethodException if an accessor method for this
  376. * propety cannot be found
  377. */
  378. public String getMappedProperty(Object bean, String name)
  379. throws
  380. IllegalAccessException,
  381. InvocationTargetException,
  382. NoSuchMethodException {
  383. return getMappedPropertyLocale(bean, name, null);
  384. }
  385. /**
  386. * Return the value of the (possibly nested) locale-sensitive property
  387. * of the specified name, for the specified bean,
  388. * as a String using the specified pattern.
  389. *
  390. * @param bean Bean whose property is to be extracted
  391. * @param name Possibly nested name of the property to be extracted
  392. * @param pattern The convertion pattern
  393. *
  394. * @exception IllegalAccessException if the caller does not have
  395. * access to the property accessor method
  396. * @exception IllegalArgumentException if a nested reference to a
  397. * property returns null
  398. * @exception InvocationTargetException if the property accessor method
  399. * throws an exception
  400. * @exception NoSuchMethodException if an accessor method for this
  401. * propety cannot be found
  402. */
  403. public String getNestedProperty(
  404. Object bean,
  405. String name,
  406. String pattern)
  407. throws
  408. IllegalAccessException,
  409. InvocationTargetException,
  410. NoSuchMethodException {
  411. Object value = getPropertyUtils().getNestedProperty(bean, name);
  412. return getLocaleConvertUtils().convert(value, pattern);
  413. }
  414. /**
  415. * Return the value of the (possibly nested) locale-sensitive property
  416. * of the specified name, for the specified bean, as a String using the default
  417. * convertion pattern of the corresponding {@link LocaleConverter}.
  418. *
  419. * @param bean Bean whose property is to be extracted
  420. * @param name Possibly nested name of the property to be extracted
  421. *
  422. * @exception IllegalAccessException if the caller does not have
  423. * access to the property accessor method
  424. * @exception IllegalArgumentException if a nested reference to a
  425. * property returns null
  426. * @exception InvocationTargetException if the property accessor method
  427. * throws an exception
  428. * @exception NoSuchMethodException if an accessor method for this
  429. * propety cannot be found
  430. */
  431. public String getNestedProperty(Object bean, String name)
  432. throws
  433. IllegalAccessException,
  434. InvocationTargetException,
  435. NoSuchMethodException {
  436. return getNestedProperty(bean, name, null);
  437. }
  438. /**
  439. * Return the value of the specified locale-sensitive property
  440. * of the specified bean, no matter which property reference
  441. * format is used, as a String using the specified convertion pattern.
  442. *
  443. * @param bean Bean whose property is to be extracted
  444. * @param name Possibly indexed and/or nested name of the property
  445. * to be extracted
  446. * @param pattern The convertion pattern
  447. *
  448. * @exception IllegalAccessException if the caller does not have
  449. * access to the property accessor method
  450. * @exception InvocationTargetException if the property accessor method
  451. * throws an exception
  452. * @exception NoSuchMethodException if an accessor method for this
  453. * propety cannot be found
  454. */
  455. public String getProperty(Object bean, String name, String pattern)
  456. throws
  457. IllegalAccessException,
  458. InvocationTargetException,
  459. NoSuchMethodException {
  460. return getNestedProperty(bean, name, pattern);
  461. }
  462. /**
  463. * Return the value of the specified locale-sensitive property
  464. * of the specified bean, no matter which property reference
  465. * format is used, as a String using the default
  466. * convertion pattern of the corresponding {@link LocaleConverter}.
  467. *
  468. * @param bean Bean whose property is to be extracted
  469. * @param name Possibly indexed and/or nested name of the property
  470. * to be extracted
  471. *
  472. * @exception IllegalAccessException if the caller does not have
  473. * access to the property accessor method
  474. * @exception InvocationTargetException if the property accessor method
  475. * throws an exception
  476. * @exception NoSuchMethodException if an accessor method for this
  477. * propety cannot be found
  478. */
  479. public String getProperty(Object bean, String name)
  480. throws
  481. IllegalAccessException,
  482. InvocationTargetException,
  483. NoSuchMethodException {
  484. return getNestedProperty(bean, name);
  485. }
  486. /**
  487. * Set the specified locale-sensitive property value, performing type
  488. * conversions as required to conform to the type of the destination property
  489. * using the default convertion pattern of the corresponding {@link LocaleConverter}.
  490. *
  491. * @param bean Bean on which setting is to be performed
  492. * @param name Property name (can be nested/indexed/mapped/combo)
  493. * @param value Value to be set
  494. *
  495. * @exception IllegalAccessException if the caller does not have
  496. * access to the property accessor method
  497. * @exception InvocationTargetException if the property accessor method
  498. * throws an exception
  499. */
  500. public void setProperty(Object bean, String name, Object value)
  501. throws
  502. IllegalAccessException,
  503. InvocationTargetException {
  504. setProperty(bean, name, value, null);
  505. }
  506. /**
  507. * Set the specified locale-sensitive property value, performing type
  508. * conversions as required to conform to the type of the destination
  509. * property using the specified convertion pattern.
  510. *
  511. * @param bean Bean on which setting is to be performed
  512. * @param name Property name (can be nested/indexed/mapped/combo)
  513. * @param value Value to be set
  514. * @param pattern The convertion pattern
  515. *
  516. * @exception IllegalAccessException if the caller does not have
  517. * access to the property accessor method
  518. * @exception InvocationTargetException if the property accessor method
  519. * throws an exception
  520. */
  521. public void setProperty(
  522. Object bean,
  523. String name,
  524. Object value,
  525. String pattern)
  526. throws
  527. IllegalAccessException,
  528. InvocationTargetException {
  529. // Trace logging (if enabled)
  530. if (log.isTraceEnabled()) {
  531. StringBuffer sb = new StringBuffer(" setProperty(");
  532. sb.append(bean);
  533. sb.append(", ");
  534. sb.append(name);
  535. sb.append(", ");
  536. if (value == null) {
  537. sb.append("<NULL>");
  538. }
  539. else if (value instanceof String) {
  540. sb.append((String) value);
  541. }
  542. else if (value instanceof String[]) {
  543. String values[] = (String[]) value;
  544. sb.append('[');
  545. for (int i = 0; i < values.length; i++) {
  546. if (i > 0) {
  547. sb.append(',');
  548. }
  549. sb.append(values[i]);
  550. }
  551. sb.append(']');
  552. }
  553. else {
  554. sb.append(value.toString());
  555. }
  556. sb.append(')');
  557. log.trace(sb.toString());
  558. }
  559. Descriptor propInfo = calculate(bean, name);
  560. if (propInfo != null) {
  561. Class type = definePropertyType(propInfo.getTarget(), name, propInfo.getPropName());
  562. if (type != null) {
  563. Object newValue = convert(type, propInfo.getIndex(), value, pattern);
  564. invokeSetter(propInfo.getTarget(), propInfo.getPropName(),
  565. propInfo.getKey(), propInfo.getIndex(), newValue);
  566. }
  567. }
  568. }
  569. /**
  570. * Calculate the property type.
  571. *
  572. * @param target The bean
  573. * @param name The property name
  574. * @param propName The Simple name of target property
  575. *
  576. * @exception IllegalAccessException if the caller does not have
  577. * access to the property accessor method
  578. * @exception InvocationTargetException if the property accessor method
  579. * throws an exception
  580. */
  581. protected Class definePropertyType(Object target, String name, String propName)
  582. throws IllegalAccessException, InvocationTargetException {
  583. Class type = null; // Java type of target property
  584. if (target instanceof DynaBean) {
  585. DynaClass dynaClass = ((DynaBean) target).getDynaClass();
  586. DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
  587. if (dynaProperty == null) {
  588. return null; // Skip this property setter
  589. }
  590. type = dynaProperty.getType();
  591. }
  592. else {
  593. PropertyDescriptor descriptor = null;
  594. try {
  595. descriptor =
  596. getPropertyUtils().getPropertyDescriptor(target, name);
  597. if (descriptor == null) {
  598. return null; // Skip this property setter
  599. }
  600. }
  601. catch (NoSuchMethodException e) {
  602. return null; // Skip this property setter
  603. }
  604. if (descriptor instanceof MappedPropertyDescriptor) {
  605. type = ((MappedPropertyDescriptor) descriptor).
  606. getMappedPropertyType();
  607. }
  608. else if (descriptor instanceof IndexedPropertyDescriptor) {
  609. type = ((IndexedPropertyDescriptor) descriptor).
  610. getIndexedPropertyType();
  611. }
  612. else {
  613. type = descriptor.getPropertyType();
  614. }
  615. }
  616. return type;
  617. }
  618. /**
  619. * Convert the specified value to the required type using the
  620. * specified convertion pattern.
  621. *
  622. * @param type The Java type of target property
  623. * @param index The indexed subscript value (if any)
  624. * @param value The value to be converted
  625. * @param pattern The convertion pattern
  626. */
  627. protected Object convert(Class type, int index, Object value, String pattern) {
  628. if (log.isTraceEnabled()) {
  629. log.trace("Converting value '" + value + "' to type:" + type);
  630. }
  631. Object newValue = null;
  632. if (type.isArray() && (index < 0)) { // Scalar value into array
  633. if (value instanceof String) {
  634. String values[] = new String[1];
  635. values[0] = (String) value;
  636. newValue = getLocaleConvertUtils().convert((String[]) values, type, pattern);
  637. }
  638. else if (value instanceof String[]) {
  639. newValue = getLocaleConvertUtils().convert((String[]) value, type, pattern);
  640. }
  641. else {
  642. newValue = value;
  643. }
  644. }
  645. else if (type.isArray()) { // Indexed value into array
  646. if (value instanceof String) {
  647. newValue = getLocaleConvertUtils().convert((String) value,
  648. type.getComponentType(), pattern);
  649. }
  650. else if (value instanceof String[]) {
  651. newValue = getLocaleConvertUtils().convert(((String[]) value)[0],
  652. type.getComponentType(), pattern);
  653. }
  654. else {
  655. newValue = value;
  656. }
  657. }
  658. else { // Value into scalar
  659. if (value instanceof String) {
  660. newValue = getLocaleConvertUtils().convert((String) value, type, pattern);
  661. }
  662. else if (value instanceof String[]) {
  663. newValue = getLocaleConvertUtils().convert(((String[]) value)[0],
  664. type, pattern);
  665. }
  666. else {
  667. newValue = value;
  668. }
  669. }
  670. return newValue;
  671. }
  672. /**
  673. * Convert the specified value to the required type.
  674. *
  675. * @param type The Java type of target property
  676. * @param index The indexed subscript value (if any)
  677. * @param value The value to be converted
  678. */
  679. protected Object convert(Class type, int index, Object value) {
  680. Object newValue = null;
  681. if (type.isArray() && (index < 0)) { // Scalar value into array
  682. if (value instanceof String) {
  683. String values[] = new String[1];
  684. values[0] = (String) value;
  685. newValue = ConvertUtils.convert((String[]) values, type);
  686. }
  687. else if (value instanceof String[]) {
  688. newValue = ConvertUtils.convert((String[]) value, type);
  689. }
  690. else {
  691. newValue = value;
  692. }
  693. }
  694. else if (type.isArray()) { // Indexed value into array
  695. if (value instanceof String) {
  696. newValue = ConvertUtils.convert((String) value,
  697. type.getComponentType());
  698. }
  699. else if (value instanceof String[]) {
  700. newValue = ConvertUtils.convert(((String[]) value)[0],
  701. type.getComponentType());
  702. }
  703. else {
  704. newValue = value;
  705. }
  706. }
  707. else { // Value into scalar
  708. if (value instanceof String) {
  709. newValue = ConvertUtils.convert((String) value, type);
  710. }
  711. else if (value instanceof String[]) {
  712. newValue = ConvertUtils.convert(((String[]) value)[0],
  713. type);
  714. }
  715. else {
  716. newValue = value;
  717. }
  718. }
  719. return newValue;
  720. }
  721. /**
  722. * Invoke the setter method.
  723. *
  724. * @param target The bean
  725. * @param propName The Simple name of target property
  726. * @param key The Mapped key value (if any)
  727. * @param index The indexed subscript value (if any)
  728. * @param newValue The value to be set
  729. *
  730. * @exception IllegalAccessException if the caller does not have
  731. * access to the property accessor method
  732. * @exception InvocationTargetException if the property accessor method
  733. * throws an exception
  734. */
  735. protected void invokeSetter(Object target, String propName, String key, int index, Object newValue)
  736. throws IllegalAccessException, InvocationTargetException {
  737. try {
  738. if (index >= 0) {
  739. getPropertyUtils().setIndexedProperty(target, propName,
  740. index, newValue);
  741. }
  742. else if (key != null) {
  743. getPropertyUtils().setMappedProperty(target, propName,
  744. key, newValue);
  745. }
  746. else {
  747. getPropertyUtils().setProperty(target, propName, newValue);
  748. }
  749. }
  750. catch (NoSuchMethodException e) {
  751. throw new InvocationTargetException
  752. (e, "Cannot set " + propName);
  753. }
  754. }
  755. /**
  756. * Resolve any nested expression to get the actual target bean.
  757. *
  758. * @param bean The bean
  759. * @param name The property name
  760. *
  761. * @exception IllegalAccessException if the caller does not have
  762. * access to the property accessor method
  763. * @exception InvocationTargetException if the property accessor method
  764. * throws an exception
  765. */
  766. protected Descriptor calculate(Object bean, String name)
  767. throws IllegalAccessException, InvocationTargetException {
  768. String propName = null; // Simple name of target property
  769. int index = -1; // Indexed subscript value (if any)
  770. String key = null; // Mapped key value (if any)
  771. Object target = bean;
  772. int delim = name.lastIndexOf(PropertyUtils.NESTED_DELIM);
  773. if (delim >= 0) {
  774. try {
  775. target =
  776. getPropertyUtils().getProperty(bean, name.substring(0, delim));
  777. }
  778. catch (NoSuchMethodException e) {
  779. return null; // Skip this property setter
  780. }
  781. name = name.substring(delim + 1);
  782. if (log.isTraceEnabled()) {
  783. log.trace(" Target bean = " + target);
  784. log.trace(" Target name = " + name);
  785. }
  786. }
  787. // Calculate the property name, index, and key values
  788. propName = name;
  789. int i = propName.indexOf(PropertyUtils.INDEXED_DELIM);
  790. if (i >= 0) {
  791. int k = propName.indexOf(PropertyUtils.INDEXED_DELIM2);
  792. try {
  793. index =
  794. Integer.parseInt(propName.substring(i + 1, k));
  795. }
  796. catch (NumberFormatException e) {
  797. ;
  798. }
  799. propName = propName.substring(0, i);
  800. }
  801. int j = propName.indexOf(PropertyUtils.MAPPED_DELIM);
  802. if (j >= 0) {
  803. int k = propName.indexOf(PropertyUtils.MAPPED_DELIM2);
  804. try {
  805. key = propName.substring(j + 1, k);
  806. }
  807. catch (IndexOutOfBoundsException e) {
  808. ;
  809. }
  810. propName = propName.substring(0, j);
  811. }
  812. return new Descriptor(target, name, propName, key, index);
  813. }
  814. protected class Descriptor {
  815. private int index = -1; // Indexed subscript value (if any)
  816. private String name;
  817. private String propName; // Simple name of target property
  818. private String key; // Mapped key value (if any)
  819. private Object target;
  820. public Descriptor(Object target, String name, String propName, String key, int index) {
  821. setTarget(target);
  822. setName(name);
  823. setPropName(propName);
  824. setKey(key);
  825. setIndex(index);
  826. }
  827. public Object getTarget() {
  828. return target;
  829. }
  830. public void setTarget(Object target) {
  831. this.target = target;
  832. }
  833. public String getKey() {
  834. return key;
  835. }
  836. public void setKey(String key) {
  837. this.key = key;
  838. }
  839. public int getIndex() {
  840. return index;
  841. }
  842. public void setIndex(int index) {
  843. this.index = index;
  844. }
  845. public String getName() {
  846. return name;
  847. }
  848. public void setName(String name) {
  849. this.name = name;
  850. }
  851. public String getPropName() {
  852. return propName;
  853. }
  854. public void setPropName(String propName) {
  855. this.propName = propName;
  856. }
  857. }
  858. }