blob: c3ecc27f9489114e045d3140bab164ecaeb907d0 [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.internal;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlInfo;
import org.apache.commons.jexl3.JexlOptions;
import org.apache.commons.jexl3.internal.TemplateEngine.TemplateExpression;
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.jexl3.parser.ASTIdentifier;
import org.apache.commons.jexl3.parser.ASTJexlLambda;
import org.apache.commons.jexl3.parser.ASTJexlScript;
import org.apache.commons.jexl3.parser.JexlNode;
import java.io.Writer;
/**
* The type of interpreter to use during evaluation of templates.
* <p>This context exposes its writer as '$jexl' to the scripts.</p>
* <p>public for introspection purpose.</p>
*/
public class TemplateInterpreter extends Interpreter {
/** The array of template expressions. */
private final TemplateExpression[] exprs;
/** The writer used to output. */
private final Writer writer;
/**
* Helper ctor.
* <p>Stores the different properties required to create a Template interpreter.
*/
static class Arguments {
/** The engine. */
Engine jexl;
/** The options. */
JexlOptions options;
/** The context. */
JexlContext jcontext;
/** The frame. */
Frame jframe;
/** The expressions. */
TemplateExpression[] expressions;
/** The writer. */
Writer out;
/**
* Sole ctor.
* @param e the JEXL engine
*/
Arguments(final Engine e) {
this.jexl = e;
}
/**
* Sets the options.
* @param o the options
* @return this instance
*/
Arguments options(final JexlOptions o) {
this.options = o;
return this;
}
/**
* Sets the context.
* @param j the context
* @return this instance
*/
Arguments context(final JexlContext j) {
this.jcontext = j;
return this;
}
/**
* Sets the frame.
* @param f the frame
* @return this instance
*/
Arguments frame(final Frame f) {
this.jframe = f;
return this;
}
/**
* Sets the expressions.
* @param e the expressions
* @return this instance
*/
Arguments expressions(final TemplateExpression[] e) {
this.expressions = e;
return this;
}
/**
* Sets the writer.
* @param o the writer
* @return this instance
*/
Arguments writer(final Writer o) {
this.out = o;
return this;
}
}
/**
* Creates a template interpreter instance.
* @param args the template interpreter arguments
*/
TemplateInterpreter(final Arguments args) {
super(args.jexl, args.options, args.jcontext, args.jframe);
exprs = args.expressions;
writer = args.out;
block = new LexicalFrame(frame, null);
}
/**
* Includes a call to another template.
* <p>
* Includes another template using this template initial context and writer.</p>
* @param script the TemplateScript to evaluate
* @param args the arguments
*/
public void include(final TemplateScript script, final Object... args) {
script.evaluate(context, writer, args);
}
/**
* Prints a unified expression evaluation result.
* @param e the expression number
*/
public void print(final int e) {
if (e < 0 || e >= exprs.length) {
return;
}
TemplateEngine.TemplateExpression expr = exprs[e];
if (expr.isDeferred()) {
expr = expr.prepare(frame, context);
}
if (expr instanceof TemplateEngine.CompositeExpression) {
printComposite((TemplateEngine.CompositeExpression) expr);
} else {
doPrint(expr.getInfo(), expr.evaluate(this));
}
}
/**
* Prints a composite expression.
* @param composite the composite expression
*/
private void printComposite(final TemplateEngine.CompositeExpression composite) {
final TemplateEngine.TemplateExpression[] cexprs = composite.exprs;
Object value;
for (final TemplateExpression cexpr : cexprs) {
value = cexpr.evaluate(this);
doPrint(cexpr.getInfo(), value);
}
}
/**
* Prints to output.
* <p>
* This will dynamically try to find the best suitable method in the writer through uberspection.
* Subclassing Writer by adding 'print' methods should be the preferred way to specialize output.
* </p>
* @param info the source info
* @param arg the argument to print out
*/
private void doPrint(final JexlInfo info, final Object arg) {
try {
if (writer != null) {
if (arg instanceof CharSequence) {
writer.write(arg.toString());
} else if (arg != null) {
final Object[] value = {arg};
final JexlUberspect uber = jexl.getUberspect();
final JexlMethod method = uber.getMethod(writer, "print", value);
if (method != null) {
method.invoke(writer, value);
} else {
writer.write(arg.toString());
}
}
}
} catch (final java.io.IOException xio) {
throw TemplateEngine.createException(info, "call print", null, xio);
} catch (final java.lang.Exception xany) {
throw TemplateEngine.createException(info, "invoke print", null, xany);
}
}
@Override
protected Object resolveNamespace(final String prefix, final JexlNode node) {
return "jexl".equals(prefix)? this : super.resolveNamespace(prefix, node);
}
@Override
protected Object visit(final ASTIdentifier node, final Object data) {
final String name = node.getName();
if ("$jexl".equals(name)) {
return writer;
}
return super.visit(node, data);
}
@Override
protected Object visit(final ASTJexlScript script, final Object data) {
if (script instanceof ASTJexlLambda && !((ASTJexlLambda) script).isTopLevel()) {
return new Closure(this, (ASTJexlLambda) script) {
@Override
protected Interpreter createInterpreter(final JexlContext context, final Frame local) {
final JexlOptions opts = jexl.options(script, context);
final TemplateInterpreter.Arguments targs = new TemplateInterpreter.Arguments(jexl)
.context(context)
.options(opts)
.frame(local)
.expressions(exprs)
.writer(writer);
return new TemplateInterpreter(targs);
}
};
}
// otherwise...
final int numChildren = script.jjtGetNumChildren();
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;
}
}