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