1 package org.apache.velocity.runtime.directive; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 import org.apache.velocity.Template; 23 import org.apache.velocity.context.InternalContextAdapter; 24 import org.apache.velocity.exception.MethodInvocationException; 25 import org.apache.velocity.exception.ParseErrorException; 26 import org.apache.velocity.exception.ResourceNotFoundException; 27 import org.apache.velocity.exception.TemplateInitException; 28 import org.apache.velocity.runtime.RuntimeServices; 29 import org.apache.velocity.runtime.parser.ParseException; 30 import org.apache.velocity.runtime.parser.node.Node; 31 import org.apache.velocity.runtime.parser.node.ParserTreeConstants; 32 import org.apache.velocity.runtime.parser.node.SimpleNode; 33 import org.apache.velocity.util.introspection.Info; 34 35 import java.io.IOException; 36 import java.io.StringReader; 37 import java.io.Writer; 38 39 /** 40 * Evaluates the directive argument as a VTL string, using the existing 41 * context. 42 * 43 * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a> 44 * @version $Id$ 45 * @since 1.6 46 */ 47 public class Evaluate extends Directive 48 { 49 50 /** 51 * Return name of this directive. 52 * @return The name of this directive. 53 */ 54 @Override getName()55 public String getName() 56 { 57 return "evaluate"; 58 } 59 60 /** 61 * Return type of this directive. 62 * @return The type of this directive. 63 */ 64 @Override getType()65 public int getType() 66 { 67 return LINE; 68 } 69 70 /** 71 * Initialize and check arguments. 72 * @param rs 73 * @param context 74 * @param node 75 * @throws TemplateInitException 76 */ 77 @Override init(RuntimeServices rs, InternalContextAdapter context, Node node)78 public void init(RuntimeServices rs, InternalContextAdapter context, 79 Node node) 80 throws TemplateInitException 81 { 82 super.init( rs, context, node ); 83 84 /* 85 * Check that there is exactly one argument and it is a string or reference. 86 */ 87 88 int argCount = node.jjtGetNumChildren(); 89 if (argCount == 0) 90 { 91 throw new TemplateInitException( 92 "#" + getName() + "() requires exactly one argument", 93 null, 94 rsvc.getLogContext().getStackTrace(), 95 context.getCurrentTemplateName(), 96 node.getColumn(), 97 node.getLine()); 98 } 99 if (argCount > 1) 100 { 101 /* 102 * use line/col of second argument 103 */ 104 105 throw new TemplateInitException( 106 "#" + getName() + "() requires exactly one argument", 107 null, 108 rsvc.getLogContext().getStackTrace(), 109 context.getCurrentTemplateName(), 110 node.jjtGetChild(1).getColumn(), 111 node.jjtGetChild(1).getLine()); 112 } 113 114 Node childNode = node.jjtGetChild(0); 115 if ( childNode.getType() != ParserTreeConstants.JJTSTRINGLITERAL && 116 childNode.getType() != ParserTreeConstants.JJTREFERENCE ) 117 { 118 throw new TemplateInitException( 119 "#" + getName() + "() argument must be a string literal or reference", 120 null, 121 rsvc.getLogContext().getStackTrace(), 122 context.getCurrentTemplateName(), 123 childNode.getColumn(), 124 childNode.getLine()); 125 } 126 } 127 128 /** 129 * Evaluate the argument, convert to a String, and evaluate again 130 * (with the same context). 131 * @param context 132 * @param writer 133 * @param node 134 * @return True if the directive rendered successfully. 135 * @throws IOException 136 * @throws ResourceNotFoundException 137 * @throws ParseErrorException 138 * @throws MethodInvocationException 139 */ 140 @Override render(InternalContextAdapter context, Writer writer, Node node)141 public boolean render(InternalContextAdapter context, Writer writer, 142 Node node) throws IOException, ResourceNotFoundException, 143 ParseErrorException, MethodInvocationException 144 { 145 146 /* 147 * Evaluate the string with the current context. We know there is 148 * exactly one argument and it is a string or reference. 149 */ 150 151 Object value = node.jjtGetChild(0).value( context ); 152 String sourceText; 153 if ( value != null ) 154 { 155 sourceText = value.toString(); 156 } 157 else 158 { 159 sourceText = ""; 160 } 161 162 /* 163 * The new string needs to be parsed since the text has been dynamically generated. 164 */ 165 String templateName = context.getCurrentTemplateName(); 166 Template template = (Template)context.getCurrentResource(); 167 if (template == null) 168 { 169 template = new Template(); 170 template.setName(templateName); 171 } 172 SimpleNode nodeTree = null; 173 174 try 175 { 176 nodeTree = rsvc.parse(new StringReader(sourceText), template); 177 } 178 catch (ParseException | TemplateInitException pex) 179 { 180 // use the line/column from the template 181 Info info = new Info( templateName, node.getLine(), node.getColumn() ); 182 throw new ParseErrorException( pex.getMessage(), info, rsvc.getLogContext().getStackTrace() ); 183 } 184 185 /* 186 * now we want to init and render. Chain the context 187 * to prevent any changes to the current context. 188 */ 189 190 if (nodeTree != null) 191 { 192 context.pushCurrentTemplateName(templateName); 193 194 try 195 { 196 try 197 { 198 nodeTree.init(context, rsvc); 199 } 200 catch (TemplateInitException pex) 201 { 202 Info info = new Info( templateName, node.getLine(), node.getColumn() ); 203 throw new ParseErrorException( pex.getMessage(), info, rsvc.getLogContext().getStackTrace() ); 204 } 205 206 try 207 { 208 preRender(context); 209 210 /* 211 * now render, and let any exceptions fly 212 */ 213 nodeTree.render(context, writer); 214 } 215 catch (StopCommand stop) 216 { 217 if (!stop.isFor(this)) 218 { 219 throw stop; 220 } 221 } 222 catch (ParseErrorException pex) 223 { 224 // convert any parsing errors to the correct line/col 225 Info info = new Info( templateName, node.getLine(), node.getColumn() ); 226 throw new ParseErrorException( pex.getMessage(), info, rsvc.getLogContext().getStackTrace() ); 227 } 228 } 229 finally 230 { 231 context.popCurrentTemplateName(); 232 postRender(context); 233 } 234 return true; 235 } 236 237 return false; 238 } 239 240 } 241