Coverage Report - org.webmacro.engine.WMTemplate
 
Classes in this File Line Coverage Branch Coverage Complexity
WMTemplate
71%
83/116
66%
8/12
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  37
     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  1678
         this("wm", broker);
 106  1678
     }
 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  1678
     {
 114  1678
         _broker = broker;
 115  1678
         _parserName = parserName;
 116  1678
     }
 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  1678
             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  1678
         if (!_parsed)
 171  
         {
 172  1678
             Block newContent = null;
 173  1678
             Map newParameters = null;
 174  1678
             Map newMacros = null;
 175  1678
             Reader in = null;
 176  1678
             BuildContext bc = null;
 177  
             try
 178  
             {
 179  1678
                 Parser parser = getParser();
 180  1678
                 in = getReader();
 181  1678
                 BlockBuilder bb = parser.parseBlock(getName(), in);
 182  1670
                 in.close();
 183  1670
                 bc = new BuildContext(_broker);
 184  
                 // put global macros from Broker into the BuildContext
 185  1670
                 Map globalMacros = _broker.getMacros();
 186  1670
                 for (Iterator i=globalMacros.entrySet().iterator(); i.hasNext(); ) {
 187  1
                     Map.Entry entry = (Map.Entry)i.next();
 188  1
                     bc.putMacro((String)entry.getKey(),(MacroDefinition) entry.getValue());
 189  1
                 }
 190  1670
                 newParameters = bc.getMap();
 191  1670
                 newMacros = bc.getMacros();
 192  1670
                 newContent = (Block) bb.build(bc);
 193  
             }
 194  10
             catch (BuildException be)
 195  
             {
 196  10
                 if (bc != null)
 197  10
                     be.setContextLocation(bc.getCurrentLocation());
 198  10
                 _log.error("Template contained invalid data", be);
 199  10
                 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  8
             catch (Exception e)
 207  
             {
 208  8
                 _log.error("Error parsing template: " + this, e);
 209  8
                 BuildException be = new BuildException("Error parsing template: " + this, e);
 210  8
                 if (bc != null)
 211  0
                     be.setContextLocation(bc.getCurrentLocation());
 212  8
                 throw be;
 213  
             }
 214  
             finally
 215  
             {
 216  18
                 try
 217  
                 {
 218  1678
                     if (in != null)
 219  1678
                         in.close();
 220  
                 }
 221  0
                 catch (IOException e)
 222  
                 {
 223  0
                     e = null; // Real error reported above
 224  1678
                 }
 225  1678
                 _parameters = newParameters;
 226  1678
                 _content = newContent;
 227  1678
                 _macros = newMacros;
 228  1678
                 _parsed = true;
 229  1660
             }
 230  1660
         }
 231  
         else
 232  
         {
 233  0
             _log.debug("Ignoring parse request on already parsed template " + this);
 234  
         }
 235  1660
     }
 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  20
         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  1638
             FastWriter fw = FastWriter.getInstance(_broker);
 259  1638
             write(fw, context);
 260  1595
             String ret = fw.toString();
 261  1595
             fw.close();
 262  1595
             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  3
             FastWriter fw = FastWriter.getInstance(_broker, encoding);
 282  3
             write(fw, context);
 283  3
             byte[] ret = fw.toByteArray();
 284  3
             fw.close();
 285  3
             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  0
         FastWriter fw = FastWriter.getInstance(_broker, out);
 297  0
         write(fw, context);
 298  0
         fw.flush();
 299  0
         fw.close();
 300  0
     }
 301  
 
 302  
     public void write(OutputStream out, String encoding, Context context)
 303  
             throws PropertyException, IOException {
 304  7
         FastWriter fw = FastWriter.getInstance(_broker, out, encoding);
 305  7
         write(fw, context);
 306  7
         fw.flush();
 307  7
         fw.close();
 308  7
     }
 309  
 
 310  
     public final void write (FastWriter out, Context context)
 311  
             throws IOException, PropertyException
 312  
     {
 313  
         try
 314  
         {
 315  3439
             if (!_parsed)
 316  
             {
 317  240
                 parse();
 318  
             }
 319  
         }
 320  1
         catch (TemplateException e)
 321  
         {
 322  1
             _log.error("Template: Unable to parse template: " + this, e);
 323  1
             out.write(context.getEvaluationExceptionHandler()
 324  
                     .errorString("Template failed to parse. Reason: \n"
 325  
                     + e.toString()));
 326  3438
         }
 327  
 
 328  
         try
 329  
         {
 330  3439
             _content.write(out, context);
 331  
         }
 332  43
         catch (PropertyException e)
 333  
         {
 334  43
             e.setContextLocation(context.getCurrentLocation());
 335  43
             throw e;
 336  
         }
 337  0
         catch (IOException ioe)
 338  
         {
 339  0
             throw ioe;
 340  
         }
 341  1
         catch (Exception e)
 342  
         {
 343  1
             String warning =
 344  
                     "Template: Exception evaluating template " + this;
 345  1
             _log.warn(warning, e);
 346  
 
 347  1
             out.write(context.getEvaluationExceptionHandler()
 348  
                     .warningString("Could not interpret template. Reason: \n"
 349  
                     + warning + "\n" + e.toString()));
 350  3395
         }
 351  3396
     }
 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  58
             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  6
             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  19
         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