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  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