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 java.io.IOException;
23 import java.io.Writer;
24 import java.util.List;
25 import java.util.ArrayList;
26
27 import org.apache.velocity.Template;
28 import org.apache.velocity.app.event.EventHandlerUtil;
29 import org.apache.velocity.context.InternalContextAdapter;
30 import org.apache.velocity.exception.MethodInvocationException;
31 import org.apache.velocity.exception.ParseErrorException;
32 import org.apache.velocity.exception.ResourceNotFoundException;
33 import org.apache.velocity.exception.VelocityException;
34 import org.apache.velocity.exception.TemplateInitException;
35 import org.apache.velocity.runtime.RuntimeConstants;
36 import org.apache.velocity.runtime.RuntimeServices;
37 import org.apache.velocity.runtime.parser.node.Node;
38 import org.apache.velocity.runtime.parser.node.SimpleNode;
39
40 /**
41 * Pluggable directive that handles the <code>#parse()</code>
42 * statement in VTL.
43 *
44 * <pre>
45 * Notes:
46 * -----
47 * 1) The parsed source material can only come from somewhere in
48 * the TemplateRoot tree for security reasons. There is no way
49 * around this. If you want to include content from elsewhere on
50 * your disk, use a link from somwhere under Template Root to that
51 * content.
52 *
53 * 2) There is a limited parse depth. It is set as a property
54 * "directive.parse.max.depth = 10" by default. This 10 deep
55 * limit is a safety feature to prevent infinite loops.
56 * </pre>
57 *
58 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
59 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
60 * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
61 * @version $Id: Parse.java 696387 2008-09-17 18:19:16Z nbubna $
62 */
63 public class Parse extends InputBase
64 {
65 private int maxDepth;
66
67 /**
68 * Return name of this directive.
69 * @return The name of this directive.
70 */
71 public String getName()
72 {
73 return "parse";
74 }
75
76 /**
77 * Return type of this directive.
78 * @return The type of this directive.
79 */
80 public int getType()
81 {
82 return LINE;
83 }
84
85 /**
86 * Init's the #parse directive.
87 * @param rs
88 * @param context
89 * @param node
90 * @throws TemplateInitException
91 */
92 public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
93 throws TemplateInitException
94 {
95 super.init(rs, context, node);
96
97 this.maxDepth = rsvc.getInt(RuntimeConstants.PARSE_DIRECTIVE_MAXDEPTH, 10);
98 }
99
100 /**
101 * iterates through the argument list and renders every
102 * argument that is appropriate. Any non appropriate
103 * arguments are logged, but render() continues.
104 * @param context
105 * @param writer
106 * @param node
107 * @return True if the directive rendered successfully.
108 * @throws IOException
109 * @throws ResourceNotFoundException
110 * @throws ParseErrorException
111 * @throws MethodInvocationException
112 */
113 public boolean render( InternalContextAdapter context,
114 Writer writer, Node node)
115 throws IOException, ResourceNotFoundException, ParseErrorException,
116 MethodInvocationException
117 {
118 /*
119 * if rendering is no longer allowed (after a stop), we can safely
120 * skip execution of all the parse directives.
121 */
122 if(!context.getAllowRendering())
123 {
124 return true;
125 }
126
127 /*
128 * did we get an argument?
129 */
130 if ( node.jjtGetChild(0) == null)
131 {
132 rsvc.getLog().error("#parse() null argument");
133 return false;
134 }
135
136 /*
137 * does it have a value? If you have a null reference, then no.
138 */
139 Object value = node.jjtGetChild(0).value( context );
140
141 if ( value == null)
142 {
143 rsvc.getLog().error("#parse() null argument");
144 return false;
145 }
146
147 /*
148 * get the path
149 */
150 String sourcearg = value.toString();
151
152 /*
153 * check to see if the argument will be changed by the event cartridge
154 */
155
156
157 String arg = EventHandlerUtil.includeEvent( rsvc, context, sourcearg, context.getCurrentTemplateName(), getName());
158
159 /*
160 * a null return value from the event cartridge indicates we should not
161 * input a resource.
162 */
163 boolean blockinput = false;
164 if (arg == null)
165 blockinput = true;
166
167
168 if (maxDepth > 0)
169 {
170 /*
171 * see if we have exceeded the configured depth.
172 */
173 Object[] templateStack = context.getTemplateNameStack();
174 if (templateStack.length >= maxDepth)
175 {
176 StringBuffer path = new StringBuffer();
177 for( int i = 0; i < templateStack.length; ++i)
178 {
179 path.append( " > " + templateStack[i] );
180 }
181 rsvc.getLog().error("Max recursion depth reached (" +
182 templateStack.length + ')' + " File stack:" +
183 path);
184 return false;
185 }
186 }
187
188 /*
189 * now use the Runtime resource loader to get the template
190 */
191
192 Template t = null;
193
194 try
195 {
196 if (!blockinput)
197 t = rsvc.getTemplate( arg, getInputEncoding(context) );
198 }
199 catch ( ResourceNotFoundException rnfe )
200 {
201 /*
202 * the arg wasn't found. Note it and throw
203 */
204 rsvc.getLog().error("#parse(): cannot find template '" + arg +
205 "', called from template " +
206 context.getCurrentTemplateName() + " at (" +
207 getLine() + ", " + getColumn() + ")" );
208 throw rnfe;
209 }
210 catch ( ParseErrorException pee )
211 {
212 /*
213 * the arg was found, but didn't parse - syntax error
214 * note it and throw
215 */
216
217 rsvc.getLog().error("#parse(): syntax error in #parse()-ed template '"
218 + arg + "', called from template " +
219 context.getCurrentTemplateName() + " at (" +
220 getLine() + ", " + getColumn() + ")" );
221
222 throw pee;
223 }
224 /**
225 * pass through application level runtime exceptions
226 */
227 catch( RuntimeException e )
228 {
229 throw e;
230 }
231 catch ( Exception e)
232 {
233 String msg = "#parse() : arg = " + arg + '.';
234 rsvc.getLog().error(msg, e);
235 throw new VelocityException(msg, e);
236 }
237
238 /**
239 * Add the template name to the macro libraries list
240 */
241 List macroLibraries = context.getMacroLibraries();
242
243 /**
244 * if macroLibraries are not set create a new one
245 */
246 if (macroLibraries == null)
247 {
248 macroLibraries = new ArrayList();
249 }
250
251 context.setMacroLibraries(macroLibraries);
252
253 macroLibraries.add(arg);
254
255 /*
256 * and render it
257 */
258 try
259 {
260 if (!blockinput) {
261 context.pushCurrentTemplateName(arg);
262 ((SimpleNode) t.getData()).render( context, writer );
263 }
264 }
265
266 /*
267 * if it's a MIE, it came from the render.... throw it...
268 */
269 catch( MethodInvocationException e )
270 {
271 throw e;
272 }
273
274 /**
275 * pass through application level runtime exceptions
276 */
277 catch( RuntimeException e )
278 {
279 throw e;
280 }
281
282
283 catch ( Exception e )
284 {
285 String msg = "Exception rendering #parse(" + arg + ')';
286 rsvc.getLog().error(msg, e);
287 throw new VelocityException(msg, e);
288 }
289 finally
290 {
291 if (!blockinput)
292 context.popCurrentTemplateName();
293 }
294
295 /*
296 * note - a blocked input is still a successful operation as this is
297 * expected behavior.
298 */
299
300 return true;
301 }
302
303 }
304