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