Skip to content

Commit

Permalink
JEXL-406: exposed TemplateInterpreter.Arguments to allow TemplateInte…
Browse files Browse the repository at this point in the history
…rpreter derivation (internal, not supported api);

- added test in different package;
  • Loading branch information
Henri Biestro committed Sep 22, 2023
1 parent fcf59af commit f97dd2f
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class TemplateInterpreter extends Interpreter {
* Helper ctor.
* <p>Stores the different properties required to create a Template interpreter.
*/
static class Arguments {
public static class Arguments {
/** The engine. */
Engine jexl;
/** The options. */
Expand Down Expand Up @@ -120,7 +120,7 @@ Arguments writer(final Writer o) {
* Creates a template interpreter instance.
* @param args the template interpreter arguments
*/
TemplateInterpreter(final Arguments args) {
protected TemplateInterpreter(final Arguments args) {
super(args.jexl, args.options, args.jcontext, args.jframe);
exprs = args.expressions;
writer = args.out;
Expand Down Expand Up @@ -243,17 +243,11 @@ protected Object visit(final ASTFunctionNode node, final Object data) {
if ("include".equals(functionName)) {
// evaluate the arguments
Object[] argv = visit(argNode, null);
if (argv != null && argv.length > 0) {
if (argv[0] instanceof TemplateScript) {
final TemplateScript script = (TemplateScript) argv[0];
if (argv.length > 1) {
argv = Arrays.copyOfRange(argv, 1, argv.length);
} else {
argv = null;
}
include(script, argv);
return null;
}
if (argv != null && argv.length > 0 && argv[0] instanceof TemplateScript) {
final TemplateScript script = (TemplateScript) argv[0];
argv = argv.length > 1? Arrays.copyOfRange(argv, 1, argv.length) : null;
include(script, argv);
return null;
}
}
// fail safe
Expand All @@ -270,24 +264,24 @@ protected Object visit(final ASTJexlScript script, final Object data) {
@Override
protected Interpreter createInterpreter(final JexlContext context, final Frame local, final JexlOptions options) {
final TemplateInterpreter.Arguments targs = new TemplateInterpreter.Arguments(jexl)
.context(context)
.options(options)
.frame(local)
.expressions(exprs)
.writer(writer);
.context(context)
.options(options)
.frame(local)
.expressions(exprs)
.writer(writer);
return jexl.createTemplateInterpreter(targs);
}
};
}
// otherwise...
final int numChildren = script.jjtGetNumChildren();
Object result = null;
for (int i = 0; i < numChildren; i++) {
Object result = null;
for (int i = 0; i < numChildren; i++) {
final JexlNode child = script.jjtGetChild(i);
result = child.jjtAccept(this, data);
cancelCheck(child);
}
return result;
result = child.jjtAccept(this, data);
cancelCheck(child);
}
return result;
}

}
2 changes: 1 addition & 1 deletion src/test/java/org/apache/commons/jexl3/Issues400Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public String join(int[] list, String str) {
}

@Test
public void test406() {
public void test406a() {
final JexlEngine jexl = new JexlBuilder()
.cache(64)
.strict(true)
Expand Down
108 changes: 108 additions & 0 deletions src/test/java/org/example/SomeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.example;

import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.JexlOptions;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.jexl3.internal.Engine;
import org.apache.commons.jexl3.internal.Frame;
import org.apache.commons.jexl3.internal.Interpreter;
import org.apache.commons.jexl3.internal.TemplateEngine;
import org.apache.commons.jexl3.internal.TemplateInterpreter;
import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.commons.jexl3.parser.JexlNode;
import org.junit.Assert;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class SomeTest {

public static class MyMath {
public double cos(final double x) {
return Math.cos(x);
}
}

/**
* User namespace needs to be allowed through permissions.
*/
@Test
public void testCustomFunctionPermissions() {
Map<String, Object> funcs = new HashMap<String, Object>();
funcs.put("math", new MyMath());
JexlPermissions permissions = JexlPermissions.parse("org.example.*");
JexlEngine jexl = new JexlBuilder().permissions(permissions).namespaces(funcs).create();
JexlContext jc = new MapContext();
jc.set("pi", Math.PI);
JexlExpression e = jexl.createExpression("math:cos(pi)");
Number result = (Number) e.evaluate(jc);
Assert.assertEquals(-1, result.intValue());
}

/**
* Engine creating dedicated template interpreter.
*/
public static class Engine406 extends Engine {
public Engine406(final JexlBuilder conf) {
super(conf);
}
@Override public TemplateInterpreter createTemplateInterpreter(final TemplateInterpreter.Arguments args) {
return new TemplateInterpreter406(args);
}
}

/** Counting the number of node interpretation calls. */
static AtomicInteger CALL406 = new AtomicInteger(0);

public static class TemplateInterpreter406 extends TemplateInterpreter {
protected TemplateInterpreter406(final Arguments args) {
super(args);
}
public Object interpret(final JexlNode node) {
CALL406.incrementAndGet();
return super.interpret(node);
}
}

@Test
public void test406b() {
final JexlEngine jexl = new JexlBuilder() {
@Override
public JexlEngine create() {
return new Engine406(this);
}
}.cache(64).strict(true).safe(false).create();
String src = "`Call ${x}`";
JexlScript script = jexl.createScript(src, "x");
Object result = script.execute(null, 406);
Assert.assertEquals("Call 406", result);
Assert.assertEquals(1, CALL406.get());
result = script.execute(null, 42);
Assert.assertEquals("Call 42", result);
Assert.assertEquals(2, CALL406.get());
}
}

0 comments on commit f97dd2f

Please sign in to comment.