View Javadoc

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