Author: markt
Date: Fri Jul  4 18:47:02 2014
New Revision: 1607906

URL: http://svn.apache.org/r1607906
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=56652
Add support for method parameters that use arrays and varargs to 
ELProcessor.defineFunction()

Modified:
    tomcat/trunk/java/javax/el/ELProcessor.java
    tomcat/trunk/java/org/apache/el/lang/ELSupport.java
    tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java
    tomcat/trunk/java/org/apache/el/parser/AstFunction.java
    tomcat/trunk/test/javax/el/TestELProcessor.java
    tomcat/trunk/test/javax/el/TesterFunctions.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/javax/el/ELProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/javax/el/ELProcessor.java?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/java/javax/el/ELProcessor.java (original)
+++ tomcat/trunk/java/javax/el/ELProcessor.java Fri Jul  4 18:47:02 2014
@@ -134,7 +134,17 @@ public class ELProcessor {
                     if (types.length == typeNames.length) {
                         boolean match = true;
                         for (int i = 0; i < types.length; i++) {
-                            if (!types[i].getName().equals(typeNames[i])) {
+                            if (i == types.length -1 && method.isVarArgs()) {
+                                String typeName = typeNames[i];
+                                if (typeName.endsWith("...")) {
+                                    typeName = typeName.substring(0, 
typeName.length() - 3);
+                                    if (!typeName.equals(types[i].getName())) {
+                                        match = false;
+                                    }
+                                } else {
+                                    match = false;
+                                }
+                            } else if 
(!types[i].getName().equals(typeNames[i])) {
                                 match = false;
                                 break;
                             }
@@ -235,7 +245,58 @@ public class ELProcessor {
                     ImportHandler importHandler = context.getImportHandler();
                     for (int i = 0; i < parameterTypeNames.length; i++) {
                         String parameterTypeName = 
parameterTypeNames[i].trim();
-                        if (!PRIMITIVES.contains(parameterTypeName) &&
+                        int dimension = 0;
+                        int bracketPos = parameterTypeName.indexOf('[');
+                        if (bracketPos > -1) {
+                            String parameterTypeNameOnly =
+                                    parameterTypeName.substring(0, 
bracketPos).trim();
+                            while (bracketPos > -1) {
+                                dimension++;
+                                bracketPos = parameterTypeName.indexOf('[', 
bracketPos+ 1);
+                            }
+                            parameterTypeName = parameterTypeNameOnly;
+                        }
+                        boolean varArgs = false;
+                        if (parameterTypeName.endsWith("...")) {
+                            varArgs = true;
+                            dimension = 1;
+                            parameterTypeName = parameterTypeName.substring(
+                                    0, parameterTypeName.length() -3);
+                        }
+                        boolean isPrimitive = 
PRIMITIVES.contains(parameterTypeName);
+                        if (isPrimitive && dimension > 0) {
+                            // When in an array, class name changes for 
primitive
+                            switch(parameterTypeName)
+                            {
+                                case "boolean":
+                                    parameterTypeName = "Z";
+                                    break;
+                                case "byte":
+                                    parameterTypeName = "B";
+                                    break;
+                                case "char":
+                                    parameterTypeName = "C";
+                                    break;
+                                case "double":
+                                    parameterTypeName = "D";
+                                    break;
+                                case "float":
+                                    parameterTypeName = "F";
+                                    break;
+                                case "int":
+                                    parameterTypeName = "I";
+                                    break;
+                                case "long":
+                                    parameterTypeName = "J";
+                                    break;
+                                case "short":
+                                    parameterTypeName = "S";
+                                    break;
+                                default:
+                                    // Should never happen
+                                    break;
+                            }
+                        } else  if (!isPrimitive &&
                                 !parameterTypeName.contains(".")) {
                             Class<?> clazz = importHandler.resolveClass(
                                     parameterTypeName);
@@ -246,8 +307,27 @@ public class ELProcessor {
                                         parameterTypeNames[i], methodName,
                                         className));
                             }
-                            parameterTypeNames[i] = clazz.getName();
+                            parameterTypeName = clazz.getName();
+                        }
+                        if (dimension > 0) {
+                            // Convert to array form of class name
+                            StringBuilder sb = new StringBuilder();
+                            for (int j = 0; j < dimension; j++) {
+                                sb.append('[');
+                            }
+                            if (!isPrimitive) {
+                                sb.append('L');
+                            }
+                            sb.append(parameterTypeName);
+                            if (!isPrimitive) {
+                                sb.append(';');
+                            }
+                            parameterTypeName = sb.toString();
+                        }
+                        if (varArgs) {
+                            parameterTypeName += "...";
                         }
+                        parameterTypeNames[i] = parameterTypeName;
                     }
                 }
             }

Modified: tomcat/trunk/java/org/apache/el/lang/ELSupport.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/lang/ELSupport.java?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/el/lang/ELSupport.java (original)
+++ tomcat/trunk/java/org/apache/el/lang/ELSupport.java Fri Jul  4 18:47:02 2014
@@ -19,6 +19,7 @@ package org.apache.el.lang;
 
 import java.beans.PropertyEditor;
 import java.beans.PropertyEditorManager;
+import java.lang.reflect.Array;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.security.AccessController;
@@ -478,10 +479,34 @@ public class ELSupport {
             return Collections.EMPTY_MAP;
         }
 
+        // Handle arrays
+        if (type.isArray()) {
+            return coerceToArray(obj, type);
+        }
+
         throw new ELException(MessageFactory.get("error.convert",
                 obj, obj.getClass(), type));
     }
 
+    private static Object coerceToArray(final Object obj,
+            final Class<?> type) {
+        // Note: Nested arrays will result in nested calls to this method.
+
+        // Cast the input object to an array (calling method has checked it is
+        // an array)
+        Object[] array = (Object[]) obj;
+        // Get the target type for the array elements
+        Class<?> componentType = type.getComponentType();
+        // Create a new array of the correct type
+        Object result = Array.newInstance(componentType, array.length);
+        // Coerce each element in turn.
+        for (int i = 0; i < array.length; i++) {
+            Array.set(result, i, coerceToType(array[i], componentType));
+        }
+
+        return result;
+    }
+
     public static final boolean isBigDecimalOp(final Object obj0,
             final Object obj1) {
         return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);

Modified: tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java (original)
+++ tomcat/trunk/java/org/apache/el/lang/ExpressionBuilder.java Fri Jul  4 
18:47:02 2014
@@ -210,12 +210,14 @@ public final class ExpressionBuilder imp
                         "error.fnMapper.method", funcNode.getOutputName()));
             }
 
