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