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