• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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