blob: 4808f3803191539a46898da211e356afd634503e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl3;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.jexl3.junit.Asserter;
import java.util.Arrays;
import java.util.Date;
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlPropertyGet;
import org.apache.commons.jexl3.introspection.JexlPropertySet;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for calling methods on objects
*
* @since 2.0
*/
@SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
public class MethodTest extends JexlTestCase {
private Asserter asserter;
private static final String METHOD_STRING = "Method string";
public MethodTest() {
super("MethodTest");
}
public static class VarArgs {
public String callInts() {
final int result = -5000;
return "Varargs:" + result;
}
public String callInts(final Integer... args) {
int result = 0;
if (args != null) {
for (final Integer arg : args) {
result += arg != null ? arg : -100;
}
} else {
result = -1000;
}
return "Varargs:" + result;
}
public String callMixed(final Integer fixed, final Integer... args) {
int result = fixed;
if (args != null) {
for (final Integer arg : args) {
result += arg != null ? arg : -100;
}
} else {
result -= 1000;
}
return "Mixed:" + result;
}
public String callMixed(final String mixed, final Integer... args) {
int result = 0;
if (args != null) {
for (final Integer arg : args) {
result += arg != null ? arg : -100;
}
} else {
result = -1000;
}
return mixed + ":" + result;
}
public String concat(final String... strs) {
if (strs.length > 0) {
final StringBuilder strb = new StringBuilder(strs[0]);
for (int s = 1; s < strs.length; ++s) {
strb.append(", ");
strb.append(strs[s]);
}
return strb.toString();
} else {
return "";
}
}
}
public static class EnhancedContext extends JexlEvalContext {
int factor = 6;
final Map<String, Object> funcs;
EnhancedContext(final Map<String, Object> funcs) {
this.funcs = funcs;
}
@Override
public Object resolveNamespace(final String name) {
return funcs.get(name);
}
}
public static class ContextualFunctor {
private final EnhancedContext context;
public ContextualFunctor(final EnhancedContext theContext) {
context = theContext;
}
public int ratio(final int n) {
context.factor -= 1;
return n / context.factor;
}
}
@Before
@Override
public void setUp() {
asserter = new Asserter(JEXL);
}
@Test
public void testCallVarArgMethod() throws Exception {
final VarArgs test = new VarArgs();
asserter.setVariable("test", test);
asserter.assertExpression("test.callInts()", test.callInts());
asserter.assertExpression("test.callInts(1)", test.callInts(1));
asserter.assertExpression("test.callInts(1,2,3,4,5)", test.callInts(1, 2, 3, 4, 5));
asserter.assertExpression("test.concat(['1', '2', '3'])", test.concat(new String[]{"1", "2", "3"}));
asserter.assertExpression("test.concat('1', '2', '3')", test.concat("1", "2", "3"));
}
@Test
public void testCallMixedVarArgMethod() throws Exception {
final VarArgs test = new VarArgs();
asserter.setVariable("test", test);
Assert.assertEquals("Mixed:1", test.callMixed(Integer.valueOf(1)));
asserter.assertExpression("test.callMixed(1)", test.callMixed(1));
// Java and JEXL equivalent behavior: 'Mixed:-999' expected
//{
Assert.assertEquals("Mixed:-999", test.callMixed(Integer.valueOf(1), (Integer[]) null));
asserter.assertExpression("test.callMixed(1, null)", "Mixed:-999");
//}
asserter.assertExpression("test.callMixed(1,2)", test.callMixed(1, 2));
asserter.assertExpression("test.callMixed(1,2,3,4,5)", test.callMixed(1, 2, 3, 4, 5));
}
@Test
public void testCallJexlVarArgMethod() throws Exception {
final VarArgs test = new VarArgs();
asserter.setVariable("test", test);
Assert.assertEquals("jexl:0", test.callMixed("jexl"));
asserter.assertExpression("test.callMixed('jexl')", "jexl:0");
// Java and JEXL equivalent behavior: 'jexl:-1000' expected
//{
Assert.assertEquals("jexl:-1000", test.callMixed("jexl", (Integer[]) null));
asserter.assertExpression("test.callMixed('jexl', null)", "jexl:-1000");
//}
asserter.assertExpression("test.callMixed('jexl', 2)", test.callMixed("jexl", 2));
asserter.assertExpression("test.callMixed('jexl',2,3,4,5)", test.callMixed("jexl", 2, 3, 4, 5));
}
public static class Functor {
private boolean overKill = false;
private String under = null;
void setKill(final boolean ok) {
overKill = ok;
}
public int ten() {
return 10;
}
public int plus10(final int num) {
return num + 10;
}
public static int TWENTY() {
return 20;
}
public static int PLUS20(final int num) {
return num + 20;
}
public static Class<?> NPEIfNull(final Object x) {
return x.getClass();
}
public Object over(final String f, final int i) {
if (overKill) {
throw new UnsupportedOperationException("kill " + f + " + " + i);
}
return f + " + " + i;
}
public Object over(final String f, final Date g) {
return f + " + " + g;
}
public Object over(final String f, final String g) {
return f + " + " + g;
}
public void setUnder(final String str) {
if (overKill) {
throw new UnsupportedOperationException("kill " + str);
}
under = str;
}
public String getUnder() {
if (overKill) {
throw new UnsupportedOperationException("kill " + under);
}
return under;
}
}
public static class FunctorOver extends Functor {
public Object over(final Object f, final Object g) {
return f + " + " + g;
}
}
@Test
public void testInvoke() throws Exception {
Functor func = new Functor();
Assert.assertEquals(Integer.valueOf(10), JEXL.invokeMethod(func, "ten"));
Assert.assertEquals(Integer.valueOf(42), JEXL.invokeMethod(func, "PLUS20", Integer.valueOf(22)));
try {
JEXL.invokeMethod(func, "nonExistentMethod");
Assert.fail("method does not exist!");
} catch (final Exception xj0) {
// ignore
}
try {
JEXL.invokeMethod(func, "NPEIfNull", (Object[]) null);
Assert.fail("method should have thrown!");
} catch (final Exception xj0) {
// ignore
}
Object result;
try {
result = JEXL.invokeMethod(func, "over", "foo", 42);
Assert.assertEquals("foo + 42", result);
} catch (final Exception xj0) {
// ignore
result = xj0;
}
try {
result = JEXL.invokeMethod(func, "over", null, null);
Assert.fail("method should have thrown!");
} catch (final Exception xj0) {
// ignore
result = xj0;
}
func = new FunctorOver();
try {
result = JEXL.invokeMethod(func, "over", null, null);
Assert.assertEquals("null + null", result);
} catch (final Exception xj0) {
Assert.fail("method should not have thrown!");
}
}
@Test
public void testAmbiguousInvoke() throws Exception {
// JEXL-299
final Functor func = new Functor();
final JexlContext ctxt = new MapContext();
ctxt.set("func", func);
Object result;
// basic call works
result = JEXL.invokeMethod(func, "over", "foo", 42);
Assert.assertEquals("foo + 42", result);
// ambiguous call fails
try {
JEXL.invokeMethod(func, "over", "not null", null);
Assert.fail("should be ambiguous");
} catch (final JexlException.Method xinvoke) {
Assert.assertEquals("over(String, Object)", xinvoke.getMethodSignature());
}
// another ambiguous call fails
try {
final String[] arg2 = new String[]{"more", "than", "one"};
JEXL.invokeMethod(func, "over", "not null", arg2);
Assert.fail("should be ambiguous");
} catch (final JexlException.Method xinvoke) {
Assert.assertEquals("over(String, String[])", xinvoke.getMethodSignature());
}
}
@Test
public void testTryFailed() throws Exception {
// JEXL-257
final Functor func = new Functor();
final JexlContext ctxt = new MapContext();
ctxt.set("func", func);
Object result;
final JexlUberspect uber = JEXL.getUberspect();
// tryInvoke
final JexlMethod method = uber.getMethod(func, "over", "foo", 42);
Assert.assertNotNull(method);
// tryInvoke succeeds
result = method.tryInvoke("over", func, "foo", 42);
Assert.assertEquals("foo + 42", result);
// tryInvoke fails
func.setKill(true);
try {
/*result = */method.tryInvoke("over", func, "foo", 42);
Assert.fail("should throw TryFailed");
} catch (final JexlException.TryFailed xfail) {
Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
}
func.setKill(false);
final JexlPropertySet setter = uber.getPropertySet(func, "under", "42");
result = setter.tryInvoke(func, "under", "42");
Assert.assertFalse(setter.tryFailed(result));
Assert.assertEquals("42", result);
final JexlPropertyGet getter = uber.getPropertyGet(func, "under");
result = getter.tryInvoke(func, "under");
Assert.assertFalse(getter.tryFailed(result));
Assert.assertEquals("42", result);
func.setKill(true);
try {
/*result = */setter.tryInvoke(func, "under", "42");
Assert.fail("should throw TryFailed");
} catch (final JexlException.TryFailed xfail) {
Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
}
func.setKill(false);
result = setter.tryInvoke(func, "under", "-42");
Assert.assertEquals("-42", result);
func.setKill(true);
try {
/*result = */getter.tryInvoke(func, "under");
Assert.fail("should throw TryFailed");
} catch (final JexlException.TryFailed xfail) {
Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
}
func.setKill(false);
result = getter.tryInvoke(func, "under");
Assert.assertFalse(getter.tryFailed(result));
Assert.assertEquals("-42", result);
}
@Test
public void testTryFailedScript() throws Exception {
// JEXL-257
final Functor func = new Functor();
final JexlContext ctxt = new MapContext();
ctxt.set("func", func);
Object result;
final JexlUberspect uber = JEXL.getUberspect();
final JexlScript method = JEXL.createScript("(x, y)->{ func.over(x, y) }");
// tryInvoke
//JexlMethod method = uber.getMethod(func, "over", "foo", 42);
Assert.assertNotNull(method);
// tryInvoke succeeds
result = method.execute(ctxt, "foo", 42);
Assert.assertEquals("foo + 42", result);
// tryInvoke fails
func.setKill(true);
try {
/*result = */method.execute(ctxt, "foo", 42);
Assert.fail("should throw TryFailed");
} catch (final JexlException xfail) {
Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
}
func.setKill(false);
final JexlScript setter = JEXL.createScript("(x)->{ func.under = x }");
//JexlPropertySet setter = uber.getPropertySet(func, "under", "42");
result = setter.execute(ctxt, "42");
Assert.assertEquals("42", result);
final JexlScript getter = JEXL.createScript("func.under");
Assert.assertEquals("42", result);
func.setKill(true);
try {
/*result = */setter.execute(ctxt, "42");
Assert.fail("should throw TryFailed");
} catch (final JexlException xfail) {
Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
}
func.setKill(false);
result = setter.execute(ctxt, "-42");
Assert.assertEquals("-42", result);
func.setKill(true);
try {
/*result = */getter.execute(ctxt);
Assert.fail("should throw TryFailed");
} catch (final JexlException xfail) {
Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
}
func.setKill(false);
result = getter.execute(ctxt);
Assert.assertEquals("-42", result);
}
/**
* test a simple method expression
*/
@Test
public void testMethod() throws Exception {
// tests a simple method expression
asserter.setVariable("foo", new Foo());
asserter.assertExpression("foo.bar()", METHOD_STRING);
}
@Test
public void testMulti() throws Exception {
asserter.setVariable("foo", new Foo());
asserter.assertExpression("foo.innerFoo.bar()", METHOD_STRING);
}
/**
* test some String method calls
*/
@Test
public void testStringMethods() throws Exception {
asserter.setVariable("foo", "abcdef");
asserter.assertExpression("foo.substring(3)", "def");
asserter.assertExpression("foo.substring(0,(size(foo)-3))", "abc");
asserter.assertExpression("foo.substring(0,size(foo)-3)", "abc");
asserter.assertExpression("foo.substring(0,foo.length()-3)", "abc");
asserter.assertExpression("foo.substring(0, 1+1)", "ab");
}
/**
* Ensures static methods on objects can be called.
*/
@Test
public void testStaticMethodInvocation() throws Exception {
asserter.setVariable("aBool", Boolean.FALSE);
asserter.assertExpression("aBool.valueOf('true')", Boolean.TRUE);
}
@Test
public void testStaticMethodInvocationOnClasses() throws Exception {
asserter.setVariable("Boolean", Boolean.class);
asserter.assertExpression("Boolean.valueOf('true')", Boolean.TRUE);
}
public static class MyMath {
public double cos(final double x) {
return Math.cos(x);
}
}
@Test
public void testTopLevelCall() throws Exception {
final java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
funcs.put(null, new Functor());
funcs.put("math", new MyMath());
funcs.put("cx", ContextualFunctor.class);
final EnhancedContext jc = new EnhancedContext(funcs);
JexlExpression e = JEXL.createExpression("ten()");
Object o = e.evaluate(jc);
Assert.assertEquals("Result is not 10", new Integer(10), o);
e = JEXL.createExpression("plus10(10)");
o = e.evaluate(jc);
Assert.assertEquals("Result is not 20", new Integer(20), o);
e = JEXL.createExpression("plus10(ten())");
o = e.evaluate(jc);
Assert.assertEquals("Result is not 20", new Integer(20), o);
jc.set("pi", new Double(Math.PI));
e = JEXL.createExpression("math:cos(pi)");
o = e.evaluate(jc);
Assert.assertEquals(Double.valueOf(-1), o);
e = JEXL.createExpression("cx:ratio(10) + cx:ratio(20)");
o = e.evaluate(jc);
Assert.assertEquals(Integer.valueOf(7), o);
}
@Test
public void testNamespaceCall() throws Exception {
final java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
funcs.put("func", new Functor());
funcs.put("FUNC", Functor.class);
JexlExpression e = JEXL.createExpression("func:ten()");
final JexlEvalContext jc = new EnhancedContext(funcs);
Object o = e.evaluate(jc);
Assert.assertEquals("Result is not 10", new Integer(10), o);
e = JEXL.createExpression("func:plus10(10)");
o = e.evaluate(jc);
Assert.assertEquals("Result is not 20", new Integer(20), o);
e = JEXL.createExpression("func:plus10(func:ten())");
o = e.evaluate(jc);
Assert.assertEquals("Result is not 20", new Integer(20), o);
e = JEXL.createExpression("FUNC:PLUS20(10)");
o = e.evaluate(jc);
Assert.assertEquals("Result is not 30", new Integer(30), o);
e = JEXL.createExpression("FUNC:PLUS20(FUNC:TWENTY())");
o = e.evaluate(jc);
Assert.assertEquals("Result is not 40", new Integer(40), o);
}
public static class Edge {
private Edge() {
}
public int exec(final int arg) {
return 1;
}
public int exec(final int[] arg) {
return 20;
}
public int exec(final String arg) {
return 2;
}
public int exec(final String... arg) {
return 200;
}
public int exec(final Object args) {
return 3;
}
public int exec(final Object... args) {
return 4;
}
public int exec(final Boolean x, final int arg) {
return 1;
}
public int exec(final Boolean x, final int[] arg) {
return 20;
}
public int exec(final Boolean x, final String arg) {
return 2;
}
public int exec(final Boolean x, final Object args) {
return 3;
}
public int exec(final Boolean x, final Object... args) {
return 4;
}
public Class<?>[] execute(final Object... args) {
final Class<?>[] clazz = new Class<?>[args.length];
for (int a = 0; a < args.length; ++a) {
clazz[a] = args[a] != null ? args[a].getClass() : Void.class;
}
return clazz;
}
}
private boolean eqExecute(final Object lhs, final Object rhs) {
if (lhs instanceof Class<?>[] && rhs instanceof Class<?>[]) {
final Class<?>[] lhsa = (Class<?>[]) lhs;
final Class<?>[] rhsa = (Class<?>[]) rhs;
return Arrays.deepEquals(lhsa, rhsa);
}
return false;
}
@Test
public void testNamespaceCallEdge() throws Exception {
final java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
final Edge func = new Edge();
funcs.put("func", func);
Object o;
Object c;
JexlExpression e;
final JexlEvalContext jc = new EnhancedContext(funcs);
try {
for (int i = 0; i < 2; ++i) {
e = JEXL.createExpression("func:exec([1, 2])");
o = e.evaluate(jc);
Assert.assertEquals("exec(int[] arg): " + i, 20, o);
e = JEXL.createExpression("func:exec(1, 2)");
o = e.evaluate(jc);
Assert.assertEquals("exec(Object... args): " + i, 4, o);
e = JEXL.createExpression("func:exec([10.0, 20.0])");
o = e.evaluate(jc);
Assert.assertEquals("exec(Object args): " + i, 3, o);
e = JEXL.createExpression("func:exec('1', 2)");
o = e.evaluate(jc);
Assert.assertEquals("exec(Object... args): " + i, 4, o);
// no way to differentiate between a single arg call with an array and a vararg call with same args
Assert.assertEquals("exec(String... args): " + i, func.exec("1", "2"), func.exec(new String[]{"1", "2"}));
e = JEXL.createExpression("func:exec(['1', '2'])");
o = e.evaluate(jc);
Assert.assertEquals("exec(String... args): " + i, func.exec(new String[]{"1", "2"}), o);
e = JEXL.createExpression("func:exec('1', '2')");
o = e.evaluate(jc);
Assert.assertEquals("exec(String... args): " + i, func.exec("1", "2"), o);
e = JEXL.createExpression("func:exec(true, [1, 2])");
o = e.evaluate(jc);
Assert.assertEquals("exec(int[] arg): " + i, 20, o);
e = JEXL.createExpression("func:exec(true, 1, 2)");
o = e.evaluate(jc);
Assert.assertEquals("exec(Object... args): " + i, 4, o);
e = JEXL.createExpression("func:exec(true, ['1', '2'])");
o = e.evaluate(jc);
Assert.assertEquals("exec(Object args): " + i, 3, o);
e = JEXL.createExpression("func:exec(true, '1', '2')");
o = e.evaluate(jc);
Assert.assertEquals("exec(Object... args): " + i, 4, o);
e = JEXL.createExpression("func:execute(true, '1', '2')");
o = e.evaluate(jc);
c = func.execute(Boolean.TRUE, "1", "2");
Assert.assertTrue("execute(Object... args): " + i, eqExecute(o, c));
e = JEXL.createExpression("func:execute([true])");
o = e.evaluate(jc);
c = func.execute(new boolean[]{true});
Assert.assertTrue("execute(Object... args): " + i, eqExecute(o, c));
}
} catch (final JexlException xjexl) {
Assert.fail(xjexl.toString());
}
}
public static class ScriptContext extends MapContext implements JexlContext.NamespaceResolver {
Map<String, Object> nsScript;
ScriptContext(final Map<String, Object> ns) {
nsScript = ns;
}
@Override
public Object resolveNamespace(final String name) {
if (name == null) {
return this;
}
if ("script".equals(name)) {
return nsScript;
}
if ("functor".equals(name)) {
return (NamespaceFunctor) context -> {
final Map<String, Object> values = new HashMap<String, Object>();
if ("gin".equals(context.get("base"))) {
values.put("drink", "gin fizz");
} else {
values.put("drink", "champaign");
}
return values;
};
}
return null;
}
}
@Test
public void testScriptCall() throws Exception {
JexlContext context = new MapContext();
final JexlScript plus = JEXL.createScript("a + b", new String[]{"a", "b"});
context.set("plus", plus);
JexlScript forty2 = JEXL.createScript("plus(4, 2) * plus(4, 3)");
Object o = forty2.execute(context);
Assert.assertEquals("Result is not 42", new Integer(42), o);
final Map<String, Object> foo = new HashMap<String, Object>();
foo.put("plus", plus);
context.set("foo", foo);
forty2 = JEXL.createScript("foo.plus(4, 2) * foo.plus(4, 3)");
o = forty2.execute(context);
Assert.assertEquals("Result is not 42", new Integer(42), o);
context = new ScriptContext(foo);
forty2 = JEXL.createScript("script:plus(4, 2) * script:plus(4, 3)");
o = forty2.execute(context);
Assert.assertEquals("Result is not 42", new Integer(42), o);
final JexlArithmetic ja = JEXL.getArithmetic();
final JexlMethod mplus = new JexlMethod() {
@Override
public Object invoke(final Object obj, final Object ... params) throws Exception {
if (obj instanceof Map<?, ?>) {
return ja.add(params[0], params[1]);
} else {
throw new Exception("not a script context");
}
}
@Override
public Object tryInvoke(final String name, final Object obj, final Object ... params) {
try {
if ("plus".equals(name)) {
return invoke(obj, params);
}
} catch (final Exception xany) {
// ignore and fail by returning this
}
return this;
}
@Override
public boolean tryFailed(final Object rval) {
// this is the marker for failure
return rval == this;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public Class<?> getReturnType() {
return Object.class;
}
};
foo.put("PLUS", mplus);
forty2 = JEXL.createScript("script:PLUS(4, 2) * script:PLUS(4, 3)");
o = forty2.execute(context);
Assert.assertEquals("Result is not 42", new Integer(42), o);
context.set("foo.bar", foo);
forty2 = JEXL.createScript("foo.'bar'.PLUS(4, 2) * foo.bar.PLUS(4, 3)");
o = forty2.execute(context);
Assert.assertEquals("Result is not 42", new Integer(42), o);
}
@Test
public void testFizzCall() throws Exception {
final ScriptContext context = new ScriptContext(new HashMap<String, Object>());
JexlScript bar = JEXL.createScript("functor:get('drink')");
Object o;
o = bar.execute(context);
Assert.assertEquals("Wrong choice", "champaign", o);
context.set("base", "gin");
o = bar.execute(context);
Assert.assertEquals("Wrong choice", "gin fizz", o);
// despite being called twice, the functor is created only once.
context.set("base", "wine");
bar = JEXL.createScript("var glass = functor:get('drink'); base = 'gin'; functor:get('drink')");
o = bar.execute(context);
Assert.assertEquals("Wrong choice", "champaign", o);
}
public static class ZArithmetic extends JexlArithmetic {
public ZArithmetic(final boolean astrict) {
super(astrict);
}
public int zzzz(final int z) {
return 38 + z;
}
}
public static class ZSpace {
public int zzz(final int z) {
return 39 + z;
}
}
public static class ZContext extends MapContext {
public ZContext(final Map<String,Object> map) {
super(map);
}
public int zz(final int z) {
return 40 + z;
}
public int z(final int z) {
return 181 + z;
}
}
@Test
public void testVariousFunctionLocation() throws Exception {
// see JEXL-190
final Map<String, Object> vars = new HashMap<String, Object>();
final Map<String,Object> funcs = new HashMap<String,Object>();
funcs.put(null, new ZSpace());
final JexlEngine jexl = new JexlBuilder().namespaces(funcs).arithmetic(new ZArithmetic(true)).create();
final JexlContext zjc = new ZContext(vars); // that implements a z(int x) function
final String z41 = "z(41)";
final JexlScript callz41 = jexl.createScript(z41);
Object onovar = callz41.execute(zjc);
Assert.assertEquals(222, onovar);
// override z() with global var
final JexlScript z241 = jexl.createScript("(x)->{ return x + 241}");
vars.put("z", z241);
final Object oglobal = callz41.execute(zjc);
Assert.assertEquals(282, oglobal);
// clear global and execute again
vars.remove("z");
onovar = callz41.execute(zjc);
Assert.assertEquals(222, onovar);
// override z() with local var
final String slocal = "var z = (x)->{ return x + 141}; z(1)";
final JexlScript jlocal = jexl.createScript(slocal);
final Object olocal = jlocal.execute(zjc);
Assert.assertEquals(142, olocal);
// and now try the context, the null namespace and the arithmetic
Assert.assertEquals(42, jexl.createScript("zz(2)").execute(zjc));
Assert.assertEquals(42, jexl.createScript("zzz(3)").execute(zjc));
Assert.assertEquals(42, jexl.createScript("zzzz(4)").execute(zjc));
}
}