View Javadoc

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