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