001: package org.apache.velocity;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.BufferedReader;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.InputStreamReader;
026: import java.io.UnsupportedEncodingException;
027: import java.io.Writer;
028:
029: import org.apache.velocity.context.Context;
030: import org.apache.velocity.context.InternalContextAdapterImpl;
031: import org.apache.velocity.exception.MethodInvocationException;
032: import org.apache.velocity.exception.ParseErrorException;
033: import org.apache.velocity.exception.ResourceNotFoundException;
034: import org.apache.velocity.exception.TemplateInitException;
035: import org.apache.velocity.exception.VelocityException;
036: import org.apache.velocity.runtime.parser.ParseException;
037: import org.apache.velocity.runtime.parser.node.SimpleNode;
038: import org.apache.velocity.runtime.resource.Resource;
039:
040: /**
041: * This class is used for controlling all template
042: * operations. This class uses a parser created
043: * by JavaCC to create an AST that is subsequently
044: * traversed by a Visitor.
045: *
046: * <pre>
047: * // set up and initialize Velocity before this code block
048: *
049: * Template template = Velocity.getTemplate("test.wm");
050: * Context context = new VelocityContext();
051: *
052: * context.put("foo", "bar");
053: * context.put("customer", new Customer());
054: *
055: * template.merge(context, writer);
056: * </pre>
057: *
058: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
059: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
060: * @version $Id: Template.java 490011 2006-12-24 12:19:09Z henning $
061: */
062: public class Template extends Resource {
063: private VelocityException errorCondition = null;
064:
065: /** Default constructor */
066: public Template() {
067: }
068:
069: /**
070: * gets the named resource as a stream, parses and inits
071: *
072: * @return true if successful
073: * @throws ResourceNotFoundException if template not found
074: * from any available source.
075: * @throws ParseErrorException if template cannot be parsed due
076: * to syntax (or other) error.
077: * @throws IOException problem reading input stream
078: */
079: public boolean process() throws ResourceNotFoundException,
080: ParseErrorException, IOException {
081: data = null;
082: InputStream is = null;
083: errorCondition = null;
084:
085: /*
086: * first, try to get the stream from the loader
087: */
088: try {
089: is = resourceLoader.getResourceStream(name);
090: } catch (ResourceNotFoundException rnfe) {
091: /*
092: * remember and re-throw
093: */
094:
095: errorCondition = rnfe;
096: throw rnfe;
097: }
098:
099: /*
100: * if that worked, lets protect in case a loader impl
101: * forgets to throw a proper exception
102: */
103:
104: if (is != null) {
105: /*
106: * now parse the template
107: */
108:
109: try {
110: BufferedReader br = new BufferedReader(
111: new InputStreamReader(is, encoding));
112:
113: data = rsvc.parse(br, name);
114: initDocument();
115: return true;
116: } catch (UnsupportedEncodingException uce) {
117: String msg = "Template.process : Unsupported input encoding : "
118: + encoding + " for template " + name;
119:
120: errorCondition = new ParseErrorException(msg);
121: throw errorCondition;
122: } catch (ParseException pex) {
123: /*
124: * remember the error and convert
125: */
126: errorCondition = new ParseErrorException(pex);
127: throw errorCondition;
128: } catch (TemplateInitException pex) {
129: errorCondition = new ParseErrorException(pex);
130: throw errorCondition;
131: }
132: /**
133: * pass through runtime exceptions
134: */
135: catch (RuntimeException e) {
136: throw e;
137: } finally {
138: /*
139: * Make sure to close the inputstream when we are done.
140: */
141: is.close();
142: }
143: } else {
144: /*
145: * is == null, therefore we have some kind of file issue
146: */
147:
148: errorCondition = new ResourceNotFoundException(
149: "Unknown resource error for resource " + name);
150: throw errorCondition;
151: }
152: }
153:
154: /**
155: * initializes the document. init() is not longer
156: * dependant upon context, but we need to let the
157: * init() carry the template name down throught for VM
158: * namespace features
159: * @throws TemplateInitException When a problem occurs during the document initialization.
160: */
161: public void initDocument() throws TemplateInitException {
162: /*
163: * send an empty InternalContextAdapter down into the AST to initialize it
164: */
165:
166: InternalContextAdapterImpl ica = new InternalContextAdapterImpl(
167: new VelocityContext());
168:
169: try {
170: /*
171: * put the current template name on the stack
172: */
173:
174: ica.pushCurrentTemplateName(name);
175:
176: /*
177: * init the AST
178: */
179:
180: ((SimpleNode) data).init(ica, rsvc);
181: } finally {
182: /*
183: * in case something blows up...
184: * pull it off for completeness
185: */
186:
187: ica.popCurrentTemplateName();
188: }
189:
190: }
191:
192: /**
193: * The AST node structure is merged with the
194: * context to produce the final output.
195: *
196: * @param context Conext with data elements accessed by template
197: * @param writer output writer for rendered template
198: * @throws ResourceNotFoundException if template not found
199: * from any available source.
200: * @throws ParseErrorException if template cannot be parsed due
201: * to syntax (or other) error.
202: * @throws MethodInvocationException When a method on a referenced object in the context could not invoked.
203: * @throws IOException Might be thrown while rendering.
204: */
205: public void merge(Context context, Writer writer)
206: throws ResourceNotFoundException, ParseErrorException,
207: MethodInvocationException, IOException {
208: /*
209: * we shouldn't have to do this, as if there is an error condition,
210: * the application code should never get a reference to the
211: * Template
212: */
213:
214: if (errorCondition != null) {
215: throw errorCondition;
216: }
217:
218: if (data != null) {
219: /*
220: * create an InternalContextAdapter to carry the user Context down
221: * into the rendering engine. Set the template name and render()
222: */
223:
224: InternalContextAdapterImpl ica = new InternalContextAdapterImpl(
225: context);
226:
227: try {
228: ica.pushCurrentTemplateName(name);
229: ica.setCurrentResource(this );
230:
231: ((SimpleNode) data).render(ica, writer);
232: } finally {
233: /*
234: * lets make sure that we always clean up the context
235: */
236: ica.popCurrentTemplateName();
237: ica.setCurrentResource(null);
238: }
239: } else {
240: /*
241: * this shouldn't happen either, but just in case.
242: */
243:
244: String msg = "Template.merge() failure. The document is null, "
245: + "most likely due to parsing error.";
246:
247: throw new RuntimeException(msg);
248:
249: }
250: }
251: }
|