##### Coverage Report - org.webmacro.engine.WMTemplate

Classes in this File | Line Coverage | Branch Coverage | Complexity
WMTemplate |
50% | 59/116
41% | 5/12
| 2.5789473684210527;2.579

1 |  |
     /*
2 |  |
      * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
3 |  |
      *
4 |  |
      * Redistribution and use in source and binary forms, with or without
5 |  |
      * modification, are permitted under the terms of either of the following
6 |  |
      * Open Source licenses:
7 |  |
      *
8 |  |
      * The GNU General Public License, version 2, or any later version, as
9 |  |
      * published by the Free Software Foundation
10 |  |
      * (http://www.fsf.org/copyleft/gpl.html);
11 |  |
      *
12 |  |
      *  or
13 |  |
      *
14 |  |
      * The Semiotek Public License (http://webmacro.org/LICENSE.)
15 |  |
      *
16 |  |
      * This software is provided "as is", with NO WARRANTY, not even the
17 |  |
      * implied warranties of fitness to purpose, or merchantability. You
18 |  |
      * assume all risks and liabilities associated with its use.
19 |  |
      *
20 |  |
      * See www.webmacro.org for more information on the WebMacro project.
21 |  |
      */
22 |  |
23 |  |
24 |  |
     package org.webmacro.engine;
25 |  |
26 |  |
     import java.io.IOException;
27 |  |
     import java.io.OutputStream;
28 |  |
     import java.io.Reader;
29 |  |
     import java.util.Iterator;
30 |  |
     import java.util.Map;
31 |  |
32 |  |
     import org.slf4j.Logger;
33 |  |
     import org.slf4j.LoggerFactory;
34 |  |
35 |  |
     import org.webmacro.Broker;
36 |  |
     import org.webmacro.Context;
37 |  |
     import org.webmacro.FastWriter;
38 |  |
     import org.webmacro.PropertyException;
39 |  |
     import org.webmacro.Template;
40 |  |
     import org.webmacro.TemplateException;
41 |  |
     import org.webmacro.TemplateVisitor;
42 |  |
     import org.webmacro.WMConstants;
43 |  |
44 |  |
     /**
45 |  |
      * Template objects represent the user defined layout into which the
46 |  |
      * webmacro package will substitute values. It is a very simple kind of
47 |  |
      * interpreted language containing text, blocks, and directives. Text is
48 |  |
      * to be passed through verbatim. LList group text and directives into
49 |  |
      * linear lists and sublists. Directives determine how subsequent blocks
50 |  |
      * are to be processed and constitute the commands of the language.
51 |  |
      * <p>
52 |  |
      * The Template is lazily evaluated: it does not parse or open the
53 |  |
      * supplied filename until it is used. Once it has parsed the file, it
54 |  |
      * never alters its data. The intent is to allow a Template to be parsed
55 |  |
      * once (somewhat expensive) and then used many times; and also not to
56 |  |
      * incur any parsing costs at all if the Template is never actually used.
57 |  |
      * <p>
58 |  |
      * CONCURRENCY: You must parse() the template before making it available
59 |  |
      * to other threads. After a template has been parsed it is immutable and
60 |  |
      * can be shared between threads without synchronization. This class
61 |  |
      * performs no synchronization itself but instead relies on the
62 |  |
      * TemplateProvider/Broker to synchronize access.
63 |  |
      */
64 |  |
     abstract public class WMTemplate implements Template
