diff options
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/Evaluate.java')
-rw-r--r-- | velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/Evaluate.java | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/Evaluate.java b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/Evaluate.java new file mode 100644 index 00000000..89ccdb6b --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/Evaluate.java @@ -0,0 +1,240 @@ +package org.apache.velocity.runtime.directive; + +/* + * 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. + */ + +import org.apache.velocity.Template; +import org.apache.velocity.context.InternalContextAdapter; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.TemplateInitException; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.runtime.parser.ParseException; +import org.apache.velocity.runtime.parser.node.Node; +import org.apache.velocity.runtime.parser.node.ParserTreeConstants; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.util.introspection.Info; + +import java.io.IOException; +import java.io.StringReader; +import java.io.Writer; + +/** + * Evaluates the directive argument as a VTL string, using the existing + * context. + * + * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a> + * @version $Id$ + * @since 1.6 + */ +public class Evaluate extends Directive +{ + + /** + * Return name of this directive. + * @return The name of this directive. + */ + @Override + public String getName() + { + return "evaluate"; + } + + /** + * Return type of this directive. + * @return The type of this directive. + */ + @Override + public int getType() + { + return LINE; + } + + /** + * Initialize and check arguments. + * @param rs + * @param context + * @param node + * @throws TemplateInitException + */ + @Override + public void init(RuntimeServices rs, InternalContextAdapter context, + Node node) + throws TemplateInitException + { + super.init( rs, context, node ); + + /* + * Check that there is exactly one argument and it is a string or reference. + */ + + int argCount = node.jjtGetNumChildren(); + if (argCount == 0) + { + throw new TemplateInitException( + "#" + getName() + "() requires exactly one argument", + null, + rsvc.getLogContext().getStackTrace(), + context.getCurrentTemplateName(), + node.getColumn(), + node.getLine()); + } + if (argCount > 1) + { + /* + * use line/col of second argument + */ + + throw new TemplateInitException( + "#" + getName() + "() requires exactly one argument", + null, + rsvc.getLogContext().getStackTrace(), + context.getCurrentTemplateName(), + node.jjtGetChild(1).getColumn(), + node.jjtGetChild(1).getLine()); + } + + Node childNode = node.jjtGetChild(0); + if ( childNode.getType() != ParserTreeConstants.JJTSTRINGLITERAL && + childNode.getType() != ParserTreeConstants.JJTREFERENCE ) + { + throw new TemplateInitException( + "#" + getName() + "() argument must be a string literal or reference", + null, + rsvc.getLogContext().getStackTrace(), + context.getCurrentTemplateName(), + childNode.getColumn(), + childNode.getLine()); + } + } + + /** + * Evaluate the argument, convert to a String, and evaluate again + * (with the same context). + * @param context + * @param writer + * @param node + * @return True if the directive rendered successfully. + * @throws IOException + * @throws ResourceNotFoundException + * @throws ParseErrorException + * @throws MethodInvocationException + */ + @Override + public boolean render(InternalContextAdapter context, Writer writer, + Node node) throws IOException, ResourceNotFoundException, + ParseErrorException, MethodInvocationException + { + + /* + * Evaluate the string with the current context. We know there is + * exactly one argument and it is a string or reference. + */ + + Object value = node.jjtGetChild(0).value( context ); + String sourceText; + if ( value != null ) + { + sourceText = value.toString(); + } + else + { + sourceText = ""; + } + + /* + * The new string needs to be parsed since the text has been dynamically generated. + */ + String templateName = context.getCurrentTemplateName(); + Template template = (Template)context.getCurrentResource(); + if (template == null) + { + template = new Template(); + template.setName(templateName); + } + SimpleNode nodeTree = null; + + try + { + nodeTree = rsvc.parse(new StringReader(sourceText), template); + } + catch (ParseException | TemplateInitException pex) + { + // use the line/column from the template + Info info = new Info( templateName, node.getLine(), node.getColumn() ); + throw new ParseErrorException( pex.getMessage(), info, rsvc.getLogContext().getStackTrace() ); + } + + /* + * now we want to init and render. Chain the context + * to prevent any changes to the current context. + */ + + if (nodeTree != null) + { + context.pushCurrentTemplateName(templateName); + + try + { + try + { + nodeTree.init(context, rsvc); + } + catch (TemplateInitException pex) + { + Info info = new Info( templateName, node.getLine(), node.getColumn() ); + throw new ParseErrorException( pex.getMessage(), info, rsvc.getLogContext().getStackTrace() ); + } + + try + { + preRender(context); + + /* + * now render, and let any exceptions fly + */ + nodeTree.render(context, writer); + } + catch (StopCommand stop) + { + if (!stop.isFor(this)) + { + throw stop; + } + } + catch (ParseErrorException pex) + { + // convert any parsing errors to the correct line/col + Info info = new Info( templateName, node.getLine(), node.getColumn() ); + throw new ParseErrorException( pex.getMessage(), info, rsvc.getLogContext().getStackTrace() ); + } + } + finally + { + context.popCurrentTemplateName(); + postRender(context); + } + return true; + } + + return false; + } + +} |