0001: //--------------------------------------------------------------------------
0002: // Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
0003: // All rights reserved.
0004: //
0005: // Redistribution and use in source and binary forms, with or without
0006: // modification, are permitted provided that the following conditions are
0007: // met:
0008: //
0009: // Redistributions of source code must retain the above copyright notice,
0010: // this list of conditions and the following disclaimer.
0011: // Redistributions in binary form must reproduce the above copyright
0012: // notice, this list of conditions and the following disclaimer in the
0013: // documentation and/or other materials provided with the distribution.
0014: // Neither the name of the Drew Davidson nor the names of its contributors
0015: // may be used to endorse or promote products derived from this software
0016: // without specific prior written permission.
0017: //
0018: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0019: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0020: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
0021: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
0022: // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
0023: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
0024: // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
0025: // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
0026: // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0027: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
0028: // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
0029: // DAMAGE.
0030: //--------------------------------------------------------------------------
0031: package ognl;
0032:
0033: import java.beans.IndexedPropertyDescriptor;
0034: import java.beans.IntrospectionException;
0035: import java.beans.Introspector;
0036: import java.beans.PropertyDescriptor;
0037: import java.lang.reflect.AccessibleObject;
0038: import java.lang.reflect.Constructor;
0039: import java.lang.reflect.Field;
0040: import java.lang.reflect.InvocationTargetException;
0041: import java.lang.reflect.Member;
0042: import java.lang.reflect.Method;
0043: import java.lang.reflect.Modifier;
0044: import java.lang.reflect.Proxy;
0045: import java.math.BigDecimal;
0046: import java.math.BigInteger;
0047: import java.security.Permission;
0048: import java.util.ArrayList;
0049: import java.util.Arrays;
0050: import java.util.Collection;
0051: import java.util.Collections;
0052: import java.util.Enumeration;
0053: import java.util.HashMap;
0054: import java.util.Iterator;
0055: import java.util.List;
0056: import java.util.Map;
0057: import java.util.Set;
0058:
0059: /**
0060: * This is an abstract class with static methods that define runtime
0061: * caching information in OGNL.
0062: * @author Luke Blanshard (blanshlu@netscape.net)
0063: * @author Drew Davidson (drew@ognl.org)
0064: */
0065: public abstract class OgnlRuntime extends Object {
0066: public static final Object NotFound = new Object();
0067: public static final List NotFoundList = new ArrayList();
0068: public static final Map NotFoundMap = new HashMap();
0069: public static final Object[] NoArguments = new Object[] {};
0070: public static final Class[] NoArgumentTypes = new Class[] {};
0071:
0072: /** Token returned by TypeConverter for no conversion possible */
0073: public static final Object NoConversionPossible = "ognl.NoConversionPossible";
0074:
0075: /** Not an indexed property */
0076: public static int INDEXED_PROPERTY_NONE = 0;
0077: /** JavaBeans IndexedProperty */
0078: public static int INDEXED_PROPERTY_INT = 1;
0079: /** OGNL ObjectIndexedProperty */
0080: public static int INDEXED_PROPERTY_OBJECT = 2;
0081:
0082: public static final String NULL_STRING = "" + null;
0083:
0084: private static final String SET_PREFIX = "set";
0085: private static final String GET_PREFIX = "get";
0086: private static final String IS_PREFIX = "is";
0087:
0088: /**
0089: Prefix padding for hexadecimal numbers to HEX_LENGTH.
0090: */
0091: private static final Map HEX_PADDING = new HashMap();
0092:
0093: /**
0094: Hexadecimal prefix for printing "pointers".
0095: */
0096: private static final String HEX_PREFIX = "0x";
0097:
0098: private static final int HEX_LENGTH = 8;
0099: /**
0100: Returned by <CODE>getUniqueDescriptor()</CODE> when the
0101: object is <CODE>null</CODE>.
0102: */
0103: private static final String NULL_OBJECT_STRING = "<null>";
0104:
0105: private static ClassCache methodAccessors = new ClassCache();
0106: private static ClassCache propertyAccessors = new ClassCache();
0107: private static ClassCache elementsAccessors = new ClassCache();
0108: private static ClassCache nullHandlers = new ClassCache();
0109: private static ClassCache propertyDescriptorCache = new ClassCache();
0110: private static ClassCache constructorCache = new ClassCache();
0111: private static ClassCache staticMethodCache = new ClassCache();
0112: private static ClassCache instanceMethodCache = new ClassCache();
0113: private static ClassCache invokePermissionCache = new ClassCache();
0114: private static ClassCache fieldCache = new ClassCache();
0115: private static List super classes = new ArrayList(); /* Used by fieldCache lookup */
0116: private static ClassCache[] declaredMethods = new ClassCache[] {
0117: new ClassCache(), new ClassCache() }; /* set, get */
0118: private static Map primitiveTypes = new HashMap(101);
0119: private static ClassCache primitiveDefaults = new ClassCache();
0120: private static Map methodParameterTypesCache = new HashMap(101);
0121: private static Map ctorParameterTypesCache = new HashMap(101);
0122: private static SecurityManager securityManager = System
0123: .getSecurityManager();
0124: private static EvaluationPool evaluationPool = new EvaluationPool();
0125: private static ObjectArrayPool objectArrayPool = new ObjectArrayPool();
0126:
0127: /**
0128: This is a highly specialized map for storing values keyed by Class objects.
0129: */
0130: private static class ClassCache extends Object {
0131: /* this MUST be a power of 2 */
0132: private static final int TABLE_SIZE = 512;
0133:
0134: /* ...and now you see why. The table size is used as a mask for generating hashes */
0135: private static final int TABLE_SIZE_MASK = TABLE_SIZE - 1;
0136:
0137: private Entry[] table;
0138:
0139: private static class Entry extends Object {
0140: protected Entry next;
0141: protected Class key;
0142: protected Object value;
0143:
0144: public Entry(Class key, Object value) {
0145: super ();
0146: this .key = key;
0147: this .value = value;
0148: }
0149: }
0150:
0151: public ClassCache() {
0152: super ();
0153: this .table = new Entry[TABLE_SIZE];
0154: }
0155:
0156: public final Object get(Class key) {
0157: Object result = null;
0158: int i = key.hashCode() & TABLE_SIZE_MASK;
0159:
0160: for (Entry entry = table[i]; entry != null; entry = entry.next) {
0161: if (entry.key == key) {
0162: result = entry.value;
0163: break;
0164: }
0165: }
0166: return result;
0167: }
0168:
0169: public final Object put(Class key, Object value) {
0170: Object result = null;
0171: int i = key.hashCode() & TABLE_SIZE_MASK;
0172: Entry entry = table[i];
0173:
0174: if (entry == null) {
0175: table[i] = new Entry(key, value);
0176: } else {
0177: if (entry.key == key) {
0178: result = entry.value;
0179: entry.value = value;
0180: } else {
0181: while (true) {
0182: if (entry.key == key) {
0183: /* replace value */
0184: result = entry.value;
0185: entry.value = value;
0186: break;
0187: } else {
0188: if (entry.next == null) {
0189: /* add value */
0190: entry.next = new Entry(key, value);
0191: break;
0192: }
0193: }
0194: entry = entry.next;
0195: }
0196: }
0197: }
0198: return result;
0199: }
0200: }
0201:
0202: static {
0203: PropertyAccessor p = new ArrayPropertyAccessor();
0204: setPropertyAccessor(Object.class, new ObjectPropertyAccessor());
0205: setPropertyAccessor(byte[].class, p);
0206: setPropertyAccessor(short[].class, p);
0207: setPropertyAccessor(char[].class, p);
0208: setPropertyAccessor(int[].class, p);
0209: setPropertyAccessor(long[].class, p);
0210: setPropertyAccessor(float[].class, p);
0211: setPropertyAccessor(double[].class, p);
0212: setPropertyAccessor(Object[].class, p);
0213: setPropertyAccessor(List.class, new ListPropertyAccessor());
0214: setPropertyAccessor(Map.class, new MapPropertyAccessor());
0215: setPropertyAccessor(Set.class, new SetPropertyAccessor());
0216: setPropertyAccessor(Iterator.class,
0217: new IteratorPropertyAccessor());
0218: setPropertyAccessor(Enumeration.class,
0219: new EnumerationPropertyAccessor());
0220:
0221: ElementsAccessor e = new ArrayElementsAccessor();
0222: setElementsAccessor(Object.class, new ObjectElementsAccessor());
0223: setElementsAccessor(byte[].class, e);
0224: setElementsAccessor(short[].class, e);
0225: setElementsAccessor(char[].class, e);
0226: setElementsAccessor(int[].class, e);
0227: setElementsAccessor(long[].class, e);
0228: setElementsAccessor(float[].class, e);
0229: setElementsAccessor(double[].class, e);
0230: setElementsAccessor(Object[].class, e);
0231: setElementsAccessor(Collection.class,
0232: new CollectionElementsAccessor());
0233: setElementsAccessor(Map.class, new MapElementsAccessor());
0234: setElementsAccessor(Iterator.class,
0235: new IteratorElementsAccessor());
0236: setElementsAccessor(Enumeration.class,
0237: new EnumerationElementsAccessor());
0238: setElementsAccessor(Number.class, new NumberElementsAccessor());
0239:
0240: NullHandler nh = new ObjectNullHandler();
0241: setNullHandler(Object.class, nh);
0242: setNullHandler(byte[].class, nh);
0243: setNullHandler(short[].class, nh);
0244: setNullHandler(char[].class, nh);
0245: setNullHandler(int[].class, nh);
0246: setNullHandler(long[].class, nh);
0247: setNullHandler(float[].class, nh);
0248: setNullHandler(double[].class, nh);
0249: setNullHandler(Object[].class, nh);
0250:
0251: MethodAccessor ma = new ObjectMethodAccessor();
0252: setMethodAccessor(Object.class, ma);
0253: setMethodAccessor(byte[].class, ma);
0254: setMethodAccessor(short[].class, ma);
0255: setMethodAccessor(char[].class, ma);
0256: setMethodAccessor(int[].class, ma);
0257: setMethodAccessor(long[].class, ma);
0258: setMethodAccessor(float[].class, ma);
0259: setMethodAccessor(double[].class, ma);
0260: setMethodAccessor(Object[].class, ma);
0261:
0262: primitiveTypes.put("boolean", Boolean.TYPE);
0263: primitiveTypes.put("byte", Byte.TYPE);
0264: primitiveTypes.put("short", Short.TYPE);
0265: primitiveTypes.put("char", Character.TYPE);
0266: primitiveTypes.put("int", Integer.TYPE);
0267: primitiveTypes.put("long", Long.TYPE);
0268: primitiveTypes.put("float", Float.TYPE);
0269: primitiveTypes.put("double", Double.TYPE);
0270:
0271: primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
0272: primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
0273: primitiveDefaults.put(Short.TYPE, new Short((short) 0));
0274: primitiveDefaults.put(Character.TYPE, new Character((char) 0));
0275: primitiveDefaults.put(Integer.TYPE, new Integer(0));
0276: primitiveDefaults.put(Long.TYPE, new Long(0L));
0277: primitiveDefaults.put(Float.TYPE, new Float(0.0f));
0278: primitiveDefaults.put(Double.TYPE, new Double(0.0));
0279: primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
0280: primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
0281: }
0282:
0283: /**
0284: Gets the "target" class of an object for looking up accessors that
0285: are registered on the target. If the object is a Class object this
0286: will return the Class itself, else it will return object's getClass()
0287: result.
0288: */
0289: public static Class getTargetClass(Object o) {
0290: return (o == null) ? null : ((o instanceof Class) ? (Class) o
0291: : o.getClass());
0292: }
0293:
0294: /**
0295: Returns the base name (the class name without the
0296: package name prepended) of the object given.
0297: */
0298: public static String getBaseName(Object o) {
0299: return (o == null) ? null : getClassBaseName(o.getClass());
0300: }
0301:
0302: /**
0303: Returns the base name (the class name without the
0304: package name prepended) of the class given.
0305: */
0306: public static String getClassBaseName(Class c) {
0307: String s = c.getName();
0308:
0309: return s.substring(s.lastIndexOf('.') + 1);
0310: }
0311:
0312: public static String getClassName(Object o, boolean fullyQualified) {
0313: if (!(o instanceof Class)) {
0314: o = o.getClass();
0315: }
0316: return getClassName((Class) o, fullyQualified);
0317: }
0318:
0319: public static String getClassName(Class c, boolean fullyQualified) {
0320: return fullyQualified ? c.getName() : getClassBaseName(c);
0321: }
0322:
0323: /**
0324: Returns the package name of the object's class.
0325: */
0326: public static String getPackageName(Object o) {
0327: return (o == null) ? null : getClassPackageName(o.getClass());
0328: }
0329:
0330: /**
0331: Returns the package name of the class given.
0332: */
0333: public static String getClassPackageName(Class c) {
0334: String s = c.getName();
0335: int i = s.lastIndexOf('.');
0336:
0337: return (i < 0) ? null : s.substring(0, i);
0338: }
0339:
0340: /**
0341: Returns a "pointer" string in the usual format for these
0342: things - 0x<hex digits>.
0343: */
0344: public static String getPointerString(int num) {
0345: StringBuffer result = new StringBuffer();
0346: String hex = Integer.toHexString(num), pad;
0347: Integer l = new Integer(hex.length());
0348:
0349: //result.append(HEX_PREFIX);
0350: if ((pad = (String) HEX_PADDING.get(l)) == null) {
0351: StringBuffer pb = new StringBuffer();
0352:
0353: for (int i = hex.length(); i < HEX_LENGTH; i++) {
0354: pb.append('0');
0355: }
0356: pad = new String(pb);
0357: HEX_PADDING.put(l, pad);
0358: }
0359: result.append(pad);
0360: result.append(hex);
0361: return new String(result);
0362: }
0363:
0364: /**
0365: Returns a "pointer" string in the usual format for these
0366: things - 0x<hex digits> for the object given. This will
0367: always return a unique value for each object.
0368: */
0369: public static String getPointerString(Object o) {
0370: return getPointerString((o == null) ? 0 : System
0371: .identityHashCode(o));
0372: }
0373:
0374: /**
0375: Returns a unique descriptor string that includes the object's
0376: class and a unique integer identifier. If fullyQualified is
0377: true then the class name will be fully qualified to include
0378: the package name, else it will be just the class' base name.
0379: */
0380: public static String getUniqueDescriptor(Object object,
0381: boolean fullyQualified) {
0382: StringBuffer result = new StringBuffer();
0383:
0384: if (object != null) {
0385: if (object instanceof Proxy) {
0386: Class interfaceClass = object.getClass()
0387: .getInterfaces()[0];
0388:
0389: result.append(getClassName(interfaceClass,
0390: fullyQualified));
0391: result.append('^');
0392: object = Proxy.getInvocationHandler(object);
0393: }
0394: result.append(getClassName(object, fullyQualified));
0395: result.append('@');
0396: result.append(getPointerString(object));
0397: } else {
0398: result.append(NULL_OBJECT_STRING);
0399: }
0400: return new String(result);
0401: }
0402:
0403: /**
0404: Returns a unique descriptor string that includes the object's
0405: class' base name and a unique integer identifier.
0406: */
0407: public static String getUniqueDescriptor(Object object) {
0408: return getUniqueDescriptor(object, false);
0409: }
0410:
0411: /**
0412: Utility to convert a List into an Object[] array. If the list is zero
0413: elements this will return a constant array; toArray() on List always
0414: returns a new object and this is wasteful for our purposes.
0415: */
0416: public static Object[] toArray(List list) {
0417: Object[] result;
0418: int size = list.size();
0419:
0420: if (size == 0) {
0421: result = NoArguments;
0422: } else {
0423: result = getObjectArrayPool().create(list.size());
0424: for (int i = 0; i < size; i++) {
0425: result[i] = list.get(i);
0426: }
0427: }
0428: return result;
0429: }
0430:
0431: /**
0432: Returns the parameter types of the given method.
0433: */
0434: public static Class[] getParameterTypes(Method m) {
0435: synchronized (methodParameterTypesCache) {
0436: Class[] result;
0437:
0438: if ((result = (Class[]) methodParameterTypesCache.get(m)) == null) {
0439: methodParameterTypesCache.put(m, result = m
0440: .getParameterTypes());
0441: }
0442: return result;
0443: }
0444: }
0445:
0446: /**
0447: Returns the parameter types of the given method.
0448: */
0449: public static Class[] getParameterTypes(Constructor c) {
0450: synchronized (ctorParameterTypesCache) {
0451: Class[] result;
0452:
0453: if ((result = (Class[]) ctorParameterTypesCache.get(c)) == null) {
0454: ctorParameterTypesCache.put(c, result = c
0455: .getParameterTypes());
0456: }
0457: return result;
0458: }
0459: }
0460:
0461: /**
0462: * Gets the SecurityManager that OGNL uses to determine permissions for
0463: * invoking methods.
0464: *
0465: * @return SecurityManager for OGNL
0466: */
0467: public static SecurityManager getSecurityManager() {
0468: return securityManager;
0469: }
0470:
0471: /**
0472: * Sets the SecurityManager that OGNL uses to determine permissions for
0473: * invoking methods.
0474: *
0475: * @param value SecurityManager to set
0476: */
0477: public static void setSecurityManager(SecurityManager value) {
0478: securityManager = value;
0479: }
0480:
0481: /**
0482: Permission will be named "invoke.<declaring-class>.<method-name>".
0483: */
0484: public static Permission getPermission(Method method) {
0485: Permission result = null;
0486: Class mc = method.getDeclaringClass();
0487:
0488: synchronized (invokePermissionCache) {
0489: Map permissions = (Map) invokePermissionCache.get(mc);
0490:
0491: if (permissions == null) {
0492: invokePermissionCache.put(mc,
0493: permissions = new HashMap(101));
0494: }
0495: if ((result = (Permission) permissions
0496: .get(method.getName())) == null) {
0497: result = new OgnlInvokePermission("invoke."
0498: + mc.getName() + "." + method.getName());
0499: permissions.put(method.getName(), result);
0500: }
0501: }
0502: return result;
0503: }
0504:
0505: public static Object invokeMethod(Object target, Method method,
0506: Object[] argsArray) throws InvocationTargetException,
0507: IllegalAccessException {
0508: Object result;
0509: boolean wasAccessible = true;
0510:
0511: if (securityManager != null) {
0512: try {
0513: securityManager.checkPermission(getPermission(method));
0514: } catch (SecurityException ex) {
0515: throw new IllegalAccessException("Method [" + method
0516: + "] cannot be accessed.");
0517: }
0518: }
0519: if (!Modifier.isPublic(method.getModifiers())
0520: || !Modifier.isPublic(method.getDeclaringClass()
0521: .getModifiers())) {
0522: if (!(wasAccessible = ((AccessibleObject) method)
0523: .isAccessible())) {
0524: ((AccessibleObject) method).setAccessible(true);
0525: }
0526: }
0527: result = method.invoke(target, argsArray);
0528: if (!wasAccessible) {
0529: ((AccessibleObject) method).setAccessible(false);
0530: }
0531: return result;
0532: }
0533:
0534: /**
0535: * Gets the class for a method argument that is appropriate for looking up methods
0536: * by reflection, by looking for the standard primitive wrapper classes and
0537: * exchanging for them their underlying primitive class objects. Other classes are
0538: * passed through unchanged.
0539: *
0540: * @param arg an object that is being passed to a method
0541: * @return the class to use to look up the method
0542: */
0543: public static final Class getArgClass(Object arg) {
0544: if (arg == null)
0545: return null;
0546: Class c = arg.getClass();
0547: if (c == Boolean.class)
0548: return Boolean.TYPE;
0549: else if (c.getSuperclass() == Number.class) {
0550: if (c == Integer.class)
0551: return Integer.TYPE;
0552: if (c == Double.class)
0553: return Double.TYPE;
0554: if (c == Byte.class)
0555: return Byte.TYPE;
0556: if (c == Long.class)
0557: return Long.TYPE;
0558: if (c == Float.class)
0559: return Float.TYPE;
0560: if (c == Short.class)
0561: return Short.TYPE;
0562: } else if (c == Character.class)
0563: return Character.TYPE;
0564: return c;
0565: }
0566:
0567: /**
0568: * Tells whether the given object is compatible with the given class
0569: * ---that is, whether the given object can be passed as an argument
0570: * to a method or constructor whose parameter type is the given class.
0571: * If object is null this will return true because null is compatible
0572: * with any type.
0573: */
0574: public static final boolean isTypeCompatible(Object object, Class c) {
0575: boolean result = true;
0576:
0577: if (object != null) {
0578: if (c.isPrimitive()) {
0579: if (getArgClass(object) != c) {
0580: result = false;
0581: }
0582: } else if (!c.isInstance(object)) {
0583: result = false;
0584: }
0585: }
0586: return result;
0587: }
0588:
0589: /**
0590: * Tells whether the given array of objects is compatible with the given array of
0591: * classes---that is, whether the given array of objects can be passed as arguments
0592: * to a method or constructor whose parameter types are the given array of classes.
0593: */
0594: public static final boolean areArgsCompatible(Object[] args,
0595: Class[] classes) {
0596: boolean result = true;
0597:
0598: if (args.length != classes.length) {
0599: result = false;
0600: } else {
0601: for (int index = 0, count = args.length; result
0602: && (index < count); ++index) {
0603: result = isTypeCompatible(args[index], classes[index]);
0604: }
0605: }
0606: return result;
0607: }
0608:
0609: /**
0610: * Tells whether the first array of classes is more specific than the second.
0611: * Assumes that the two arrays are of the same length.
0612: */
0613: public static final boolean isMoreSpecific(Class[] classes1,
0614: Class[] classes2) {
0615: for (int index = 0, count = classes1.length; index < count; ++index) {
0616: Class c1 = classes1[index], c2 = classes2[index];
0617: if (c1 == c2)
0618: continue;
0619: else if (c1.isPrimitive())
0620: return true;
0621: else if (c1.isAssignableFrom(c2))
0622: return false;
0623: else if (c2.isAssignableFrom(c1))
0624: return true;
0625: }
0626:
0627: // They are the same! So the first is not more specific than the second.
0628: return false;
0629: }
0630:
0631: public static final String getModifierString(int modifiers) {
0632: String result;
0633:
0634: if (Modifier.isPublic(modifiers))
0635: result = "public";
0636: else if (Modifier.isProtected(modifiers))
0637: result = "protected";
0638: else if (Modifier.isPrivate(modifiers))
0639: result = "private";
0640: else
0641: result = "";
0642: if (Modifier.isStatic(modifiers))
0643: result = "static " + result;
0644: if (Modifier.isFinal(modifiers))
0645: result = "final " + result;
0646: if (Modifier.isNative(modifiers))
0647: result = "native " + result;
0648: if (Modifier.isSynchronized(modifiers))
0649: result = "synchronized " + result;
0650: if (Modifier.isTransient(modifiers))
0651: result = "transient " + result;
0652: return result;
0653: }
0654:
0655: public static final Class classForName(OgnlContext context,
0656: String className) throws ClassNotFoundException {
0657: Class result = (Class) primitiveTypes.get(className);
0658:
0659: if (result == null) {
0660: ClassResolver resolver;
0661:
0662: if ((context == null)
0663: || ((resolver = context.getClassResolver()) == null)) {
0664: resolver = OgnlContext.DEFAULT_CLASS_RESOLVER;
0665: }
0666: result = resolver.classForName(className, context);
0667: }
0668: return result;
0669: }
0670:
0671: public static final boolean isInstance(OgnlContext context,
0672: Object value, String className) throws OgnlException {
0673: try {
0674: Class c = classForName(context, className);
0675: return c.isInstance(value);
0676: } catch (ClassNotFoundException e) {
0677: throw new OgnlException("No such class: " + className, e);
0678: }
0679: }
0680:
0681: public static Object getPrimitiveDefaultValue(Class forClass) {
0682: return primitiveDefaults.get(forClass);
0683: }
0684:
0685: public static Object getConvertedType(OgnlContext context,
0686: Object target, Member member, String propertyName,
0687: Object value, Class type) {
0688: return context.getTypeConverter().convertValue(context, target,
0689: member, propertyName, value, type);
0690: }
0691:
0692: public static boolean getConvertedTypes(OgnlContext context,
0693: Object target, Member member, String propertyName,
0694: Class[] parameterTypes, Object[] args, Object[] newArgs) {
0695: boolean result = false;
0696:
0697: if (parameterTypes.length == args.length) {
0698: result = true;
0699: for (int i = 0, ilast = parameterTypes.length - 1; result
0700: && (i <= ilast); i++) {
0701: Object arg = args[i];
0702: Class type = parameterTypes[i];
0703:
0704: if (isTypeCompatible(arg, type)) {
0705: newArgs[i] = arg;
0706: } else {
0707: Object v = getConvertedType(context, target,
0708: member, propertyName, arg, type);
0709:
0710: if (v == OgnlRuntime.NoConversionPossible) {
0711: result = false;
0712: } else {
0713: newArgs[i] = v;
0714: }
0715: }
0716: }
0717: }
0718: return result;
0719: }
0720:
0721: public static Method getConvertedMethodAndArgs(OgnlContext context,
0722: Object target, String propertyName, List methods,
0723: Object[] args, Object[] newArgs) {
0724: Method result = null;
0725: TypeConverter converter = context.getTypeConverter();
0726:
0727: if ((converter != null) && (methods != null)) {
0728: for (int i = 0, icount = methods.size(); (result == null)
0729: && (i < icount); i++) {
0730: Method m = (Method) methods.get(i);
0731: Class[] parameterTypes = getParameterTypes(m);
0732:
0733: if (getConvertedTypes(context, target, m, propertyName,
0734: parameterTypes, args, newArgs)) {
0735: result = m;
0736: }
0737: }
0738: }
0739: return result;
0740: }
0741:
0742: public static Constructor getConvertedConstructorAndArgs(
0743: OgnlContext context, Object target, List constructors,
0744: Object[] args, Object[] newArgs) {
0745: Constructor result = null;
0746: TypeConverter converter = context.getTypeConverter();
0747:
0748: if ((converter != null) && (constructors != null)) {
0749: for (int i = 0, icount = constructors.size(); (result == null)
0750: && (i < icount); i++) {
0751: Constructor ctor = (Constructor) constructors.get(i);
0752: Class[] parameterTypes = getParameterTypes(ctor);
0753:
0754: if (getConvertedTypes(context, target, ctor, null,
0755: parameterTypes, args, newArgs)) {
0756: result = ctor;
0757: }
0758: }
0759: }
0760: return result;
0761: }
0762:
0763: /**
0764: Gets the appropriate method to be called for the given target, method name and arguments.
0765: If successful this method will return the Method within the target that can be called
0766: and the converted arguments in actualArgs. If unsuccessful this method will return
0767: null and the actualArgs will be empty.
0768: */
0769: public static Method getAppropriateMethod(OgnlContext context,
0770: Object source, Object target, String methodName,
0771: String propertyName, List methods, Object[] args,
0772: Object[] actualArgs) {
0773: Method result = null;
0774: Class[] resultParameterTypes = null;
0775:
0776: if (methods != null) {
0777: for (int i = 0, icount = methods.size(); i < icount; i++) {
0778: Method m = (Method) methods.get(i);
0779: Class[] mParameterTypes = getParameterTypes(m);
0780:
0781: if (areArgsCompatible(args, mParameterTypes)
0782: && ((result == null) || isMoreSpecific(
0783: mParameterTypes, resultParameterTypes))) {
0784: result = m;
0785: resultParameterTypes = mParameterTypes;
0786: System.arraycopy(args, 0, actualArgs, 0,
0787: args.length);
0788: for (int j = 0; j < mParameterTypes.length; j++) {
0789: Class type = mParameterTypes[j];
0790:
0791: if (type.isPrimitive()
0792: && (actualArgs[j] == null)) {
0793: actualArgs[j] = getConvertedType(context,
0794: source, result, propertyName, null,
0795: type);
0796: }
0797: }
0798: }
0799: }
0800: }
0801: if (result == null) {
0802: result = getConvertedMethodAndArgs(context, target,
0803: propertyName, methods, args, actualArgs);
0804: }
0805: return result;
0806: }
0807:
0808: public static Object callAppropriateMethod(OgnlContext context,
0809: Object source, Object target, String methodName,
0810: String propertyName, List methods, Object[] args)
0811: throws MethodFailedException {
0812: Throwable reason = null;
0813: Object[] actualArgs = objectArrayPool.create(args.length);
0814:
0815: try {
0816: Method method = getAppropriateMethod(context, source,
0817: target, methodName, propertyName, methods, args,
0818: actualArgs);
0819:
0820: if ((method == null)
0821: || !isMethodAccessible(context, source, method,
0822: propertyName)) {
0823: StringBuffer buffer = new StringBuffer();
0824:
0825: if (args != null) {
0826: for (int i = 0, ilast = args.length - 1; i <= ilast; i++) {
0827: Object arg = args[i];
0828:
0829: buffer.append((arg == null) ? NULL_STRING : arg
0830: .getClass().getName());
0831: if (i < ilast) {
0832: buffer.append(", ");
0833: }
0834: }
0835: }
0836: throw new NoSuchMethodException(methodName + "("
0837: + buffer + ")");
0838: }
0839: return invokeMethod(target, method, actualArgs);
0840: } catch (NoSuchMethodException e) {
0841: reason = e;
0842: } catch (IllegalAccessException e) {
0843: reason = e;
0844: } catch (InvocationTargetException e) {
0845: reason = e.getTargetException();
0846: } finally {
0847: objectArrayPool.recycle(actualArgs);
0848: }
0849: throw new MethodFailedException(source, methodName, reason);
0850: }
0851:
0852: public static final Object callStaticMethod(OgnlContext context,
0853: String className, String methodName, Object[] args)
0854: throws OgnlException, MethodFailedException {
0855: try {
0856: Object result;
0857: Class targetClass = classForName(context, className);
0858: MethodAccessor ma = getMethodAccessor(targetClass);
0859:
0860: return ma.callStaticMethod(context, targetClass,
0861: methodName, args);
0862: } catch (ClassNotFoundException ex) {
0863: throw new MethodFailedException(className, methodName, ex);
0864: }
0865: }
0866:
0867: public static final Object callMethod(OgnlContext context,
0868: Object target, String methodName, String propertyName,
0869: Object[] args) throws OgnlException, MethodFailedException {
0870: Object result;
0871:
0872: if (target != null) {
0873: MethodAccessor ma = getMethodAccessor(target.getClass());
0874:
0875: result = ma.callMethod(context, target, methodName, args);
0876: } else {
0877: throw new NullPointerException("target is null for method "
0878: + methodName);
0879: }
0880: return result;
0881: }
0882:
0883: public static final Object callConstructor(OgnlContext context,
0884: String className, Object[] args) throws OgnlException {
0885: Throwable reason = null;
0886: Object[] actualArgs = args;
0887:
0888: try {
0889: Constructor ctor = null;
0890: Class[] ctorParameterTypes = null;
0891: Class target = classForName(context, className);
0892: List constructors = getConstructors(target);
0893:
0894: for (int i = 0, icount = constructors.size(); i < icount; i++) {
0895: Constructor c = (Constructor) constructors.get(i);
0896: Class[] cParameterTypes = getParameterTypes(c);
0897:
0898: if (areArgsCompatible(args, cParameterTypes)
0899: && (ctor == null || isMoreSpecific(
0900: cParameterTypes, ctorParameterTypes))) {
0901: ctor = c;
0902: ctorParameterTypes = cParameterTypes;
0903: }
0904: }
0905: if (ctor == null) {
0906: actualArgs = objectArrayPool.create(args.length);
0907: if ((ctor = getConvertedConstructorAndArgs(context,
0908: target, constructors, args, actualArgs)) == null) {
0909: throw new NoSuchMethodException();
0910: }
0911: }
0912: if (!context.getMemberAccess().isAccessible(context,
0913: target, ctor, null)) {
0914: throw new IllegalAccessException("access denied to "
0915: + target.getName() + "()");
0916: }
0917: return ctor.newInstance(actualArgs);
0918: } catch (ClassNotFoundException e) {
0919: reason = e;
0920: } catch (NoSuchMethodException e) {
0921: reason = e;
0922: } catch (IllegalAccessException e) {
0923: reason = e;
0924: } catch (InvocationTargetException e) {
0925: reason = e.getTargetException();
0926: } catch (InstantiationException e) {
0927: reason = e;
0928: } finally {
0929: if (actualArgs != args) {
0930: objectArrayPool.recycle(actualArgs);
0931: }
0932: }
0933:
0934: throw new MethodFailedException(className, "new", reason);
0935: }
0936:
0937: public static final Object getMethodValue(OgnlContext context,
0938: Object target, String propertyName) throws OgnlException,
0939: IllegalAccessException, NoSuchMethodException,
0940: IntrospectionException {
0941: return getMethodValue(context, target, propertyName, false);
0942: }
0943:
0944: /**
0945: If the checkAccessAndExistence flag is true this method will check to see if the
0946: method exists and if it is accessible according to the context's MemberAccess.
0947: If neither test passes this will return NotFound.
0948: */
0949: public static final Object getMethodValue(OgnlContext context,
0950: Object target, String propertyName,
0951: boolean checkAccessAndExistence) throws OgnlException,
0952: IllegalAccessException, NoSuchMethodException,
0953: IntrospectionException {
0954: Object result = null;
0955: Method m = getGetMethod(context, (target == null) ? null
0956: : target.getClass(), propertyName);
0957:
0958: if (checkAccessAndExistence) {
0959: if ((m == null)
0960: || !context.getMemberAccess().isAccessible(context,
0961: target, m, propertyName)) {
0962: result = NotFound;
0963: }
0964: }
0965: if (result == null) {
0966: if (m != null) {
0967: try {
0968: result = invokeMethod(target, m, NoArguments);
0969: } catch (InvocationTargetException ex) {
0970: throw new OgnlException(propertyName, ex
0971: .getTargetException());
0972: }
0973: } else {
0974: throw new NoSuchMethodException(propertyName);
0975: }
0976: }
0977: return result;
0978: }
0979:
0980: public static final boolean setMethodValue(OgnlContext context,
0981: Object target, String propertyName, Object value)
0982: throws OgnlException, IllegalAccessException,
0983: NoSuchMethodException, MethodFailedException,
0984: IntrospectionException {
0985: return setMethodValue(context, target, propertyName, value,
0986: false);
0987: }
0988:
0989: public static final boolean setMethodValue(OgnlContext context,
0990: Object target, String propertyName, Object value,
0991: boolean checkAccessAndExistence) throws OgnlException,
0992: IllegalAccessException, NoSuchMethodException,
0993: MethodFailedException, IntrospectionException {
0994: boolean result = true;
0995: Method m = getSetMethod(context, (target == null) ? null
0996: : target.getClass(), propertyName);
0997:
0998: if (checkAccessAndExistence) {
0999: if ((m == null)
1000: || !context.getMemberAccess().isAccessible(context,
1001: target, m, propertyName)) {
1002: result = false;
1003: }
1004: }
1005: if (result) {
1006: if (m != null) {
1007: Object[] args = objectArrayPool.create(value);
1008:
1009: try {
1010: callAppropriateMethod(context, target, target, m
1011: .getName(), propertyName, Collections
1012: .nCopies(1, m), args);
1013: } finally {
1014: objectArrayPool.recycle(args);
1015: }
1016: } else {
1017: result = false;
1018: }
1019: }
1020: return result;
1021: }
1022:
1023: public static final List getConstructors(Class targetClass) {
1024: List result;
1025:
1026: synchronized (constructorCache) {
1027: if ((result = (List) constructorCache.get(targetClass)) == null) {
1028: constructorCache.put(targetClass, result = Arrays
1029: .asList(targetClass.getConstructors()));
1030: }
1031: }
1032: return result;
1033: }
1034:
1035: public static final Map getMethods(Class targetClass,
1036: boolean staticMethods) {
1037: ClassCache cache = (staticMethods ? staticMethodCache
1038: : instanceMethodCache);
1039: Map result;
1040:
1041: synchronized (cache) {
1042: if ((result = (Map) cache.get(targetClass)) == null) {
1043: cache.put(targetClass, result = new HashMap(23));
1044: for (Class c = targetClass; c != null; c = c
1045: .getSuperclass()) {
1046: Method[] ma = c.getDeclaredMethods();
1047:
1048: for (int i = 0, icount = ma.length; i < icount; i++) {
1049: if (Modifier.isStatic(ma[i].getModifiers()) == staticMethods) {
1050: List ml = (List) result
1051: .get(ma[i].getName());
1052:
1053: if (ml == null)
1054: result.put(ma[i].getName(),
1055: ml = new ArrayList());
1056: ml.add(ma[i]);
1057: }
1058: }
1059: }
1060: }
1061: }
1062: return result;
1063: }
1064:
1065: public static final List getMethods(Class targetClass, String name,
1066: boolean staticMethods) {
1067: return (List) getMethods(targetClass, staticMethods).get(name);
1068: }
1069:
1070: public static final Map getFields(Class targetClass) {
1071: Map result;
1072:
1073: synchronized (fieldCache) {
1074: if ((result = (Map) fieldCache.get(targetClass)) == null) {
1075: Field fa[];
1076:
1077: result = new HashMap(23);
1078: fa = targetClass.getDeclaredFields();
1079: for (int i = 0; i < fa.length; i++) {
1080: result.put(fa[i].getName(), fa[i]);
1081: }
1082: fieldCache.put(targetClass, result);
1083: }
1084: }
1085: return result;
1086: }
1087:
1088: public static final Field getField(Class inClass, String name) {
1089: Field result = null;
1090:
1091: synchronized (fieldCache) {
1092: Object o = getFields(inClass).get(name);
1093:
1094: if (o == null) {
1095: super classes.clear();
1096: for (Class sc = inClass; (sc != null)
1097: && (result == null); sc = sc.getSuperclass()) {
1098: if ((o = getFields(sc).get(name)) == NotFound)
1099: break;
1100: super classes.add(sc);
1101: if ((result = (Field) o) != null)
1102: break;
1103: }
1104: /*
1105: Bubble the found value (either cache miss or actual field)
1106: to all supeclasses that we saw for quicker access next time.
1107: */
1108: for (int i = 0, icount = super classes.size(); i < icount; i++) {
1109: getFields((Class) super classes.get(i)).put(name,
1110: (result == null) ? NotFound : result);
1111: }
1112: } else {
1113: if (o instanceof Field) {
1114: result = (Field) o;
1115: } else {
1116: if (result == NotFound)
1117: result = null;
1118: }
1119: }
1120: }
1121: return result;
1122: }
1123:
1124: public static final Object getFieldValue(OgnlContext context,
1125: Object target, String propertyName)
1126: throws NoSuchFieldException {
1127: return getFieldValue(context, target, propertyName, false);
1128: }
1129:
1130: public static final Object getFieldValue(OgnlContext context,
1131: Object target, String propertyName,
1132: boolean checkAccessAndExistence)
1133: throws NoSuchFieldException {
1134: Object result = null;
1135: Field f = getField((target == null) ? null : target.getClass(),
1136: propertyName);
1137:
1138: if (checkAccessAndExistence) {
1139: if ((f == null)
1140: || !context.getMemberAccess().isAccessible(context,
1141: target, f, propertyName)) {
1142: result = NotFound;
1143: }
1144: }
1145: if (result == null) {
1146: if (f == null) {
1147: throw new NoSuchFieldException(propertyName);
1148: } else {
1149: try {
1150: Object state = null;
1151:
1152: if ((f != null)
1153: && !Modifier.isStatic(f.getModifiers())) {
1154: state = context.getMemberAccess().setup(
1155: context, target, f, propertyName);
1156: result = f.get(target);
1157: context.getMemberAccess().restore(context,
1158: target, f, propertyName, state);
1159: } else
1160: throw new NoSuchFieldException(propertyName);
1161: } catch (IllegalAccessException ex) {
1162: throw new NoSuchFieldException(propertyName);
1163: }
1164: }
1165: }
1166: return result;
1167: }
1168:
1169: public static final boolean setFieldValue(OgnlContext context,
1170: Object target, String propertyName, Object value)
1171: throws OgnlException {
1172: boolean result = false;
1173:
1174: try {
1175: Field f = getField((target == null) ? null : target
1176: .getClass(), propertyName);
1177: Object state;
1178:
1179: if ((f != null) && !Modifier.isStatic(f.getModifiers())) {
1180: state = context.getMemberAccess().setup(context,
1181: target, f, propertyName);
1182: try {
1183: if (isTypeCompatible(value, f.getType())
1184: || ((value = getConvertedType(context,
1185: target, f, propertyName, value, f
1186: .getType())) != null)) {
1187: f.set(target, value);
1188: result = true;
1189: }
1190: } finally {
1191: context.getMemberAccess().restore(context, target,
1192: f, propertyName, state);
1193: }
1194: }
1195: } catch (IllegalAccessException ex) {
1196: throw new NoSuchPropertyException(target, propertyName, ex);
1197: }
1198: return result;
1199: }
1200:
1201: public static final boolean isFieldAccessible(OgnlContext context,
1202: Object target, Class inClass, String propertyName) {
1203: return isFieldAccessible(context, target, getField(inClass,
1204: propertyName), propertyName);
1205: }
1206:
1207: public static final boolean isFieldAccessible(OgnlContext context,
1208: Object target, Field field, String propertyName) {
1209: return context.getMemberAccess().isAccessible(context, target,
1210: field, propertyName);
1211: }
1212:
1213: public static final boolean hasField(OgnlContext context,
1214: Object target, Class inClass, String propertyName) {
1215: Field f = getField(inClass, propertyName);
1216:
1217: return (f != null)
1218: && isFieldAccessible(context, target, f, propertyName);
1219: }
1220:
1221: public static final Object getStaticField(OgnlContext context,
1222: String className, String fieldName) throws OgnlException {
1223: Exception reason = null;
1224: try {
1225: Class c = classForName(context, className);
1226:
1227: /*
1228: Check for virtual static field "class"; this cannot interfere with
1229: normal static fields because it is a reserved word.
1230: */
1231: if (fieldName.equals("class")) {
1232: return c;
1233: } else {
1234: Field f = c.getField(fieldName);
1235: if (!Modifier.isStatic(f.getModifiers()))
1236: throw new OgnlException("Field " + fieldName
1237: + " of class " + className
1238: + " is not static");
1239: return f.get(null);
1240: }
1241: } catch (ClassNotFoundException e) {
1242: reason = e;
1243: } catch (NoSuchFieldException e) {
1244: reason = e;
1245: } catch (SecurityException e) {
1246: reason = e;
1247: } catch (IllegalAccessException e) {
1248: reason = e;
1249: }
1250:
1251: throw new OgnlException("Could not get static field "
1252: + fieldName + " from class " + className, reason);
1253: }
1254:
1255: public static final List getDeclaredMethods(Class targetClass,
1256: String propertyName, boolean findSets) {
1257: List result = null;
1258: ClassCache cache = declaredMethods[findSets ? 0 : 1];
1259:
1260: synchronized (cache) {
1261: Map propertyCache = (Map) cache.get(targetClass);
1262:
1263: if ((propertyCache == null)
1264: || ((result = (List) propertyCache
1265: .get(propertyName)) == null)) {
1266: String baseName = Character.toUpperCase(propertyName
1267: .charAt(0))
1268: + propertyName.substring(1);
1269: int len = baseName.length();
1270:
1271: for (Class c = targetClass; c != null; c = c
1272: .getSuperclass()) {
1273: Method[] methods = c.getDeclaredMethods();
1274:
1275: for (int i = 0; i < methods.length; i++) {
1276: String ms = methods[i].getName();
1277:
1278: if (ms.endsWith(baseName)) {
1279: boolean isSet = false, isGet = false, isIs = false;
1280:
1281: if ((isSet = ms.startsWith(SET_PREFIX))
1282: || (isGet = ms
1283: .startsWith(GET_PREFIX))
1284: || (isIs = ms.startsWith(IS_PREFIX))) {
1285: int prefixLength = (isIs ? 2 : 3);
1286:
1287: if (isSet == findSets) {
1288: if (baseName.length() == (ms
1289: .length() - prefixLength)) {
1290: if (result == null) {
1291: result = new ArrayList();
1292: }
1293: result.add(methods[i]);
1294: }
1295: }
1296: }
1297: }
1298: }
1299: }
1300: if (propertyCache == null) {
1301: cache.put(targetClass, propertyCache = new HashMap(
1302: 101));
1303: }
1304: propertyCache.put(propertyName,
1305: (result == null) ? NotFoundList : result);
1306: }
1307: return (result == NotFoundList) ? null : result;
1308: }
1309: }
1310:
1311: public static final Method getGetMethod(OgnlContext context,
1312: Class targetClass, String propertyName)
1313: throws IntrospectionException, OgnlException {
1314: Method result = null;
1315: PropertyDescriptor pd = getPropertyDescriptor(targetClass,
1316: propertyName);
1317:
1318: if (pd == null) {
1319: List methods = getDeclaredMethods(targetClass,
1320: propertyName, false /* find 'get' methods */);
1321:
1322: if (methods != null) {
1323: for (int i = 0, icount = methods.size(); i < icount; i++) {
1324: Method m = (Method) methods.get(i);
1325: Class[] mParameterTypes = getParameterTypes(m);
1326:
1327: if (mParameterTypes.length == 0) {
1328: result = m;
1329: break;
1330: }
1331: }
1332: }
1333: } else {
1334: result = pd.getReadMethod();
1335: }
1336: return result;
1337: }
1338:
1339: public static final boolean isMethodAccessible(OgnlContext context,
1340: Object target, Method method, String propertyName) {
1341: return (method == null) ? false : context.getMemberAccess()
1342: .isAccessible(context, target, method, propertyName);
1343: }
1344:
1345: public static final boolean hasGetMethod(OgnlContext context,
1346: Object target, Class targetClass, String propertyName)
1347: throws IntrospectionException, OgnlException {
1348: return isMethodAccessible(context, target, getGetMethod(
1349: context, targetClass, propertyName), propertyName);
1350: }
1351:
1352: public static final Method getSetMethod(OgnlContext context,
1353: Class targetClass, String propertyName)
1354: throws IntrospectionException, OgnlException {
1355: Method result = null;
1356: PropertyDescriptor pd = getPropertyDescriptor(targetClass,
1357: propertyName);
1358:
1359: if (pd == null) {
1360: List methods = getDeclaredMethods(targetClass,
1361: propertyName, true /* find 'set' methods */);
1362:
1363: if (methods != null) {
1364: for (int i = 0, icount = methods.size(); i < icount; i++) {
1365: Method m = (Method) methods.get(i);
1366: Class[] mParameterTypes = getParameterTypes(m);
1367:
1368: if (mParameterTypes.length == 1) {
1369: result = m;
1370: break;
1371: }
1372: }
1373: }
1374: } else {
1375: result = pd.getWriteMethod();
1376: }
1377: return result;
1378: }
1379:
1380: public static final boolean hasSetMethod(OgnlContext context,
1381: Object target, Class targetClass, String propertyName)
1382: throws IntrospectionException, OgnlException {
1383: return isMethodAccessible(context, target, getSetMethod(
1384: context, targetClass, propertyName), propertyName);
1385: }
1386:
1387: public static final boolean hasGetProperty(OgnlContext context,
1388: Object target, Object oname) throws IntrospectionException,
1389: OgnlException {
1390: Class targetClass = (target == null) ? null : target.getClass();
1391: String name = oname.toString();
1392:
1393: return hasGetMethod(context, target, targetClass, name)
1394: || hasField(context, target, targetClass, name);
1395: }
1396:
1397: public static final boolean hasSetProperty(OgnlContext context,
1398: Object target, Object oname) throws IntrospectionException,
1399: OgnlException {
1400: Class targetClass = (target == null) ? null : target.getClass();
1401: String name = oname.toString();
1402:
1403: return hasSetMethod(context, target, targetClass, name)
1404: || hasField(context, target, targetClass, name);
1405: }
1406:
1407: private static final boolean indexMethodCheck(List methods) {
1408: boolean result = false;
1409:
1410: if (methods.size() > 0) {
1411: Method fm = (Method) methods.get(0);
1412: Class[] fmpt = getParameterTypes(fm);
1413: int fmpc = fmpt.length;
1414: Class lastMethodClass = fm.getDeclaringClass();
1415:
1416: result = true;
1417: for (int i = 1; result && (i < methods.size()); i++) {
1418: Method m = (Method) methods.get(i);
1419: Class c = m.getDeclaringClass();
1420:
1421: // Check to see if more than one method implemented per class
1422: if (lastMethodClass == c) {
1423: result = false;
1424: } else {
1425: Class[] mpt = getParameterTypes(fm);
1426: int mpc = fmpt.length;
1427:
1428: if (fmpc != mpc) {
1429: result = false;
1430: }
1431: for (int j = 0; j < fmpc; j++) {
1432: if (fmpt[j] != mpt[j]) {
1433: result = false;
1434: break;
1435: }
1436: }
1437: }
1438: lastMethodClass = c;
1439: }
1440: }
1441: return result;
1442: }
1443:
1444: private static final void findObjectIndexedPropertyDescriptors(
1445: Class targetClass, Map intoMap) throws OgnlException {
1446: Map allMethods = getMethods(targetClass, false);
1447: Map pairs = new HashMap(101);
1448:
1449: for (Iterator it = allMethods.keySet().iterator(); it.hasNext();) {
1450: String methodName = (String) it.next();
1451: List methods = (List) allMethods.get(methodName);
1452:
1453: /*
1454: Only process set/get where there is exactly one implementation
1455: of the method per class and those implementations are all the
1456: same
1457: */
1458: if (indexMethodCheck(methods)) {
1459: boolean isGet = false, isSet = false;
1460: Method m = (Method) methods.get(0);
1461:
1462: if (((isSet = methodName.startsWith(SET_PREFIX)) || (isGet = methodName
1463: .startsWith(GET_PREFIX)))
1464: && (methodName.length() > 3)) {
1465: String propertyName = Introspector
1466: .decapitalize(methodName.substring(3));
1467: Class[] parameterTypes = getParameterTypes(m);
1468: int parameterCount = parameterTypes.length;
1469:
1470: if (isGet && (parameterCount == 1)
1471: && (m.getReturnType() != Void.TYPE)) {
1472: List pair = (List) pairs.get(propertyName);
1473:
1474: if (pair == null) {
1475: pairs.put(propertyName,
1476: pair = new ArrayList());
1477: }
1478: pair.add(m);
1479: }
1480: if (isSet && (parameterCount == 2)
1481: && (m.getReturnType() == Void.TYPE)) {
1482: List pair = (List) pairs.get(propertyName);
1483:
1484: if (pair == null) {
1485: pairs.put(propertyName,
1486: pair = new ArrayList());
1487: }
1488: pair.add(m);
1489: }
1490: }
1491: }
1492: }
1493: for (Iterator it = pairs.keySet().iterator(); it.hasNext();) {
1494: String propertyName = (String) it.next();
1495: List methods = (List) pairs.get(propertyName);
1496:
1497: if (methods.size() == 2) {
1498: Method method1 = (Method) methods.get(0), method2 = (Method) methods
1499: .get(1), setMethod = (method1
1500: .getParameterTypes().length == 2) ? method1
1501: : method2, getMethod = (setMethod == method1) ? method2
1502: : method1;
1503: Class keyType = getMethod.getParameterTypes()[0], propertyType = getMethod
1504: .getReturnType();
1505:
1506: if (keyType == setMethod.getParameterTypes()[0]) {
1507: if (propertyType == setMethod.getParameterTypes()[1]) {
1508: ObjectIndexedPropertyDescriptor propertyDescriptor;
1509:
1510: try {
1511: propertyDescriptor = new ObjectIndexedPropertyDescriptor(
1512: propertyName, propertyType,
1513: getMethod, setMethod);
1514: } catch (Exception ex) {
1515: throw new OgnlException(
1516: "creating object indexed property descriptor for '"
1517: + propertyName + "' in "
1518: + targetClass, ex);
1519: }
1520: intoMap.put(propertyName, propertyDescriptor);
1521: }
1522: }
1523:
1524: }
1525: }
1526: }
1527:
1528: /**
1529: This method returns the property descriptors for the given class as a Map
1530: */
1531: public static final Map getPropertyDescriptors(Class targetClass)
1532: throws IntrospectionException, OgnlException {
1533: Map result;
1534:
1535: synchronized (propertyDescriptorCache) {
1536: if ((result = (Map) propertyDescriptorCache
1537: .get(targetClass)) == null) {
1538: PropertyDescriptor[] pda = Introspector.getBeanInfo(
1539: targetClass).getPropertyDescriptors();
1540:
1541: result = new HashMap(101);
1542: for (int i = 0, icount = pda.length; i < icount; i++) {
1543: result.put(pda[i].getName(), pda[i]);
1544: }
1545: findObjectIndexedPropertyDescriptors(targetClass,
1546: result);
1547: propertyDescriptorCache.put(targetClass, result);
1548: }
1549: }
1550: return result;
1551: }
1552:
1553: /**
1554: This method returns a PropertyDescriptor for the given class and property name using
1555: a Map lookup (using getPropertyDescriptorsMap()).
1556: */
1557: public static final PropertyDescriptor getPropertyDescriptor(
1558: Class targetClass, String propertyName)
1559: throws IntrospectionException, OgnlException {
1560: return (targetClass == null) ? null
1561: : (PropertyDescriptor) getPropertyDescriptors(
1562: targetClass).get(propertyName);
1563: }
1564:
1565: public static final PropertyDescriptor[] getPropertyDescriptorsArray(
1566: Class targetClass) throws IntrospectionException {
1567: PropertyDescriptor[] result = null;
1568:
1569: if (targetClass != null) {
1570: synchronized (propertyDescriptorCache) {
1571: if ((result = (PropertyDescriptor[]) propertyDescriptorCache
1572: .get(targetClass)) == null) {
1573: propertyDescriptorCache.put(targetClass,
1574: result = Introspector.getBeanInfo(
1575: targetClass)
1576: .getPropertyDescriptors());
1577: }
1578: }
1579: }
1580: return result;
1581: }
1582:
1583: /**
1584: Gets the property descriptor with the given name for the target class given.
1585: @param targetClass Class for which property descriptor is desired
1586: @param name Name of property
1587: @return PropertyDescriptor of the named property or null if
1588: the class has no property with the given name
1589: */
1590: public static final PropertyDescriptor getPropertyDescriptorFromArray(
1591: Class targetClass, String name)
1592: throws IntrospectionException {
1593: PropertyDescriptor result = null;
1594: PropertyDescriptor[] pda = getPropertyDescriptorsArray(targetClass);
1595:
1596: for (int i = 0, icount = pda.length; (result == null)
1597: && (i < icount); i++) {
1598: if (pda[i].getName().compareTo(name) == 0) {
1599: result = pda[i];
1600: }
1601: }
1602: return result;
1603: }
1604:
1605: public static final void setMethodAccessor(Class cls,
1606: MethodAccessor accessor) {
1607: synchronized (methodAccessors) {
1608: methodAccessors.put(cls, accessor);
1609: }
1610: }
1611:
1612: public static final MethodAccessor getMethodAccessor(Class cls)
1613: throws OgnlException {
1614: MethodAccessor answer = (MethodAccessor) getHandler(cls,
1615: methodAccessors);
1616: if (answer != null)
1617: return answer;
1618: throw new OgnlException("No method accessor for " + cls);
1619: }
1620:
1621: public static final void setPropertyAccessor(Class cls,
1622: PropertyAccessor accessor) {
1623: synchronized (propertyAccessors) {
1624: propertyAccessors.put(cls, accessor);
1625: }
1626: }
1627:
1628: public static final PropertyAccessor getPropertyAccessor(Class cls)
1629: throws OgnlException {
1630: PropertyAccessor answer = (PropertyAccessor) getHandler(cls,
1631: propertyAccessors);
1632: if (answer != null)
1633: return answer;
1634:
1635: throw new OgnlException("No property accessor for class " + cls);
1636: }
1637:
1638: public static final ElementsAccessor getElementsAccessor(Class cls)
1639: throws OgnlException {
1640: ElementsAccessor answer = (ElementsAccessor) getHandler(cls,
1641: elementsAccessors);
1642: if (answer != null)
1643: return answer;
1644: throw new OgnlException("No elements accessor for class " + cls);
1645: }
1646:
1647: public static final void setElementsAccessor(Class cls,
1648: ElementsAccessor accessor) {
1649: synchronized (elementsAccessors) {
1650: elementsAccessors.put(cls, accessor);
1651: }
1652: }
1653:
1654: public static final NullHandler getNullHandler(Class cls)
1655: throws OgnlException {
1656: NullHandler answer = (NullHandler) getHandler(cls, nullHandlers);
1657: if (answer != null)
1658: return answer;
1659: throw new OgnlException("No null handler for class " + cls);
1660: }
1661:
1662: public static final void setNullHandler(Class cls,
1663: NullHandler handler) {
1664: synchronized (nullHandlers) {
1665: nullHandlers.put(cls, handler);
1666: }
1667: }
1668:
1669: private static final Object getHandler(Class forClass,
1670: ClassCache handlers) {
1671: Object answer = null;
1672:
1673: synchronized (handlers) {
1674: if ((answer = handlers.get(forClass)) == null) {
1675: Class keyFound;
1676:
1677: if (forClass.isArray()) {
1678: answer = handlers.get(Object[].class);
1679: keyFound = null;
1680: } else {
1681: keyFound = forClass;
1682: outer: for (Class c = forClass; c != null; c = c
1683: .getSuperclass()) {
1684: answer = handlers.get(c);
1685: if (answer == null) {
1686: Class[] interfaces = c.getInterfaces();
1687: for (int index = 0, count = interfaces.length; index < count; ++index) {
1688: Class iface = interfaces[index];
1689:
1690: answer = handlers.get(iface);
1691: if (answer == null) {
1692: /* Try super-interfaces */
1693: answer = getHandler(iface, handlers);
1694: }
1695: if (answer != null) {
1696: keyFound = iface;
1697: break outer;
1698: }
1699: }
1700: } else {
1701: keyFound = c;
1702: break;
1703: }
1704: }
1705: }
1706: if (answer != null) {
1707: if (keyFound != forClass) {
1708: handlers.put(forClass, answer);
1709: }
1710: }
1711: }
1712: }
1713: return answer;
1714: }
1715:
1716: public static final Object getProperty(OgnlContext context,
1717: Object source, Object name) throws OgnlException {
1718: PropertyAccessor accessor;
1719:
1720: if (source == null) {
1721: throw new OgnlException(
1722: "source is null for getProperty(null, \"" + name
1723: + "\")");
1724: }
1725: if ((accessor = getPropertyAccessor(getTargetClass(source))) == null) {
1726: throw new OgnlException("No property accessor for "
1727: + getTargetClass(source).getName());
1728: }
1729: return accessor.getProperty(context, source, name);
1730: }
1731:
1732: public static final void setProperty(OgnlContext context,
1733: Object target, Object name, Object value)
1734: throws OgnlException {
1735: PropertyAccessor accessor;
1736:
1737: if (target == null) {
1738: throw new OgnlException(
1739: "target is null for setProperty(null, \"" + name
1740: + "\", " + value + ")");
1741: }
1742: if ((accessor = getPropertyAccessor(getTargetClass(target))) == null) {
1743: throw new OgnlException("No property accessor for "
1744: + getTargetClass(target).getName());
1745: }
1746: accessor.setProperty(context, target, name, value);
1747: }
1748:
1749: /**
1750: Determines the index property type, if any. Returns <code>INDEXED_PROPERTY_NONE</code> if the
1751: property is not index-accessible as determined by OGNL or JavaBeans. If it is indexable
1752: then this will return whether it is a JavaBeans indexed property, conforming to the
1753: indexed property patterns (returns <code>INDEXED_PROPERTY_INT</code>) or if it conforms
1754: to the OGNL arbitrary object indexable (returns <code>INDEXED_PROPERTY_OBJECT</code>).
1755: */
1756: public static final int getIndexedPropertyType(OgnlContext context,
1757: Class sourceClass, String name) throws OgnlException {
1758: int result = INDEXED_PROPERTY_NONE;
1759:
1760: try {
1761: PropertyDescriptor pd = getPropertyDescriptor(sourceClass,
1762: name);
1763:
1764: if (pd != null) {
1765: if (pd instanceof IndexedPropertyDescriptor) {
1766: result = INDEXED_PROPERTY_INT;
1767: } else {
1768: if (pd instanceof ObjectIndexedPropertyDescriptor) {
1769: result = INDEXED_PROPERTY_OBJECT;
1770: }
1771: }
1772: }
1773: } catch (Exception ex) {
1774: throw new OgnlException("problem determining if '" + name
1775: + "' is an indexed property", ex);
1776: }
1777: return result;
1778: }
1779:
1780: public static final Object getIndexedProperty(OgnlContext context,
1781: Object source, String name, Object index)
1782: throws OgnlException {
1783: Throwable reason = null;
1784: Object[] args = objectArrayPool.create(index);
1785:
1786: try {
1787: PropertyDescriptor pd = getPropertyDescriptor(
1788: (source == null) ? null : source.getClass(), name);
1789: Method m;
1790:
1791: if (pd instanceof IndexedPropertyDescriptor) {
1792: m = ((IndexedPropertyDescriptor) pd)
1793: .getIndexedReadMethod();
1794: } else {
1795: if (pd instanceof ObjectIndexedPropertyDescriptor) {
1796: m = ((ObjectIndexedPropertyDescriptor) pd)
1797: .getIndexedReadMethod();
1798: } else {
1799: throw new OgnlException("property '" + name
1800: + "' is not an indexed property");
1801: }
1802: }
1803: return callMethod(context, source, m.getName(), name, args);
1804: } catch (OgnlException ex) {
1805: throw ex;
1806: } catch (Exception ex) {
1807: throw new OgnlException(
1808: "getting indexed property descriptor for '" + name
1809: + "'", ex);
1810: } finally {
1811: objectArrayPool.recycle(args);
1812: }
1813: }
1814:
1815: public static final void setIndexedProperty(OgnlContext context,
1816: Object source, String name, Object index, Object value)
1817: throws OgnlException {
1818: Throwable reason = null;
1819: Object[] args = objectArrayPool.create(index, value);
1820:
1821: try {
1822: PropertyDescriptor pd = getPropertyDescriptor(
1823: (source == null) ? null : source.getClass(), name);
1824: Method m;
1825:
1826: if (pd instanceof IndexedPropertyDescriptor) {
1827: m = ((IndexedPropertyDescriptor) pd)
1828: .getIndexedWriteMethod();
1829: } else {
1830: if (pd instanceof ObjectIndexedPropertyDescriptor) {
1831: m = ((ObjectIndexedPropertyDescriptor) pd)
1832: .getIndexedWriteMethod();
1833: } else {
1834: throw new OgnlException("property '" + name
1835: + "' is not an indexed property");
1836: }
1837: }
1838: callMethod(context, source, m.getName(), name, args);
1839: } catch (OgnlException ex) {
1840: throw ex;
1841: } catch (Exception ex) {
1842: throw new OgnlException(
1843: "getting indexed property descriptor for '" + name
1844: + "'", ex);
1845: } finally {
1846: objectArrayPool.recycle(args);
1847: }
1848: }
1849:
1850: public static EvaluationPool getEvaluationPool() {
1851: return evaluationPool;
1852: }
1853:
1854: public static ObjectArrayPool getObjectArrayPool() {
1855: return objectArrayPool;
1856: }
1857: }
|