You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by di...@apache.org on 2007/10/28 09:19:16 UTC
svn commit: r589299 -
/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
Author: dion
Date: Sun Oct 28 01:19:15 2007
New Revision: 589299
URL: http://svn.apache.org/viewvc?rev=589299&view=rev
Log:
JEXL-25 bring across code from Peters update
Modified:
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
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=589299&r1=589298&r2=589299&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 Sun Oct 28 01:19:15 2007
@@ -17,21 +17,24 @@
package org.apache.commons.jexl.util.introspection;
+import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
-import java.util.Map;
import java.util.List;
+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;
/**
@@ -40,10 +43,13 @@
*
* @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. */
+ /**
+ * index of the first character of the property.
+ */
private static final int PROPERTY_START_INDEX = 3;
/**
@@ -86,21 +92,45 @@
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.");
+ + 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() + ","
- + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
- + " if used in more than once, this may lead to" + " unexpected results.");
+ + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
+ + " if used in more than once, this may lead to" + " unexpected results.");
return new EnumerationIterator((Enumeration) obj);
+ } else {
+ // look for an iterator() method to support the JDK5 Iterable
+ // interface or any user tools/DTOs that want to work in
+ // foreach without implementing the Collection interface
+ Class type = obj.getClass();
+ try {
+ Method iter = type.getMethod("iterator", null);
+ Class returns = iter.getReturnType();
+ if (Iterator.class.isAssignableFrom(returns)) {
+ return (Iterator) iter.invoke(obj, null);
+ } else {
+ rlog.error("iterator() method of reference in #foreach loop at "
+ + i + " does not return a true Iterator.");
+ }
+ // CSOFF: EmptyBlock
+ } catch (NoSuchMethodException nsme) {
+ // eat this one, but let all other exceptions thru
+ } catch (IllegalArgumentException e) { // CSON: EmptyBlock
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
}
- /* we have no clue what this is */
+ /* 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());
+ + i.getColumn() + "]" + " in template " + i.getTemplateName());
return null;
}
@@ -114,7 +144,17 @@
}
Method m = introspector.getMethod(obj.getClass(), methodName, args);
- if (m == null && obj instanceof Class) {
+ 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);
}
@@ -136,9 +176,15 @@
executor = new PropertyExecutor(rlog, introspector, claz, identifier);
/*
- * look for boolean isFoo()
+ * 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);
}
@@ -157,7 +203,7 @@
/**
* {@inheritDoc}
*/
- public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) {
+ public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) {
Class claz = obj.getClass();
VelMethod vm = null;
@@ -211,23 +257,54 @@
/**
* An implementation of {@link VelMethod}.
+ * CSOFF: VisibilityModifier
*/
- public class VelMethodImpl implements VelMethod {
- /** the method. */
- protected Method method = null;
+ 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) {
- 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) {
@@ -244,6 +321,72 @@
}
/**
+ * @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() {
@@ -263,18 +406,21 @@
public Class getReturnType() {
return method.getReturnType();
}
- }
+ } // CSON: VisibilityModifier
/**
* {@inheritDoc}
*/
public static class VelGetterImpl implements VelPropertyGet {
- /** executor for performing the get. */
- protected AbstractExecutor ae = null;
+ /**
+ * 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) {
@@ -313,14 +459,19 @@
/**
* {@inheritDoc}
*/
- public class VelSetterImpl implements VelPropertySet {
- /** the method to call. */
+ public static class VelSetterImpl implements VelPropertySet {
+ /**
+ * the method to call.
+ */
protected VelMethod vm = null;
- /** the key for indexed and other properties. */
+ /**
+ * 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) {
@@ -329,16 +480,19 @@
/**
* 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.
+ * @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} */
+ /**
+ * {@inheritDoc}
+ */
public Object invoke(Object o, Object value) throws Exception {
List al = new ArrayList();
@@ -352,12 +506,16 @@
return vm.invoke(o, al.toArray());
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public boolean isCacheable() {
return true;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public String getMethodName() {
return vm.getMethodName();
}