Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: ognl/OgnlRuntime.java


1   //--------------------------------------------------------------------------
2   //  Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
3   //  All rights reserved.
4   //
5   //  Redistribution and use in source and binary forms, with or without
6   //  modification, are permitted provided that the following conditions are
7   //  met:
8   //
9   //  Redistributions of source code must retain the above copyright notice,
10  //  this list of conditions and the following disclaimer.
11  //  Redistributions in binary form must reproduce the above copyright
12  //  notice, this list of conditions and the following disclaimer in the
13  //  documentation and/or other materials provided with the distribution.
14  //  Neither the name of the Drew Davidson nor the names of its contributors
15  //  may be used to endorse or promote products derived from this software
16  //  without specific prior written permission.
17  //
18  //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  //  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  //  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  //  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  //  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  //  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25  //  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  //  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  //  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28  //  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29  //  DAMAGE.
30  //--------------------------------------------------------------------------
31  package ognl;
32  
33  import java.beans.*;
34  import java.io.*;
35  import java.lang.reflect.*;
36  import java.math.*;
37  import java.security.*;
38  import java.util.*;
39  
40  /**
41   * This is an abstract class with static methods that define runtime
42   * caching information in OGNL.
43   * @author Luke Blanshard (blanshlu@netscape.net)
44   * @author Drew Davidson (drew@ognl.org)
45   */
46  public abstract class OgnlRuntime extends Object
47  {
48      public static final Object              NotFound = new Object();
49      public static final List                NotFoundList = new ArrayList();
50      public static final Map                 NotFoundMap = new HashMap();
51      public static final Object[]            NoArguments = new Object[] {};
52      public static final Class[]             NoArgumentTypes = new Class[] {};
53  
54      /** Token returned by TypeConverter for no conversion possible */
55      public static final Object              NoConversionPossible = "ognl.NoConversionPossible";
56  
57      /** Not an indexed property */
58      public static int                       INDEXED_PROPERTY_NONE = 0;
59      /** JavaBeans IndexedProperty */
60      public static int                       INDEXED_PROPERTY_INT = 1;
61      /** OGNL ObjectIndexedProperty */
62      public static int                       INDEXED_PROPERTY_OBJECT = 2;
63  
64      public static final String              NULL_STRING = "" + null;
65  
66      private static final String             SET_PREFIX = "set";
67      private static final String             GET_PREFIX = "get";
68      private static final String             IS_PREFIX = "is";
69  
70      /**
71          Prefix padding for hexadecimal numbers to HEX_LENGTH.
72       */
73      private static final Map          HEX_PADDING = new HashMap();
74  
75      /**
76          Hexadecimal prefix for printing "pointers".
77       */
78      private static final String          HEX_PREFIX = "0x";
79  
80      private static final int        HEX_LENGTH = 8;
81      /**
82          Returned by <CODE>getUniqueDescriptor()</CODE> when the
83          object is <CODE>null</CODE>.
84       */
85      private static final String            NULL_OBJECT_STRING = "<null>";
86  
87  
88      private static ClassCache               methodAccessors = new ClassCache();
89      private static ClassCache               propertyAccessors = new ClassCache();
90      private static ClassCache               elementsAccessors = new ClassCache();
91      private static ClassCache               nullHandlers = new ClassCache();
92      private static ClassCache               propertyDescriptorCache = new ClassCache();
93      private static ClassCache               constructorCache = new ClassCache();
94      private static ClassCache               staticMethodCache = new ClassCache();
95      private static ClassCache               instanceMethodCache = new ClassCache();
96      private static ClassCache               invokePermissionCache = new ClassCache();
97      private static ClassCache               fieldCache = new ClassCache();
98      private static List                     superclasses = new ArrayList(); /* Used by fieldCache lookup */
99      private static ClassCache[]             declaredMethods = new ClassCache[] { new ClassCache(), new ClassCache() };   /* set, get */
100     private static Map                      primitiveTypes = new HashMap(101);
101     private static ClassCache               primitiveDefaults = new ClassCache();
102     private static Map                      methodParameterTypesCache = new HashMap(101);
103     private static Map                      ctorParameterTypesCache = new HashMap(101);
104     private static SecurityManager          securityManager = System.getSecurityManager();
105     private static EvaluationPool           evaluationPool = new EvaluationPool();
106     private static ObjectArrayPool          objectArrayPool = new ObjectArrayPool();
107 
108     /**
109         This is a highly specialized map for storing values keyed by Class objects.
110      */
111     private static class ClassCache extends Object
112     {
113         /* this MUST be a power of 2 */
114         private static final int    TABLE_SIZE = 512;
115 
116         /* ...and now you see why.  The table size is used as a mask for generating hashes */
117         private static final int    TABLE_SIZE_MASK = TABLE_SIZE - 1;
118 
119         private Entry[]             table;
120 
121         private static class Entry extends Object
122         {
123             protected Entry                 next;
124             protected Class                 key;
125             protected Object                value;
126 
127             public Entry(Class key, Object value)
128             {
129                 super();
130                 this.key = key;
131                 this.value = value;
132             }
133         }
134 
135         public ClassCache()
136         {
137             super();
138             this.table = new Entry[TABLE_SIZE];
139         }
140 
141         public final Object get(Class key)
142         {
143             Object      result = null;
144             int         i = key.hashCode() & TABLE_SIZE_MASK;
145 
146             for (Entry entry = table[i]; entry != null; entry = entry.next) {
147                 if (entry.key == key) {
148                     result = entry.value;
149                     break;
150                 }
151             }
152             return result;
153         }
154 
155         public final Object put(Class key, Object value)
156         {
157             Object      result = null;
158             int         i = key.hashCode() & TABLE_SIZE_MASK;
159             Entry       entry = table[i];
160 
161             if (entry == null) {
162                 table[i] = new Entry(key, value);
163             } else {
164                 if (entry.key == key) {
165                     result = entry.value;
166                     entry.value = value;
167                 } else {
168                     while (true) {
169                         if (entry.key == key) {
170                             /* replace value */
171                             result = entry.value;
172                             entry.value = value;
173                             break;
174                         } else {
175                             if (entry.next == null) {
176                                 /* add value */
177                                 entry.next = new Entry(key, value);
178                                 break;
179                             }
180                         }
181                         entry = entry.next;
182                     }
183                 }
184             }
185             return result;
186         }
187     }
188 
189     static
190     {
191         PropertyAccessor p = new ArrayPropertyAccessor();
192         setPropertyAccessor( Object.class, new ObjectPropertyAccessor() );
193         setPropertyAccessor( byte[].class, p );
194         setPropertyAccessor( short[].class, p );
195         setPropertyAccessor( char[].class, p );
196         setPropertyAccessor( int[].class, p );
197         setPropertyAccessor( long[].class, p );
198         setPropertyAccessor( float[].class, p );
199         setPropertyAccessor( double[].class, p );
200         setPropertyAccessor( Object[].class, p );
201         setPropertyAccessor( List.class, new ListPropertyAccessor() );
202         setPropertyAccessor( Map.class, new MapPropertyAccessor() );
203         setPropertyAccessor( Set.class, new SetPropertyAccessor() );
204         setPropertyAccessor( Iterator.class, new IteratorPropertyAccessor() );
205         setPropertyAccessor( Enumeration.class, new EnumerationPropertyAccessor() );
206 
207         ElementsAccessor e = new ArrayElementsAccessor();
208         setElementsAccessor( Object.class, new ObjectElementsAccessor() );
209         setElementsAccessor( byte[].class, e );
210         setElementsAccessor( short[].class, e );
211         setElementsAccessor( char[].class, e );
212         setElementsAccessor( int[].class, e );
213         setElementsAccessor( long[].class, e );
214         setElementsAccessor( float[].class, e );
215         setElementsAccessor( double[].class, e );
216         setElementsAccessor( Object[].class, e );
217         setElementsAccessor( Collection.class, new CollectionElementsAccessor() );
218         setElementsAccessor( Map.class, new MapElementsAccessor() );
219         setElementsAccessor( Iterator.class, new IteratorElementsAccessor() );
220         setElementsAccessor( Enumeration.class, new EnumerationElementsAccessor() );
221         setElementsAccessor( Number.class, new NumberElementsAccessor() );
222 
223         NullHandler nh = new ObjectNullHandler();
224         setNullHandler( Object.class,  nh);
225         setNullHandler( byte[].class, nh );
226         setNullHandler( short[].class, nh );
227         setNullHandler( char[].class, nh );
228         setNullHandler( int[].class, nh );
229         setNullHandler( long[].class, nh );
230         setNullHandler( float[].class, nh );
231         setNullHandler( double[].class, nh );
232         setNullHandler( Object[].class, nh );
233 
234         MethodAccessor  ma = new ObjectMethodAccessor();
235         setMethodAccessor( Object.class, ma );
236         setMethodAccessor( byte[].class, ma );
237         setMethodAccessor( short[].class, ma );
238         setMethodAccessor( char[].class, ma );
239         setMethodAccessor( int[].class, ma );
240         setMethodAccessor( long[].class, ma );
241         setMethodAccessor( float[].class, ma );
242         setMethodAccessor( double[].class, ma );
243         setMethodAccessor( Object[].class, ma );
244 
245         primitiveTypes.put("boolean", Boolean.TYPE );
246         primitiveTypes.put("byte", Byte.TYPE );
247         primitiveTypes.put("short", Short.TYPE );
248         primitiveTypes.put("char", Character.TYPE );
249         primitiveTypes.put("int", Integer.TYPE );
250         primitiveTypes.put("long", Long.TYPE );
251         primitiveTypes.put("float", Float.TYPE );
252         primitiveTypes.put("double", Double.TYPE );
253 
254         primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
255         primitiveDefaults.put(Byte.TYPE, new Byte((byte)0));
256         primitiveDefaults.put(Short.TYPE, new Short((short)0));
257         primitiveDefaults.put(Character.TYPE, new Character((char)0));
258         primitiveDefaults.put(Integer.TYPE, new Integer(0));
259         primitiveDefaults.put(Long.TYPE, new Long(0L));
260         primitiveDefaults.put(Float.TYPE, new Float(0.0f));
261         primitiveDefaults.put(Double.TYPE, new Double(0.0));
262         primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
263         primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
264     }
265 
266     /**
267         Gets the "target" class of an object for looking up accessors that
268         are registered on the target.  If the object is a Class object this
269         will return the Class itself, else it will return object's getClass()
270         result.
271      */
272     public static Class getTargetClass(Object o)
273     {
274         return (o == null) ? null : ((o instanceof Class) ? (Class)o : o.getClass());
275     }
276 
277     /**
278         Returns the base name (the class name without the
279         package name prepended) of the object given.
280      */
281     public static String getBaseName(Object o)
282     {
283         return (o == null) ? null : getClassBaseName(o.getClass());
284     }
285 
286     /**
287         Returns the base name (the class name without the
288         package name prepended) of the class given.
289      */
290     public static String getClassBaseName(Class c)
291     {
292         String      s = c.getName();
293 
294         return s.substring(s.lastIndexOf('.') + 1);
295     }
296 
297     public static String getClassName(Object o, boolean fullyQualified)
298     {
299       if (!(o instanceof Class)) {
300         o = o.getClass();
301       }
302       return getClassName((Class)o, fullyQualified);
303     }
304 
305     public static String getClassName(Class c, boolean fullyQualified)
306     {
307       return fullyQualified ? c.getName() : getClassBaseName(c);
308     }
309 
310     /**
311         Returns the package name of the object's class.
312      */
313     public static String getPackageName(Object o)
314     {
315         return (o == null) ? null : getClassPackageName(o.getClass());
316     }
317 
318     /**
319         Returns the package name of the class given.
320      */
321     public static String getClassPackageName(Class c)
322     {
323         String      s = c.getName();
324         int         i = s.lastIndexOf('.');
325 
326         return (i < 0) ? null : s.substring(0, i);
327     }
328 
329     /**
330         Returns a "pointer" string in the usual format for these
331         things - 0x<hex digits>.
332      */
333     public static String getPointerString(int num)
334     {
335       StringBuffer  result = new StringBuffer();
336       String      hex = Integer.toHexString(num),
337               pad;
338     Integer      l = new Integer(hex.length());
339 
340         //result.append(HEX_PREFIX);
341         if ((pad = (String)HEX_PADDING.get(l)) == null) {
342         StringBuffer  pb = new StringBuffer();
343 
344           for (int i = hex.length(); i < HEX_LENGTH; i++) {
345             pb.append('0');
346           }
347           pad = new String(pb);
348           HEX_PADDING.put(l, pad);
349       }
350       result.append(pad);
351         result.append(hex);
352         return new String(result);
353     }
354 
355     /**
356         Returns a "pointer" string in the usual format for these
357         things - 0x<hex digits> for the object given.  This will
358         always return a unique value for each object.
359      */
360     public static String getPointerString(Object o)
361     {
362         return getPointerString((o == null) ? 0 : System.identityHashCode(o));
363     }
364 
365     /**
366         Returns a unique descriptor string that includes the object's
367         class and a unique integer identifier.  If fullyQualified is
368         true then the class name will be fully qualified to include
369         the package name, else it will be just the class' base name.
370      */
371     public static String getUniqueDescriptor(Object object, boolean fullyQualified)
372     {
373       StringBuffer  result = new StringBuffer();
374 
375         if (object != null) {
376           if (object instanceof Proxy) {
377             Class    interfaceClass = object.getClass().getInterfaces()[0];
378 
379             result.append(getClassName(interfaceClass, fullyQualified));
380             result.append('^');
381             object = Proxy.getInvocationHandler(object);
382           }
383             result.append(getClassName(object, fullyQualified));
384             result.append('@');
385             result.append(getPointerString(object));
386         } else {
387             result.append(NULL_OBJECT_STRING);
388         }
389         return new String(result);
390     }
391 
392     /**
393         Returns a unique descriptor string that includes the object's
394         class' base name and a unique integer identifier.
395      */
396     public static String getUniqueDescriptor(Object object)
397     {
398         return getUniqueDescriptor(object, false);
399     }
400 
401     /**
402         Utility to convert a List into an Object[] array.  If the list is zero
403         elements this will return a constant array; toArray() on List always
404         returns a new object and this is wasteful for our purposes.
405      */
406     public static Object[] toArray(List list)
407     {
408         Object[]        result;
409         int             size = list.size();
410 
411         if (size == 0) {
412             result = NoArguments;
413         } else {
414             result = getObjectArrayPool().create(list.size());
415             for (int i = 0; i < size; i++) {
416                 result[i] = list.get(i);
417             }
418         }
419         return result;
420     }
421 
422     /**
423         Returns the parameter types of the given method.
424      */
425     public static Class[] getParameterTypes(Method m)
426     {
427         synchronized(methodParameterTypesCache) {
428             Class[]     result;
429 
430             if ((result = (Class[])methodParameterTypesCache.get(m)) == null) {
431                 methodParameterTypesCache.put(m, result = m.getParameterTypes());
432             }
433             return result;
434         }
435     }
436 
437     /**
438         Returns the parameter types of the given method.
439      */
440     public static Class[] getParameterTypes(Constructor c)
441     {
442         synchronized(ctorParameterTypesCache) {
443             Class[]     result;
444 
445             if ((result = (Class[])ctorParameterTypesCache.get(c)) == null) {
446                 ctorParameterTypesCache.put(c, result = c.getParameterTypes());
447             }
448             return result;
449         }
450     }
451 
452     /**
453         Permission will be named "invoke.<declaring-class>.<method-name>".
454      */
455     public static Permission getPermission(Method method)
456     {
457         Permission              result = null;
458         Class                   mc = method.getDeclaringClass();
459 
460         synchronized(invokePermissionCache) {
461             Map                     permissions = (Map)invokePermissionCache.get(mc);
462 
463             if (permissions == null) {
464                 invokePermissionCache.put(mc, permissions = new HashMap(101));
465             }
466             if ((result = (Permission)permissions.get(method.getName())) == null) {
467                 result = new OgnlInvokePermission("invoke." + mc.getName() + "." + method.getName());
468                 permissions.put(method.getName(), result);
469             }
470         }
471         return result;
472     }
473 
474     public static Object invokeMethod( Object target, Method method, Object[] argsArray ) throws InvocationTargetException, IllegalAccessException
475     {
476         Object      result;
477         boolean     wasAccessible = true;
478 
479         if (securityManager != null) {
480             try {
481                 securityManager.checkPermission(getPermission(method));
482             } catch (SecurityException ex) {
483                 throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
484             }
485         }
486         if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
487             if (!(wasAccessible = ((AccessibleObject)method).isAccessible())) {
488                 ((AccessibleObject)method).setAccessible(true);
489             }
490         }
491         result = method.invoke( target, argsArray );
492         if (!wasAccessible) {
493             ((AccessibleObject)method).setAccessible(false);
494         }
495         return result;
496     }
497 
498       /**
499        * Gets the class for a method argument that is appropriate for looking up methods
500        * by reflection, by looking for the standard primitive wrapper classes and
501        * exchanging for them their underlying primitive class objects.  Other classes are
502        * passed through unchanged.
503        *
504        * @param arg an object that is being passed to a method
505        * @return the class to use to look up the method
506        */
507     public static final Class getArgClass( Object arg )
508     {
509         if ( arg == null )
510             return null;
511         Class c = arg.getClass();
512         if ( c == Boolean.class )
513             return Boolean.TYPE;
514         else if ( c.getSuperclass() == Number.class ) {
515             if ( c == Integer.class )
516                 return Integer.TYPE;
517             if ( c == Double.class )
518                 return Double.TYPE;
519             if ( c == Byte.class )
520                 return Byte.TYPE;
521             if ( c == Long.class )
522                 return Long.TYPE;
523             if ( c == Float.class )
524                 return Float.TYPE;
525             if ( c == Short.class )
526                 return Short.TYPE;
527         }
528         else if ( c == Character.class )
529             return Character.TYPE;
530         return c;
531     }
532 
533       /**
534        * Tells whether the given object is compatible with the given class
535        * ---that is, whether the given object can be passed as an argument
536        * to a method or constructor whose parameter type is the given class.
537        * If object is null this will return true because null is compatible
538        * with any type.
539        */
540     public static final boolean isTypeCompatible( Object object, Class c )
541     {
542         boolean         result = true;
543 
544         if ( object != null ) {
545             if ( c.isPrimitive() ) {
546                 if ( getArgClass(object) != c ) {
547                     result = false;
548                 }
549             } else if ( !c.isInstance(object) ) {
550                 result = false;
551             }
552         }
553         return result;
554     }
555 
556       /**
557        * Tells whether the given array of objects is compatible with the given array of
558        * classes---that is, whether the given array of objects can be passed as arguments
559        * to a method or constructor whose parameter types are the given array of classes.
560        */
561     public static final boolean areArgsCompatible( Object[] args, Class[] classes )
562     {
563         boolean     result = true;
564 
565         if ( args.length != classes.length ) {
566             result = false;
567         } else {
568             for ( int index=0, count=args.length; result && (index < count); ++index ) {
569               result = isTypeCompatible(args[index], classes[index]);
570             }
571         }
572         return result;
573     }
574 
575       /**
576        * Tells whether the first array of classes is more specific than the second.
577        * Assumes that the two arrays are of the same length.
578        */
579     public static final boolean isMoreSpecific( Class[] classes1, Class[] classes2 )
580     {
581         for ( int index=0, count=classes1.length; index < count; ++index )
582           {
583             Class c1 = classes1[index], c2 = classes2[index];
584             if ( c1 == c2 )
585                 continue;
586             else if ( c1.isPrimitive() )
587                 return true;
588             else if ( c1.isAssignableFrom(c2) )
589                 return false;
590             else if ( c2.isAssignableFrom(c1) )
591                 return true;
592           }
593 
594           // They are the same!  So the first is not more specific than the second.
595         return false;
596     }
597 
598     public static final String getModifierString(int modifiers)
599     {
600         String      result;
601 
602         if (Modifier.isPublic(modifiers))
603             result = "public";
604         else
605         if (Modifier.isProtected(modifiers))
606             result = "protected";
607         else
608         if (Modifier.isPrivate(modifiers))
609             result = "private";
610         else
611             result = "";
612         if (Modifier.isStatic(modifiers))
613             result = "static " + result;
614         if (Modifier.isFinal(modifiers))
615             result = "final " + result;
616         if (Modifier.isNative(modifiers))
617             result = "native " + result;
618         if (Modifier.isSynchronized(modifiers))
619             result = "synchronized " + result;
620         if (Modifier.isTransient(modifiers))
621             result = "transient " + result;
622         return result;
623     }
624 
625     public static final Class classForName( OgnlContext context, String className ) throws ClassNotFoundException
626     {
627         Class           result = (Class)primitiveTypes.get(className);
628 
629         if (result == null) {
630             ClassResolver   resolver;
631 
632             if ((context == null) || ((resolver = context.getClassResolver()) == null)) {
633                 resolver = OgnlContext.DEFAULT_CLASS_RESOLVER;
634             }
635             result = resolver.classForName(className, context);
636         }
637         return result;
638     }
639 
640     public static final boolean isInstance( OgnlContext context, Object value, String className ) throws OgnlException
641     {
642         try
643           {
644             Class c = classForName( context, className);
645             return c.isInstance( value );
646           }
647         catch (ClassNotFoundException e)
648           {
649             throw new OgnlException( "No such class: " + className, e );
650           }
651     }
652 
653     public static Object getPrimitiveDefaultValue( Class forClass )
654     {
655         return primitiveDefaults.get(forClass);
656     }
657 
658     public static Object getConvertedType( OgnlContext context, Object target, Member member, String propertyName, Object value, Class type)
659     {
660         return context.getTypeConverter().convertValue(context, target, member, propertyName, value, type);
661     }
662 
663     public static boolean getConvertedTypes( OgnlContext context, Object target, Member member, String propertyName, Class[] parameterTypes, Object[] args, Object[] newArgs)
664     {
665         boolean         result = false;
666 
667         if (parameterTypes.length == args.length) {
668             result = true;
669             for (int i = 0, ilast = parameterTypes.length - 1; result && (i <= ilast); i++) {
670                 Object      arg = args[i];
671                 Class       type = parameterTypes[i];
672 
673                 if (isTypeCompatible(arg, type)) {
674                     newArgs[i] = arg;
675                 } else {
676                     Object      v = getConvertedType(context, target, member, propertyName, arg, type);
677 
678                     if (v == OgnlRuntime.NoConversionPossible) {
679                         result = false;
680                     } else {
681                         newArgs[i] = v;
682                     }
683                 }
684             }
685         }
686         return result;
687     }
688 
689     public static Method getConvertedMethodAndArgs( OgnlContext context, Object target, String propertyName, List methods, Object[] args, Object[] newArgs)
690     {
691         Method          result = null;
692         TypeConverter   converter = context.getTypeConverter();
693 
694         if ((converter != null) && (methods != null)) {
695             for (int i = 0, icount = methods.size(); (result == null) && (i < icount); i++) {
696                 Method      m = (Method)methods.get(i);
697                 Class[]     parameterTypes = getParameterTypes(m);
698 
699                 if (getConvertedTypes( context, target, m, propertyName, parameterTypes, args, newArgs )) {
700                     result = m;
701                 }
702             }
703         }
704         return result;
705     }
706 
707     public static Constructor getConvertedConstructorAndArgs( OgnlContext context, Object target, List constructors, Object[] args, Object[] newArgs )
708     {
709         Constructor     result = null;
710         TypeConverter   converter = context.getTypeConverter();
711 
712         if ((converter != null) && (constructors != null)) {
713             for (int i = 0, icount = constructors.size(); (result == null) && (i < icount); i++) {
714                 Constructor     ctor = (Constructor)constructors.get(i);
715                 Class[]         parameterTypes = getParameterTypes(ctor);
716 
717                 if (getConvertedTypes( context, target, ctor, null, parameterTypes, args, newArgs )) {
718                     result = ctor;
719                 }
720             }
721         }
722         return result;
723     }
724 
725     /**
726         Gets the appropriate method to be called for the given target, method name and arguments.
727         If successful this method will return the Method within the target that can be called
728         and the converted arguments in actualArgs.  If unsuccessful this method will return
729         null and the actualArgs will be empty.
730      */
731     public static Method getAppropriateMethod( OgnlContext context, Object source, Object target, String methodName, String propertyName, List methods, Object[] args, Object[] actualArgs )
732     {
733         Method      result = null;
734         Class[]     resultParameterTypes = null;
735 
736         if (methods != null) {
737             for (int i = 0, icount = methods.size(); i < icount; i++) {
738                 Method  m = (Method)methods.get(i);
739                 Class[] mParameterTypes = getParameterTypes(m);
740 
741                 if ( areArgsCompatible(args, mParameterTypes) && ((result == null) || isMoreSpecific(mParameterTypes, resultParameterTypes)) ) {
742                     result = m;
743                     resultParameterTypes = mParameterTypes;
744                     System.arraycopy(args, 0, actualArgs, 0, args.length);
745                     for (int j = 0; j < mParameterTypes.length; j++) {
746                         Class       type = mParameterTypes[j];
747 
748                         if (type.isPrimitive() && (actualArgs[j] == null)) {
749                             actualArgs[j] = getConvertedType(context, source, result, propertyName, null, type);
750                         }
751                     }
752                 }
753             }
754         }
755         if ( result == null ) {
756             result = getConvertedMethodAndArgs( context, target, propertyName, methods, args, actualArgs );
757         }
758         return result;
759     }
760 
761     public static Object callAppropriateMethod( OgnlContext context, Object source, Object target, String methodName, String propertyName, List methods, Object[] args ) throws MethodFailedException
762     {
763         Throwable   reason = null;
764         Object[]    actualArgs = objectArrayPool.create(args.length);
765 
766         try {
767             Method      method = getAppropriateMethod( context, source, target, methodName, propertyName, methods, args, actualArgs );
768 
769             if ( (method == null) || !isMethodAccessible(context, source, method, propertyName) )
770             {
771                 StringBuffer        buffer = new StringBuffer();
772 
773                 if (args != null) {
774                     for (int i = 0, ilast = args.length - 1; i <= ilast; i++) {
775                         Object      arg = args[i];
776 
777                         buffer.append((arg == null) ? NULL_STRING : arg.getClass().getName());
778                         if (i < ilast) {
779                             buffer.append(", ");
780                         }
781                     }
782                 }
783                 throw new NoSuchMethodException( methodName + "(" + buffer + ")" );
784             }
785             return invokeMethod(target, method, actualArgs);
786           }
787         catch (NoSuchMethodException e)
788           { reason = e; }
789         catch (IllegalAccessException e)
790           { reason = e; }
791         catch (InvocationTargetException e)
792           { reason = e.getTargetException(); }
793         finally {
794             objectArrayPool.recycle(actualArgs);
795         }
796         throw new MethodFailedException( source, methodName, reason );
797     }
798 
799     public static final Object callStaticMethod( OgnlContext context, String className, String methodName, Object[] args ) throws OgnlException, MethodFailedException
800     {
801         try {
802             Object          result;
803             Class           targetClass = classForName(context, className);
804             MethodAccessor  ma = getMethodAccessor(targetClass);
805 
806             return ma.callStaticMethod(context, targetClass, methodName, args);
807         } catch (ClassNotFoundException ex) {
808             throw new MethodFailedException(className, methodName, ex);
809         }
810     }
811 
812     public static final Object callMethod( OgnlContext context, Object target, String methodName, String propertyName, Object[] args ) throws OgnlException, MethodFailedException
813     {
814         Object          result;
815 
816         if (target != null) {
817             MethodAccessor  ma = getMethodAccessor(target.getClass());
818 
819             result = ma.callMethod(context, target, methodName, args);
820         } else {
821             throw new NullPointerException("target is null for method " + methodName);
822         }
823         return result;
824     }
825 
826     public static final Object callConstructor( OgnlContext context, String className, Object[] args ) throws OgnlException
827     {
828         Throwable       reason = null;
829         Object[]        actualArgs = args;
830 
831         try
832           {
833             Constructor     ctor = null;
834             Class[]         ctorParameterTypes = null;
835             Class           target = classForName(context, className);
836             List            constructors = getConstructors(target);
837 
838             for (int i = 0, icount = constructors.size(); i < icount; i++)
839             {
840                 Constructor     c = (Constructor)constructors.get(i);
841                 Class[]         cParameterTypes = getParameterTypes(c);
842 
843                 if ( areArgsCompatible(args, cParameterTypes) && (ctor == null || isMoreSpecific(cParameterTypes, ctorParameterTypes)) ) {
844                     ctor = c;
845                     ctorParameterTypes = cParameterTypes;
846                 }
847               }
848             if ( ctor == null )
849             {
850                 actualArgs = objectArrayPool.create(args.length);
851                 if ((ctor = getConvertedConstructorAndArgs( context, target, constructors, args, actualArgs )) == null) {
852                     throw new NoSuchMethodException();
853                 }
854             }
855             if (!context.getMemberAccess().isAccessible(context, target, ctor, null)) {
856                 throw new IllegalAccessException("access denied to " + target.getName() + "()");
857             }
858             return ctor.newInstance( actualArgs );
859           }
860         catch (ClassNotFoundException e)
861           { reason = e; }
862         catch (NoSuchMethodException e)
863           { reason = e; }
864         catch (IllegalAccessException e)
865           { reason = e; }
866         catch (InvocationTargetException e)
867           { reason = e.getTargetException(); }
868         catch (InstantiationException e)
869           { reason = e; }
870         finally {
871             if (actualArgs != args) {
872                 objectArrayPool.recycle(actualArgs);
873             }
874         }
875 
876         throw new MethodFailedException( className, "new", reason );
877     }
878 
879     public static final Object getMethodValue(OgnlContext context, Object target, String propertyName) throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
880     {
881         return getMethodValue(context, target, propertyName, false);
882     }
883 
884     /**
885         If the checkAccessAndExistence flag is true this method will check to see if the
886         method exists and if it is accessible according to the context's MemberAccess.
887         If neither test passes this will return NotFound.
888      */
889     public static final Object getMethodValue(OgnlContext context, Object target, String propertyName, boolean checkAccessAndExistence) throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
890     {
891         Object              result = null;
892         Method              m = getGetMethod(context, (target == null) ? null : target.getClass(), propertyName);
893 
894         if (checkAccessAndExistence) {
895             if ((m == null) || !context.getMemberAccess().isAccessible(context, target, m, propertyName)) {
896                 result = NotFound;
897             }
898         }
899         if (result == null) {
900             if (m != null)
901             {
902                 try
903                 {
904                     result = invokeMethod(target, m, NoArguments);
905                 }
906                 catch (InvocationTargetException ex)
907                 {
908                     throw new OgnlException(propertyName, ex.getTargetException());
909                 }
910             } else {
911                 throw new NoSuchMethodException(propertyName);
912             }
913         }
914         return result;
915     }
916 
917     public static final boolean setMethodValue(OgnlContext context, Object target, String propertyName, Object value) throws OgnlException, IllegalAccessException, NoSuchMethodException, MethodFailedException, IntrospectionException
918     {
919         return setMethodValue(context, target, propertyName, value, false);
920     }
921 
922     public static final boolean setMethodValue(OgnlContext context, Object target, String propertyName, Object value, boolean checkAccessAndExistence) throws OgnlException, IllegalAccessException, NoSuchMethodException, MethodFailedException, IntrospectionException
923     {
924         boolean     result = true;
925         Method      m = getSetMethod(context, (target == null) ? null : target.getClass(), propertyName);
926 
927         if (checkAccessAndExistence) {
928             if ((m == null) || !context.getMemberAccess().isAccessible(context, target, m, propertyName)) {
929                 result = false;
930             }
931         }
932         if (result) {
933             if (m != null) {
934                 Object[]        args = objectArrayPool.create(value);
935 
936                 try {
937                     callAppropriateMethod(context, target, target, m.getName(), propertyName, Collections.nCopies(1, m), args);
938                 } finally {
939                     objectArrayPool.recycle(args);
940                 }
941             } else {
942                 result = false;
943             }
944         }
945         return result;
946     }
947 
948     public static final List getConstructors(Class targetClass)
949     {
950         List        result;
951 
952         synchronized(constructorCache) {
953             if ((result = (List)constructorCache.get(targetClass)) == null) {
954                 constructorCache.put(targetClass, result = Arrays.asList(targetClass.getConstructors()));
955             }
956         }
957         return result;
958     }
959 
960     public static final Map getMethods( Class targetClass, boolean staticMethods )
961     {
962         ClassCache  cache = (staticMethods ? staticMethodCache : instanceMethodCache);
963         Map         result;
964 
965         synchronized(cache) {
966             if ((result = (Map)cache.get(targetClass)) == null)
967             {
968                 cache.put(targetClass, result = new HashMap(23));
969                 for (Class c = targetClass; c != null; c = c.getSuperclass()) {
970                     Method[]        ma = c.getDeclaredMethods();
971 
972                     for (int i = 0, icount = ma.length; i < icount; i++)
973                     {
974                         if (Modifier.isStatic(ma[i].getModifiers()) == staticMethods) {
975                             List        ml = (List)result.get(ma[i].getName());
976 
977                             if (ml == null)
978                                 result.put(ma[i].getName(), ml = new ArrayList());
979                             ml.add(ma[i]);
980                         }
981                     }
982                 }
983             }
984         }
985         return result;
986     }
987 
988     public static final List getMethods( Class targetClass, String name, boolean staticMethods )
989     {
990         return (List)getMethods(targetClass, staticMethods).get(name);
991     }
992 
993     public static final Map getFields(Class targetClass)
994     {
995         Map         result;
996 
997         synchronized(fieldCache) {
998             if ((result = (Map)fieldCache.get(targetClass)) == null)
999             {
1000                Field       fa[];
1001
1002                result = new HashMap(23);
1003                fa = targetClass.getDeclaredFields();
1004                for (int i = 0; i < fa.length; i++) {
1005                    result.put(fa[i].getName(), fa[i]);
1006                }
1007                fieldCache.put(targetClass, result);
1008            }
1009        }
1010        return result;
1011    }
1012
1013    public static final Field getField(Class inClass, String name)
1014    {
1015        Field       result = null;
1016
1017        synchronized(fieldCache)
1018        {
1019            Object      o = getFields(inClass).get(name);
1020
1021            if (o == null)
1022            {
1023                superclasses.clear();
1024                for (Class sc = inClass; (sc != null) && (result == null); sc = sc.getSuperclass())
1025                {
1026                    if ((o = getFields(sc).get(name)) == NotFound)
1027                        break;
1028                    superclasses.add(sc);
1029                    if ((result = (Field)o) != null)
1030                        break;
1031                }
1032                /*
1033                    Bubble the found value (either cache miss or actual field)
1034                    to all supeclasses that we saw for quicker access next time.
1035                */
1036                for (int i = 0, icount = superclasses.size(); i < icount; i++)
1037                {
1038                    getFields((Class)superclasses.get(i)).put(name, (result == null) ? NotFound : result);
1039                }
1040            }
1041            else
1042            {
1043                if (o instanceof Field)
1044                {
1045                    result = (Field)o;
1046                }
1047                else
1048                {
1049                    if (result == NotFound)
1050                        result = null;
1051                }
1052            }
1053        }
1054        return result;
1055    }
1056
1057    public static final Object getFieldValue(OgnlContext context, Object target, String propertyName) throws NoSuchFieldException
1058    {
1059        return getFieldValue(context, target, propertyName, false);
1060    }
1061
1062    public static final Object getFieldValue(OgnlContext context, Object target, String propertyName, boolean checkAccessAndExistence) throws NoSuchFieldException
1063    {
1064        Object          result = null;
1065        Field           f = getField((target == null) ? null : target.getClass(), propertyName);
1066
1067        if (checkAccessAndExistence) {
1068            if ((f == null) || !context.getMemberAccess().isAccessible(context, target, f, propertyName)) {
1069                result = NotFound;
1070            }
1071        }
1072        if (result == null) {
1073            if (f == null) {
1074                throw new NoSuchFieldException(propertyName);
1075            } else {
1076                try
1077                {
1078                    Object      state = null;
1079
1080                    if ((f != null) && !Modifier.isStatic(f.getModifiers()))
1081                    {
1082                        state = context.getMemberAccess().setup(context, target, f, propertyName);
1083                        result = f.get(target);
1084                        context.getMemberAccess().restore(context, target, f, propertyName, state);
1085                    }
1086                    else
1087                        throw new NoSuchFieldException(propertyName);
1088                }
1089                catch (IllegalAccessException ex)
1090                {
1091                    throw new NoSuchFieldException(propertyName);
1092                }
1093            }
1094        }
1095        return result;
1096    }
1097
1098    public static final boolean setFieldValue(OgnlContext context, Object target, String propertyName, Object value) throws OgnlException
1099    {
1100        boolean         result = false;
1101
1102        try
1103        {
1104            Field       f = getField( (target == null) ? null : target.getClass(), propertyName );
1105            Object      state;
1106
1107            if ((f != null) && !Modifier.isStatic(f.getModifiers()))
1108            {
1109                state = context.getMemberAccess().setup(context, target, f, propertyName);
1110                try
1111                {
1112                    if (isTypeCompatible(value, f.getType()) || ((value = getConvertedType( context, target, f, propertyName, value, f.getType())) != null)) {
1113                        f.set(target, value);
1114                        result = true;
1115                    }
1116                }
1117                finally
1118                {
1119                    context.getMemberAccess().restore(context, target, f, propertyName, state);
1120                }
1121            }
1122        }
1123        catch (IllegalAccessException ex)
1124        {
1125            throw new NoSuchPropertyException(target, propertyName, ex);
1126        }
1127        return result;
1128    }
1129
1130    public static final boolean isFieldAccessible(OgnlContext context, Object target, Class inClass, String propertyName)
1131    {
1132        return isFieldAccessible(context, target, getField(inClass, propertyName), propertyName);
1133    }
1134
1135    public static final boolean isFieldAccessible(OgnlContext context, Object target, Field field, String propertyName)
1136    {
1137        return context.getMemberAccess().isAccessible(context, target, field, propertyName);
1138    }
1139
1140    public static final boolean hasField(OgnlContext context, Object target, Class inClass, String propertyName)
1141    {
1142        Field       f = getField(inClass, propertyName);
1143
1144        return (f != null) && isFieldAccessible(context, target, f, propertyName);
1145    }
1146
1147    public static final Object getStaticField( OgnlContext context, String className, String fieldName ) throws OgnlException
1148    {
1149        Exception reason = null;
1150        try
1151          {
1152            Class c = classForName(context, className);
1153
1154            /*
1155                Check for virtual static field "class"; this cannot interfere with
1156                normal static fields because it is a reserved word.
1157             */
1158            if (fieldName.equals("class"))
1159              {
1160                return c;
1161              }
1162            else
1163              {
1164                Field f = c.getField(fieldName);
1165                if ( !Modifier.isStatic(f.getModifiers()) )
1166                    throw new OgnlException( "Field " + fieldName + " of class " + className + " is not static" );
1167                return f.get(null);
1168              }
1169          }
1170        catch (ClassNotFoundException e)
1171          { reason = e; }
1172        catch (NoSuchFieldException e)
1173          { reason = e; }
1174        catch (SecurityException e)
1175          { reason = e; }
1176        catch (IllegalAccessException e)
1177          { reason = e; }
1178
1179        throw new OgnlException( "Could not get static field " + fieldName + " from class " + className, reason );
1180    }
1181
1182    public static final List getDeclaredMethods(Class targetClass, String propertyName, boolean findSets)
1183    {
1184        List        result = null;
1185        ClassCache  cache = declaredMethods[findSets ? 0 : 1];
1186
1187        synchronized(cache) {
1188            Map         propertyCache = (Map)cache.get(targetClass);
1189
1190            if ((propertyCache == null) || ((result = (List)propertyCache.get(propertyName)) == null)) {
1191                String      baseName = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
1192                int         len = baseName.length();
1193
1194                for (Class c = targetClass; c != null; c = c.getSuperclass()) {
1195                    Method[]        methods = c.getDeclaredMethods();
1196
1197                    for (int i = 0; i < methods.length; i++) {
1198                        String      ms = methods[i].getName();
1199
1200                        if (ms.endsWith(baseName)) {
1201                            boolean     isSet = false,
1202                                        isGet = false,
1203                                        isIs = false;
1204
1205                            if ((isSet = ms.startsWith(SET_PREFIX)) || (isGet = ms.startsWith(GET_PREFIX)) || (isIs = ms.startsWith(IS_PREFIX))) {
1206                                int     prefixLength = (isIs ? 2 : 3);
1207
1208                                if (isSet == findSets) {
1209                                    if (baseName.length() == (ms.length() - prefixLength)) {
1210                                        if (result == null) {
1211                                            result = new ArrayList();
1212                                        }
1213                                        result.add(methods[i]);
1214                                    }
1215                                }
1216                            }
1217                        }
1218                    }
1219                }
1220                if (propertyCache == null) {
1221                    cache.put(targetClass, propertyCache = new HashMap(101));
1222                }
1223                propertyCache.put(propertyName, (result == null) ? NotFoundList : result);
1224            }
1225            return (result == NotFoundList) ? null : result;
1226        }
1227    }
1228
1229    public static final Method getGetMethod(OgnlContext context, Class targetClass, String propertyName) throws IntrospectionException, OgnlException
1230    {
1231        Method              result = null;
1232        PropertyDescriptor  pd = getPropertyDescriptor(targetClass, propertyName);
1233
1234        if (pd == null) {
1235            List        methods = getDeclaredMethods(targetClass, propertyName, false /* find 'get' methods */);
1236
1237            if (methods != null) {
1238                for (int i = 0, icount = methods.size(); i < icount; i++) {
1239                    Method      m = (Method)methods.get(i);
1240                    Class[]     mParameterTypes = getParameterTypes(m);
1241
1242                    if (mParameterTypes.length == 0) {
1243                        result = m;
1244                        break;
1245                    }
1246                }
1247            }
1248        } else {
1249            result = pd.getReadMethod();
1250        }
1251        return result;
1252    }
1253
1254    public static final boolean isMethodAccessible(OgnlContext context, Object target, Method method, String propertyName)
1255    {
1256        return (method == null) ? false : context.getMemberAccess().isAccessible(context, target, method, propertyName);
1257    }
1258
1259    public static final boolean hasGetMethod(OgnlContext context, Object target, Class targetClass, String propertyName) throws IntrospectionException, OgnlException
1260    {
1261        return isMethodAccessible(context, target, getGetMethod(context, targetClass, propertyName), propertyName);
1262    }
1263
1264    public static final Method getSetMethod(OgnlContext context, Class targetClass, String propertyName) throws IntrospectionException, OgnlException
1265    {
1266        Method              result = null;
1267        PropertyDescriptor  pd = getPropertyDescriptor(targetClass, propertyName);
1268
1269        if (pd == null) {
1270            List        methods = getDeclaredMethods(targetClass, propertyName, true /* find 'set' methods */);
1271
1272            if (methods != null) {
1273                for (int i = 0, icount = methods.size(); i < icount; i++) {
1274                    Method      m = (Method)methods.get(i);
1275                    Class[]     mParameterTypes = getParameterTypes(m);
1276
1277                    if (mParameterTypes.length == 1) {
1278                        result = m;
1279                        break;
1280                    }
1281                }
1282            }
1283        } else {
1284            result = pd.getWriteMethod();
1285        }
1286        return result;
1287    }
1288
1289    public static final boolean hasSetMethod(OgnlContext context, Object target, Class targetClass, String propertyName) throws IntrospectionException, OgnlException
1290    {
1291        return isMethodAccessible(context, target, getSetMethod(context, targetClass, propertyName), propertyName);
1292    }
1293
1294    public static final boolean hasGetProperty( OgnlContext context, Object target, Object oname ) throws IntrospectionException, OgnlException
1295    {
1296        Class       targetClass = (target == null) ? null : target.getClass();
1297        String      name = oname.toString();
1298
1299        return hasGetMethod( context, target, targetClass, name ) || hasField( context, target, targetClass, name );
1300    }
1301
1302    public static final boolean hasSetProperty( OgnlContext context, Object target, Object oname ) throws IntrospectionException, OgnlException
1303    {
1304        Class       targetClass = (target == null) ? null : target.getClass();
1305        String      name = oname.toString();
1306
1307        return hasSetMethod( context, target, targetClass, name ) || hasField( context, target, targetClass, name );
1308    }
1309
1310    private static final boolean indexMethodCheck(List methods)
1311    {
1312        boolean         result = false;
1313
1314        if (methods.size() > 0) {
1315            Method          fm = (Method)methods.get(0);
1316            Class[]         fmpt = getParameterTypes(fm);
1317            int             fmpc = fmpt.length;
1318            Class           lastMethodClass = fm.getDeclaringClass();
1319
1320            result = true;
1321            for (int i = 1; result && (i < methods.size()); i++) {
1322                Method      m = (Method)methods.get(i);
1323                Class       c = m.getDeclaringClass();
1324
1325                // Check to see if more than one method implemented per class
1326                if (lastMethodClass == c) {
1327                    result = false;
1328                } else {
1329                    Class[]     mpt = getParameterTypes(fm);
1330                    int         mpc = fmpt.length;
1331
1332                    if (fmpc != mpc) {
1333                        result = false;
1334                    }
1335                    for (int j = 0; j < fmpc; j++) {
1336                        if (fmpt[j] != mpt[j]) {
1337                            result = false;
1338                            break;
1339                        }
1340                    }
1341                }
1342                lastMethodClass = c;
1343            }
1344        }
1345        return result;
1346    }
1347
1348    private static final void findObjectIndexedPropertyDescriptors(Class targetClass, Map intoMap) throws OgnlException
1349    {
1350        Map     allMethods = getMethods(targetClass, false);
1351        Map     pairs = new HashMap(101);
1352
1353        for (Iterator it = allMethods.keySet().iterator(); it.hasNext(); ) {
1354            String      methodName = (String)it.next();
1355            List        methods = (List)allMethods.get(methodName);
1356
1357            /*
1358                Only process set/get where there is exactly one implementation
1359                of the method per class and those implementations are all the
1360                same
1361             */
1362            if (indexMethodCheck(methods)) {
1363                boolean     isGet = false,
1364                            isSet = false;
1365                Method      m = (Method)methods.get(0);
1366
1367                if (((isSet = methodName.startsWith(SET_PREFIX)) || (isGet = methodName.startsWith(GET_PREFIX))) && (methodName.length() > 3)) {
1368                    String      propertyName = Introspector.decapitalize(methodName.substring(3));
1369                    Class[]     parameterTypes = getParameterTypes(m);
1370                    int         parameterCount = parameterTypes.length;
1371
1372                    if (isGet && (parameterCount == 1) && (m.getReturnType() != Void.TYPE)) {
1373                        List        pair = (List)pairs.get(propertyName);
1374
1375                        if (pair == null) {
1376                            pairs.put(propertyName, pair = new ArrayList());
1377                        }
1378                        pair.add(m);
1379                    }
1380                    if (isSet && (parameterCount == 2) && (m.getReturnType() == Void.TYPE)) {
1381                        List        pair = (List)pairs.get(propertyName);
1382
1383                        if (pair == null) {
1384                            pairs.put(propertyName, pair = new ArrayList());
1385                        }
1386                        pair.add(m);
1387                    }
1388                }
1389            }
1390        }
1391        for (Iterator it = pairs.keySet().iterator(); it.hasNext();) {
1392            String      propertyName = (String)it.next();
1393            List        methods = (List)pairs.get(propertyName);
1394
1395            if (methods.size() == 2) {
1396                Method      method1 = (Method)methods.get(0),
1397                            method2 = (Method)methods.get(1),
1398                            setMethod = (method1.getParameterTypes().length == 2) ? method1 : method2,
1399                            getMethod = (setMethod == method1) ? method2 : method1;
1400                Class       keyType = getMethod.getParameterTypes()[0],
1401                            propertyType = getMethod.getReturnType();
1402
1403                if (keyType == setMethod.getParameterTypes()[0]) {
1404                    if (propertyType == setMethod.getParameterTypes()[1]) {
1405                        ObjectIndexedPropertyDescriptor     propertyDescriptor;
1406
1407                        try {
1408                            propertyDescriptor = new ObjectIndexedPropertyDescriptor(propertyName, propertyType, getMethod, setMethod);
1409                        } catch (Exception ex) {
1410                            throw new OgnlException("creating object indexed property descriptor for '" + propertyName + "' in " + targetClass, ex);
1411                        }
1412                        intoMap.put(propertyName, propertyDescriptor);
1413                    }
1414                }
1415
1416            }
1417        }
1418    }
1419
1420    /**
1421        This method returns the property descriptors for the given class as a Map
1422     */
1423    public static final Map getPropertyDescriptors(Class targetClass) throws IntrospectionException, OgnlException
1424    {
1425        Map     result;
1426
1427        synchronized(propertyDescriptorCache) {
1428            if ((result = (Map)propertyDescriptorCache.get(targetClass)) == null) {
1429                PropertyDescriptor[]    pda = Introspector.getBeanInfo(targetClass).getPropertyDescriptors();
1430
1431                result = new HashMap(101);
1432                for (int i = 0, icount = pda.length; i < icount; i++) {
1433                    result.put(pda[i].getName(), pda[i]);
1434                }
1435                findObjectIndexedPropertyDescriptors(targetClass, result);
1436                propertyDescriptorCache.put(targetClass, result);
1437            }
1438        }
1439        return result;
1440    }
1441
1442    /**
1443        This method returns a PropertyDescriptor for the given class and property name using
1444        a Map lookup (using getPropertyDescriptorsMap()).
1445     */
1446    public static final PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName) throws IntrospectionException, OgnlException
1447    {
1448        return (targetClass == null) ? null : (PropertyDescriptor)getPropertyDescriptors(targetClass).get(propertyName);
1449    }
1450
1451    public static final PropertyDescriptor[] getPropertyDescriptorsArray(Class targetClass) throws IntrospectionException
1452    {
1453        PropertyDescriptor[]    result = null;
1454
1455        if (targetClass != null) {
1456            synchronized(propertyDescriptorCache) {
1457                if ((result = (PropertyDescriptor[])propertyDescriptorCache.get(targetClass)) == null) {
1458                    propertyDescriptorCache.put(targetClass, result = Introspector.getBeanInfo(targetClass).getPropertyDescriptors());
1459                }
1460            }
1461        }
1462        return result;
1463    }
1464
1465    /**
1466        Gets the property descriptor with the given name for the target class given.
1467        @param targetClass      Class for which property descriptor is desired
1468        @param name             Name of property
1469        @return                 PropertyDescriptor of the named property or null if
1470                                the class has no property with the given name
1471     */
1472    public static final PropertyDescriptor getPropertyDescriptorFromArray(Class targetClass, String name) throws IntrospectionException
1473    {
1474        PropertyDescriptor      result = null;
1475        PropertyDescriptor[]    pda = getPropertyDescriptorsArray(targetClass);
1476
1477        for (int i = 0, icount = pda.length; (result == null) && (i < icount); i++) {
1478            if (pda[i].getName().compareTo(name) == 0) {
1479                result = pda[i];
1480            }
1481        }
1482        return result;
1483    }
1484
1485    public static final void setMethodAccessor(Class cls, MethodAccessor accessor)
1486    {
1487        synchronized(methodAccessors) {
1488            methodAccessors.put( cls, accessor );
1489        }
1490    }
1491
1492    public static final MethodAccessor getMethodAccessor( Class cls ) throws OgnlException
1493    {
1494        MethodAccessor answer = (MethodAccessor)getHandler( cls, methodAccessors );
1495        if ( answer != null )
1496            return answer;
1497        throw new OgnlException( "No method accessor for " + cls );
1498    }
1499
1500    public static final void setPropertyAccessor(Class cls, PropertyAccessor accessor)
1501    {
1502        synchronized(propertyAccessors) {
1503            propertyAccessors.put( cls, accessor );
1504        }
1505    }
1506
1507    public static final PropertyAccessor getPropertyAccessor( Class cls ) throws OgnlException
1508    {
1509        PropertyAccessor answer = (PropertyAccessor)getHandler( cls, propertyAccessors );
1510        if ( answer != null )
1511            return answer;
1512
1513        throw new OgnlException( "No property accessor for class " + cls );
1514    }
1515
1516    public static final ElementsAccessor getElementsAccessor( Class cls ) throws OgnlException
1517    {
1518        ElementsAccessor answer = (ElementsAccessor)getHandler( cls, elementsAccessors );
1519        if ( answer != null )
1520            return answer;
1521        throw new OgnlException( "No elements accessor for class " + cls );
1522    }
1523
1524    public static final void setElementsAccessor( Class cls, ElementsAccessor accessor )
1525    {
1526        synchronized(elementsAccessors) {
1527            elementsAccessors.put( cls, accessor );
1528        }
1529    }
1530
1531    public static final NullHandler getNullHandler( Class cls ) throws OgnlException
1532    {
1533        NullHandler answer = (NullHandler)getHandler( cls, nullHandlers );
1534        if ( answer != null )
1535            return answer;
1536        throw new OgnlException( "No null handler for class " + cls );
1537    }
1538
1539    public static final void setNullHandler( Class cls, NullHandler handler )
1540    {
1541        synchronized(nullHandlers) {
1542            nullHandlers.put( cls, handler );
1543        }
1544    }
1545
1546    private static final Object getHandler( Class forClass, ClassCache handlers )
1547    {
1548        Object answer = null;
1549
1550        synchronized(handlers) {
1551            if ((answer = handlers.get(forClass)) == null)
1552            {
1553                Class   keyFound;
1554
1555                if (forClass.isArray())
1556                {
1557                    answer = handlers.get(Object[].class);
1558                    keyFound = null;
1559                }
1560                else
1561                {
1562                    keyFound = forClass;
1563                    outer:
1564                        for ( Class c = forClass; c != null; c = c.getSuperclass() )
1565                        {
1566                            answer = handlers.get(c);
1567                            if ( answer == null )
1568                            {
1569                                Class[] interfaces = c.getInterfaces();
1570                                for ( int index=0, count=interfaces.length; index < count; ++index )
1571                                {
1572                                    Class   iface = interfaces[index];
1573
1574                                    answer = handlers.get(iface);
1575                                    if (answer == null)
1576                                    {
1577                                        /* Try super-interfaces */
1578                                        answer = getHandler(iface, handlers);
1579                                    }
1580                                    if ( answer != null )
1581                                    {
1582                                        keyFound = iface;
1583                                        break outer;
1584                                    }
1585                                }
1586                            }
1587                            else
1588                            {
1589                                keyFound = c;
1590                                break;
1591                            }
1592                        }
1593                }
1594                if ( answer != null )
1595                {
1596                    if ( keyFound != forClass )
1597                    {
1598                        handlers.put( forClass, answer );
1599                    }
1600                }
1601            }
1602        }
1603        return answer;
1604    }
1605
1606    public static final Object getProperty( OgnlContext context, Object source, Object name ) throws OgnlException
1607    {
1608        PropertyAccessor        accessor;
1609
1610        if (source == null) {
1611            throw new OgnlException("source is null for getProperty(null, \"" + name + "\")");
1612        }
1613        if ((accessor = getPropertyAccessor(getTargetClass(source))) == null) {
1614            throw new OgnlException("No property accessor for " + getTargetClass(source).getName());
1615        }
1616        return accessor.getProperty( context, source, name );
1617    }
1618
1619    public static final void setProperty( OgnlContext context, Object target, Object name, Object value ) throws OgnlException
1620    {
1621        PropertyAccessor        accessor;
1622
1623        if (target == null) {
1624            throw new OgnlException("target is null for setProperty(null, \"" + name + "\", " + value + ")");
1625        }
1626        if ((accessor = getPropertyAccessor(getTargetClass(target))) == null) {
1627            throw new OgnlException("No property accessor for " + getTargetClass(target).getName());
1628        }
1629        accessor.setProperty( context, target, name, value );
1630    }
1631
1632    /**
1633        Determines the index property type, if any.  Returns <code>INDEXED_PROPERTY_NONE</code> if the
1634        property is not index-accessible as determined by OGNL or JavaBeans.  If it is indexable
1635        then this will return whether it is a JavaBeans indexed property, conforming to the
1636        indexed property patterns (returns <code>INDEXED_PROPERTY_INT</code>) or if it conforms
1637        to the OGNL arbitrary object indexable (returns <code>INDEXED_PROPERTY_OBJECT</code>).
1638     */
1639    public static final int getIndexedPropertyType( OgnlContext context, Class sourceClass, String name) throws OgnlException
1640    {
1641        int     result = INDEXED_PROPERTY_NONE;
1642
1643        try {
1644            PropertyDescriptor  pd = getPropertyDescriptor(sourceClass, name);
1645
1646            if (pd != null) {
1647                if (pd instanceof IndexedPropertyDescriptor) {
1648                    result = INDEXED_PROPERTY_INT;
1649                } else {
1650                    if (pd instanceof ObjectIndexedPropertyDescriptor) {
1651                        result = INDEXED_PROPERTY_OBJECT;
1652                    }
1653                }
1654            }
1655        } catch (Exception ex) {
1656            throw new OgnlException("problem determining if '" + name + "' is an indexed property", ex);
1657        }
1658        return result;
1659    }
1660
1661    public static final Object getIndexedProperty( OgnlContext context, Object source, String name, Object index ) throws OgnlException
1662    {
1663        Throwable       reason = null;
1664        Object[]        args = objectArrayPool.create(index);
1665
1666        try {
1667            PropertyDescriptor          pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name);
1668            Method                      m;
1669
1670            if (pd instanceof IndexedPropertyDescriptor) {
1671                m = ((IndexedPropertyDescriptor)pd).getIndexedReadMethod();
1672            } else {
1673                if (pd instanceof ObjectIndexedPropertyDescriptor) {
1674                    m = ((ObjectIndexedPropertyDescriptor)pd).getIndexedReadMethod();
1675                } else {
1676                    throw new OgnlException("property '" + name + "' is not an indexed property");
1677                }
1678            }
1679            return callMethod(context, source, m.getName(), name, args);
1680        } catch (OgnlException ex) {
1681            throw ex;
1682        } catch (Exception ex) {
1683            throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex);
1684        } finally {
1685            objectArrayPool.recycle(args);
1686        }
1687    }
1688
1689    public static final void setIndexedProperty( OgnlContext context, Object source, String name, Object index, Object value ) throws OgnlException
1690    {
1691        Throwable       reason = null;
1692        Object[]        args = objectArrayPool.create(index, value);
1693
1694        try {
1695            PropertyDescriptor          pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name);
1696            Method                      m;
1697
1698            if (pd instanceof IndexedPropertyDescriptor) {
1699                m = ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod();
1700            } else {
1701                if (pd instanceof ObjectIndexedPropertyDescriptor) {
1702                    m = ((ObjectIndexedPropertyDescriptor)pd).getIndexedWriteMethod();
1703                } else {
1704                    throw new OgnlException("property '" + name + "' is not an indexed property");
1705                }
1706            }
1707            callMethod(context, source, m.getName(), name, args);
1708        } catch (OgnlException ex) {
1709            throw ex;
1710        } catch (Exception ex) {
1711            throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex);
1712        } finally {
1713            objectArrayPool.recycle(args);
1714        }
1715    }
1716
1717    public static EvaluationPool getEvaluationPool()
1718    {
1719        return evaluationPool;
1720    }
1721
1722    public static ObjectArrayPool getObjectArrayPool()
1723    {
1724        return objectArrayPool;
1725    }
1726}
1727