View Javadoc

1   package org.apache.velocity;
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.BufferedReader;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.UnsupportedEncodingException;
27  import java.io.Writer;
28  import java.util.List;
29  
30  import org.apache.velocity.context.Context;
31  import org.apache.velocity.context.InternalContextAdapterImpl;
32  import org.apache.velocity.exception.MethodInvocationException;
33  import org.apache.velocity.exception.ParseErrorException;
34  import org.apache.velocity.exception.ResourceNotFoundException;
35  import org.apache.velocity.exception.TemplateInitException;
36  import org.apache.velocity.exception.VelocityException;
37  import org.apache.velocity.runtime.parser.ParseException;
38  import org.apache.velocity.runtime.parser.node.SimpleNode;
39  import org.apache.velocity.runtime.resource.Resource;
40  import org.apache.velocity.runtime.resource.ResourceManager;
41  
42  /**
43   * This class is used for controlling all template
44   * operations. This class uses a parser created
45   * by JavaCC to create an AST that is subsequently
46   * traversed by a Visitor.
47   *
48   * <pre>
49   * // set up and initialize Velocity before this code block
50   *
51   * Template template = Velocity.getTemplate("test.wm");
52   * Context context = new VelocityContext();
53   *
54   * context.put("foo", "bar");
55   * context.put("customer", new Customer());
56   *
57   * template.merge(context, writer);
58   * </pre>
59   *
60   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
61   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
62   * @version $Id: Template.java 685724 2008-08-13 23:12:12Z nbubna $
63   */
64  public class Template extends Resource
65  {
66      private VelocityException errorCondition = null;
67  
68      /** Default constructor */
69      public Template()
70      {
71          super();
72          
73          setType(ResourceManager.RESOURCE_TEMPLATE);
74      }
75  
76      /**
77       *  gets the named resource as a stream, parses and inits
78       *
79       * @return true if successful
80       * @throws ResourceNotFoundException if template not found
81       *          from any available source.
82       * @throws ParseErrorException if template cannot be parsed due
83       *          to syntax (or other) error.
84       * @throws IOException problem reading input stream
85       */
86      public boolean process()
87          throws ResourceNotFoundException, ParseErrorException, IOException
88      {
89          data = null;
90          InputStream is = null;
91          errorCondition = null;
92  
93          /*
94           *  first, try to get the stream from the loader
95           */
96          try
97          {
98              is = resourceLoader.getResourceStream(name);
99          }
100         catch( ResourceNotFoundException rnfe )
101         {
102             /*
103              *  remember and re-throw
104              */
105 
106             errorCondition = rnfe;
107             throw rnfe;
108         }
109 
110         /*
111          *  if that worked, lets protect in case a loader impl
112          *  forgets to throw a proper exception
113          */
114 
115         if (is != null)
116         {
117             /*
118              *  now parse the template
119              */
120 
121             try
122             {
123                 BufferedReader br = new BufferedReader( new InputStreamReader( is, encoding ) );
124                 data = rsvc.parse( br, name);
125                 initDocument();
126                 return true;
127             }
128             catch( UnsupportedEncodingException  uce )
129             {
130                 String msg = "Template.process : Unsupported input encoding : " + encoding
131                 + " for template " + name;
132 
133                 errorCondition  = new ParseErrorException( msg );
134                 throw errorCondition;
135             }
136             catch ( ParseException pex )
137             {
138                 /*
139                  *  remember the error and convert
140                  */
141                 errorCondition =  new ParseErrorException( pex );
142                 throw errorCondition;
143             }
144             catch ( TemplateInitException pex )
145             {
146                 errorCondition = new ParseErrorException( pex );
147                 throw errorCondition;
148             }
149             /**
150              * pass through runtime exceptions
151              */
152             catch( RuntimeException e )
153             {
154                 throw new RuntimeException("Exception thrown processing Template "+getName(), e);
155             }
156             finally
157             {
158                 /*
159                  *  Make sure to close the inputstream when we are done.
160                  */
161                 is.close();
162             }
163         }
164         else
165         {
166             /*
167              *  is == null, therefore we have some kind of file issue
168              */
169 
170             errorCondition = new ResourceNotFoundException("Unknown resource error for resource " + name );
171             throw errorCondition;
172         }
173     }
174 
175     /**
176      *  initializes the document.  init() is not longer
177      *  dependant upon context, but we need to let the
178      *  init() carry the template name down throught for VM
179      *  namespace features
180      * @throws TemplateInitException When a problem occurs during the document initialization.
181      */
182     public void initDocument()
183     throws TemplateInitException
184     {
185         /*
186          *  send an empty InternalContextAdapter down into the AST to initialize it
187          */
188 
189         InternalContextAdapterImpl ica = new InternalContextAdapterImpl(  new VelocityContext() );
190 
191         try
192         {
193             /*
194              *  put the current template name on the stack
195              */
196 
197             ica.pushCurrentTemplateName( name );
198             ica.setCurrentResource( this );
199 
200             /*
201              *  init the AST
202              */
203 
204             ((SimpleNode)data).init( ica, rsvc);
205         }
206         finally
207         {
208             /*
209              *  in case something blows up...
210              *  pull it off for completeness
211              */
212 
213             ica.popCurrentTemplateName();
214             ica.setCurrentResource( null );
215         }
216 
217     }
218 
219     /**
220      * The AST node structure is merged with the
221      * context to produce the final output.
222      *
223      *  @param context Conext with data elements accessed by template
224      *  @param writer output writer for rendered template
225      *  @throws ResourceNotFoundException if template not found
226      *          from any available source.
227      *  @throws ParseErrorException if template cannot be parsed due
228      *          to syntax (or other) error.
229      *  @throws MethodInvocationException When a method on a referenced object in the context could not invoked.
230      *  @throws IOException  Might be thrown while rendering.
231      */
232     public void merge( Context context, Writer writer)
233         throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException
234     {
235         merge(context, writer, null);
236     }
237 
238     
239     /**
240      * The AST node structure is merged with the
241      * context to produce the final output.
242      *
243      *  @param context Conext with data elements accessed by template
244      *  @param writer output writer for rendered template
245      *  @param macroLibraries a list of template files containing macros to be used when merging
246      *  @throws ResourceNotFoundException if template not found
247      *          from any available source.
248      *  @throws ParseErrorException if template cannot be parsed due
249      *          to syntax (or other) error.
250      *  @throws MethodInvocationException When a method on a referenced object in the context could not invoked.
251      *  @throws IOException  Might be thrown while rendering.
252      *  @since 1.6
253      */
254     public void merge( Context context, Writer writer, List macroLibraries)
255         throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException
256     {
257         /*
258          *  we shouldn't have to do this, as if there is an error condition,
259          *  the application code should never get a reference to the
260          *  Template
261          */
262 
263         if (errorCondition != null)
264         {
265             throw errorCondition;
266         }
267 
268         if( data != null)
269         {
270             /*
271              *  create an InternalContextAdapter to carry the user Context down
272              *  into the rendering engine.  Set the template name and render()
273              */
274 
275             InternalContextAdapterImpl ica = new InternalContextAdapterImpl( context );
276 
277             /**
278              * Set the macro libraries
279              */
280             ica.setMacroLibraries(macroLibraries);
281 
282             if (macroLibraries != null)
283             {
284                 for (int i = 0; i < macroLibraries.size(); i++)
285                 {
286                     /**
287                      * Build the macro library
288                      */
289                     try
290                     {
291                         rsvc.getTemplate((String) macroLibraries.get(i));
292                     }
293                     catch (ResourceNotFoundException re)
294                     {
295                         /*
296                         * the macro lib wasn't found.  Note it and throw
297                         */
298                         rsvc.getLog().error("template.merge(): " +
299                                 "cannot find template " +
300                                 (String) macroLibraries.get(i));
301                         throw re;
302                     }
303                     catch (ParseErrorException pe)
304                     {
305                         /*
306                         * the macro lib was found, but didn't parse - syntax error
307                         *  note it and throw
308                         */
309                         rsvc.getLog().error("template.merge(): " +
310                                 "syntax error in template " +
311                                 (String) macroLibraries.get(i) + ".");
312                         throw pe;
313                     }
314                     
315                     catch (Exception e)
316                     {
317                         throw new RuntimeException("Template.merge(): parse failed in template  " +
318                                 (String) macroLibraries.get(i) + ".", e);
319                     }
320                 }
321             }
322 
323             try
324             {
325                 ica.pushCurrentTemplateName( name );
326                 ica.setCurrentResource( this );
327 
328                 ( (SimpleNode) data ).render( ica, writer);
329             }
330             finally
331             {
332                 /*
333                  *  lets make sure that we always clean up the context
334                  */
335                 ica.popCurrentTemplateName();
336                 ica.setCurrentResource( null );
337             }
338         }
339         else
340         {
341             /*
342              * this shouldn't happen either, but just in case.
343              */
344 
345             String msg = "Template.merge() failure. The document is null, " +
346                 "most likely due to parsing error.";
347 
348             throw new RuntimeException(msg);
349 
350         }
351     }
352 }