65 |  |
     {
66 |  |
67 | 2 |
         static Logger _log =  LoggerFactory.getLogger(WMTemplate.class);
68 |  |
         /**
69 |  |
          * The resource broker used to resolve things in this template.
70 |  |
          */
71 |  |
         final protected Broker _broker;
72 |  |
73 |  |
         /**
74 |  |
          * Whether template has already been parsed.
75 |  |
          */
76 |  |
         private boolean _parsed;
77 |  |
78 |  |
         /**
79 |  |
          * What this template contains is a top level block.
80 |  |
          */
81 |  |
         protected Block _content;
82 |  |
83 |  |
         /**
84 |  |
          * Which parser (grammar) is used to parse this template,
85 |  |
          * typically "wm".
86 |  |
          */
87 |  |
         private String _parserName;
88 |  |
89 |  |
         /**
90 |  |
          * Template parameters.
91 |  |
          */
92 |  |
         private Map _parameters;
93 |  |
94 |  |
         /**
95 |  |
          * Template Macros.
96 |  |
          */
97 |  |
         private Map _macros;
98 |  |
99 |  |
100 |  |
         /**
101 |  |
          * Create a new Template. Constructors must supply a broker.
102 |  |
          */
103 |  |
         protected WMTemplate (Broker broker)
104 |  |
         {
105 | 228 |
             this("wm", broker);
106 | 228 |
         }
107 |  |
108 |  |
         /**
109 |  |
          * Create a new Template specifying both the broker and the
110 |  |
          * parsing language.
111 |  |
          */
112 |  |
         protected WMTemplate (String parserName, Broker broker)
113 | 228 |
         {
114 | 228 |
             _broker = broker;
115 | 228 |
             _parserName = parserName;
116 | 228 |
         }
117 |  |
118 |  |
         /**
119 |  |
          * Get the stream the template should be read from. Parse will
120 |  |
          * call this method in order to locate a stream.
121 |  |
          * @exception IOException if unable to read template
122 |  |
          */
123 |  |
         protected abstract Reader getReader () throws IOException;
124 |  |
125 |  |
         /**
126 |  |
          * Return a name for this template. For example, if the template reads
127 |  |
          * from a file you might want to mention which it is--will be used to
128 |  |
          * produce error messages describing which template had a problem.
129 |  |
          */
130 |  |
         public abstract String toString ();
131 |  |
132 |  |
         /**
133 |  |
          * Return a name for this template. If not overridden, uses toString().
134 |  |
          */
135 |  |
         public String getName ()
136 |  |
         {
137 | 0 |
             return toString();
138 |  |
         }
139 |  |
140 |  |
         /**
141 |  |
          * Set the name for this template.  Default implementation does nothing.
142 |  |
          */
143 |  |
         public void setName (String name)
144 |  |
         {
145 | 0 |
         }
146 |  |
147 |  |
         /**
148 |  |
          * Subclasses can override this if they wish to invoke a parser
149 |  |
          * other than the WM parser, or choose the parser on some more
150 |  |
          * complicated condition. This method will be called by parse();
151 |  |
          */
152 |  |
         protected Parser getParser () throws TemplateException
153 |  |
         {
154 |  |
             try
155 |  |
             {
156 | 228 |
                 return (Parser) _broker.get("parser", "wm");
157 |  |
             }
158 | 0 |
             catch (Exception e)
159 |  |
             {
160 | 0 |
                 throw new TemplateException("Could not load parser type " +
161 |  |
                         _parserName, e);
162 |  |
             }
163 |  |
         }
164 |  |
165 |  |
         /**
166 |  |
          * Template API.
167 |  |
          */
168 |  |
         public void parse () throws IOException, TemplateException
169 |  |
         {
170 | 228 |
             if (!_parsed)
171 |  |
             {
172 | 228 |
                 Block newContent = null;
173 | 228 |
                 Map newParameters = null;
174 | 228 |
                 Map newMacros = null;
175 | 228 |
                 Reader in = null;
176 | 228 |
                 BuildContext bc = null;
177 |  |
                 try
178 |  |
                 {
179 | 228 |
                     Parser parser = getParser();
180 | 228 |
                     in = getReader();
181 | 228 |
                     BlockBuilder bb = parser.parseBlock(getName(), in);
182 | 224 |
                     in.close();
183 | 224 |
                     bc = new BuildContext(_broker);
184 |  |
                     // put global macros from Broker into the BuildContext
185 | 224 |
                     Map globalMacros = _broker.getMacros();
186 | 224 |
                     for (Iterator i=globalMacros.entrySet().iterator(); i.hasNext(); ) {
187 | 0 |
                         Map.Entry entry = (Map.Entry)i.next();
188 | 0 |
                         bc.putMacro((String)entry.getKey(),(MacroDefinition) entry.getValue());
189 | 0 |
                     }
190 | 224 |
                     newParameters = bc.getMap();
191 | 224 |
                     newMacros = bc.getMacros();
192 | 224 |
                     newContent = (Block) bb.build(bc);
193 |  |
                 }
194 | 0 |
                 catch (BuildException be)
195 |  |
                 {
196 | 0 |
                     if (bc != null)
197 | 0 |
                         be.setContextLocation(bc.getCurrentLocation());
198 | 0 |
                     _log.error("Template contained invalid data", be);
199 | 0 |
                     throw be;
200 |  |
                 }
201 | 0 |
                 catch (IOException e)
202 |  |
                 {
203 | 0 |
                     _log.error("Template: Could not read template: " + this);
204 | 0 |
                     throw e;
205 |  |
                 }
206 | 4 |
                 catch (Exception e)
207 |  |
                 {
208 | 4 |
                     _log.error("Error parsing template: " + this, e);
209 | 4 |
                     BuildException be = new BuildException("Error parsing template: " + this, e);
210 | 4 |
                     if (bc != null)
211 | 0 |
                         be.setContextLocation(bc.getCurrentLocation());
212 | 4 |
                     throw be;
213 |  |
                 }
214 |  |
                 finally
215 |  |
                 {
216 | 4 |
                     try
217 |  |
                     {
218 | 228 |
                         if (in != null)
219 | 228 |
                             in.close();
220 |  |
                     }
221 | 0 |
                     catch (IOException e)
222 |  |
                     {
223 | 0 |
                         e = null; // Real error reported above
224 | 228 |
                     }
225 | 228 |
                     _parameters = newParameters;
226 | 228 |
                     _content = newContent;
227 | 228 |
                     _macros = newMacros;
228 | 228 |
                     _parsed = true;
229 | 224 |
                 }
230 | 224 |
             }
231 |  |
             else
232 |  |
             {
233 | 0 |
                 _log.debug("Ignoring parse request on already parsed template " + this);
234 |  |
             }
235 | 224 |
         }
236 |  |
237 |  |
         /**
238 |  |
          * Get the #macros' defined for this template.
239 |  |
          *
240 |  |
          * @return the #macro's defined for this template, or null if this template has
241 |  |
          * not yet been <code>parse()'d</code>.
242 |  |
          */
243 |  |
         public Map getMacros ()
244 |  |
         {
245 | 0 |
             return _macros;
246 |  |
         }
247 |  |
248 |  |
         /**
249 |  |
          * Parse the Template against the supplied context data and
250 |  |
          * return it as a string. If the operation fails for some reason,
251 |  |
          * such as unable to read template or unable to introspect the context
252 |  |
          * then this method will return a null string.
253 |  |
          */
254 |  |
         public final String evaluateAsString (Context context) throws PropertyException
255 |  |
         {
256 |  |
             try
257 |  |
             {
258 | 30 |
                 FastWriter fw = FastWriter.getInstance(_broker);
259 | 30 |
                 write(fw, context);
260 | 28 |
                 String ret = fw.toString();
261 | 28 |
                 fw.close();
262 | 28 |
                 return ret;
263 |  |
             }
264 | 0 |
             catch (IOException e)
265 |  |
             {
266 | 0 |
                 _log.error("Template: Could not write to ByteArrayOutputStream!", e);
267 | 0 |
                 return null;
268 |  |
             }
269 |  |
         }
270 |  |
271 |  |
         /**
272 |  |
          * Parse the Template against the supplied context data and
273 |  |
          * return it as a byte array. If the operation fails for some reason,
274 |  |
          * such as unable to read template or unable to introspect the context
275 |  |
          * then this method will return a null string.
276 |  |
          */
277 |  |
         public final byte[] evaluateAsBytes (String encoding, Context context) throws PropertyException
278 |  |
         {
279 |  |
             try
280 |  |
             {
281 | 0 |
                 FastWriter fw = FastWriter.getInstance(_broker, encoding);
282 | 0 |
                 write(fw, context);
283 | 0 |
                 byte[] ret = fw.toByteArray();
284 | 0 |
                 fw.close();
285 | 0 |
                 return ret;
286 |  |
             }
287 | 0 |
             catch (IOException e)
288 |  |
             {
289 | 0 |
                 _log.error("Template: Could not write to ByteArrayOutputStream!", e);
290 | 0 |
                 return null;
291 |  |
             }
292 |  |
         }
293 |  |
294 |  |
         public void write(OutputStream out, Context context)
295 |  |
                 throws PropertyException, IOException {
296 | 16 |
             FastWriter fw = FastWriter.getInstance(_broker, out);
297 | 16 |
             write(fw, context);
298 | 16 |
             fw.flush();
299 | 16 |
             fw.close();
300 | 16 |
         }
301 |  |
302 |  |
         public void write(OutputStream out, String encoding, Context context)
303 |  |
                 throws PropertyException, IOException {
304 | 0 |
             FastWriter fw = FastWriter.getInstance(_broker, out, encoding);
305 | 0 |
             write(fw, context);
306 | 0 |
             fw.flush();
307 | 0 |
             fw.close();
308 | 0 |
         }
309 |  |
310 |  |
         public final void write (FastWriter out, Context context)
311 |  |
                 throws IOException, PropertyException
312 |  |
         {
313 |  |
             try
314 |  |
             {
315 | 1718 |
                 if (!_parsed)
316 |  |
                 {
317 | 0 |
                     parse();
318 |  |
                 }
319 |  |
             }
320 | 0 |
             catch (TemplateException e)
321 |  |
             {
322 | 0 |
                 _log.error("Template: Unable to parse template: " + this, e);
323 | 0 |
                 out.write(context.getEvaluationExceptionHandler()
324 |  |
                         .errorString("Template failed to parse. Reason: \n"
325 |  |
                         + e.toString()));
326 | 1718 |
             }
327 |  |
328 |  |
             try
329 |  |
             {
330 | 1718 |
                 _content.write(out, context);
331 |  |
             }
332 | 24 |
             catch (PropertyException e)
333 |  |
             {
334 | 24 |
                 e.setContextLocation(context.getCurrentLocation());
335 | 24 |
                 throw e;
336 |  |
             }
337 | 0 |
             catch (IOException ioe)
338 |  |
             {
339 | 0 |
                 throw ioe;
340 |  |
             }
341 | 0 |
             catch (Exception e)
342 |  |
             {
343 | 0 |
                 String warning =
344 |  |
                         "Template: Exception evaluating template " + this;
345 | 0 |
                 _log.warn(warning, e);
346 |  |
347 | 0 |
                 out.write(context.getEvaluationExceptionHandler()
348 |  |
                         .warningString("Could not interpret template. Reason: \n"
349 |  |
                         + warning + "\n" + e.toString()));
350 | 1694 |
             }
351 | 1694 |
         }
352 |  |
353 |  |
         public void accept (TemplateVisitor v)
354 |  |
         {
355 | 0 |
             _content.accept(v);
356 | 0 |
         }
357 |  |
358 |  |
359 |  |
         /**
360 |  |
          * return the default encoding either from the WebMacro config
361 |  |
          * or the JVM settings.
362 |  |
          *
363 |  |
          * Note for Unix users: you may need to set the environmental variable
364 |  |
          * LC_ALL=[locale] to get the default one set up.
365 |  |
          */
366 |  |
367 |  |
         protected final String getDefaultEncoding ()
368 |  |
         {
369 |  |
             try
370 |  |
             {
371 | 228 |
                 return (String) _broker.get("config", WMConstants.TEMPLATE_INPUT_ENCODING);
372 |  |
             }
373 | 0 |
             catch (Exception e)
374 |  |
             {
375 | 0 |
                 return System.getProperty("file.encoding");
376 |  |
             }
377 |  |
         }
378 |  |
379 |  |
380 |  |
         /**
381 |  |
          * Template API.
382 |  |
          */
383 |  |
         public Object getParam (String key)
384 |  |
                 throws IOException, TemplateException
385 |  |
         {
386 |  |
             try
387 |  |
             {
388 | 0 |
                 return _parameters.get(key);
389 |  |
             }
390 | 0 |
             catch (NullPointerException e)
391 |  |
             {
392 | 0 |
                 parse();
393 | 0 |
                 return _parameters.get(key);
394 |  |
             }
395 |  |
         }
396 |  |
397 |  |
         public Map getParameters ()
398 |  |
         {
399 | 0 |
             return _parameters;
400 |  |
         }
401 |  |
402 |  |
         public void setParam (String key, Object value)
403 |  |
         {
404 | 0 |
             _parameters.put(key, value);
405 | 0 |
         }
406 |  |
407 |  |
408 |  |
     }
409 |  |