Skip to content

Commit 31f42ac

Browse files
committed
Issue #11: Support of static functions with varargs
Validating vararg-functions allow dynamic parameter count and invoking vararg-functions uses the existing parameter-resolution which already supports varargs. Also fixed an NPE in ReflectionUtil when providing null as vararg- parameter.
1 parent 13cf093 commit 31f42ac

File tree

5 files changed

+81
-8
lines changed

5 files changed

+81
-8
lines changed

src/main/java/org/glassfish/expressly/Messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ error.property.notfound=Property ''{1}'' not found on {0}
5151
error.fnMapper.null=Expression uses functions, but no FunctionMapper was provided
5252
error.fnMapper.method=Function ''{0}'' not found
5353
error.fnMapper.paramcount=Function ''{0}'' specifies {1} params, but {2} were supplied
54+
error.fnMapper.minparamcount=Function ''{0}'' requires at least {1} params, but {2} were supplied
5455

5556
# **ExpressionImpl
5657
error.context.null=ELContext was null

src/main/java/org/glassfish/expressly/lang/ExpressionBuilder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,12 @@ public void visit(Node node) throws ELException {
240240

241241
int parameterCount = functionMethod.getParameterCount();
242242
int argumentCount = ((AstMethodArguments) node.jjtGetChild(0)).getParameterCount();
243-
if (argumentCount != parameterCount) {
243+
if(functionMethod.isVarArgs()) {
244+
// last param of the method is the vararg -> 0..n vararg-arguments allowed
245+
if (argumentCount < parameterCount - 1) {
246+
throw new ELException(MessageFactory.get("error.fnMapper.minparamcount", funcNode.getOutputName(), parameterCount - 1, argumentCount));
247+
}
248+
} else if (argumentCount != parameterCount) {
244249
throw new ELException(MessageFactory.get("error.fnMapper.paramcount", funcNode.getOutputName(), parameterCount, argumentCount));
245250
}
246251
} else if (node instanceof AstIdentifier && varMapper != null) {

src/main/java/org/glassfish/expressly/parser/AstFunction.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.glassfish.expressly.lang.EvaluationContext;
2424
import org.glassfish.expressly.util.MessageFactory;
25+
import org.glassfish.expressly.util.ReflectionUtil;
2526

2627
import jakarta.el.ELClass;
2728
import jakarta.el.ELException;
@@ -167,12 +168,11 @@ public Object getValue(EvaluationContext ctx) throws ELException {
167168
Class<?>[] paramTypes = functionMethod.getParameterTypes();
168169
Object[] params = ((AstMethodArguments) this.children[0]).getParameters(ctx);
169170
Object result = null;
170-
for (int i = 0; i < params.length; i++) {
171-
try {
172-
params[i] = ctx.convertToType(params[i], paramTypes[i]);
173-
} catch (ELException ele) {
174-
throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ele);
175-
}
171+
try {
172+
params = ReflectionUtil.buildParameters(ctx, paramTypes, functionMethod.isVarArgs(), params);
173+
}
174+
catch(ELException ele) {
175+
throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ele);
176176
}
177177

178178
try {

src/main/java/org/glassfish/expressly/util/ReflectionUtil.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ public static Object[] buildParameters(ELContext context, Class<?>[] parameterTy
655655
}
656656

657657
// Last parameter is the varargs
658-
if (parameterTypes.length == paramCount && parameterTypes[varArgIndex] == params[varArgIndex].getClass()) {
658+
if (parameterTypes.length == paramCount &&
659+
(params[varArgIndex] == null || parameterTypes[varArgIndex] == params[varArgIndex].getClass())) {
659660
parameters[varArgIndex] = params[varArgIndex];
660661
} else {
661662
Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType();

src/test/java/org/glassfish/el/test/ELProcessorTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626

2727
import jakarta.el.ELProcessor;
2828
import jakarta.el.ELManager;
29+
import jakarta.el.ELException;
2930
import jakarta.el.ExpressionFactory;
3031
import jakarta.el.MethodExpression;
3132
import jakarta.el.ELContext;
3233
import java.lang.reflect.Method;
34+
import java.util.Arrays;
3335

3436
public class ELProcessorTest {
3537

@@ -160,6 +162,61 @@ public void defineFuncTest() {
160162
}
161163
assertTrue(caught);
162164
}
165+
166+
@Test
167+
public void defineVarargFuncTest()
168+
{
169+
Class<?> c = MyBean.class;
170+
Method meth1 = null;
171+
Method meth2 = null;
172+
try {
173+
meth1 = c.getMethod("join", new Class<?>[] {String[].class});
174+
meth2 = c.getMethod("joinPrefixed", new Class<?>[] {String.class, String[].class});
175+
} catch(Exception e) {
176+
System.out.printf("Exception: ", e);
177+
}
178+
try {
179+
elp.defineFunction("xx", "", meth1);
180+
String joined = elp.eval("xx:join()");
181+
assertEquals("", joined);
182+
joined = elp.eval("xx:join('abc')");
183+
assertEquals("abc", joined);
184+
joined = elp.eval("xx:join('abc','def')");
185+
assertEquals("abc,def", joined);
186+
joined = elp.eval("xx:join('abc',null)"); // null is converted to empty string
187+
assertEquals("abc,", joined);
188+
joined = elp.eval("xx:join(null)"); // this is join((String[])null) in Java
189+
assertEquals("<null array>", joined);
190+
} catch(NoSuchMethodException ex) {
191+
192+
}
193+
194+
boolean caught = false;
195+
try {
196+
elp.defineFunction("xx", "", meth2);
197+
Integer sum = elp.eval("xx:joinPrefixed()");
198+
assertEquals(0, sum.intValue());
199+
} catch (ELException ex) {
200+
caught = true;
201+
} catch (NoSuchMethodException ex) {
202+
203+
}
204+
assertTrue(caught);
205+
206+
try {
207+
elp.defineFunction("xx", "", meth2);
208+
String joined = elp.eval("xx:joinPrefixed('res:')");
209+
assertEquals("res:", joined);
210+
joined = elp.eval("xx:joinPrefixed('res:', 'abc')");
211+
assertEquals("res:abc", joined);
212+
joined = elp.eval("xx:joinPrefixed('res:', 'abc', 'def')");
213+
assertEquals("res:abc,def", joined);
214+
joined = elp.eval("xx:joinPrefixed('res:', null)");
215+
assertEquals("res:<null array>", joined);
216+
} catch(NoSuchMethodException ex) {
217+
218+
}
219+
}
163220
/*
164221
@Test
165222
public void testBean() {
@@ -200,6 +257,15 @@ public int getFoo(int i) {
200257
public static int getBar() {
201258
return 64;
202259
}
260+
public static String join(String... args) {
261+
if(args == null)
262+
return "<null array>";
263+
264+
return String.join(",", args);
265+
}
266+
public static String joinPrefixed(String prefix, String... args) {
267+
return prefix + join(args);
268+
}
203269
}
204270
}
205271

0 commit comments

Comments
 (0)