You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ra...@apache.org on 2009/08/01 02:36:52 UTC
svn commit: r799779 [3/3] - in
/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl: ./
context/ parser/ scripting/ util/ util/introspection/
Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java Sat Aug 1 00:36:51 2009
@@ -17,22 +17,22 @@
package org.apache.commons.jexl.util.introspection;
/**
- * Little class to carry in info such as template name, line and column for
- * information error reporting from the uberspector implementations
+ * Little class to carry in info such as a file or template name, line and column for
+ * information error reporting from the uberspector implementations.
*
- * Taken from velocity for self-sufficiency.
+ * Originally taken from velocity for self-sufficiency.
*
* @since 1.0
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @version $Id$
*/
-public class Info {
+public class Info implements DebugInfo {
/** line number. */
- private int line;
+ private final int line;
/** column number. */
- private int column;
+ private final int column;
/** name. */
- private String templateName;
+ private final String name;
/**
* Create info.
* @param tn template name
@@ -40,17 +40,49 @@
* @param c column
*/
public Info(String tn, int l, int c) {
- templateName = tn;
+ name = tn;
line = l;
column = c;
}
/**
+ * Formats this info in the form 'name@line:column'.
+ * @return the formatted info
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(name != null? name : "");
+ if (line > 0) {
+ sb.append("@");
+ sb.append(line);
+ if (column > 0) {
+ sb.append(":");
+ sb.append(column);
+ }
+ }
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ public String debugString() {
+ return toString();
+ }
+
+ /**
+ * Gets the file/script/url name.
+ * @return template name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
* Gets the template name.
* @return template name
*/
+ @Deprecated
public String getTemplateName() {
- return templateName;
+ return name;
}
/**
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=799779&r1=799778&r2=799779&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 Aug 1 00:36:51 2009
@@ -14,36 +14,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.commons.jexl.util.introspection;
import java.lang.reflect.Method;
-
+import java.lang.reflect.Constructor;
import org.apache.commons.logging.Log;
-
/**
* This basic function of this class is to return a Method
* object for a particular class given the name of a method
* and the parameters to the method in the form of an Object[]
- *
+ * <p>
* The first time the Introspector sees a
* class it creates a class method map for the
* class in question. Basically the class method map
- * is a Hashtable where Method objects are keyed by a
+ * is a map where Method objects are keyed by a
* concatenation of the method name and the names of
* classes that make up the parameters.
- *
+ * </p>
+ * <p>
* For example, a method with the following signature:
- *
+ * <code>
* public void method(String a, StringBuffer b)
- *
+ * </code>
* would be mapped by the key:
- *
- * "method" + "java.lang.String" + "java.lang.StringBuffer"
- *
+ * <code>
+ * { "method", {"java.lang.String", "java.lang.StringBuffer" } }
+ * </code>
+ * </p>
+ * <p>
* This mapping is performed for all the methods in a class
- * and stored for
+ * and stored for the lifetime of the instance. It is thus advised when possible
+ * to share one instance of this class accross Uberspect instances.
+ * </p>
* @since 1.0
* @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
* @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
@@ -51,60 +54,68 @@
* @author <a href="mailto:paulo.gaspar@krankikom.de">Paulo Gaspar</a>
* @version $Id$
*/
-public class Introspector extends IntrospectorBase {
+public final class Introspector extends IntrospectorBase {
/**
* define a public string so that it can be looked for
* if interested.
*/
-
public static final String CACHEDUMP_MSG =
"Introspector : detected classloader change. Dumping cache.";
/**
- * our engine runtime services.
- */
- private final Log rlog;
-
- /**
- * Recieves our RuntimeServices object.
+ * Creates a new instance.
* @param logger a {@link Log}.
*/
public Introspector(Log logger) {
super(logger);
- this.rlog = logger;
}
/**
- * Gets the method defined by <code>name</code> and
- * <code>params</code> for the Class <code>c</code>.
+ * Gets the method defined by <code>key</code> for the Class <code>c</code>.
*
* @param c Class in which the method search is taking place
- * @param name Name of the method being searched for
- * @param params An array of Objects (not Classes) that describe the
- * the parameters
+ * @param key MethodKey of the method being searched for
*
* @return The desired Method object.
* @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
* CSOFF: RedundantThrows
*/
@Override
- public Method getMethod(Class<?> c, String name, Object[] params) throws IllegalArgumentException {
+ public Method getMethod(Class<?> c, MethodKey key) throws IllegalArgumentException {
// just delegate to the base class
try {
- return super.getMethod(c, name, params);
- } catch (MethodMap.AmbiguousException ae) {
+ return super.getMethod(c, key);
+ } catch (MethodKey.AmbiguousException ae) {
// whoops. Ambiguous. Make a nice log message and return null...
- 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(", ");
- }
- msg.append(null == params[i] ? "null" : params[i].getClass().getName());
+ if (rlog != null) {
+ rlog.error("ambiguous method invocation: "
+ + c.getName() + "."
+ + key.debugString());
}
- msg.append(") for class ").append(c.getName());
+ }
+ return null;
+ }
+
+ // CSON: RedundantThrows
+ /**
+ * Gets the method defined by <code>key</code> for the Class <code>c</code>.
+ *
+ * @param key MethodKey of the method being searched for
+ *
+ * @return The desired Method object.
+ * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
+ * CSOFF: RedundantThrows
+ */
+ @Override
+ public Constructor<?> getConstructor(MethodKey key) throws IllegalArgumentException {
+ // just delegate to the base class
+ try {
+ return super.getConstructor(key);
+ } catch (MethodKey.AmbiguousException ae) {
+ // whoops. Ambiguous. Make a nice log message and return null...
if (rlog != null) {
- rlog.error(msg.toString());
+ rlog.error("ambiguous constructor invocation: new "
+ + key.debugString());
}
}
return null;
@@ -119,4 +130,4 @@
super.clearCache();
rlog.info(CACHEDUMP_MSG);
}
-}
\ No newline at end of file
+}
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=799779&r1=799778&r2=799779&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 Aug 1 00:36:51 2009
@@ -18,9 +18,15 @@
package org.apache.commons.jexl.util.introspection;
import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.ref.SoftReference;
import java.util.HashSet;
import java.util.Map;
+import java.util.HashMap;
+import java.util.WeakHashMap;
import java.util.Set;
+import java.util.List;
+import java.util.LinkedList;
import org.apache.commons.logging.Log;
@@ -54,20 +60,26 @@
*/
public class IntrospectorBase {
/** the logger. */
- private final Log rlog;
+ protected final Log rlog;
/**
- * Holds the method maps for the classes we know about, keyed by Class
- * Made WeakHashMap so we wont prevent a class from being GCed.
- * object.
+ * Holds the method maps for the classes we know about, keyed by Class.
+ * Implemented as WeakHashMap so we wont prevent an unused class from being GCed.
*/
- protected final Map<Class<?>, ClassMap> classMethodMaps = new java.util.WeakHashMap<Class<?>, ClassMap>();
-
+ private final Map<Class<?>, ClassMap> classMethodMaps = new WeakHashMap<Class<?>, ClassMap>();
/**
* Holds the qualified class names for the classes we hold in the
* classMethodMaps hash.
*/
private Set<String> cachedClassNames = new HashSet<String>();
+ /**
+ * Holds the map of classes ctors we know about as well as unknown ones.
+ */
+ private final Map<MethodKey, Constructor<?>> constructorsMap = new HashMap<MethodKey, Constructor<?>>();
+ /**
+ * Holds the set of classes we have introspected.
+ */
+ private Map<String, SoftReference<Class<?>>> constructibleClasses = new HashMap<String, SoftReference<Class<?>>>();
/**
* Create the introspector.
@@ -78,33 +90,21 @@
}
/**
- * Gets the method defined by <code>name</code> and <code>params</code>
- * for the Class <code>c</code>.
+ * Gets the method defined by the <code>MethodKey</code> for the class <code>c</code>.
*
- * @param c Class in which the method search is taking place
- * @param name Name of the method being searched for
- * @param params An array of Objects (not Classes) that describe the the
- * parameters
+ * @param c Class in which the method search is taking place
+ * @param key Key of the method being searched for
* @return The desired Method object.
* @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
- * @throws MethodMap.AmbiguousException When the method map contains more than
+ * @throws MethodKey.AmbiguousException When the method map contains more than
* one match for the requested signature.
* CSOFF: RedundantThrows
*/
- public Method getMethod(Class<?> c, String name, Object[] params)
- throws IllegalArgumentException, MethodMap.AmbiguousException {
- if (c == null) {
- throw new IllegalArgumentException("Introspector.getMethod(): Class method key was null: " + name);
- }
-
- if (params == null) {
- throw new IllegalArgumentException("params object is null!");
- }
-
+ public Method getMethod(Class<?> c, MethodKey key) {
ClassMap classMap = getMap(c);
+ return classMap.findMethod(key);
- return classMap.findMethod(name, params);
- } // CSON: RedundantThrows
+ } // CSON: RedundantThrows
/**
* Gets the accessible methods names known for a given class.
@@ -121,6 +121,80 @@
}
/**
+ * A Constructor get cache-miss.
+ */
+ private static class CacheMiss {
+ /** The constructor used as cache-miss. */
+ public CacheMiss() {}
+ }
+ /** The cache-miss marker for the constructors map. */
+ private static final Constructor<?> CTOR_MISS = CacheMiss.class.getConstructors()[0];
+
+ /**
+ * Gets the constructor defined by the <code>MethodKey</code>.
+ *
+ * @param key Key of the constructor being searched for
+ * @return The desired Constructor object.
+ * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
+ * @throws MethodKey.AmbiguousException When the method map contains more than
+ * one match for the requested signature.
+ * CSOFF: RedundantThrows
+ */
+ public Constructor<?> getConstructor(final MethodKey key) {
+ Constructor<?> ctor = null;
+ synchronized(constructorsMap) {
+ ctor = constructorsMap.get(key);
+ // that's a clear miss
+ if (CTOR_MISS.equals(ctor)) {
+ return null;
+ }
+ // let's introspect...
+ if (ctor == null) {
+ final String cname = key.getMethod();
+ // do we know about this class?
+ Class<?> clazz = null;
+ SoftReference<Class<?>> sc = constructibleClasses.get(cname);
+ if (sc != null) {
+ clazz = sc.get();
+ if (clazz == null) {
+ // we knew about this class & it's gone, clear cache
+ constructorsMap.clear();
+ constructibleClasses = new HashMap<String, SoftReference<Class<?>>>();
+ }
+ }
+ try {
+ // do find the most specific ctor
+ if (clazz == null) {
+ clazz = Class.forName(cname);
+ // add it to list of know loaded classes
+ constructibleClasses.put(cname, new SoftReference<Class<?>>(clazz));
+ }
+ List<Constructor<?>> l = new LinkedList<Constructor<?>>();
+ for(Constructor<?> ictor : clazz.getConstructors()) {
+ l.add(ictor);
+ }
+ // try to find one
+ ctor = MethodKey.CONSTRUCTORS.getMostSpecific(l, key.getParameters());
+ if (ctor != null) {
+ constructorsMap.put(key, ctor);
+ } else {
+ constructorsMap.put(key, CTOR_MISS);
+ }
+ } catch(ClassNotFoundException xnotfound) {
+ if (rlog.isDebugEnabled()) {
+ rlog.debug("could not load class " + cname, xnotfound);
+ }
+ ctor = null;
+ } catch(MethodKey.AmbiguousException xambiguous) {
+ rlog.warn("ambiguous ctor detected for " + cname, xambiguous);
+ ctor = null;
+ }
+ }
+ }
+ return ctor;
+ }
+
+ /**
* Gets the ClassMap for a given class.
* @param c the class
* @return the class map
@@ -156,7 +230,7 @@
* @param c class.
* @return a {@link ClassMap}
*/
- protected ClassMap createClassMap(Class<?> c) {
+ private ClassMap createClassMap(Class<?> c) {
ClassMap classMap = new ClassMap(c,rlog);
classMethodMaps.put(c, classMap);
cachedClassNames.add(c.getName());
@@ -168,15 +242,10 @@
* Clears the classmap and classname caches.
*/
protected void clearCache() {
- /*
- * since we are synchronizing on this object, we have to clear it rather
- * than just dump it.
- */
+ // since we are synchronizing on this object, we have to clear it rather
+ // than just dump it.
classMethodMaps.clear();
-
- /*
- * for speed, we can just make a new one and let the old one be GC'd
- */
+ // for speed, we can just make a new one and let the old one be GC'd
cachedClassNames = new HashSet<String>();
}
}
\ No newline at end of file
Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java Sat Aug 1 00:36:51 2009
@@ -0,0 +1,625 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl.util.introspection;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * A method key usable by the introspector cache.
+ * <p>
+ * This stores a method (or class) name and parameters.
+ * </p>
+ * <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.
+ * </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.
+ *
+ * For the parameters methods:
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author Nathan Bubna
+ */
+public final class MethodKey {
+ /** The hash code. */
+ private final int hashCode;
+ /** The method name. */
+ private final String method;
+ /** The parameters. */
+ private final Class<?>[] params;
+ /** A marker for empty parameter list. */
+ private static final Class<?>[] NOARGS = new Class<?>[0];
+ /** The hash code constants. */
+ private static final int HASH = 37;
+
+ /**
+ * Creates a key from a method.
+ * @param aMethod the method to generate the key from.
+ */
+ MethodKey(Method aMethod) {
+ this(aMethod.getName(), aMethod.getParameterTypes());
+ }
+
+ /**
+ * Creates a key from a method name and a set of arguments.
+ * @param aMethod the method to generate the key from
+ * @param args the intended method arguments
+ */
+ public MethodKey(String aMethod, Object[] args) {
+ super();
+ // !! keep this in sync with the other ctor (hash code) !!
+ this.method = aMethod;
+ int hash = this.method.hashCode();
+ final int size;
+ // CSOFF: InnerAssignment
+ if (args != null && (size = args.length) > 0) {
+ this.params = new Class<?>[size];
+ for (int p = 0; p < size; ++p) {
+ Object arg = args[p];
+ // null arguments use void as Void.class as marker
+ Class<?> parm = arg == null ? Void.class : arg.getClass();
+ hash = (HASH * hash) + parm.hashCode();
+ this.params[p] = parm;
+ }
+ } else {
+ this.params = NOARGS;
+ }
+ this.hashCode = hash;
+ }
+
+ /**
+ * Creates a key from a method name and a set of parameters.
+ * @param aMethod the method to generate the key from
+ * @param args the intended method parameters
+ */
+ MethodKey(String aMethod, Class<?>[] args) {
+ super();
+ // !! keep this in sync with the other ctor (hash code) !!
+ this.method = aMethod.intern();
+ int hash = this.method.hashCode();
+ final int size;
+ // CSOFF: InnerAssignment
+ if (args != null && (size = args.length) > 0) {
+ this.params = new Class<?>[size];
+ for (int p = 0; p < size; ++p) {
+ Class<?> parm = ClassMap.MethodCache.primitiveClass(args[p]);
+ hash = (HASH * hash) + parm.hashCode();
+ this.params[p] = parm;
+ }
+ } else {
+ this.params = NOARGS;
+ }
+ this.hashCode = hash;
+ }
+
+ /**
+ * Gets this key's method name.
+ * @return the method name
+ */
+ String getMethod() {
+ return method;
+ }
+
+ /**
+ * Gets this key's method parameter classes.
+ * @return the parameters
+ */
+ Class<?>[] getParameters() {
+ return params;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object arg) {
+ if (arg instanceof MethodKey) {
+ MethodKey key = (MethodKey) arg;
+ return method.equals(key.method) && Arrays.equals(params, key.params);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(method);
+ for (Class<?> c : params) {
+ builder.append(c == Void.class ? "null" : c.getName());
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Outputs a human readable debug representation of this key.
+ * @return method(p0, p1, ...)
+ */
+ public String debugString() {
+ StringBuilder builder = new StringBuilder(method);
+ builder.append('(');
+ for (int i = 0; i < params.length; i++) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ builder.append(Void.class == params[i] ? "null" : params[i].getName());
+ }
+ builder.append(')');
+ return builder.toString();
+ }
+
+ /**
+ * whether a method/ctor is more specific than a previously compared one.
+ */
+ private static final int MORE_SPECIFIC = 0;
+ /**
+ * whether a method/ctor is less specific than a previously compared one.
+ */
+ private static final int LESS_SPECIFIC = 1;
+ /**
+ * A method/ctor doesn't match a previously compared one.
+ */
+ private static final int INCOMPARABLE = 2;
+
+ /**
+ * Simple distinguishable exception, used when
+ * we run across ambiguous overloading. Caught
+ * by the introspector.
+ */
+ public static class AmbiguousException extends RuntimeException {
+ /**
+ * Version Id for serializable.
+ */
+ private static final long serialVersionUID = -2314636505414551664L;
+ }
+
+ /**
+ * Utility for parameters matching.
+ * @param <T> method or contructor
+ */
+ public abstract static class Parameters<T> {
+ /**
+ * Extract the parameter types from its applicable argument.
+ * @param app a method or constructor
+ * @return the parameters
+ */
+ protected abstract Class<?>[] getParameterTypes(T app);
+
+ // CSOFF: RedundantThrows
+ /**
+ * Gets the most specific method that is applicable to actual argument types.
+ * @param methods a list of methods.
+ * @param classes list of argument types.
+ * @return the most specific method.
+ * @throws MethodKey.AmbiguousException if there is more than one.
+ */
+ protected T getMostSpecific(List<T> methods, Class<?>[] classes) {
+ LinkedList<T> applicables = getApplicables(methods, classes);
+
+ if (applicables.isEmpty()) {
+ return null;
+ }
+
+ if (applicables.size() == 1) {
+ return applicables.getFirst();
+ }
+
+ /*
+ * This list will contain the maximally specific methods. Hopefully at
+ * the end of the below loop, the list will contain exactly one method,
+ * (the most specific method) otherwise we have ambiguity.
+ */
+
+ LinkedList<T> maximals = new LinkedList<T>();
+
+ for (Iterator<T> applicable = applicables.iterator();
+ applicable.hasNext();) {
+ T app = applicable.next();
+ Class<?>[] appArgs = getParameterTypes(app);
+
+ boolean lessSpecific = false;
+
+ for (Iterator<T> maximal = maximals.iterator();
+ !lessSpecific && maximal.hasNext();) {
+ T max = maximal.next();
+
+ // CSOFF: MissingSwitchDefault
+ switch (moreSpecific(appArgs, getParameterTypes(max))) {
+ case MORE_SPECIFIC:
+ /*
+ * This method is more specific than the previously
+ * known maximally specific, so remove the old maximum.
+ */
+ maximal.remove();
+ break;
+
+ case LESS_SPECIFIC:
+ /*
+ * This method is less specific than some of the
+ * currently known maximally specific methods, so we
+ * won't add it into the set of maximally specific
+ * methods
+ */
+
+ lessSpecific = true;
+ break;
+ }
+ } // CSON: MissingSwitchDefault
+
+ if (!lessSpecific) {
+ maximals.addLast(app);
+ }
+ }
+
+ if (maximals.size() > 1) {
+ // We have more than one maximally specific method
+ throw new AmbiguousException();
+ }
+
+ return maximals.getFirst();
+ } // CSON: RedundantThrows
+
+ /**
+ * Determines which method signature (represented by a class array) is more
+ * specific. This defines a partial ordering on the method signatures.
+ *
+ * @param c1 first signature to compare
+ * @param c2 second signature to compare
+ * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
+ * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
+ */
+ private int moreSpecific(Class<?>[] c1, Class<?>[] c2) {
+ boolean c1MoreSpecific = false;
+ boolean c2MoreSpecific = false;
+
+ // compare lengths to handle comparisons where the size of the arrays
+ // doesn't match, but the methods are both applicable due to the fact
+ // that one is a varargs method
+ if (c1.length > c2.length) {
+ return MORE_SPECIFIC;
+ }
+ if (c2.length > c1.length) {
+ return LESS_SPECIFIC;
+ }
+
+ // ok, move on and compare those of equal lengths
+ for (int i = 0; i < c1.length; ++i) {
+ if (c1[i] != c2[i]) {
+ boolean last = (i == c1.length - 1);
+ c1MoreSpecific = c1MoreSpecific || isStrictConvertible(c2[i], c1[i], last);
+ c2MoreSpecific = c2MoreSpecific || isStrictConvertible(c1[i], c2[i], last);
+ }
+ }
+
+ if (c1MoreSpecific) {
+ if (c2MoreSpecific) {
+ /*
+ * Incomparable due to cross-assignable arguments (i.e.
+ * foo(String, Object) vs. foo(Object, String))
+ */
+
+ return INCOMPARABLE;
+ }
+
+ return MORE_SPECIFIC;
+ }
+
+ if (c2MoreSpecific) {
+ return LESS_SPECIFIC;
+ }
+
+ /*
+ * Incomparable due to non-related arguments (i.e.
+ * foo(Runnable) vs. foo(Serializable))
+ */
+
+ return INCOMPARABLE;
+ }
+
+ /**
+ * Returns all methods that are applicable to actual argument types.
+ *
+ * @param methods list of all candidate methods
+ * @param classes the actual types of the arguments
+ * @return a list that contains only applicable methods (number of
+ * formal and actual arguments matches, and argument types are assignable
+ * to formal types through a method invocation conversion).
+ */
+ protected LinkedList<T> getApplicables(List<T> methods, Class<?>[] classes) {
+ LinkedList<T> list = new LinkedList<T>();
+
+ for (Iterator<T> imethod = methods.iterator(); imethod.hasNext();) {
+ T method = imethod.next();
+ if (isApplicable(method, classes)) {
+ list.add(method);
+ }
+
+ }
+ return list;
+ }
+
+ /**
+ * Returns true if the supplied method is applicable to actual
+ * argument types.
+ *
+ * @param method method that will be called
+ * @param classes arguments to method
+ * @return true if method is applicable to arguments
+ */
+ private boolean isApplicable(T method, Class<?>[] classes) {
+ Class<?>[] methodArgs = getParameterTypes(method);
+
+ if (methodArgs.length > classes.length) {
+ // if there's just one more methodArg than class arg
+ // and the last methodArg is an array, then treat it as a vararg
+ return methodArgs.length == classes.length + 1 && methodArgs[methodArgs.length - 1].isArray();
+ } else if (methodArgs.length == classes.length) {
+ // this will properly match when the last methodArg
+ // is an array/varargs and the last class is the type of array
+ // (e.g. String when the method is expecting String...)
+ for (int i = 0; i < classes.length; ++i) {
+ if (!isConvertible(methodArgs[i], classes[i], false)) {
+ // if we're on the last arg and the method expects an array
+ if (i == classes.length - 1 && methodArgs[i].isArray()) {
+ // check to see if the last arg is convertible
+ // to the array's component type
+ return isConvertible(methodArgs[i], classes[i], true);
+ }
+ return false;
+ }
+ }
+ } else if (methodArgs.length > 0) // more arguments given than the method accepts; check for varargs
+ {
+ // check that the last methodArg is an array
+ Class<?> lastarg = methodArgs[methodArgs.length - 1];
+ if (!lastarg.isArray()) {
+ return false;
+ }
+
+ // check that they all match up to the last method arg
+ for (int i = 0; i < methodArgs.length - 1; ++i) {
+ if (!isConvertible(methodArgs[i], classes[i], false)) {
+ return false;
+ }
+ }
+
+ // check that all remaining arguments are convertible to the vararg type
+ Class<?> vararg = lastarg.getComponentType();
+ for (int i = methodArgs.length - 1; i < classes.length; ++i) {
+ if (!isConvertible(vararg, classes[i], false)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @see isInvocationConvertible(Class, Class, boolean)
+ * @param formal the formal parameter type to which the actual
+ * parameter type should be convertible
+ * @param actual the actual parameter type.
+ * @param possibleVarArg whether or not we're dealing with the last parameter
+ * in the method declaration
+ * @return see isMethodInvocationConvertible.
+ */
+ private boolean isConvertible(Class<?> formal, Class<?> actual,
+ boolean possibleVarArg) {
+ // if we see Void.class as the class of an argument most likely
+ // obtained through a MethodKey, we consider it
+ // as a wildcard; non primitives are thus convertible.
+ if (actual.equals(Void.class) && !formal.isPrimitive()) {
+ return true;
+ }
+ return isInvocationConvertible(formal, actual, possibleVarArg);
+ }
+
+ /**
+ * @see isStrictInvocationConvertible(Class, Class, boolean)
+ * @param formal the formal parameter type to which the actual
+ * parameter type should be convertible
+ * @param actual the actual parameter type.
+ * @param possibleVarArg whether or not we're dealing with the last parameter
+ * in the method declaration
+ * @return see isStrictMethodInvocationConvertible.
+ */
+ private boolean isStrictConvertible(Class<?> formal, Class<?> actual,
+ boolean possibleVarArg) {
+ return isStrictInvocationConvertible(formal, actual, possibleVarArg);
+ }
+
+ }
+ /**
+ * Determines whether a type represented by a class object is
+ * convertible to another type represented by a class object using a
+ * method invocation conversion, treating object types of primitive
+ * types as if they were primitive types (that is, a Boolean actual
+ * parameter type matches boolean primitive formal type). This behavior
+ * is because this method is used to determine applicable methods for
+ * an actual parameter list, and primitive types are represented by
+ * their object duals in reflective method calls.
+ *
+ * @param formal the formal parameter type to which the actual
+ * parameter type should be convertible
+ * @param actual the actual parameter type.
+ * @param possibleVarArg whether or not we're dealing with the last parameter
+ * in the method declaration
+ * @return true if either formal type is assignable from actual type,
+ * or formal is a primitive type and actual is its corresponding object
+ * type or an object type of a primitive type that can be converted to
+ * the formal type.
+ */
+ public static boolean isInvocationConvertible(Class<?> formal,
+ Class<?> actual,
+ boolean possibleVarArg) {
+ /* if it's a null, it means the arg was null */
+ if (actual == null && !formal.isPrimitive()) {
+ return true;
+ }
+
+ /* Check for identity or widening reference conversion */
+ if (actual != null && formal.isAssignableFrom(actual)) {
+ return true;
+ }
+
+ // CSOFF: NeedBraces
+ /* Check for boxing with widening primitive conversion. Note that
+ * actual parameters are never primitives. */
+ if (formal.isPrimitive()) {
+ if (formal == Boolean.TYPE && actual == Boolean.class)
+ return true;
+ if (formal == Character.TYPE && actual == Character.class)
+ return true;
+ if (formal == Byte.TYPE && actual == Byte.class)
+ return true;
+ if (formal == Short.TYPE
+ && (actual == Short.class || actual == Byte.class))
+ return true;
+ if (formal == Integer.TYPE
+ && (actual == Integer.class || actual == Short.class
+ || actual == Byte.class))
+ return true;
+ if (formal == Long.TYPE
+ && (actual == Long.class || actual == Integer.class
+ || actual == Short.class || actual == Byte.class))
+ return true;
+ if (formal == Float.TYPE
+ && (actual == Float.class || actual == Long.class
+ || actual == Integer.class || actual == Short.class
+ || actual == Byte.class))
+ return true;
+ if (formal == Double.TYPE
+ && (actual == Double.class || actual == Float.class
+ || actual == Long.class || actual == Integer.class
+ || actual == Short.class || actual == Byte.class))
+ return true;
+ }
+ // CSON: NeedBraces
+
+ /* Check for vararg conversion. */
+ if (possibleVarArg && formal.isArray()) {
+ if (actual.isArray()) {
+ actual = actual.getComponentType();
+ }
+ return isInvocationConvertible(formal.getComponentType(),
+ actual, false);
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether a type represented by a class object is
+ * convertible to another type represented by a class object using a
+ * method invocation conversion, without matching object and primitive
+ * types. This method is used to determine the more specific type when
+ * comparing signatures of methods.
+ *
+ * @param formal the formal parameter type to which the actual
+ * parameter type should be convertible
+ * @param actual the actual parameter type.
+ * @param possibleVarArg whether or not we're dealing with the last parameter
+ * in the method declaration
+ * @return true if either formal type is assignable from actual type,
+ * or formal and actual are both primitive types and actual can be
+ * subject to widening conversion to formal.
+ */
+ public static boolean isStrictInvocationConvertible(Class<?> formal,
+ Class<?> actual,
+ boolean possibleVarArg) {
+ /* we shouldn't get a null into, but if so */
+ if (actual == null && !formal.isPrimitive()) {
+ return true;
+ }
+
+ /* Check for identity or widening reference conversion */
+ if (formal.isAssignableFrom(actual)) {
+ return true;
+ }
+
+ // CSOFF: NeedBraces
+ /* Check for widening primitive conversion. */
+ if (formal.isPrimitive()) {
+ if (formal == Short.TYPE && (actual == Byte.TYPE))
+ return true;
+ if (formal == Integer.TYPE
+ && (actual == Short.TYPE || actual == Byte.TYPE))
+ return true;
+ if (formal == Long.TYPE
+ && (actual == Integer.TYPE || actual == Short.TYPE
+ || actual == Byte.TYPE))
+ return true;
+ if (formal == Float.TYPE
+ && (actual == Long.TYPE || actual == Integer.TYPE
+ || actual == Short.TYPE || actual == Byte.TYPE))
+ return true;
+ if (formal == Double.TYPE
+ && (actual == Float.TYPE || actual == Long.TYPE
+ || actual == Integer.TYPE || actual == Short.TYPE
+ || actual == Byte.TYPE))
+ return true;
+ }
+ // CSON: NeedBraces
+
+ /* Check for vararg conversion. */
+ if (possibleVarArg && formal.isArray()) {
+ if (actual.isArray()) {
+ actual = actual.getComponentType();
+ }
+ return isStrictInvocationConvertible(formal.getComponentType(),
+ actual, false);
+ }
+ return false;
+ }
+
+ /**
+ * The parameter matching service for methods.
+ */
+ public static final Parameters<Method> METHODS = new Parameters<Method>() {
+ protected Class<?>[] getParameterTypes(Method app) {
+ return app.getParameterTypes();
+ }
+ };
+
+
+ /**
+ * The parameter matching service for constructors.
+ */
+ public static final Parameters<Constructor<?>> CONSTRUCTORS = new Parameters<Constructor<?>>() {
+ protected Class<?>[] getParameterTypes(Constructor<?> app) {
+ return app.getParameterTypes();
+ }
+ };
+}
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
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=799779&r1=799778&r2=799779&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 Aug 1 00:36:51 2009
@@ -20,8 +20,6 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -34,20 +32,7 @@
* @version $Id$
* @since 1.0
*/
-public class MethodMap {
- /**
- * whether a method is more specific than a previously compared one.
- */
- private static final int MORE_SPECIFIC = 0;
- /**
- * whether a method is less specific than a previously compared one.
- */
- private static final int LESS_SPECIFIC = 1;
- /**
- * A method doesn't match a previously compared one.
- */
- private static final int INCOMPARABLE = 2;
-
+final class MethodMap {
/**
* Keep track of all methods with the same name.
*/
@@ -118,26 +103,26 @@
* @param args the actual arguments with which the method is called
* @return the most specific applicable method, or null if no
* method is applicable.
- * @throws AmbiguousException if there is more than one maximally
+ * @throws MethodKey.AmbiguousException if there is more than one maximally
* specific applicable method
*/
// CSOFF: RedundantThrows
- public Method find(String methodName, Object[] args) throws AmbiguousException {
- return find(new ClassMap.MethodKey(methodName, args));
+ public Method find(String methodName, Object[] args) throws MethodKey.AmbiguousException {
+ return find(new MethodKey(methodName, args));
}
/**
* Finds a method by key.
* @param methodKey the key
* @return the method
- * @throws AmbiguousException if find is ambiguous
+ * @throws MethodKey.AmbiguousException if find is ambiguous
*/
- Method find(ClassMap.MethodKey methodKey) throws AmbiguousException {
+ Method find(MethodKey methodKey) throws MethodKey.AmbiguousException {
List<Method> methodList = get(methodKey.getMethod());
if (methodList == null) {
return null;
}
- return getMostSpecific(methodList, methodKey.getParameters());
+ return MethodKey.METHODS.getMostSpecific(methodList, methodKey.getParameters());
} // CSON: RedundantThrows
@@ -154,247 +139,4 @@
}
- // CSOFF: RedundantThrows
- /**
- * Gets the most specific method that is applicable to actual argument types.
- * @param methods a list of methods.
- * @param classes list of argument types.
- * @return the most specific method.
- * @throws AmbiguousException if there is more than one.
- */
- private static Method getMostSpecific(List<Method> methods, Class<?>[] classes)
- throws AmbiguousException {
- LinkedList<Method> applicables = getApplicables(methods, classes);
-
- if (applicables.isEmpty()) {
- return null;
- }
-
- if (applicables.size() == 1) {
- return applicables.getFirst();
- }
-
- /*
- * This list will contain the maximally specific methods. Hopefully at
- * the end of the below loop, the list will contain exactly one method,
- * (the most specific method) otherwise we have ambiguity.
- */
-
- LinkedList<Method> maximals = new LinkedList<Method>();
-
- for (Iterator<Method> applicable = applicables.iterator();
- applicable.hasNext();) {
- Method app = applicable.next();
- Class<?>[] appArgs = app.getParameterTypes();
- boolean lessSpecific = false;
-
- for (Iterator<Method> maximal = maximals.iterator();
- !lessSpecific && maximal.hasNext();) {
- Method max = maximal.next();
-
- // CSOFF: MissingSwitchDefault
- switch (moreSpecific(appArgs, max.getParameterTypes())) {
- case MORE_SPECIFIC:
- /*
- * This method is more specific than the previously
- * known maximally specific, so remove the old maximum.
- */
- maximal.remove();
- break;
-
- case LESS_SPECIFIC:
- /*
- * This method is less specific than some of the
- * currently known maximally specific methods, so we
- * won't add it into the set of maximally specific
- * methods
- */
-
- lessSpecific = true;
- break;
- }
- } // CSON: MissingSwitchDefault
-
- if (!lessSpecific) {
- maximals.addLast(app);
- }
- }
-
- if (maximals.size() > 1) {
- // We have more than one maximally specific method
- throw new AmbiguousException();
- }
-
- return maximals.getFirst();
- } // CSON: RedundantThrows
-
-
- /**
- * Determines which method signature (represented by a class array) is more
- * specific. This defines a partial ordering on the method signatures.
- *
- * @param c1 first signature to compare
- * @param c2 second signature to compare
- * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
- * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
- */
- private static int moreSpecific(Class<?>[] c1, Class<?>[] c2) {
- boolean c1MoreSpecific = false;
- boolean c2MoreSpecific = false;
-
- // compare lengths to handle comparisons where the size of the arrays
- // doesn't match, but the methods are both applicable due to the fact
- // that one is a varargs method
- if (c1.length > c2.length) {
- return MORE_SPECIFIC;
- }
- if (c2.length > c1.length) {
- return LESS_SPECIFIC;
- }
-
- // ok, move on and compare those of equal lengths
- for (int i = 0; i < c1.length; ++i) {
- if (c1[i] != c2[i]) {
- boolean last = (i == c1.length - 1);
- c1MoreSpecific = c1MoreSpecific
- || isStrictConvertible(c2[i], c1[i], last);
- c2MoreSpecific = c2MoreSpecific
- || isStrictConvertible(c1[i], c2[i], last);
- }
- }
-
- if (c1MoreSpecific) {
- if (c2MoreSpecific) {
- /*
- * Incomparable due to cross-assignable arguments (i.e.
- * foo(String, Object) vs. foo(Object, String))
- */
-
- return INCOMPARABLE;
- }
-
- return MORE_SPECIFIC;
- }
-
- if (c2MoreSpecific) {
- return LESS_SPECIFIC;
- }
-
- /*
- * Incomparable due to non-related arguments (i.e.
- * foo(Runnable) vs. foo(Serializable))
- */
-
- return INCOMPARABLE;
- }
-
- /**
- * Returns all methods that are applicable to actual argument types.
- *
- * @param methods list of all candidate methods
- * @param classes the actual types of the arguments
- * @return a list that contains only applicable methods (number of
- * formal and actual arguments matches, and argument types are assignable
- * to formal types through a method invocation conversion).
- */
- private static LinkedList<Method> getApplicables(List<Method> methods, Class<?>[] classes) {
- LinkedList<Method> list = new LinkedList<Method>();
-
- for (Iterator<Method> imethod = methods.iterator(); imethod.hasNext();) {
- Method method = imethod.next();
- if (isApplicable(method, classes)) {
- list.add(method);
- }
-
- }
- return list;
- }
-
- /**
- * Returns true if the supplied method is applicable to actual
- * argument types.
- *
- * @param method method that will be called
- * @param classes arguments to method
- * @return true if method is applicable to arguments
- */
- private static boolean isApplicable(Method method, Class<?>[] classes) {
- Class<?>[] methodArgs = method.getParameterTypes();
-
- if (methodArgs.length > classes.length) {
- // if there's just one more methodArg than class arg
- // and the last methodArg is an array, then treat it as a vararg
- return methodArgs.length == classes.length + 1
- && methodArgs[methodArgs.length - 1].isArray();
- } else if (methodArgs.length == classes.length) {
- // this will properly match when the last methodArg
- // is an array/varargs and the last class is the type of array
- // (e.g. String when the method is expecting String...)
- for (int i = 0; i < classes.length; ++i) {
- if (!isConvertible(methodArgs[i], classes[i], false)) {
- // if we're on the last arg and the method expects an array
- if (i == classes.length - 1 && methodArgs[i].isArray()) {
- // check to see if the last arg is convertible
- // to the array's component type
- return isConvertible(methodArgs[i], classes[i], true);
- }
- return false;
- }
- }
- } else if (methodArgs.length > 0) // more arguments given than the method accepts; check for varargs
- {
- // check that the last methodArg is an array
- Class<?> lastarg = methodArgs[methodArgs.length - 1];
- if (!lastarg.isArray()) {
- return false;
- }
-
- // check that they all match up to the last method arg
- for (int i = 0; i < methodArgs.length - 1; ++i) {
- if (!isConvertible(methodArgs[i], classes[i], false)) {
- return false;
- }
- }
-
- // check that all remaining arguments are convertible to the vararg type
- Class<?> vararg = lastarg.getComponentType();
- for (int i = methodArgs.length - 1; i < classes.length; ++i) {
- if (!isConvertible(vararg, classes[i], false)) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * @see IntrospectionUtils#isMethodInvocationConvertible(Class, Class, boolean)
- * @param formal the formal parameter type to which the actual
- * parameter type should be convertible
- * @param actual the actual parameter type.
- * @param possibleVarArg whether or not we're dealing with the last parameter
- * in the method declaration
- * @return see isMethodInvocationConvertible.
- */
- private static boolean isConvertible(Class<?> formal, Class<?> actual,
- boolean possibleVarArg) {
- return IntrospectionUtils.
- isMethodInvocationConvertible(formal, actual, possibleVarArg);
- }
-
- /**
- * @see IntrospectionUtils#isStrictMethodInvocationConvertible(Class, Class, boolean)
- * @param formal the formal parameter type to which the actual
- * parameter type should be convertible
- * @param actual the actual parameter type.
- * @param possibleVarArg whether or not we're dealing with the last parameter
- * in the method declaration
- * @return see isStrictMethodInvocationConvertible.
- */
- private static boolean isStrictConvertible(Class<?> formal, Class<?> actual,
- boolean possibleVarArg) {
- return IntrospectionUtils.
- isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
- }
}
\ No newline at end of file
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=799779&r1=799778&r2=799779&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 Aug 1 00:36:51 2009
@@ -18,6 +18,7 @@
package org.apache.commons.jexl.util.introspection;
import java.util.Iterator;
+import java.lang.reflect.Constructor;
/**
* 'Federated' introspection/reflection interface to allow the introspection
@@ -28,11 +29,6 @@
* @version $Id$
*/
public interface Uberspect {
- /**
- * Initializer - will be called before use.
- */
- void init();
-
/** Gets underlying introspector.
* @return the introspector
*/
@@ -44,9 +40,17 @@
* @param obj to get the iterator for.
* @return an iterator over obj.
*/
- Iterator<?> getIterator(Object obj, Info info);
+ Iterator<?> getIterator(Object obj, DebugInfo info);
/**
+ * Returns a class constructor.
+ * @param ctorHandle a class or class name
+ * @param args constructor arguments
+ * @param info template info
+ * @return a {@link Constructor}.
+ */
+ Constructor<?> getConstructor(Object ctorHandle, Object[] args, DebugInfo info);
+ /**
* Returns a general method, corresponding to $foo.bar( $woogie ).
* @param obj the object
* @param method the method name
@@ -54,7 +58,7 @@
* @param info template info
* @return a {@link VelMethod}.
*/
- VelMethod getMethod(Object obj, String method, Object[] args, Info info);
+ VelMethod getMethod(Object obj, String method, Object[] args, DebugInfo info);
/**
* Property getter - returns VelPropertyGet appropos for #set($foo =
@@ -64,7 +68,7 @@
* @param info template info
* @return a {@link VelPropertyGet}.
*/
- VelPropertyGet getPropertyGet(Object obj, String identifier, Info info);
+ VelPropertyGet getPropertyGet(Object obj, Object identifier, DebugInfo info);
/**
* Property setter - returns VelPropertySet appropos for #set($foo.bar =
@@ -75,5 +79,5 @@
* @param info template info
* @return a {@link VelPropertySet}.
*/
- VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info info);
+ VelPropertySet getPropertySet(Object obj, Object identifier, Object arg, DebugInfo info);
}
\ No newline at end of file
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=799779&r1=799778&r2=799779&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 Aug 1 00:36:51 2009
@@ -17,77 +17,55 @@
package org.apache.commons.jexl.util.introspection;
-import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
-import org.apache.commons.jexl.util.AbstractExecutor;
import org.apache.commons.jexl.util.ArrayIterator;
-import org.apache.commons.jexl.util.ArrayListWrapper;
-import org.apache.commons.jexl.util.BooleanPropertyExecutor;
import org.apache.commons.jexl.util.EnumerationIterator;
-import org.apache.commons.jexl.util.GetExecutor;
-import org.apache.commons.jexl.util.PropertyExecutor;
-import org.apache.commons.jexl.util.MapGetExecutor;
import org.apache.commons.logging.Log;
/**
* Implementation of Uberspect to provide the default introspective
- * functionality of Velocity.
+ * functionality of JEXL.
+ * <p>This is the class to derive to customize introspection.</p>
*
* @since 1.0
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
* @version $Id$
*/
-public class UberspectImpl implements Uberspect, UberspectLoggable {
- /**
- * Index of the first character of the property.
- */
- private static final int PROPERTY_START_INDEX = 3;
- /**
- * 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.
- */
- private Introspector introspector;
-
+public class UberspectImpl extends org.apache.commons.jexl.util.Introspector implements Uberspect {
/** {@inheritDoc} */
public Introspector getIntrospector() {
return introspector;
}
+
/**
- * init - does nothing - we need to have setRuntimeLogger called before
- * getting our introspector, as the default vel introspector depends upon
- * it.
+ * Creates a new UberspectImpl.
+ * @param runtimeLogger the logger used for all logging needs
*/
- public void init() {
+ public UberspectImpl(Log runtimeLogger) {
+ this(runtimeLogger, new Introspector(runtimeLogger));
}
/**
- * Sets the runtime logger - this must be called before anything else
- * besides init() as to get the logger. Makes the pull model appealing...
- * @param runtimeLogger service to use for logging.
+ * Creates a new UberspectImpl.
+ * @param runtimeLogger the logger used for all logging needs
+ * @param intro the introspector to use
*/
- public void setRuntimeLogger(Log runtimeLogger) {
- rlog = runtimeLogger;
- introspector = new Introspector(rlog);
+ public UberspectImpl(Log runtimeLogger, Introspector intro) {
+ super(runtimeLogger, intro);
}
/**
* {@inheritDoc}
*/
- public Iterator<?> getIterator(Object obj, Info i) {
+ public Iterator<?> getIterator(Object obj, DebugInfo i) {
if (obj.getClass().isArray()) {
return new ArrayIterator(obj);
} else if (obj instanceof Collection<?>) {
@@ -95,18 +73,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() + ","
- + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
- + " if used in more than once, this may lead to" + " unexpected results.");
+ rlog.warn(i.debugString()
+ + "The iterative is not resetable; if used 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() + ","
- + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
- + " if used in more than once, this may lead to" + " unexpected results.");
-
+ rlog.warn(i.debugString()
+ + "The iterative is not resetable; if used more than once, "
+ + "this may lead to unexpected results.");
return new EnumerationIterator<Object>((Enumeration<Object>) obj);
} else {
// look for an iterator() method to support the JDK5 Iterable
@@ -119,12 +94,12 @@
if (Iterator.class.isAssignableFrom(returns)) {
return (Iterator<?>) iter.invoke(obj, (Object[])null);
} else {
- rlog.error("iterator() method of reference in #foreach loop at "
- + i + " does not return a true Iterator.");
+ rlog.error(i.debugString()
+ + "iterator() method does not return a true Iterator.");
}
- // CSOFF: EmptyBlock
+ // CSOFF: EmptyBlock
} catch (NoSuchMethodException nsme) {
- // eat this one, but let all other exceptions thru
+ // eat this one, but let all other exceptions thru
} catch (IllegalArgumentException e) { // CSON: EmptyBlock
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
@@ -135,8 +110,8 @@
}
/* we have no clue what this is */
- rlog.warn("Could not determine type of iterator in " + "#foreach loop " + " at [" + i.getLine() + ","
- + i.getColumn() + "]" + " in template " + i.getTemplateName());
+ rlog.warn(i.toString()
+ + "Could not determine type of iterator");
return null;
}
@@ -144,382 +119,28 @@
/**
* {@inheritDoc}
*/
- public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) {
- if (obj == null) {
- return null;
- }
-
- Method m = introspector.getMethod(obj.getClass(), methodName, args);
- if (m != null) {
- return new VelMethodImpl(m);
- } else if (obj.getClass().isArray()) {
- // check for support via our array->list wrapper
- m = introspector.getMethod(ArrayListWrapper.class, methodName, args);
- if (m != null) {
- // and create a method that knows to wrap the value
- // before invoking the method
- return new VelMethodImpl(m, true);
- }
- } else if (obj instanceof Class<?>) {
- m = introspector.getMethod((Class<?>) obj, methodName, args);
- }
-
- return (m == null) ? null : new VelMethodImpl(m);
- }
+ public Constructor<?> getConstructor(Object ctorHandle, Object[] args, DebugInfo i) {
+ return getConstructor(ctorHandle, args);
+ }
/**
* {@inheritDoc}
*/
- public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) {
- AbstractExecutor executor;
-
- Class<?> claz = obj.getClass();
-
- /*
- * first try for a getFoo() type of property (also getfoo() )
- */
-
- executor = new PropertyExecutor(rlog, introspector, claz, identifier);
-
- /*
- * Let's see if we are a map...
- */
- if (!executor.isAlive()) {
- executor = new MapGetExecutor(rlog, claz, identifier);
- }
-
- /*
- * look for boolean isFoo()
- */
- if (!executor.isAlive()) {
- executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
- }
-
- /*
- * if that didn't work, look for get("foo")
- */
-
- if (!executor.isAlive()) {
- executor = new GetExecutor(rlog, introspector, claz, identifier);
- }
-
- return new VelGetterImpl(executor);
+ public VelMethod getMethod(Object obj, String methodName, Object[] args, DebugInfo i) {
+ return getMethodExecutor(obj, methodName, args);
}
/**
* {@inheritDoc}
*/
- public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) {
- Class<?> claz = obj.getClass();
-
- VelMethod vm = null;
- try {
- /*
- * first, we introspect for the set<identifier> setter method
- */
-
- Object[] params = {arg};
-
- try {
- vm = getMethod(obj, "set" + identifier, params, i);
-
- if (vm == null) {
- throw new NoSuchMethodException();
- }
- } catch (NoSuchMethodException nsme2) {
- StringBuilder sb = new StringBuilder("set");
- sb.append(identifier);
-
- if (Character.isLowerCase(sb.charAt(PROPERTY_START_INDEX))) {
- sb.setCharAt(PROPERTY_START_INDEX, Character.toUpperCase(sb.charAt(PROPERTY_START_INDEX)));
- } else {
- sb.setCharAt(PROPERTY_START_INDEX, Character.toLowerCase(sb.charAt(PROPERTY_START_INDEX)));
- }
-
- vm = getMethod(obj, sb.toString(), params, i);
-
- if (vm == null) {
- throw new NoSuchMethodException();
- }
- }
- } catch (NoSuchMethodException nsme) {
- /*
- * right now, we only support the Map interface
- */
-
- if (Map.class.isAssignableFrom(claz)) {
- vm = getMethod(obj, "put", OBJECT_OBJECT, i);
- if (vm != null) {
- return new VelSetterImpl(vm, identifier);
- }
- }
- }
-
- return (vm == null) ? null : new VelSetterImpl(vm);
- }
-
- /**
- * An implementation of {@link VelMethod}.
- * CSOFF: VisibilityModifier
- */
- public static class VelMethodImpl implements VelMethod {
- /** the method to run. */
- final Method method;
- /** whether the method is a vararg method. */
- Boolean isVarArg;
- /** whether the method is to wrap an array so that get(index),
- * set(index) etc can be called on it.
- */
- final boolean wrapArray;
-
- /**
- * Create a new instance.
- *
- * @param m the method.
- */
- public VelMethodImpl(Method m) {
- this(m, false);
- }
-
- // CSOFF: HiddenField
- /**
- * Create a new instance.
- * @param method the method.
- * @param wrapArray whether this wraps an array.
- */
- public VelMethodImpl(Method method, boolean wrapArray) {
- this.method = method;
- this.wrapArray = wrapArray;
- // CSON: HiddenField
- }
-
- /**
- * {@inheritDoc}
- */
- public Object invoke(Object o, Object[] params) throws Exception {
- if (isVarArg()) {
- Class<?>[] formal = method.getParameterTypes();
- int index = formal.length - 1;
- Class<?> type = formal[index].getComponentType();
- if (params.length >= index) {
- params = handleVarArg(type, index, params);
- }
- } else if (wrapArray) {
- o = new ArrayListWrapper(o);
- }
-
- try {
- return method.invoke(o, params);
- } catch (InvocationTargetException e) {
- final Throwable t = e.getTargetException();
-
- if (t instanceof Exception) {
- throw (Exception) t;
- } else if (t instanceof Error) {
- throw (Error) t;
- } else {
- throw e;
- }
- }
- }
-
- /**
- * @return true if this method can accept a variable number of arguments
- */
- public boolean isVarArg() {
- if (isVarArg == null) {
- Class<?>[] formal = method.getParameterTypes();
- if (formal == null || formal.length == 0) {
- this.isVarArg = Boolean.FALSE;
- } else {
- Class<?> last = formal[formal.length - 1];
- // if the last arg is an array, then
- // we consider this a varargs method
- this.isVarArg = Boolean.valueOf(last.isArray());
- }
- }
- return isVarArg.booleanValue();
- }
-
- /**
- * @param type The vararg class type (aka component type
- * of the expected array arg)
- * @param index The index of the vararg in the method declaration
- * (This will always be one less than the number of
- * expected arguments.)
- * @param actual The actual parameters being passed to this method
- * @return The actual parameters adjusted for the varargs in order
- * to fit the method declaration.
- */
- private Object[] handleVarArg(Class<?> type, int index, Object[] actual) {
- // if no values are being passed into the vararg
- if (actual.length == index) {
- // create an empty array of the expected type
- actual = new Object[]{Array.newInstance(type, 0)};
- } else if (actual.length == index + 1) {
- // if one value is being passed into the vararg
- // make sure the last arg is an array of the expected type
- if (IntrospectionUtils.isMethodInvocationConvertible(type,
- actual[index].getClass(),
- false)) {
- // create a 1-length array to hold and replace the last param
- Object lastActual = Array.newInstance(type, 1);
- Array.set(lastActual, 0, actual[index]);
- actual[index] = lastActual;
- }
- } else if (actual.length > index + 1) {
- // if multiple values are being passed into the vararg
- // put the last and extra actual in an array of the expected type
- int size = actual.length - index;
- Object lastActual = Array.newInstance(type, size);
- for (int i = 0; i < size; i++) {
- Array.set(lastActual, i, actual[index + i]);
- }
-
- // put all into a new actual array of the appropriate size
- Object[] newActual = new Object[index + 1];
- for (int i = 0; i < index; i++) {
- newActual[i] = actual[i];
- }
- newActual[index] = lastActual;
-
- // replace the old actual array
- actual = newActual;
- }
- return actual;
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isCacheable() {
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- public String getMethodName() {
- return method.getName();
- }
-
- /**
- * {@inheritDoc}
- */
- public Class<?> getReturnType() {
- return method.getReturnType();
- }
- } // CSON: VisibilityModifier
-
-
- /**
- * Concrete instance of VelPropertyGet.
- */
- public static class VelGetterImpl implements VelPropertyGet {
- /**
- * executor for performing the get.
- */
- protected final AbstractExecutor ae;
-
- /**
- * Create the getter using an {@link AbstractExecutor} to
- * do the work.
- *
- * @param exec the executor.
- */
- public VelGetterImpl(AbstractExecutor exec) {
- ae = exec;
- }
-
- /**
- * {@inheritDoc}
- */
- public Object invoke(Object o) throws Exception {
- return ae.execute(o);
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isCacheable() {
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isAlive() {
- return ae.isAlive();
- }
-
- /**
- * {@inheritDoc}
- */
- public String getMethodName() {
- return ae.getMethod().getName();
- }
+ public VelPropertyGet getPropertyGet(Object obj, Object identifier, DebugInfo i) {
+ return getGetExecutor(obj, identifier);
}
/**
- * Concrete instance of VelPropertySet.
+ * {@inheritDoc}
*/
- public static class VelSetterImpl implements VelPropertySet {
- /**
- * the method to call.
- */
- protected VelMethod vm = null;
- /**
- * the key for indexed and other properties.
- */
- protected String putKey = null;
-
- /**
- * Create an instance.
- *
- * @param velmethod the method to call on set.
- */
- public VelSetterImpl(VelMethod velmethod) {
- this.vm = velmethod;
- }
-
- /**
- * Create an instance.
- *
- * @param velmethod the method to call on set.
- * @param key the index or other value passed to a
- * setProperty(xxx, value) method.
- */
- public VelSetterImpl(VelMethod velmethod, String key) {
- this.vm = velmethod;
- putKey = key;
- }
-
- /**
- * {@inheritDoc}
- */
- public Object invoke(Object o, Object value) throws Exception {
- if (putKey == null) {
- Object[] a0 = { value };
- return vm.invoke(o, a0);
- } else {
- Object[] a1 = { putKey, value };
- return vm.invoke(o, a1);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isCacheable() {
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- public String getMethodName() {
- return vm.getMethodName();
- }
-
+ public VelPropertySet getPropertySet(final Object obj, final Object identifier, Object arg, DebugInfo i) {
+ return getSetExecutor(obj, identifier, arg);
}
}
\ No newline at end of file
Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html Sat Aug 1 00:36:51 2009
@@ -0,0 +1,39 @@
+<html>
+ <head>
+ <title>Package Documentation for org.apache.commons.jexl.introspection Package</title>
+ </head>
+ <body bgcolor="white">
+ Provides low & high-level introspective services.
+ <br><br>
+ <p>
+ <ul>
+ <li><a href="#low">Low level</a></li>
+ <li><a href="#high">High level</a></li>
+ </ul>
+ </p>
+ <h2><a name="low">Low level</a></h2>
+ <p>
+ The IntrospectorBase, Introspector, ClassMap, IntrospectionUtils form the
+ base of the introspection service. They allow to describe classes and their
+ methods, keeping them in a cache (@see Introspector) to speed up property
+ getters/setters and method discovery used during expression evaluation.
+ </p>
+ <p>
+ The cache materialized in Introspector creates one entry per class containing a map of all
+ accessible public methods keyed by name and signature.
+ </p>
+
+ <h2><a name="high">High level</a></h2>
+
+ <p>
+ The Uberspect, VelMethod, VelPropertyGet and VelPropertySet interfaces
+ form the exposed face of introspective services.
+ </p>
+ <p>
+ The Uberspectimpl is the concrete class implementing the Uberspect interface.
+ Deriving from this class is the preferred way of augmenting Jexl introspective
+ capabilities when special needs to be fullfilled or when default behaviors
+ need to be modified.
+ </p>
+</body>
+</html>
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
------------------------------------------------------------------------------
svn:mime-type = text/html
Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html Sat Aug 1 00:36:51 2009
@@ -0,0 +1,17 @@
+<html>
+ <head>
+ <title>Package Documentation for org.apache.commons.jexl Package</title>
+ </head>
+ <body bgcolor="white">
+ Provides utilities for introspection services.
+ <br><br>
+ <p>
+ This set of classes implement the various forms of setters and getters
+ used by Jexl. These are specialized forms for 'pure' properties, discovering
+ methods of the {s,g}etProperty form, for Maps, Lists and Duck-typing -
+ attempting to discover a 'get' or 'put' method, making an object walk and
+ quack.
+ </p>
+
+</body>
+</html>
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
------------------------------------------------------------------------------
svn:mime-type = text/html