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();
         }