Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java (original) +++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java Sat Jul 18 19:26:02 2009 @@ -24,7 +24,7 @@ import org.apache.commons.logging.Log; /** - * Taken from the Velocity tree so we can be self-sufficient + * Taken from the Velocity tree so we can be self-sufficient. * * A cache of introspection information for a specific class instance. * Keys objects by an agregation of the method name and the names of classes @@ -39,13 +39,6 @@ * @since 1.0 */ public class ClassMap { - - /** - * Class passed into the constructor used to as the basis for the Method map. - */ - private final Class<?> clazz; - /** logger. */ - private final Log rlog; /** cache of methods. */ private final MethodCache methodCache; @@ -56,18 +49,15 @@ * @param log the logger. */ public ClassMap(Class<?> aClass, Log log) { - clazz = aClass; - this.rlog = log; - methodCache = new MethodCache(); - - populateMethodCache(); + methodCache = createMethodCache(aClass, log); } /** - * @return the class object whose methods are cached by this map. + * Gets the methods names cached by this map. + * @return the array of method names */ - Class<?> getCachedClass() { - return clazz; + String[] getMethodNames() { + return methodCache.names(); } /** @@ -84,11 +74,13 @@ } /** - * Populate the Map of direct hits. These - * are taken from all the public methods + * Populate the Map of direct hits. These are taken from all the public methods * that our class, its parents and their implemented interfaces provide. + * @param classToReflect the class to cache + * @param log the Log + * @return a newly allocated & filled up cache */ - private void populateMethodCache() { + private static MethodCache createMethodCache(Class<?> classToReflect, Log log) { // // Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start // with the actual declaring class and its interfaces and then move up (superclass etc.) until we @@ -103,42 +95,54 @@ // hit the public elements sooner or later because we reflect all the public elements anyway. // // Ah, the miracles of Java for(;;) ... - for (Class<?> classToReflect = getCachedClass(); classToReflect != null; classToReflect = classToReflect.getSuperclass()) { + MethodCache cache = new MethodCache(); + for (;classToReflect != null; classToReflect = classToReflect.getSuperclass()) { if (Modifier.isPublic(classToReflect.getModifiers())) { - populateMethodCacheWith(methodCache, classToReflect); + populateMethodCacheWith(cache, classToReflect, log); } Class<?>[] interfaces = classToReflect.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { - populateMethodCacheWithInterface(methodCache, interfaces[i]); + populateMethodCacheWithInterface(cache, interfaces[i], log); } } + return cache; } - /* recurses up interface hierarchy to get all super interfaces */ - private void populateMethodCacheWithInterface(MethodCache methodCache, Class<?> iface) { + /** + * Recurses up interface hierarchy to get all super interfaces. + * @param cache the cache to fill + * @param iface the interface to populate the cache from + * @param log the Log + */ + private static void populateMethodCacheWithInterface(MethodCache cache, Class<?> iface, Log log) { if (Modifier.isPublic(iface.getModifiers())) { - populateMethodCacheWith(methodCache, iface); + populateMethodCacheWith(cache, iface, log); } Class<?>[] supers = iface.getInterfaces(); for (int i = 0; i < supers.length; i++) { - populateMethodCacheWithInterface(methodCache, supers[i]); + populateMethodCacheWithInterface(cache, supers[i], log); } } - private void populateMethodCacheWith(MethodCache methodCache, Class<?> classToReflect) { + /** + * Recurses up class hierarchy to get all super classes. + * @param cache the cache to fill + * @param clazz the class to populate the cache from + * @param log the Log + */ + private static void populateMethodCacheWith(MethodCache cache, Class<?> clazz, Log log) { try { - Method[] methods = classToReflect.getDeclaredMethods(); + Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { int modifiers = methods[i].getModifiers(); if (Modifier.isPublic(modifiers)) { - methodCache.put(methods[i]); + cache.put(methods[i]); } } - } - catch (SecurityException se) // Everybody feels better with... - { - if (rlog.isDebugEnabled()) { - rlog.debug("While accessing methods of " + classToReflect + ": ", se); + } catch (SecurityException se) { + // Everybody feels better with... + if (log.isDebugEnabled()) { + log.debug("While accessing methods of " + clazz + ": ", se); } } } @@ -150,8 +154,8 @@ * @version $Id$ * <p> * It stores the association between: - * a key made of a method name & an array of argument types. (@see MethodCache.MethodKey) - * a method. + * - a key made of a method name & an array of argument types. (@see MethodCache.MethodKey) + * - a method. * </p> * <p> * Since the invocation of the associated method is dynamic, there is no need (nor way) to differentiate between @@ -161,31 +165,35 @@ * @version $Id$ */ static class MethodCache { - /** * A method that returns itself used as a marker for cache miss, * allows the underlying cache map to be strongly typed. + * @return itself as a method */ - public static Method CacheMiss() { + public static Method cacheMiss() { try { - return MethodCache.class.getMethod("CacheMiss"); + return MethodCache.class.getMethod("cacheMiss"); } // this really cant make an error... catch (Exception xio) { } return null; } - private static final Method CACHE_MISS = CacheMiss(); - private static final Map<Class<?>, Class<?>> convertPrimitives; + /** The cache miss marker method. */ + private static final Method CACHE_MISS = cacheMiss(); + /** The initial size of the primitive conversion map. */ + private static final int PRIMITIVE_SIZE = 13; + /** The primitive type to class conversion map. */ + private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES; static { - convertPrimitives = new HashMap<Class<?>, Class<?>>(13); - convertPrimitives.put(Boolean.TYPE, Boolean.class); - convertPrimitives.put(Byte.TYPE, Byte.class); - convertPrimitives.put(Character.TYPE, Character.class); - convertPrimitives.put(Double.TYPE, Double.class); - convertPrimitives.put(Float.TYPE, Float.class); - convertPrimitives.put(Integer.TYPE, Integer.class); - convertPrimitives.put(Long.TYPE, Long.class); - convertPrimitives.put(Short.TYPE, Short.class); + PRIMITIVE_TYPES = new HashMap<Class<?>, Class<?>>(PRIMITIVE_SIZE); + PRIMITIVE_TYPES.put(Boolean.TYPE, Boolean.class); + PRIMITIVE_TYPES.put(Byte.TYPE, Byte.class); + PRIMITIVE_TYPES.put(Character.TYPE, Character.class); + PRIMITIVE_TYPES.put(Double.TYPE, Double.class); + PRIMITIVE_TYPES.put(Float.TYPE, Float.class); + PRIMITIVE_TYPES.put(Integer.TYPE, Integer.class); + PRIMITIVE_TYPES.put(Long.TYPE, Long.class); + PRIMITIVE_TYPES.put(Short.TYPE, Short.class); } /** Converts a primitive type to its corresponding class. @@ -199,7 +207,7 @@ static final Class<?> primitiveClass(Class<?> parm) { // it is marginally faster to get from the map than call isPrimitive... //if (!parm.isPrimitive()) return parm; - Class<?> prim = convertPrimitives.get(parm); + Class<?> prim = PRIMITIVE_TYPES.get(parm); return prim == null ? parm : prim; } /** @@ -229,12 +237,18 @@ * @return A Method object representing the method to invoke or null. * @throws MethodMap.AmbiguousException When more than one method is a match for the parameters. */ - public Method get(final String name, final Object[] params) + Method get(final String name, final Object[] params) throws MethodMap.AmbiguousException { return get(new MethodKey(name, params)); } - public Method get(final MethodKey methodKey) + /** + * Finds a Method using a MethodKey. + * @param methodKey the method key + * @return a method + * @throws org.apache.commons.jexl.util.introspection.MethodMap.AmbiguousException + */ + Method get(final MethodKey methodKey) throws MethodMap.AmbiguousException { synchronized (methodMap) { Method cacheEntry = cache.get(methodKey); @@ -264,7 +278,11 @@ } } - public void put(Method method) { + /** + * Adds a method to the map. + * @param method the method to add + */ + void put(Method method) { synchronized (methodMap) { MethodKey methodKey = new MethodKey(method); // We don't overwrite methods. Especially not if we fill the @@ -277,27 +295,45 @@ } } } + + /** + * Gets all the method names from this map. + * @return the array of method name + */ + String[] names() { + synchronized (methodMap) { + return methodMap.names(); + } + } } /** - * A method key for the introspector cache: - * This replaces the original key scheme which used to build the key by concatenating the method name and parameters - * class names as one string with the exception that primitive types were converted to their object class equivalents. + * A method key for the introspector cache. + * <p> + * This replaces the original key scheme which used to build the key + * by concatenating the method name and parameters class names as one string + * with the exception that primitive types were converted to their object class equivalents. + * </p> + * <p> * The key is still based on the same information, it is just wrapped in an object instead. - * Primitive type classes are converted to they object equivalent to make a key; int foo(int) and int foo(Integer) do - * generate the same key. - * A key can be constructed either from arguments (array of objects) or from parameters (array of class). + * Primitive type classes are converted to they object equivalent to make a key; + * int foo(int) and int foo(Integer) do generate the same key. + * </p> + * A key can be constructed either from arguments (array of objects) or from parameters + * (array of class). * Roughly 3x faster than string key to access the map & uses less memory. */ static class MethodKey { - /** The hash code */ - final int hashCode; + /** The hash code. */ + private final int hashCode; /** The method name. */ final String method; /** The parameters. */ final Class<?>[] params; /** A marker for empty parameter list. */ - static final Class<?>[] NOARGS = new Class<?>[0]; + private static final Class<?>[] NOARGS = new Class<?>[0]; + /** The hash code constants. */ + private static final int HASH = 37; /** Builds a MethodKey from a method. * Used to store information in the method map. @@ -306,12 +342,14 @@ this(method.getName(), method.getParameterTypes()); } - /** Builds a MethodKey from a method name and a set of arguments (objects). + /** Creates a MethodKey from a method name and a set of arguments (objects). * Used to query the method map. + * @param method the method name + * @param args the arguments instances to match the method signature */ - MethodKey(String method, Object[] args) { + MethodKey(String aMethod, Object[] args) { // !! keep this in sync with the other ctor (hash code) !! - this.method = method; + this.method = aMethod; int hash = this.method.hashCode(); final int size; if (args != null && (size = args.length) > 0) { @@ -322,7 +360,7 @@ // no need to go thru primitive type conversion since these are objects Class<?> parm = arg == null ? Object.class : arg.getClass(); // } - hash = (37 * hash) + parm.hashCode(); + hash = (HASH * hash) + parm.hashCode(); this.params[p] = parm; } } else { @@ -331,12 +369,14 @@ this.hashCode = hash; } - /** Builds a MethodKey from a method name and a set of parameters (classes). + /** Creates a MethodKey from a method name and a set of parameters (classes). * Used to store information in the method map. ( @see MethodKey#primitiveClass ) + * @param method the method name + * @param args the argument classes to match the method signature */ - MethodKey(String method, Class<?>[] args) { + MethodKey(String aMethod, Class<?>[] args) { // !! keep this in sync with the other ctor (hash code) !! - this.method = method; + this.method = aMethod; int hash = this.method.hashCode(); final int size; if (args != null && (size = args.length) > 0) { @@ -345,7 +385,7 @@ // ctor(Class): { Class<?> parm = MethodCache.primitiveClass(args[p]); // } - hash = (37 * hash) + parm.hashCode(); + hash = (HASH * hash) + parm.hashCode(); this.params[p] = parm; } } else {
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java (original) +++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java Sat Jul 18 19:26:02 2009 @@ -100,7 +100,8 @@ /* * whoops. Ambiguous. Make a nice log message and return null... */ - StringBuilder msg = new StringBuilder("Introspection Error : Ambiguous method invocation ").append(name).append("( "); + StringBuilder msg = new StringBuilder("Introspection Error : Ambiguous method invocation "); + msg.append(name).append("( "); for (int i = 0; i < params.length; i++) { if (i > 0) { msg.append(", "); Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java (original) +++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java Sat Jul 18 19:26:02 2009 @@ -101,10 +101,32 @@ throw new IllegalArgumentException("params object is null!"); } - ClassMap classMap; + ClassMap classMap = getMap(c); + return classMap.findMethod(name, params); + } // CSON: RedundantThrows + + /** + * Gets the accessible methods names known for a given class. + * @param c the class + * @return the class method names + */ + public String[] getMethodNames(Class<?> c) { + if (c == null) { + return new String[0]; + } + ClassMap classMap = getMap(c); + return classMap.getMethodNames(); + + } + + /** + * Gets the ClassMap for a given class. + * @return the class map + */ + private ClassMap getMap(Class<?> c) { synchronized (classMethodMaps) { - classMap = classMethodMaps.get(c); + ClassMap classMap = classMethodMaps.get(c); /* * if we don't have this, check to see if we have it by name. if so, * then we have a classloader change so dump our caches. @@ -122,11 +144,9 @@ classMap = createClassMap(c); } + return classMap; } - - return classMap.findMethod(name, params); - } // CSON: RedundantThrows - + } /** * Creates a class map for specific class and registers it in the cache. Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java (original) +++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java Sat Jul 18 19:26:02 2009 @@ -85,6 +85,15 @@ } /** + * Returns the array of method names accessible in this class. + * @return the array of names + */ + public synchronized String[] names() { + java.util.Set<String> set = methodByNameMap.keySet(); + return set.toArray(new String[set.size()]); + } + + /** * <p> * Find a method. Attempts to find the * most specific applicable method using the @@ -113,11 +122,16 @@ * specific applicable method */ // CSOFF: RedundantThrows - public Method find(String methodName, Object[] args) - throws AmbiguousException { + public Method find(String methodName, Object[] args) throws AmbiguousException { return find(new ClassMap.MethodKey(methodName, args)); } + /** + * Finds a method by key. + * @param methodKey the key + * @return the method + * @throws AmbiguousException if find is ambiguous + */ Method find(ClassMap.MethodKey methodKey) throws AmbiguousException { List<Method> methodList = get(methodKey.method); if (methodList == null) { Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java (original) +++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java Sat Jul 18 19:26:02 2009 @@ -33,7 +33,9 @@ */ void init(); - /** Returns the underlying introspector. */ + /** Gets underlying introspector. + * @return the introspector + */ public Introspector getIntrospector(); /** Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java (original) +++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java Sat Jul 18 19:26:02 2009 @@ -46,23 +46,23 @@ */ public class UberspectImpl implements Uberspect, UberspectLoggable { /** - * index of the first character of the property. + * Index of the first character of the property. */ private static final int PROPERTY_START_INDEX = 3; - /* - * static signature for method(object,object) + /** + * Static signature for method(object,object). */ static final Class<?>[] OBJECT_OBJECT = { Object.class, Object.class }; /** * Our runtime logger. */ private Log rlog; - /** - * the default Velocity introspector. + * The default Velocity introspector. */ private Introspector introspector; - + + /** {...@inheritdoc} */ public Introspector getIntrospector() { return introspector; } @@ -95,13 +95,15 @@ } else if (obj instanceof Map<?,?>) { return ((Map<?,?>) obj).values().iterator(); } else if (obj instanceof Iterator<?>) { - rlog.warn("Warning! The iterative " + " is an Iterator in the #foreach() loop at [" + i.getLine() + "," + rlog.warn("Warning! The iterative " + + " is an Iterator in the #foreach() loop at [" + i.getLine() + "," + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable," + " if used in more than once, this may lead to" + " unexpected results."); return ((Iterator<?>) obj); } else if (obj instanceof Enumeration<?>) { - rlog.warn("Warning! The iterative " + " is an Enumeration in the #foreach() loop at [" + i.getLine() + "," + rlog.warn("Warning! The iterative " + + " is an Enumeration in the #foreach() loop at [" + i.getLine() + "," + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable," + " if used in more than once, this may lead to" + " unexpected results."); @@ -409,7 +411,10 @@ } } // CSON: VisibilityModifier - + + /** + * Concrete instance of VelPropertyGet. + */ public static class VelGetterImpl implements VelPropertyGet { /** * executor for performing the get. @@ -455,7 +460,9 @@ } } - + /** + * Concrete instance of VelPropertySet. + */ public static class VelSetterImpl implements VelPropertySet { /** * the method to call. Modified: commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java?rev=795421&r1=795420&r2=795421&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java (original) +++ commons/proper/jexl/branches/2.0/src/test/org/apache/commons/jexl/IssuesTest.java Sat Jul 18 19:26:02 2009 @@ -139,21 +139,21 @@ assertTrue("should be true", (Boolean) value); } - // JEXL-52: can be implemented by deriving Interpreter.{g,s}etAttribute; wontfix - // JEXL-50: can be implemented through a namespace:function or through JexlArithmetic derivation - wontfix - - // JEXL-46: regexp syntax; should we really add more syntactic elements? - later/vote? - // JEXL-45: unhandled division by zero; already fixed in trunk - - // JEXL-35: final API requirements; fixed in trunk ? - // JEXL-32: BigDecimal values are treated as Long values which results in loss of precision; no longer affects 2.0 // Dion - // JEXL-21: operator overloading / hooks on operator processing (wontfix, derive JexlArithmetic) - // JEXL-20: checkstyle - // JEXL-3: static method resolution; fixed in trunk - // JEXL-3: change to JexlContext (setVar, getVar) - later, watch out for JEXL-10, differentiate null versus undefined? + // JEXL-52: can be implemented by deriving Interpreter.{g,s}etAttribute; later + public void test52base() throws Exception { + JexlEngine jexl = new JexlEngine(); + // most likely, call will be in an Interpreter, getUberspect + String[] names = jexl.uberspect.getIntrospector().getMethodNames(Another.class); + assertTrue("should find methods", names.length > 0); + int found = 0; + for(String name : names) { + if ("foo".equals(name) || "goo".equals(name)) + found += 1; + } + assertTrue("should have foo & goo", found == 2); + } - // JEXL-10: Make possible checking for unresolved variables - // JEXL-11: Don't make null convertible into anything + // JEXL-10/JEXL-11: variable checking, null operand is error public void test11() throws Exception { JexlEngine jexl = new JexlEngine(); // ensure errors will throw @@ -183,7 +183,7 @@ } } public static void main(String[] args) throws Exception { - new IssuesTest().test11(); + new IssuesTest().test52base(); } } \ No newline at end of file