Save This Page
Home » freemarker-2.3.13 » freemarker.template » [javadoc | source]
    1   /*
    2    * Copyright (c) 2003-2006 The Visigoth Software Society. All rights
    3    * reserved.
    4    *
    5    * Redistribution and use in source and binary forms, with or without
    6    * modification, are permitted provided that the following conditions
    7    * are met:
    8    *
    9    * 1. Redistributions of source code must retain the above copyright
   10    *    notice, this list of conditions and the following disclaimer.
   11    *
   12    * 2. Redistributions in binary form must reproduce the above copyright
   13    *    notice, this list of conditions and the following disclaimer in
   14    *    the documentation and/or other materials provided with the
   15    *    distribution.
   16    *
   17    * 3. The end-user documentation included with the redistribution, if
   18    *    any, must include the following acknowledgement:
   19    *       "This product includes software developed by the
   20    *        Visigoth Software Society (http://www.visigoths.org/)."
   21    *    Alternately, this acknowledgement may appear in the software itself,
   22    *    if and wherever such third-party acknowledgements normally appear.
   23    *
   24    * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
   25    *    project contributors may be used to endorse or promote products derived
   26    *    from this software without prior written permission. For written
   27    *    permission, please contact visigoths@visigoths.org.
   28    *
   29    * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
   30    *    nor may "FreeMarker" or "Visigoth" appear in their names
   31    *    without prior written permission of the Visigoth Software Society.
   32    *
   33    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   34    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   35    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   36    * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
   37    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   38    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   39    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   40    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   41    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   42    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   43    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   44    * SUCH DAMAGE.
   45    * ====================================================================
   46    *
   47    * This software consists of voluntary contributions made by many
   48    * individuals on behalf of the Visigoth Software Society. For more
   49    * information on the Visigoth Software Society, please see
   50    * http://www.visigoths.org/
   51    */
   52   
   53   package freemarker.template;
   54   
   55   import java.io;
   56   import java.util;
   57   
   58   import freemarker.cache;
   59   import freemarker.core;
   60   import freemarker.template.utility;
   61   
   62   /**
   63    * Main entry point into the FreeMarker API, this class encapsulates the 
   64    * various configuration parameters with which FreeMarker is run, as well
   65    * as serves as a central template loading and caching point. Note that
   66    * this class uses a default strategy for loading 
   67    * and caching templates. You can plug in a replacement
   68    * template loading mechanism by using the {@link #setTemplateLoader(TemplateLoader)}
   69    * method.
   70    *
   71    * This object is <em>not synchronized</em>. Thus, the settings must not be changed
   72    * after you have started to access the object from multiple threads. If you use multiple
   73    * threads, set everything directly after you have instantiated the <code>Configuration</code>
   74    * object, and don't change the settings anymore.
   75    *
   76    * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
   77    * @author Attila Szegedi
   78    * @version $Id: Configuration.java,v 1.122.2.5 2006/04/26 21:25:19 ddekany Exp $
   79    */
   80   
   81   public class Configuration extends Configurable implements Cloneable {
   82       public static final String DEFAULT_ENCODING_KEY = "default_encoding"; 
   83       public static final String LOCALIZED_LOOKUP_KEY = "localized_lookup";
   84       public static final String STRICT_SYNTAX_KEY = "strict_syntax";
   85       public static final String WHITESPACE_STRIPPING_KEY = "whitespace_stripping";
   86       public static final String CACHE_STORAGE_KEY = "cache_storage";
   87       public static final String TEMPLATE_UPDATE_DELAY_KEY = "template_update_delay";
   88       public static final String AUTO_IMPORT_KEY = "auto_import";
   89       public static final String AUTO_INCLUDE_KEY = "auto_include";
   90       public static final String TAG_SYNTAX_KEY = "tag_syntax";
   91       public static final int AUTO_DETECT_TAG_SYNTAX = 0;
   92       public static final int ANGLE_BRACKET_TAG_SYNTAX = 1;
   93       public static final int SQUARE_BRACKET_TAG_SYNTAX = 2;
   94   
   95   
   96       private static Configuration defaultConfig = new Configuration();
   97       private static String cachedVersion;
   98       private boolean strictSyntax = true, localizedLookup = true, whitespaceStripping = true;
   99       private int tagSyntax = ANGLE_BRACKET_TAG_SYNTAX;
  100   
  101       private TemplateCache cache;
  102       private HashMap variables = new HashMap();
  103       private HashMap encodingMap = new HashMap();
  104       private Map autoImportMap = new HashMap();
  105       private ArrayList autoImports = new ArrayList(), autoIncludes = new ArrayList();
  106       private String defaultEncoding = SecurityUtilities.getSystemProperty("file.encoding");
  107        
  108   
  109       public Configuration() {
  110           cache = new TemplateCache();
  111           cache.setConfiguration(this);
  112           cache.setDelay(5000);
  113           loadBuiltInSharedVariables();
  114       }
  115   
  116       public Object clone() {
  117           try {
  118               Configuration copy = (Configuration)super.clone();
  119               copy.variables = new HashMap(variables);
  120               copy.encodingMap = new HashMap(encodingMap);
  121               copy.createTemplateCache(cache.getTemplateLoader(), cache.getCacheStorage());
  122               return copy;
  123           } catch (CloneNotSupportedException e) {
  124               throw new RuntimeException("Clone is not supported, but it should be: " + e.getMessage());
  125           }
  126       }
  127       
  128       private void loadBuiltInSharedVariables() {
  129           variables.put("capture_output", new CaptureOutput());
  130           variables.put("compress", StandardCompress.INSTANCE);
  131           variables.put("html_escape", new HtmlEscape());
  132           variables.put("normalize_newlines", new NormalizeNewlines());
  133           variables.put("xml_escape", new XmlEscape());
  134       }
  135       
  136       /**
  137        * Loads a preset language-to-encoding map. It assumes the usual character
  138        * encodings for most languages.
  139        * The previous content of the encoding map will be lost.
  140        * This default map currently contains the following mappings:
  141        * <table>
  142        *   <tr><td>ar</td><td>ISO-8859-6</td></tr>
  143        *   <tr><td>be</td><td>ISO-8859-5</td></tr>
  144        *   <tr><td>bg</td><td>ISO-8859-5</td></tr>
  145        *   <tr><td>ca</td><td>ISO-8859-1</td></tr>
  146        *   <tr><td>cs</td><td>ISO-8859-2</td></tr>
  147        *   <tr><td>da</td><td>ISO-8859-1</td></tr>
  148        *   <tr><td>de</td><td>ISO-8859-1</td></tr>
  149        *   <tr><td>el</td><td>ISO-8859-7</td></tr>
  150        *   <tr><td>en</td><td>ISO-8859-1</td></tr>
  151        *   <tr><td>es</td><td>ISO-8859-1</td></tr>
  152        *   <tr><td>et</td><td>ISO-8859-1</td></tr>
  153        *   <tr><td>fi</td><td>ISO-8859-1</td></tr>
  154        *   <tr><td>fr</td><td>ISO-8859-1</td></tr>
  155        *   <tr><td>hr</td><td>ISO-8859-2</td></tr>
  156        *   <tr><td>hu</td><td>ISO-8859-2</td></tr>
  157        *   <tr><td>is</td><td>ISO-8859-1</td></tr>
  158        *   <tr><td>it</td><td>ISO-8859-1</td></tr>
  159        *   <tr><td>iw</td><td>ISO-8859-8</td></tr>
  160        *   <tr><td>ja</td><td>Shift_JIS</td></tr>
  161        *   <tr><td>ko</td><td>EUC-KR</td></tr>    
  162        *   <tr><td>lt</td><td>ISO-8859-2</td></tr>
  163        *   <tr><td>lv</td><td>ISO-8859-2</td></tr>
  164        *   <tr><td>mk</td><td>ISO-8859-5</td></tr>
  165        *   <tr><td>nl</td><td>ISO-8859-1</td></tr>
  166        *   <tr><td>no</td><td>ISO-8859-1</td></tr>
  167        *   <tr><td>pl</td><td>ISO-8859-2</td></tr>
  168        *   <tr><td>pt</td><td>ISO-8859-1</td></tr>
  169        *   <tr><td>ro</td><td>ISO-8859-2</td></tr>
  170        *   <tr><td>ru</td><td>ISO-8859-5</td></tr>
  171        *   <tr><td>sh</td><td>ISO-8859-5</td></tr>
  172        *   <tr><td>sk</td><td>ISO-8859-2</td></tr>
  173        *   <tr><td>sl</td><td>ISO-8859-2</td></tr>
  174        *   <tr><td>sq</td><td>ISO-8859-2</td></tr>
  175        *   <tr><td>sr</td><td>ISO-8859-5</td></tr>
  176        *   <tr><td>sv</td><td>ISO-8859-1</td></tr>
  177        *   <tr><td>tr</td><td>ISO-8859-9</td></tr>
  178        *   <tr><td>uk</td><td>ISO-8859-5</td></tr>
  179        *   <tr><td>zh</td><td>GB2312</td></tr>
  180        *   <tr><td>zh_TW</td><td>Big5</td></tr>
  181        * </table>
  182        * @see #clearEncodingMap
  183        * @see #setEncoding
  184        */
  185       public void loadBuiltInEncodingMap() {
  186           encodingMap.clear();
  187           encodingMap.put("ar", "ISO-8859-6");
  188           encodingMap.put("be", "ISO-8859-5");
  189           encodingMap.put("bg", "ISO-8859-5");
  190           encodingMap.put("ca", "ISO-8859-1");
  191           encodingMap.put("cs", "ISO-8859-2");
  192           encodingMap.put("da", "ISO-8859-1");
  193           encodingMap.put("de", "ISO-8859-1");
  194           encodingMap.put("el", "ISO-8859-7");
  195           encodingMap.put("en", "ISO-8859-1");
  196           encodingMap.put("es", "ISO-8859-1");
  197           encodingMap.put("et", "ISO-8859-1");
  198           encodingMap.put("fi", "ISO-8859-1");
  199           encodingMap.put("fr", "ISO-8859-1");
  200           encodingMap.put("hr", "ISO-8859-2");
  201           encodingMap.put("hu", "ISO-8859-2");
  202           encodingMap.put("is", "ISO-8859-1");
  203           encodingMap.put("it", "ISO-8859-1");
  204           encodingMap.put("iw", "ISO-8859-8");
  205           encodingMap.put("ja", "Shift_JIS");
  206           encodingMap.put("ko", "EUC-KR");    
  207           encodingMap.put("lt", "ISO-8859-2");
  208           encodingMap.put("lv", "ISO-8859-2");
  209           encodingMap.put("mk", "ISO-8859-5");
  210           encodingMap.put("nl", "ISO-8859-1");
  211           encodingMap.put("no", "ISO-8859-1");
  212           encodingMap.put("pl", "ISO-8859-2");
  213           encodingMap.put("pt", "ISO-8859-1");
  214           encodingMap.put("ro", "ISO-8859-2");
  215           encodingMap.put("ru", "ISO-8859-5");
  216           encodingMap.put("sh", "ISO-8859-5");
  217           encodingMap.put("sk", "ISO-8859-2");
  218           encodingMap.put("sl", "ISO-8859-2");
  219           encodingMap.put("sq", "ISO-8859-2");
  220           encodingMap.put("sr", "ISO-8859-5");
  221           encodingMap.put("sv", "ISO-8859-1");
  222           encodingMap.put("tr", "ISO-8859-9");
  223           encodingMap.put("uk", "ISO-8859-5");
  224           encodingMap.put("zh", "GB2312");
  225           encodingMap.put("zh_TW", "Big5");
  226       }
  227   
  228       /**
  229        * Clears language-to-encoding map.
  230        * @see #loadBuiltInEncodingMap
  231        * @see #setEncoding
  232        */
  233       public void clearEncodingMap() {
  234           encodingMap.clear();
  235       }
  236   
  237       /**
  238        * Returns the default (singleton) Configuration object. Note that you can
  239        * create as many separate configurations as you wish; this global instance
  240        * is provided for convenience, or when you have no reason to use a separate
  241        * instance.
  242        * 
  243        * @deprecated The usage of the static singleton (the "default")
  244        * {@link Configuration} instance can easily cause erroneous, unpredictable
  245        * behavior. This is because multiple independent software components may use
  246        * FreeMarker internally inside the same application, so they will interfere
  247        * because of the common {@link Configuration} instance. Each such component
  248        * should use its own private {@link Configuration} object instead, that it
  249        * typically creates with <code>new Configuration()</code> when the component
  250        * is initialized.
  251        */
  252       static public Configuration getDefaultConfiguration() {
  253           return defaultConfig;
  254       }
  255   
  256       /**
  257        * Sets the Configuration object that will be retrieved from future calls
  258        * to {@link #getDefaultConfiguration()}.
  259        * 
  260        * @deprecated Using the "default" {@link Configuration} instance can
  261        * easily lead to erroneous, unpredictable behaviour.
  262        * See more {@link Configuration#getDefaultConfiguration() here...}.
  263        */
  264       static public void setDefaultConfiguration(Configuration config) {
  265           defaultConfig = config;
  266       }
  267       
  268       /**
  269        * Sets a template loader that is used to look up and load templates.
  270        * By providing your own template loader, you can customize the way
  271        * templates are loaded. Several convenience methods in this class already
  272        * allow you to install commonly used loaders:
  273        * {@link #setClassForTemplateLoading(Class, String)}, 
  274        * {@link #setDirectoryForTemplateLoading(File)}, and
  275        * {@link #setServletContextForTemplateLoading(Object, String)}. By default,
  276        * a multi-loader is used that first tries to load a template from the file
  277        * in the current directory, then from a resource on the classpath.
  278        */
  279       public synchronized void setTemplateLoader(TemplateLoader loader) {
  280           createTemplateCache(loader, cache.getCacheStorage());
  281       }
  282   
  283       private void createTemplateCache(TemplateLoader loader, CacheStorage storage)
  284       {
  285           TemplateCache oldCache = cache;
  286           cache = new TemplateCache(loader, storage);
  287           cache.setDelay(oldCache.getDelay());
  288           cache.setConfiguration(this);
  289           cache.setLocalizedLookup(localizedLookup);
  290       }
  291       /**
  292        * @return the template loader that is used to look up and load templates.
  293        * @see #setTemplateLoader
  294        */
  295       public TemplateLoader getTemplateLoader()
  296       {
  297           return cache.getTemplateLoader();
  298       }
  299   
  300       public synchronized void setCacheStorage(CacheStorage storage) {
  301           createTemplateCache(cache.getTemplateLoader(), storage);
  302       }
  303       
  304       /**
  305        * Set the explicit directory from which to load templates.
  306        */
  307       public void setDirectoryForTemplateLoading(File dir) throws IOException {
  308           TemplateLoader tl = getTemplateLoader();
  309           if (tl instanceof FileTemplateLoader) {
  310               String path = ((FileTemplateLoader) tl).baseDir.getCanonicalPath();
  311               if (path.equals(dir.getCanonicalPath()))
  312                   return;
  313           }
  314           setTemplateLoader(new FileTemplateLoader(dir));
  315       }
  316   
  317       /**
  318        * Sets the servlet context from which to load templates
  319        * @param sctxt the ServletContext object. Note that the type is <code>Object</code>
  320        *        to prevent class loading errors when user who uses FreeMarker not for
  321        *        servlets has no javax.servlet in the CLASSPATH.
  322        * @param path the path relative to the ServletContext.
  323        * If this path is absolute, it is taken to be relative
  324        * to the server's URL, i.e. http://myserver.com/
  325        * and if the path is relative, it is taken to be 
  326        * relative to the web app context, i.e.
  327        * http://myserver.context.com/mywebappcontext/
  328        */
  329       public void setServletContextForTemplateLoading(Object sctxt, String path) {
  330           try {
  331               if (path == null) {
  332                   setTemplateLoader( (TemplateLoader)
  333                           ClassUtil.forName("freemarker.cache.WebappTemplateLoader")
  334                               .getConstructor(new Class[]{ClassUtil.forName("javax.servlet.ServletContext")})
  335                                       .newInstance(new Object[]{sctxt}) );
  336               }
  337               else {
  338                   setTemplateLoader( (TemplateLoader)
  339                           ClassUtil.forName("freemarker.cache.WebappTemplateLoader")
  340                               .getConstructor(new Class[]{ClassUtil.forName("javax.servlet.ServletContext"), String.class})
  341                                       .newInstance(new Object[]{sctxt, path}) );
  342               }
  343           } catch (Exception exc) {
  344               throw new RuntimeException("Internal FreeMarker error: " + exc);
  345           }
  346       }
  347   
  348       /**
  349        * Sets a class relative to which we do the 
  350        * Class.getResource() call to load templates.
  351        */
  352       public void setClassForTemplateLoading(Class clazz, String pathPrefix) {
  353           setTemplateLoader(new ClassTemplateLoader(clazz, pathPrefix));
  354       }
  355   
  356       /**
  357        * Set the time in seconds that must elapse before checking whether there is a newer
  358        * version of a template file.
  359        * This method is thread-safe and can be called while the engine works.
  360        */
  361       public void setTemplateUpdateDelay(int delay) {
  362           cache.setDelay(1000L * delay);
  363       }
  364   
  365       /**
  366        * Sets whether directives such as if, else, etcetera
  367        * must be written as #if, #else, etcetera.
  368        * Any tag not starting with &lt;# or &lt;/# is considered as plain text
  369        * and will go to the output as is. Tag starting with &lt# or &lt/# must
  370        * be valid FTL tag, or else the template is invalid (i.e. &lt;#noSuchDirective>
  371        * is an error).
  372        */
  373   
  374       public void setStrictSyntaxMode(boolean b) {
  375           strictSyntax = b;
  376       }
  377   
  378       /**
  379        * Tells whether directives such as if, else, etcetera
  380        * must be written as #if, #else, etcetera.
  381        *
  382        * @see #setStrictSyntaxMode
  383        */
  384       public boolean getStrictSyntaxMode() {
  385           return strictSyntax;
  386       }
  387   
  388       /**
  389        * Sets whether the FTL parser will try to remove
  390        * superfluous white-space around certain FTL tags.
  391        */
  392       public void setWhitespaceStripping(boolean b) {
  393           whitespaceStripping = b;
  394       }
  395   
  396       /**
  397        * Gets whether the FTL parser will try to remove
  398        * superfluous white-space around certain FTL tags.
  399        *
  400        * @see #setWhitespaceStripping
  401        */
  402       public boolean getWhitespaceStripping() {
  403           return whitespaceStripping;
  404       }
  405       
  406       /**
  407        * Determines the syntax of the template files (angle bracket VS square bracket)
  408        * that has no <markup>ftl</markup> directive in it. The <code>tagSyntax</code>
  409        * parameter must be one of:
  410        * <ul>
  411        *   <li>{@link Configuration#AUTO_DETECT_TAG_SYNTAX}:
  412        *     use the syntax of the first FreeMarker tag (can be anything, like <tt>list</tt>,
  413        *     <tt>include</tt>, user defined, ...etc)
  414        *   <li>{@link Configuration#ANGLE_BRACKET_TAG_SYNTAX}:
  415        *     use the angle bracket syntax (the normal syntax)
  416        *   <li>{@link Configuration#SQUARE_BRACKET_TAG_SYNTAX}:
  417        *     use the square bracket syntax
  418        * </ul>
  419        *
  420        * <p>In FreeMarker 2.3.x {@link Configuration#ANGLE_BRACKET_TAG_SYNTAX} is the
  421        * default for better backward compatibility. Starting from 2.4.x {@link
  422        * Configuration#AUTO_DETECT_TAG_SYNTAX} is the default, so it is recommended to use
  423        * that even for 2.3.x.
  424        * 
  425        * <p>This setting is ignored for the templates that have <tt>ftl</tt> directive in
  426        * it. For those templates the syntax used for the <tt>ftl</tt> directive determines
  427        * the syntax.
  428        */
  429       public void setTagSyntax(int tagSyntax) {
  430           if (tagSyntax != AUTO_DETECT_TAG_SYNTAX
  431               && tagSyntax != SQUARE_BRACKET_TAG_SYNTAX
  432               && tagSyntax != ANGLE_BRACKET_TAG_SYNTAX)
  433           {
  434               throw new IllegalArgumentException("This can only be set to one of three settings: Configuration.AUTO_DETECT_TAG_SYNTAX, Configuration.ANGLE_BRACKET_SYNTAX, or Configuration.SQAUARE_BRACKET_SYNTAX");
  435           }
  436           this.tagSyntax = tagSyntax;
  437       }
  438       
  439       /**
  440        * @return whether the alternative square bracket
  441        * syntax is set as the default
  442        */
  443       public int getTagSyntax() {
  444           return tagSyntax;
  445       }
  446   
  447       /**
  448        * Equivalent to <tt>getTemplate(name, thisCfg.getLocale(), thisCfg.getEncoding(thisCfg.getLocale()), true)</tt>.
  449        */
  450       public Template getTemplate(String name) throws IOException {
  451           Locale loc = getLocale();
  452           return getTemplate(name, loc, getEncoding(loc), true);
  453       }
  454   
  455       /**
  456        * Equivalent to <tt>getTemplate(name, locale, thisCfg.getEncoding(locale), true)</tt>.
  457        */
  458       public Template getTemplate(String name, Locale locale) throws IOException {
  459           return getTemplate(name, locale, getEncoding(locale), true);
  460       }
  461   
  462       /**
  463        * Equivalent to <tt>getTemplate(name, thisCfg.getLocale(), encoding, true)</tt>.
  464        */
  465       public Template getTemplate(String name, String encoding) throws IOException {
  466           return getTemplate(name, getLocale(), encoding, true);
  467       }
  468   
  469       /**
  470        * Equivalent to <tt>getTemplate(name, locale, encoding, true)</tt>.
  471        */
  472       public Template getTemplate(String name, Locale locale, String encoding) throws IOException {
  473           return getTemplate(name, locale, encoding, true);
  474       }
  475   
  476       /**
  477        * Retrieves a template specified by a name and locale, interpreted using
  478        * the specified character encoding, either parsed or unparsed. For the
  479        * exact semantics of parameters, see 
  480        * {@link TemplateCache#getTemplate(String, Locale, String, boolean)}.
  481        * @return the requested template.
  482        * @throws FileNotFoundException if the template could not be found.
  483        * @throws IOException if there was a problem loading the template.
  484        * @throws ParseException (extends <code>IOException</code>) if the template is syntactically bad.
  485        */
  486       public Template getTemplate(String name, Locale locale, String encoding, boolean parse) throws IOException {
  487           Template result = cache.getTemplate(name, locale, encoding, parse);
  488           if (result == null) {
  489               throw new FileNotFoundException("Template " + name + " not found.");
  490           }
  491           return result;
  492       }
  493   
  494       /**
  495        * Sets the default encoding for converting bytes to characters when
  496        * reading template files in a locale for which no explicit encoding
  497        * was specified. Defaults to default system encoding.
  498        */
  499       public void setDefaultEncoding(String encoding) {
  500           defaultEncoding = encoding;
  501       }
  502   
  503       /**
  504        * Gets the default encoding for converting bytes to characters when
  505        * reading template files in a locale for which no explicit encoding
  506        * was specified. Defaults to default system encoding.
  507        */
  508       public String getDefaultEncoding() {
  509           return defaultEncoding;
  510       }
  511   
  512       /**
  513        * Gets the preferred character encoding for the given locale, or the 
  514        * default encoding if no encoding is set explicitly for the specified
  515        * locale. You can associate encodings with locales using 
  516        * {@link #setEncoding(Locale, String)} or {@link #loadBuiltInEncodingMap()}.
  517        * @param loc the locale
  518        * @return the preferred character encoding for the locale.
  519        */
  520       public String getEncoding(Locale loc) {
  521           // Try for a full name match (may include country and variant)
  522           String charset = (String) encodingMap.get(loc.toString());
  523           if (charset == null) {
  524               if (loc.getVariant().length() > 0) {
  525                   Locale l = new Locale(loc.getLanguage(), loc.getCountry());
  526                   charset = (String) encodingMap.get(l.toString());
  527                   if (charset != null) {
  528                       encodingMap.put(loc.toString(), charset);
  529                   }
  530               } 
  531               charset = (String) encodingMap.get(loc.getLanguage());
  532               if (charset != null) {
  533                   encodingMap.put(loc.toString(), charset);
  534               }
  535           }
  536           return charset != null ? charset : defaultEncoding;
  537       }
  538   
  539       /**
  540        * Sets the character set encoding to use for templates of
  541        * a given locale. If there is no explicit encoding set for some
  542        * locale, then the default encoding will be used, what you can
  543        * set with {@link #setDefaultEncoding}.
  544        *
  545        * @see #clearEncodingMap
  546        * @see #loadBuiltInEncodingMap
  547        */
  548       public void setEncoding(Locale locale, String encoding) {
  549           encodingMap.put(locale.toString(), encoding);
  550       }
  551   
  552       /**
  553        * Adds a shared variable to the configuration.
  554        * Shared variables are variables that are visible
  555        * as top-level variables for all templates which use this
  556        * configuration, if the data model does not contain a
  557        * variable with the same name.
  558        *
  559        * <p>Never use <tt>TemplateModel</tt> implementation that is not thread-safe for shared variables,
  560        * if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites.
  561        *
  562        * @param name the name used to access the data object from your template.
  563        *     If a shared variable with this name already exists, it will replace
  564        *     that.
  565        * @see #setSharedVariable(String,Object)
  566        * @see #setAllSharedVariables
  567        */
  568       public void setSharedVariable(String name, TemplateModel tm) {
  569           variables.put(name, tm);
  570       }
  571   
  572       /**
  573        * Returns the set containing the names of all defined shared variables.
  574        * The method returns a new Set object on each call that is completely
  575        * disconnected from the Configuration. That is, modifying the set will have
  576        * no effect on the Configuration object.
  577        */
  578       public Set getSharedVariableNames() {
  579           return new HashSet(variables.keySet());
  580       }
  581       
  582       /**
  583        * Adds shared variable to the configuration.
  584        * It uses {@link Configurable#getObjectWrapper()} to wrap the 
  585        * <code>obj</code>.
  586        * @see #setSharedVariable(String,TemplateModel)
  587        * @see #setAllSharedVariables
  588        */
  589       public void setSharedVariable(String name, Object obj) throws TemplateModelException {
  590           setSharedVariable(name, getObjectWrapper().wrap(obj));
  591       }
  592   
  593       /**
  594        * Adds all object in the hash as shared variable to the configuration.
  595        *
  596        * <p>Never use <tt>TemplateModel</tt> implementation that is not thread-safe for shared variables,
  597        * if the configuration is used by multiple threads! It is the typical situation for Servlet based Web sites.
  598        *
  599        * @param hash a hash model whose objects will be copied to the
  600        * configuration with same names as they are given in the hash.
  601        * If a shared variable with these names already exist, it will be replaced
  602        * with those from the map.
  603        *
  604        * @see #setSharedVariable(String,Object)
  605        * @see #setSharedVariable(String,TemplateModel)
  606        */
  607       public void setAllSharedVariables(TemplateHashModelEx hash) throws TemplateModelException {
  608           TemplateModelIterator keys = hash.keys().iterator();
  609           TemplateModelIterator values = hash.values().iterator();
  610           while(keys.hasNext())
  611           {
  612               setSharedVariable(((TemplateScalarModel)keys.next()).getAsString(), values.next());
  613           }
  614       }
  615       
  616       /**
  617        * Gets a shared variable. Shared variables are variables that are 
  618        * available to all templates. When a template is processed, and an identifier
  619        * is undefined in the data model, a shared variable object with the same identifier
  620        * is then looked up in the configuration. There are several predefined variables
  621        * that are always available through this method, see the FreeMarker manual
  622        * for a comprehensive list of them.
  623        *
  624        * @see #setSharedVariable(String,Object)
  625        * @see #setSharedVariable(String,TemplateModel)
  626        * @see #setAllSharedVariables
  627        */
  628       public TemplateModel getSharedVariable(String name) {
  629           return (TemplateModel) variables.get(name);
  630       }
  631       
  632       /**
  633        * Removes all shared variables, except the predefined ones (compress, html_escape, etc.).
  634        */
  635       public void clearSharedVariables() {
  636           variables.clear();
  637           loadBuiltInSharedVariables();
  638       }
  639       
  640       /**
  641        * Removes all entries from the template cache, thus forcing reloading of templates
  642        * on subsequent <code>getTemplate</code> calls.
  643        * This method is thread-safe and can be called while the engine works.
  644        */
  645       public void clearTemplateCache() {
  646           cache.clear();
  647       }
  648       
  649       /**
  650        * Returns if localized template lookup is enabled or not.
  651        * This method is thread-safe and can be called while the engine works.
  652        */
  653       public boolean getLocalizedLookup() {
  654           return cache.getLocalizedLookup();
  655       }
  656       
  657       /**
  658        * Enables/disables localized template lookup. Enabled by default.
  659        * This method is thread-safe and can be called while the engine works.
  660        */
  661       public void setLocalizedLookup(boolean localizedLookup) {
  662           this.localizedLookup = localizedLookup;
  663           cache.setLocalizedLookup(localizedLookup);
  664       }
  665       
  666       /**
  667        * Sets a setting by name and string value.
  668        *
  669        * In additional to the settings understood by
  670        * {@link Configurable#setSetting the super method}, it understands these:
  671        * <ul>
  672        *   <li><code>"auto_import"</code>: Sets the list of auto-imports. Example of valid value:
  673        *       <br><code>/lib/form.ftl as f, /lib/widget as w, "/lib/evil name.ftl" as odd</code>
  674        *       See: {@link #setAutoImports}
  675        *   <li><code>"auto_include"</code>: Sets the list of auto-includes. Example of valid value:
  676        *       <br><code>/include/common.ftl, "/include/evil name.ftl"</code>
  677        *       See: {@link #setAutoIncludes}
  678        *   <li><code>"default_encoding"</code>: The name of the charset, such as <code>"UTF-8"</code>.
  679        *       See: {@link #setDefaultEncoding}
  680        *   <li><code>"localized_lookup"</code>:
  681        *       <code>"true"</code>, <code>"false"</code>, <code>"yes"</code>, <code>"no"</code>,
  682        *       <code>"t"</code>, <code>"f"</code>, <code>"y"</code>, <code>"n"</code>.
  683        *       Case insensitive.
  684        *      See: {@link #setLocalizedLookup}
  685        *   <li><code>"strict_syntax"</code>: <code>"true"</code>, <code>"false"</code>, etc.
  686        *       See: {@link #setStrictSyntaxMode}
  687        *   <li><code>"whitespace_stripping"</code>: <code>"true"</code>, <code>"false"</code>, etc.
  688        *       See: {@link #setWhitespaceStripping}
  689        *   <li><code>"cache_storage"</code>: If the value contains dot, then it is
  690        *       interpreted as class name, and the object will be created with
  691        *       its parameterless constructor. If the value does not contain dot,
  692        *       then a {@link freemarker.cache.MruCacheStorage} will be used with the
  693        *       maximum strong and soft sizes specified with the setting value. Examples
  694        *       of valid setting values:
  695        *       <table border=1 cellpadding=4>
  696        *         <tr><th>Setting value<th>max. strong size<th>max. soft size
  697        *         <tr><td><code>"strong:50, soft:500"</code><td>50<td>500
  698        *         <tr><td><code>"strong:100, soft"</code><td>100<td><code>Integer.MAX_VALUE</code>
  699        *         <tr><td><code>"strong:100"</code><td>100<td>0
  700        *         <tr><td><code>"soft:100"</code><td>0<td>100
  701        *         <tr><td><code>"strong"</code><td><code>Integer.MAX_VALUE</code><td>0
  702        *         <tr><td><code>"soft"</code><td>0<td><code>Integer.MAX_VALUE</code>
  703        *       </table>
  704        *       The value is not case sensitive. The order of <tt>soft</tt> and <tt>strong</tt>
  705        *       entries is not significant.
  706        *       See also: {@link #setCacheStorage}
  707        *   <li><code>"template_update_delay"</code>: Valid positive integer, the
  708        *       update delay measured in seconds.
  709        *       See: {@link #setTemplateUpdateDelay}
  710        *   <li><code>"tag_syntax"</code>: Must be one of:
  711        *       <code>"auto_detect"</code>, <code>"angle_bracket"</code>,
  712        *       <code>"square_bracket"</code>.
  713        * </ul>
  714        *
  715        * @param key the name of the setting.
  716        * @param value the string that describes the new value of the setting.
  717        *
  718        * @throws UnknownSettingException if the key is wrong.
  719        * @throws TemplateException if the new value of the setting can't be set
  720        *     for any other reasons.
  721        */
  722       public void setSetting(String key, String value) throws TemplateException {
  723           if ("TemplateUpdateInterval".equalsIgnoreCase(key)) {
  724               key = TEMPLATE_UPDATE_DELAY_KEY;
  725           } else if ("DefaultEncoding".equalsIgnoreCase(key)) {
  726               key = DEFAULT_ENCODING_KEY;
  727           }
  728           try {
  729               if (DEFAULT_ENCODING_KEY.equals(key)) {
  730                   setDefaultEncoding(value);
  731               } else if (LOCALIZED_LOOKUP_KEY.equals(key)) {
  732                   setLocalizedLookup(StringUtil.getYesNo(value));
  733               } else if (STRICT_SYNTAX_KEY.equals(key)) {
  734                   setStrictSyntaxMode(StringUtil.getYesNo(value));
  735               } else if (WHITESPACE_STRIPPING_KEY.equals(key)) {
  736                   setWhitespaceStripping(StringUtil.getYesNo(value));
  737               } else if (CACHE_STORAGE_KEY.equals(key)) {
  738                   if (value.indexOf('.') == -1) {
  739                       int strongSize = 0;
  740                       int softSize = 0;
  741                       Map map = StringUtil.parseNameValuePairList(
  742                               value, String.valueOf(Integer.MAX_VALUE));
  743                       Iterator it = map.entrySet().iterator();
  744                       while (it.hasNext()) {
  745                           Map.Entry ent = (Map.Entry) it.next();
  746                           String pname = (String) ent.getKey();
  747                           int pvalue;
  748                           try {
  749                               pvalue = Integer.parseInt((String) ent.getValue());
  750                           } catch (NumberFormatException e) {
  751                               throw invalidSettingValueException(key, value);
  752                           }
  753                           if ("soft".equalsIgnoreCase(pname)) {
  754                               softSize = pvalue;
  755                           } else if ("strong".equalsIgnoreCase(pname)) {
  756                               strongSize = pvalue;
  757                           } else {
  758                               throw invalidSettingValueException(key, value);
  759                           }
  760                       }
  761                       if (softSize == 0 && strongSize == 0) {
  762                           throw invalidSettingValueException(key, value);
  763                       }
  764                       setCacheStorage(new MruCacheStorage(strongSize, softSize));
  765                   } else {
  766                       setCacheStorage((CacheStorage) ClassUtil.forName(value)
  767                               .newInstance());
  768                   }
  769               } else if (TEMPLATE_UPDATE_DELAY_KEY.equals(key)) {
  770                   setTemplateUpdateDelay(Integer.parseInt(value));
  771               } else if (AUTO_INCLUDE_KEY.equals(key)) {
  772                   setAutoIncludes(new SettingStringParser(value).parseAsList());
  773               } else if (AUTO_IMPORT_KEY.equals(key)) {
  774                   setAutoImports(new SettingStringParser(value).parseAsImportList());
  775               } else if (TAG_SYNTAX_KEY.equals(key)) {
  776                   if ("auto_detect".equals(value)) {
  777                       setTagSyntax(AUTO_DETECT_TAG_SYNTAX);
  778                   } else if ("angle_bracket".equals(value)) {
  779                       setTagSyntax(ANGLE_BRACKET_TAG_SYNTAX);
  780                   } else if ("square_bracket".equals(value)) {
  781                       setTagSyntax(SQUARE_BRACKET_TAG_SYNTAX);
  782                   } else {
  783                       throw invalidSettingValueException(key, value);
  784                   }
  785               } else {
  786                   super.setSetting(key, value);
  787               }
  788           } catch(Exception e) {
  789               throw new TemplateException(
  790                       "Failed to set setting " + key + " to value " + value,
  791                       e, getEnvironment());
  792           }
  793       }
  794       
  795       
  796       /**
  797        * Add an auto-imported template.
  798        * The importing will happen at the top of any template that
  799        * is vended by this Configuration object.
  800        * @param namespace the name of the namespace into which the template is imported
  801        * @param template the name of the template
  802        */
  803       public synchronized void addAutoImport(String namespace, String template) {
  804           autoImports.remove(namespace);
  805           autoImports.add(namespace);
  806           autoImportMap.put(namespace, template);
  807       }
  808       
  809       /**
  810        * Remove an auto-imported template
  811        * @param namespace the name of the namespace into which the template was imported
  812        */
  813       
  814       public synchronized void removeAutoImport(String namespace) {
  815           autoImports.remove(namespace);
  816           autoImportMap.remove(namespace);
  817       }
  818       
  819       /**
  820        * set a map of namespace names to templates for auto-importing 
  821        * a set of templates. Note that all previous auto-imports are removed.
  822        */
  823       
  824       public synchronized void setAutoImports(Map map) {
  825           autoImports = new ArrayList(map.keySet());
  826           if (map instanceof HashMap) {
  827               autoImportMap = (Map) ((HashMap) map).clone();
  828           } 
  829           else if (map instanceof SortedMap) {
  830               autoImportMap = new TreeMap(map);             
  831           }
  832           else {
  833               autoImportMap = new HashMap(map);
  834           }
  835       }
  836       
  837       void doAutoImports(Environment env) throws TemplateException, IOException {
  838           for (int i=0; i<autoImports.size(); i++) {
  839               String namespace = (String) autoImports.get(i);
  840               String templateName = (String) autoImportMap.get(namespace);
  841               env.importLib(templateName, namespace);
  842           }
  843       }
  844       
  845       /**
  846        * add a template to be automatically included at the top of any template that
  847        * is vended by this Configuration object.
  848        * @param templateName the lookup name of the template.
  849        */
  850        
  851       public synchronized void addAutoInclude(String templateName) {
  852           autoIncludes.remove(templateName);
  853           autoIncludes.add(templateName);
  854       }
  855   
  856       /**
  857        * set the list of automatically included templates.
  858        * Note that all previous auto-includes are removed.
  859        */
  860       public synchronized void setAutoIncludes(List templateNames) {
  861           autoIncludes.clear();
  862           Iterator it = templateNames.iterator();
  863           while (it.hasNext()) {
  864               Object o = it.next();
  865               if (!(o instanceof String)) {
  866                   throw new IllegalArgumentException("List items must be String-s.");
  867               }
  868               autoIncludes.add(o);
  869           }
  870       }
  871       
  872       /**
  873        * remove a template from the auto-include list.
  874        * @param templateName the lookup name of the template in question.
  875        */
  876        
  877       public synchronized void removeAutoInclude(String templateName) {
  878           autoIncludes.remove(templateName);
  879       }
  880   
  881       /**
  882        * Returns FreeMarker version number string. 
  883        * Examples of possible return values:
  884        * <code>"2.2.5"</code>, <code>"2.3pre13"</code>,
  885        * <code>"2.3pre13mod"</code>, <code>"2.3rc1"</code>, <code>"2.3"</code>,
  886        * <code>"3.0"</code>.
  887        *
  888        * <p>Notes on FreeMarker version numbering rules:
  889        * <ul>
  890        *   <li>"pre" and "rc" (lowercase!) means "preview" and "release
  891        *       candidate" respectively. It is must be followed with a
  892        *       number (as "1" for the first release candidate).
  893        *   <li>The "mod" after the version number indicates that it's an
  894        *       unreleased modified version of the released version.
  895        *       After releases, the nighly builds are such releases. E.g.
  896        *       the nightly build after releasing "2.2.1" but before releasing
  897        *       "2.2.2" is "2.2.1mod".
  898        *   <li>The 2nd version number must be present, and maybe 0,
  899        *       as in "3.0".
  900        *   <li>The 3rd version number is never 0. E.g. the version
  901        *       number string for the first release of the 2.2 series
  902        *       is "2.2", and NOT "2.2.0". 
  903        *   <li>When only the 3rd version number increases
  904        *       (2.2 -> 2.2.1, 2.2.1 -> 2.2.2 etc.), 100% backward compatiblity
  905        *       with the previous version MUST be kept.
  906        *       This means that <tt>freemarker.jar</tt> can be replaced in an
  907        *       application without risk (as far as the application doesn't depend
  908        *       on the presence of a FreeMarker bug).
  909        *       Note that backward compatibility restrictions do not apply for
  910        *       preview releases.
  911        * </ul>
  912        */
  913       public static String getVersionNumber() {
  914           if (cachedVersion != null) {
  915               return cachedVersion;
  916           }
  917           try {
  918               Properties vp = new Properties();
  919               InputStream ins = Configuration.class.getClassLoader()
  920                       .getResourceAsStream("freemarker/version.properties");
  921               if (ins == null) {
  922                   throw new RuntimeException("Version file is missing.");
  923               } else {
  924                   try {
  925                       vp.load(ins);
  926                   } finally {
  927                       ins.close();
  928                   }
  929                   String v = vp.getProperty("version");
  930                   if (v == null) {
  931                       throw new RuntimeException("Version file is corrupt: version key is missing.");
  932                   }
  933                   cachedVersion = v;
  934               }
  935               return cachedVersion;
  936           } catch (IOException e) {
  937               throw new RuntimeException("Failed to load version file: " + e);
  938           }
  939       }
  940       
  941       void doAutoIncludes(Environment env) throws TemplateException, IOException {
  942           for (int i = 0; i < autoIncludes.size(); i++) {
  943               String templateName = (String) autoIncludes.get(i);
  944               Template template = getTemplate(templateName, env.getLocale());
  945               env.include(template);
  946           }
  947       }
  948       
  949   }

Save This Page
Home » freemarker-2.3.13 » freemarker.template » [javadoc | source]