001: package org.apache.velocity.runtime.directive;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.IOException;
023: import java.io.Writer;
024:
025: import org.apache.velocity.Template;
026: import org.apache.velocity.app.event.EventHandlerUtil;
027: import org.apache.velocity.context.InternalContextAdapter;
028: import org.apache.velocity.exception.MethodInvocationException;
029: import org.apache.velocity.exception.ParseErrorException;
030: import org.apache.velocity.exception.ResourceNotFoundException;
031: import org.apache.velocity.runtime.RuntimeConstants;
032: import org.apache.velocity.runtime.parser.node.Node;
033: import org.apache.velocity.runtime.parser.node.SimpleNode;
034:
035: /**
036: * Pluggable directive that handles the <code>#parse()</code>
037: * statement in VTL.
038: *
039: * <pre>
040: * Notes:
041: * -----
042: * 1) The parsed source material can only come from somewhere in
043: * the TemplateRoot tree for security reasons. There is no way
044: * around this. If you want to include content from elsewhere on
045: * your disk, use a link from somwhere under Template Root to that
046: * content.
047: *
048: * 2) There is a limited parse depth. It is set as a property
049: * "parse_directive.maxdepth = 10" for example. There is a 20 iteration
050: * safety in the event that the parameter isn't set.
051: * </pre>
052: *
053: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
054: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
055: * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
056: * @version $Id: Parse.java 463298 2006-10-12 16:10:32Z henning $
057: */
058: public class Parse extends InputBase {
059: /**
060: * Return name of this directive.
061: * @return The name of this directive.
062: */
063: public String getName() {
064: return "parse";
065: }
066:
067: /**
068: * Return type of this directive.
069: * @return The type of this directive.
070: */
071: public int getType() {
072: return LINE;
073: }
074:
075: /**
076: * iterates through the argument list and renders every
077: * argument that is appropriate. Any non appropriate
078: * arguments are logged, but render() continues.
079: * @param context
080: * @param writer
081: * @param node
082: * @return True if the directive rendered successfully.
083: * @throws IOException
084: * @throws ResourceNotFoundException
085: * @throws ParseErrorException
086: * @throws MethodInvocationException
087: */
088: public boolean render(InternalContextAdapter context,
089: Writer writer, Node node) throws IOException,
090: ResourceNotFoundException, ParseErrorException,
091: MethodInvocationException {
092: /*
093: * if rendering is no longer allowed (after a stop), we can safely
094: * skip execution of all the parse directives.
095: */
096: if (!context.getAllowRendering()) {
097: return true;
098: }
099:
100: /*
101: * did we get an argument?
102: */
103: if (node.jjtGetChild(0) == null) {
104: rsvc.getLog().error("#parse() null argument");
105: return false;
106: }
107:
108: /*
109: * does it have a value? If you have a null reference, then no.
110: */
111: Object value = node.jjtGetChild(0).value(context);
112:
113: if (value == null) {
114: rsvc.getLog().error("#parse() null argument");
115: return false;
116: }
117:
118: /*
119: * get the path
120: */
121: String sourcearg = value.toString();
122:
123: /*
124: * check to see if the argument will be changed by the event cartridge
125: */
126:
127: String arg = EventHandlerUtil.includeEvent(rsvc, context,
128: sourcearg, context.getCurrentTemplateName(), getName());
129:
130: /*
131: * a null return value from the event cartridge indicates we should not
132: * input a resource.
133: */
134: boolean blockinput = false;
135: if (arg == null)
136: blockinput = true;
137:
138: /*
139: * see if we have exceeded the configured depth.
140: * If it isn't configured, put a stop at 20 just in case.
141: */
142:
143: Object[] templateStack = context.getTemplateNameStack();
144:
145: if (templateStack.length >= rsvc.getInt(
146: RuntimeConstants.PARSE_DIRECTIVE_MAXDEPTH, 20)) {
147: StringBuffer path = new StringBuffer();
148:
149: for (int i = 0; i < templateStack.length; ++i) {
150: path.append(" > " + templateStack[i]);
151: }
152:
153: rsvc.getLog().error(
154: "Max recursion depth reached ("
155: + templateStack.length + ')'
156: + " File stack:" + path);
157: return false;
158: }
159:
160: /*
161: * now use the Runtime resource loader to get the template
162: */
163:
164: Template t = null;
165:
166: try {
167: if (!blockinput)
168: t = rsvc.getTemplate(arg, getInputEncoding(context));
169: } catch (ResourceNotFoundException rnfe) {
170: /*
171: * the arg wasn't found. Note it and throw
172: */
173: rsvc.getLog().error(
174: "#parse(): cannot find template '" + arg
175: + "', called from template "
176: + context.getCurrentTemplateName()
177: + " at (" + getLine() + ", " + getColumn()
178: + ")");
179: throw rnfe;
180: } catch (ParseErrorException pee) {
181: /*
182: * the arg was found, but didn't parse - syntax error
183: * note it and throw
184: */
185:
186: rsvc.getLog().error(
187: "#parse(): syntax error in #parse()-ed template '"
188: + arg + "', called from template "
189: + context.getCurrentTemplateName()
190: + " at (" + getLine() + ", " + getColumn()
191: + ")");
192:
193: throw pee;
194: }
195: /**
196: * pass through application level runtime exceptions
197: */
198: catch (RuntimeException e) {
199: throw e;
200: } catch (Exception e) {
201: rsvc.getLog().error("#parse() : arg = " + arg + '.', e);
202: return false;
203: }
204:
205: /*
206: * and render it
207: */
208: try {
209: if (!blockinput) {
210: context.pushCurrentTemplateName(arg);
211: ((SimpleNode) t.getData()).render(context, writer);
212: }
213: }
214:
215: /*
216: * if it's a MIE, it came from the render.... throw it...
217: */
218: catch (MethodInvocationException e) {
219: throw e;
220: }
221:
222: /**
223: * pass through application level runtime exceptions
224: */
225: catch (RuntimeException e) {
226: throw e;
227: }
228:
229: catch (Exception e) {
230: rsvc.getLog().error(
231: "Exception rendering #parse(" + arg + ')', e);
232: return false;
233: } finally {
234: if (!blockinput)
235: context.popCurrentTemplateName();
236: }
237:
238: /*
239: * note - a blocked input is still a successful operation as this is
240: * expected behavior.
241: */
242:
243: return true;
244: }
245:
246: }
|