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