-            int pcnt = m.getParameterTypes().length;
+            int methodParameterCount = m.getParameterTypes().length;
             // AstFunction->MethodParameters->Parameters()
-            if (node.jjtGetChild(0).jjtGetNumChildren() != pcnt) {
+            int inputParameterCount = node.jjtGetChild(0).jjtGetNumChildren();
+            if (m.isVarArgs() && inputParameterCount < methodParameterCount - 
1 ||
+                    !m.isVarArgs() && inputParameterCount != 
methodParameterCount) {
                 throw new ELException(MessageFactory.get(
                         "error.fnMapper.paramcount", funcNode.getOutputName(),
-                        "" + pcnt, "" + node.jjtGetNumChildren()));
+                        "" + methodParameterCount, "" + 
node.jjtGetChild(0).jjtGetNumChildren()));
             }
         } else if (node instanceof AstIdentifier && this.varMapper != null) {
             String variable = ((AstIdentifier) node).getImage();

Modified: tomcat/trunk/java/org/apache/el/parser/AstFunction.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/el/parser/AstFunction.java?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/el/parser/AstFunction.java (original)
+++ tomcat/trunk/java/org/apache/el/parser/AstFunction.java Fri Jul  4 18:47:02 
2014
@@ -161,13 +161,28 @@ public final class AstFunction extends S
         Class<?>[] paramTypes = m.getParameterTypes();
         Object[] params = null;
         Object result = null;
-        int numParams = parameters.jjtGetNumChildren();
-        if (numParams > 0) {
-            params = new Object[numParams];
+        int inputParameterCount = parameters.jjtGetNumChildren();
+        int methodParameterCount = paramTypes.length;
+        if (inputParameterCount > 0) {
+            params = new Object[methodParameterCount];
             try {
-                for (int i = 0; i < numParams; i++) {
-                    params[i] = parameters.jjtGetChild(i).getValue(ctx);
-                    params[i] = coerceToType(params[i], paramTypes[i]);
+                for (int i = 0; i < methodParameterCount; i++) {
+                    if (m.isVarArgs() && i == methodParameterCount - 1) {
+                        if (inputParameterCount < methodParameterCount) {
+                            params[i] = null;
+                        } else {
+                            Object[] varargs =
+                                    new Object[inputParameterCount - 
methodParameterCount + 1];
+                            Class<?> target = paramTypes[i].getComponentType();
+                            for (int j = i; j < inputParameterCount; j++) {
+                                varargs[j-i] = 
parameters.jjtGetChild(j).getValue(ctx);
+                                varargs[j-i] = coerceToType(varargs[j-i], 
target);
+                            }
+                        }
+                    } else {
+                        params[i] = parameters.jjtGetChild(i).getValue(ctx);
+                        params[i] = coerceToType(params[i], paramTypes[i]);
+                    }
                 }
             } catch (ELException ele) {
                 throw new ELException(MessageFactory.get("error.function", this

Modified: tomcat/trunk/test/javax/el/TestELProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/javax/el/TestELProcessor.java?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/test/javax/el/TestELProcessor.java (original)
+++ tomcat/trunk/test/javax/el/TestELProcessor.java Fri Jul  4 18:47:02 2014
@@ -129,4 +129,66 @@ public class TestELProcessor {
         elp.eval("fn:doIt(5)");
         Assert.assertEquals("B", TesterFunctions.getCallList());
     }
+
+
+    @Test
+    public void testDefineFunctionName08() throws Exception {
+        TesterFunctions.resetCallList();
+        ELProcessor elp = new ELProcessor();
+        elp.defineFunction("fn", "", "javax.el.TesterFunctions", "void 
doIt(int[])");
+        elp.eval("fn:doIt([5].stream().toArray())");
+        Assert.assertEquals("D", TesterFunctions.getCallList());
+    }
+
+
+    @Test
+    public void testDefineFunctionName09() throws Exception {
+        TesterFunctions.resetCallList();
+        ELProcessor elp = new ELProcessor();
+        elp.defineFunction("fn", "", "javax.el.TesterFunctions", "void 
doIt(int[][])");
+        elp.eval("fn:doIt([[5].stream().toArray()].stream().toArray())");
+        Assert.assertEquals("E", TesterFunctions.getCallList());
+    }
+
+
+    @Test
+    public void testDefineFunctionName10() throws Exception {
+        TesterFunctions.resetCallList();
+        ELProcessor elp = new ELProcessor();
+        elp.defineFunction("fn", "test1", "java.lang.Integer", "Integer 
valueOf(int)");
+        elp.defineFunction("fn", "test2", "javax.el.TesterFunctions", "void 
doIt(Integer[])");
+        elp.eval("fn:test2([fn:test1(1), fn:test1(2)].stream().toArray())");
+        Assert.assertEquals("F", TesterFunctions.getCallList());
+    }
+
+
+    @Test
+    public void testDefineFunctionName11() throws Exception {
+        TesterFunctions.resetCallList();
+        ELProcessor elp = new ELProcessor();
+        elp.defineFunction("fn", "test1", "java.lang.Integer", "Integer 
valueOf(int)");
+        elp.defineFunction("fn", "test2", "javax.el.TesterFunctions", "void 
doIt(Integer[][])");
+        elp.eval("fn:test2([[fn:test1(1), 
fn:test1(2)].stream().toArray()].stream().toArray())");
+        Assert.assertEquals("G", TesterFunctions.getCallList());
+    }
+
+
+    @Test
+    public void testDefineFunctionName12() throws Exception {
+        TesterFunctions.resetCallList();
+        ELProcessor elp = new ELProcessor();
+        elp.defineFunction("fn", "test", "javax.el.TesterFunctions", "void 
doIt(long...)");
+        elp.eval("fn:test(1,2)");
+        Assert.assertEquals("H", TesterFunctions.getCallList());
+    }
+
+
+    @Test
+    public void testDefineFunctionName13() throws Exception {
+        TesterFunctions.resetCallList();
+        ELProcessor elp = new ELProcessor();
+        elp.defineFunction("fn", "test", "javax.el.TesterFunctions", "void 
doIt(Object...)");
+        elp.eval("fn:test(null, null)");
+        Assert.assertEquals("I", TesterFunctions.getCallList());
+    }
 }

Modified: tomcat/trunk/test/javax/el/TesterFunctions.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/javax/el/TesterFunctions.java?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/test/javax/el/TesterFunctions.java (original)
+++ tomcat/trunk/test/javax/el/TesterFunctions.java Fri Jul  4 18:47:02 2014
@@ -39,4 +39,28 @@ public class TesterFunctions {
     public static void doIt(@SuppressWarnings("unused") Integer a) {
         calls.append('C');
     }
+
+    public static void doIt(@SuppressWarnings("unused") int[] a) {
+        calls.append('D');
+    }
+
+    public static void doIt(@SuppressWarnings("unused") int[][] a) {
+        calls.append('E');
+    }
+
+    public static void doIt(@SuppressWarnings("unused") Integer[] a) {
+        calls.append('F');
+    }
+
+    public static void doIt(@SuppressWarnings("unused") Integer[][] a) {
+        calls.append('G');
+    }
+
+    public static void doIt(@SuppressWarnings("unused") long... a) {
+        calls.append('H');
+    }
+
+    public static void doIt(@SuppressWarnings("unused") Object... a) {
+        calls.append('I');
+    }
 }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1607906&r1=1607905&r2=1607906&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Fri Jul  4 18:47:02 2014
@@ -123,6 +123,10 @@
       <fix>
         <bug>56543</bug>: Update to the Eclipse JDT Compiler 4.4. (violetagg)
       </fix>
+      <fix>
+        <bug>56652</bug>: Add support for method parameters that use arrays and
+        varargs to <code>ELProcessor.defineFunction()</code>.(markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="WebSocket">



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to