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: [email protected]
For additional commands, e-mail: [email protected]