Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright (c) 2003 The Visigoth Software Society. All rights
    * reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    *
    * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowledgement:
   *       "This product includes software developed by the
   *        Visigoth Software Society (http://www.visigoths.org/)."
   *    Alternately, this acknowledgement may appear in the software itself,
   *    if and wherever such third-party acknowledgements normally appear.
   *
   * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
   *    project contributors may be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact visigoths@visigoths.org.
   *
   * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
   *    nor may "FreeMarker" or "Visigoth" appear in their names
   *    without prior written permission of the Visigoth Software Society.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Visigoth Software Society. For more
   * information on the Visigoth Software Society, please see
   * http://www.visigoths.org/
   */
  
  package freemarker.core;
  
  import java.io.Writer;
  import java.util.*;
  
This is a common superclass of freemarker.template.Configuration, freemarker.template.Template, and Environment classes. It provides settings that are common to each of them. FreeMarker uses a three-level setting hierarchy - the return value of every setting getter method on Configurable objects inherits its value from its parent Configurable object, unless explicitly overridden by a call to a corresponding setter method on the object itself. The parent of an Environment object is a Template object, the parent of a Template object is a Configuration object.

Author(s):
Attila Szegedi
Version:
$Id: Configurable.java,v 1.23.2.2 2007/04/02 13:46:43 szegedia Exp $
  
  public class Configurable
  {
      public static final String LOCALE_KEY = "locale";
      public static final String NUMBER_FORMAT_KEY = "number_format";
      public static final String TIME_FORMAT_KEY = "time_format";
      public static final String DATE_FORMAT_KEY = "date_format";
      public static final String DATETIME_FORMAT_KEY = "datetime_format";
      public static final String TIME_ZONE_KEY = "time_zone";
      public static final String CLASSIC_COMPATIBLE_KEY = "classic_compatible";
      public static final String TEMPLATE_EXCEPTION_HANDLER_KEY = "template_exception_handler";
      public static final String ARITHMETIC_ENGINE_KEY = "arithmetic_engine";
      public static final String OBJECT_WRAPPER_KEY = "object_wrapper";
      public static final String BOOLEAN_FORMAT_KEY = "boolean_format";
      public static final String OUTPUT_ENCODING_KEY = "output_encoding";
      public static final String URL_ESCAPING_CHARSET_KEY = "url_escaping_charset";
      public static final String STRICT_BEAN_MODELS = "strict_bean_models";
    

Since:
2.3.17
  
      public static final String AUTO_FLUSH_KEY = "auto_flush";
    

Since:
2.3.17
  
      public static final String NEW_BUILTIN_CLASS_RESOLVER_KEY = "new_builtin_class_resolver";
 
     private static final char COMMA = ',';
     
     private Configurable parent;
     private Properties properties;
     private HashMap customAttributes;
     
     private Locale locale;
     private String numberFormat;
     private String timeFormat;
     private String dateFormat;
     private String dateTimeFormat;
     private TimeZone timeZone;
     private String trueFormat;
     private String falseFormat;
     private Boolean classicCompatible;
     private ObjectWrapper objectWrapper;
     private String outputEncoding;
     private boolean outputEncodingSet;
     private String urlEscapingCharset;
     private boolean urlEscapingCharsetSet;
     private Boolean autoFlush;
     
     public Configurable() {
          = null;
          = Locale.getDefault();
          = TimeZone.getDefault();
          = "number";
          = "";
          = "";
          = "";
          = "true";
          = "false";
          = .;
          = .;
         // outputEncoding and urlEscapingCharset defaults to null,
         // which means "not specified"
         
          = new Properties();
         .setProperty("true,false");
         // as outputEncoding and urlEscapingCharset defaults to null, 
         // they are not set
         
          = new HashMap();
     }
    
    
Creates a new instance. Normally you do not need to use this constructor, as you don't use Configurable directly, but its subclasses.
 
     public Configurable(Configurable parent) {
         this. = parent;
          = null;
          = null;
          = null;
          = null;
          = null;
          = null;
          = new Properties(parent.properties);
          = new HashMap();
     }
 
     protected Object clone() throws CloneNotSupportedException {
         Configurable copy = (Configurable)super.clone();
         copy.properties = new Properties();
         copy.customAttributes = (HashMap).clone();
         return copy;
     }
    
    
Returns the parent Configurable object of this object. The parent stores the default values for this configurable. For example, the parent of the freemarker.template.Template object is the freemarker.template.Configuration object, so setting values not specfied on template level are specified by the confuration object.

Returns:
the parent Configurable object, or null, if this is the root Configurable object.
 
     public final Configurable getParent() {
         return ;
     }
    
    
Reparenting support. This is used by Environment when it includes a template - the included template becomes the parent configurable during its evaluation.
 
     final void setParent(Configurable parent) {
         this. = parent;
     }
    
    
Toggles the "Classic Compatibile" mode. For a comprehensive description of this mode, see isClassicCompatible().
 
     public void setClassicCompatible(boolean classicCompatibility) {
         this. = classicCompatibility ? . : .;
     }

    
Returns whether the engine runs in the "Classic Compatibile" mode. When this mode is active, the engine behavior is altered in following way: (these resemble the behavior of the 1.7.x line of FreeMarker engine, now named "FreeMarker Classic", hence the name).
  • handle undefined expressions gracefully. Namely when an expression "expr" evaluates to null:
    • as argument of the <assign varname=expr> directive, ${expr} directive, otherexpr == expr or otherexpr != expr conditional expressions, or hash[expr] expression, then it is treated as empty string.
    • as argument of <list expr as item> or <foreach item in expr>, the loop body is not executed (as if it were a 0-length list)
    • as argument of <if> directive, or otherwise where a boolean expression is expected, it is treated as false
  • Non-boolean models are accepted in <if> directive, or as operands of logical operators. "Empty" models (zero-length string, empty sequence or hash) are evaluated as false, all others are evaluated as true.
  • When boolean value is treated as a string (i.e. output in ${...} directive, or concatenated with other string), true values are converted to string "true", false values are converted to empty string.
  • Scalar models supplied to <list> and <foreach> are treated as a one-element list consisting of the passed model.
  • Paths parameter of <include> will be interpreted as absolute path.
In all other aspects, the engine is a 2.1 engine even in compatibility mode - you don't lose any of the new functionality by enabling it.
 
     public boolean isClassicCompatible() {
         return  != null ? .booleanValue() : .isClassicCompatible();
     }

    
Sets the locale to assume when searching for template files with no explicit requested locale.
 
     public void setLocale(Locale locale) {
         if (locale == nullthrow new IllegalArgumentException("Setting \"locale\" can't be null");
         this. = locale;
         .setProperty(locale.toString());
     }

    
Returns the time zone to use when formatting time values. Defaults to system time zone.
 
     public TimeZone getTimeZone() {
         return  != null ?  : .getTimeZone();
     }

    
Sets the time zone to use when formatting time values.
 
     public void setTimeZone(TimeZone timeZone) {
         if (timeZone == nullthrow new IllegalArgumentException("Setting \"time_zone\" can't be null");
         this. = timeZone;
         .setProperty(timeZone.getID());
     }

    
Returns the assumed locale when searching for template files with no explicit requested locale. Defaults to system locale.
 
     public Locale getLocale() {
         return  != null ?  : .getLocale();
     }

    
Sets the number format used to convert numbers to strings.
 
     public void setNumberFormat(String numberFormat) {
         if (numberFormat == nullthrow new IllegalArgumentException("Setting \"number_format\" can't be null");
         this. = numberFormat;
         .setProperty(numberFormat);
     }

    
Returns the default number format used to convert numbers to strings. Defaults to "number"
 
     public String getNumberFormat() {
         return  != null ?  : .getNumberFormat();
     }
 
     public void setBooleanFormat(String booleanFormat) {
         if (booleanFormat == null) {
             throw new IllegalArgumentException("Setting \"boolean_format\" can't be null");
         } 
         int comma = booleanFormat.indexOf();
         if(comma == -1) {
             throw new IllegalArgumentException("Setting \"boolean_format\" must consist of two comma-separated values for true and false respectively");
         }
          = booleanFormat.substring(0, comma);
          = booleanFormat.substring(comma + 1);
         .setProperty(booleanFormat);
     }
     
     public String getBooleanFormat() {
         if( == null) {
             return .getBooleanFormat(); 
         }
         return  +  + ;
     }
     
     String getBooleanFormat(boolean value) {
         return value ? getTrueFormat() : getFalseFormat(); 
     }
     
     private String getTrueFormat() {
         return  != null ?  : .getTrueFormat(); 
     }
     
     private String getFalseFormat() {
         return  != null ?  : .getFalseFormat(); 
     }

    
Sets the date format used to convert date models representing time-only values to strings.
 
     public void setTimeFormat(String timeFormat) {
         if (timeFormat == nullthrow new IllegalArgumentException("Setting \"time_format\" can't be null");
         this. = timeFormat;
         .setProperty(timeFormat);
     }

    
Returns the date format used to convert date models representing time-only dates to strings. Defaults to "time"
 
     public String getTimeFormat() {
         return  != null ?  : .getTimeFormat();
     }

    
Sets the date format used to convert date models representing date-only dates to strings.
 
     public void setDateFormat(String dateFormat) {
         if (dateFormat == nullthrow new IllegalArgumentException("Setting \"date_format\" can't be null");
         this. = dateFormat;
         .setProperty(dateFormat);
     }

    
Returns the date format used to convert date models representing date-only dates to strings. Defaults to "date"
 
     public String getDateFormat() {
         return  != null ?  : .getDateFormat();
     }

    
Sets the date format used to convert date models representing datetime dates to strings.
 
     public void setDateTimeFormat(String dateTimeFormat) {
         if (dateTimeFormat == nullthrow new IllegalArgumentException("Setting \"datetime_format\" can't be null");
         this. = dateTimeFormat;
         .setProperty(dateTimeFormat);
     }

    
Returns the date format used to convert date models representing datetime dates to strings. Defaults to "datetime"
 
     public String getDateTimeFormat() {
         return  != null ?  : .getDateTimeFormat();
     }

    
Sets the exception handler used to handle template exceptions.

Parameters:
templateExceptionHandler the template exception handler to use for handling freemarker.template.TemplateExceptions. By default, freemarker.template.TemplateExceptionHandler.HTML_DEBUG_HANDLER is used.
 
     public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) {
         if (templateExceptionHandler == nullthrow new IllegalArgumentException("Setting \"template_exception_handler\" can't be null");
         this. = templateExceptionHandler;
         .setProperty(templateExceptionHandler.getClass().getName());
     }

    
Retrieves the exception handler used to handle template exceptions.
 
         return  != null
                 ?  : .getTemplateExceptionHandler();
     }

    
Sets the arithmetic engine used to perform arithmetic operations.

Parameters:
arithmeticEngine the arithmetic engine used to perform arithmetic operations.By default, ArithmeticEngine.BIGDECIMAL_ENGINE is used.
 
     public void setArithmeticEngine(ArithmeticEngine arithmeticEngine) {
         if (arithmeticEngine == nullthrow new IllegalArgumentException("Setting \"arithmetic_engine\" can't be null");
         this. = arithmeticEngine;
         .setProperty(arithmeticEngine.getClass().getName());
     }

    
Retrieves the arithmetic engine used to perform arithmetic operations.
 
         return  != null
                 ?  : .getArithmeticEngine();
     }

    
Sets the object wrapper used to wrap objects to template models.

Parameters:
objectWrapper the object wrapper used to wrap objects to template models.By default, freemarker.template.ObjectWrapper.DEFAULT_WRAPPER is used.
 
     public void setObjectWrapper(ObjectWrapper objectWrapper) {
         if (objectWrapper == nullthrow new IllegalArgumentException("Setting \"object_wrapper\" can't be null");
         this. = objectWrapper;
         .setProperty(objectWrapper.getClass().getName());
     }

    
Retrieves the object wrapper used to wrap objects to template models.
 
     public ObjectWrapper getObjectWrapper() {
         return  != null
                 ?  : .getObjectWrapper();
     }
    
    
Sets the output encoding. Allows null, which means that the output encoding is not known.
 
     public void setOutputEncoding(String outputEncoding) {
         this. = outputEncoding;
         // java.util.Properties doesn't allow null value!
         if (outputEncoding != null) {
             .setProperty(outputEncoding);
         } else {
             .remove();
         }
          = true;
     }
     
     public String getOutputEncoding() {
         return 
                 ? 
                 : ( != null ? .getOutputEncoding() : null);
     }
    
    
Sets the URL escaping charset. Allows null, which means that the output encoding will be used for URL escaping.
 
     public void setURLEscapingCharset(String urlEscapingCharset) {
         this. = urlEscapingCharset;
         // java.util.Properties doesn't allow null value!
         if (urlEscapingCharset != null) {
             .setProperty(urlEscapingCharset);
         } else {
             .remove();
         }
          = true;
     }
     
     public String getURLEscapingCharset() {
         return 
                 ? 
                 : ( != null ? .getURLEscapingCharset() : null);
     }
    
    
Sets the TemplateClassResolver that is used when the new built-in is called in a template. That is, when a template contains the "com.example.SomeClassName"?new expression, this object will be called to resolve the "com.example.SomeClassName" string to a class. The default value is TemplateClassResolver.UNRESTRICTED_RESOLVER in FreeMarker 2.3.x, and TemplateClassResolver.SAFER_RESOLVER starting from FreeMarker 2.4.0. If you allow users to upload templates, it's important to use a custom restrictive TemplateClassResolver.

Since:
2.3.17
 
     public void setNewBuiltinClassResolver(TemplateClassResolver newBuiltinClassResolver) {
         if (newBuiltinClassResolver == nullthrow new IllegalArgumentException(
                 "Setting \"" +  + "\" can't be null.");
         this. = newBuiltinClassResolver;
                 newBuiltinClassResolver.getClass().getName());
     }

    
Retrieves the TemplateClassResolver used to resolve classes when "SomeClassName"?new is called in a template.

Since:
2.3.17
 
         return  != null
                 ?  : .getNewBuiltinClassResolver();
     }
    
    
Sets whether the output java.io.Writer is automatically flushed at the end of freemarker.template.Template.process(java.lang.Object,java.io.Writer) (and its overloads). The default is true.

Using false is needed for example when a Web page is composed from several boxes (like portlets, GUI panels, etc.) that aren't inserted with #include (or with similar directives) into a master FreeMarker template, rather they are all processed with a separate freemarker.template.Template.process(java.lang.Object,java.io.Writer) call. In a such scenario the automatic flushes would commit the HTTP response after each box, hence interfering with full-page buffering, and also possibly decreasing performance with too frequent and too early response buffer flushes.

Since:
2.3.17
 
     public void setAutoFlush(boolean autoFlush) {
         this. = autoFlush ? . : .;
         .setProperty(, String.valueOf(autoFlush));
     }
    
    
See setAutoFlush(boolean)

Since:
2.3.17
 
     public boolean getAutoFlush() {
         return  != null 
             ? .booleanValue()
             : ( != null ? .getAutoFlush() : true);
     }
     
     private static final String ALLOWED_CLASSES = "allowed_classes";
     private static final String TRUSTED_TEMPLATES = "trusted_templates";
    
    
Sets a setting by a name and string value.

List of supported names and their valid values:

  • "locale": local codes with the usual format, such as "en_US".
  • "classic_compatible": "true", "false", "yes", "no", "t", "f", "y", "n". Case insensitive.
  • "template_exception_handler": If the value contains dot, then it is interpreted as class name, and the object will be created with its parameterless constructor. If the value does not contain dot, then it must be one of these special values: "rethrow", "debug", "html_debug", "ignore" (case insensitive).
  • "arithmetic_engine": If the value contains dot, then it is interpreted as class name, and the object will be created with its parameterless constructor. If the value does not contain dot, then it must be one of these special values: "bigdecimal", "conservative" (case insensitive).
  • "object_wrapper": If the value contains dot, then it is interpreted as class name, and the object will be created with its parameterless constructor. If the value does not contain dot, then it must be one of these special values: "simple", "beans", "jython" (case insensitive).
  • "number_format": pattern as java.text.DecimalFormat defines.
  • "boolean_format": the textual value for boolean true and false, separated with comma. For example "yes,no".
  • "date_format", "time_format", "datetime_format": patterns as java.text.SimpleDateFormat defines.
  • "time_zone": time zone, with the format as java.util.TimeZone.getTimeZone defines. For example "GMT-8:00" or "America/Los_Angeles"
  • "output_encoding": Informs FreeMarker about the charset used for the output. As FreeMarker outputs character stream (not byte stream), it is not aware of the output charset unless the software that encloses it tells it explicitly with this setting. Some templates may use FreeMarker features that require this.
  • "url_escaping_charset": If this setting is set, then it overrides the value of the "output_encoding" setting when FreeMarker does URL encoding.
  • "auto_flush": see setAutoFlush(boolean). Since 2.3.17.
  • "new_builtin_class_resolver": see setNewBuiltinClassResolver(freemarker.core.TemplateClassResolver). Since 2.3.17. The value must be one of these (ignore the quotation marks):
    1. "unrestricted": Use TemplateClassResolver.UNRESTRICTED_RESOLVER
    2. "safer": Use TemplateClassResolver.SAFER_RESOLVER
    3. "allows_nothing": Use TemplateClassResolver.ALLOWS_NOTHING_RESOLVER
    4. Something that contains colon will use OptInTemplateClassResolver and is expected to store comma separated values (possibly quoted) segmented with "allowed_classes:" and/or "trusted_templates:". Examples of valid values:
      Setting value Meaning
      allowed_classes: com.example.C1, com.example.C2, trusted_templates: lib/*, safe.ftl Only allow instantiating the com.example.C1 and com.example.C2 classes. But, allow templates within the lib/ directory (like lib/foo/bar.ftl) and template safe.ftl (that does not match foo/safe.ftl, only exactly safe.ftl) to instantiate anything that TemplateClassResolver.SAFER_RESOLVER allows.
      allowed_classes: com.example.C1, com.example.C2 Only allow instantiating the com.example.C1 and com.example.C2 classes. There are no trusted templates.
      trusted_templates: lib/*, safe.ftl Do not allow instantiating any classes, except in templates inside lib/ or in template safe.ftl.
      For more details see OptInTemplateClassResolver.
    5. Otherwise if the value contains dot, it's interpreted as a full-qualified class name, and the object will be created with its parameterless constructor.

Parameters:
key the name of the setting.
value the string that describes the new value of the setting.
Throws:
Configurable.UnknownSettingException if the key is wrong.
freemarker.template.TemplateException if the new value of the setting can't be set for any other reasons.
 
     public void setSetting(String keyString valuethrows TemplateException {
         try {
             if (.equals(key)) {
                 setLocale(StringUtil.deduceLocale(value));
             } else if (.equals(key)) {
                 setNumberFormat(value);
             } else if (.equals(key)) {
                 setTimeFormat(value);
             } else if (.equals(key)) {
                 setDateFormat(value);
             } else if (.equals(key)) {
                 setDateTimeFormat(value);
             } else if (.equals(key)) {
                 setTimeZone(TimeZone.getTimeZone(value));
             } else if (.equals(key)) {
                 setClassicCompatible(StringUtil.getYesNo(value));
             } else if (.equals(key)) {
                 if (value.indexOf('.') == -1) {
                     if ("debug".equalsIgnoreCase(value)) {
                         setTemplateExceptionHandler(
                                 .);
                     } else if ("html_debug".equalsIgnoreCase(value)) {
                         setTemplateExceptionHandler(
                                 .);
                     } else if ("ignore".equalsIgnoreCase(value)) {
                         setTemplateExceptionHandler(
                                 .);
                     } else if ("rethrow".equalsIgnoreCase(value)) {
                         setTemplateExceptionHandler(
                                 .);
                     } else {
                         throw invalidSettingValueException(keyvalue);
                     }
                 } else {
                     setTemplateExceptionHandler(
                             (TemplateExceptionHandler) ClassUtil.forName(value)
                             .newInstance());
                 }
             } else if (.equals(key)) {
                 if (value.indexOf('.') == -1) { 
                     if ("bigdecimal".equalsIgnoreCase(value)) {
                         setArithmeticEngine(.);
                     } else if ("conservative".equalsIgnoreCase(value)) {
                         setArithmeticEngine(.);
                     } else {
                         throw invalidSettingValueException(keyvalue);
                     }
                 } else {
                     setArithmeticEngine(
                             (ArithmeticEngine) ClassUtil.forName(value)
                             .newInstance());
                 }
             } else if (.equals(key)) {
                 if (value.indexOf('.') == -1) {
                     if ("default".equalsIgnoreCase(value)) {
                         setObjectWrapper(.);
                     } else if ("simple".equalsIgnoreCase(value)) {
                         setObjectWrapper(.);
                     } else if ("beans".equalsIgnoreCase(value)) {
                         setObjectWrapper(.);
                     } else if ("jython".equalsIgnoreCase(value)) {
                         Class clazz = Class.forName(
                                 "freemarker.ext.jython.JythonWrapper");
                         setObjectWrapper(
                                 (ObjectWrapperclazz.getField("INSTANCE").get(null));        
                     } else {
                         throw invalidSettingValueException(keyvalue);
                     }
                     
                 } else {
                     setObjectWrapper((ObjectWrapper) ClassUtil.forName(value)
                             .newInstance());
                 }
             } else if (.equals(key)) {
                 setBooleanFormat(value);
             } else if (.equals(key)) {
                 setOutputEncoding(value);
             } else if (.equals(key)) {
                 setURLEscapingCharset(value);
             } else if (.equals(key)) {
                 setStrictBeanModels(StringUtil.getYesNo(value));
             } else if (.equals(key)) {
                 setAutoFlush(StringUtil.getYesNo(value));
             } else if (.equals(key)) {
                 if ("unrestricted".equals(value)) {
                 } else if ("safer".equals(value)) {
                     setNewBuiltinClassResolver(.);
                 } else if ("allows_nothing".equals(value)) {
                 } else if (value.indexOf(":") != -1) {
                     List segments = parseAsSegmentedList(value);
                     Set allowedClasses = null;
                     List trustedTemplates = null;
                     for (int i = 0; i < segments.size(); i++) {
                         KeyValuePair kv = (KeyValuePairsegments.get(i);
                         String segmentKey = (Stringkv.getKey();
                         List segmentValue = (Listkv.getValue();
                         if (segmentKey.equals()) {
                             allowedClasses = new HashSet(segmentValue); 
                         } else if (segmentKey.equals()) {
                             trustedTemplates = segmentValue;
                         } else {
                             throw new ParseException(
                                     "Unrecognized list segment key: " + StringUtil.jQuote(segmentKey) +
                                     ". Supported keys are: \"" +  + "\", \"" +
                                      + "\"", 0, 0);
                         }
                     }
                     setNewBuiltinClassResolver(
                             new OptInTemplateClassResolver(allowedClassestrustedTemplates));
                 } else if (value.indexOf('.') == -1) {
                     setNewBuiltinClassResolver((TemplateClassResolver) ClassUtil.forName(value)
                             .newInstance());
                 } else {
                     throw invalidSettingValueException(keyvalue);
                 }
             } else {
                 throw unknownSettingException(key);
             }
         } catch(Exception e) {
             throw new TemplateException(
                     "Failed to set setting " + 
                     StringUtil.jQuote(key) + " to value " + StringUtil.jQuote(value) +
                     "; see cause exception.",
                     egetEnvironment());
         }
     }
 
     public void setStrictBeanModels(boolean strict) {
 	if (!( instanceof BeansWrapper)) {
 	    throw new IllegalStateException("The value of the " +  +
 	            " setting isn't a " + BeansWrapper.class.getName() + ".");
 	}
     }

    
    
Returns the textual representation of a setting.

Deprecated:
This method was always defective, and certainly it always will be. Don't use it. (Simply, it's hardly possible in general to convert setting values to text in a way that ensures that setSetting(java.lang.String,java.lang.String) will work with them correctly.)
Parameters:
key the setting key. Can be any of standard XXX_KEY constants, or a custom key.
 
     public String getSetting(String key) {
         return .getProperty(key);
     }
    
    
This meant to return the String-to-String Map of the settings. So it actually should return a Properties object, but it doesn't by mistake. The returned Map is read-only, but it will reflect the further configuration changes (aliasing effect).

Deprecated:
This method was always defective, and certainly it always will be. Don't use it. (Simply, it's hardly possible in general to convert setting values to text in a way that ensures that setSettings(java.util.Properties) will work with them correctly.)
 
     public Map getSettings() {
         return Collections.unmodifiableMap();
     }
     
     protected Environment getEnvironment() {
         return this instanceof Environment
             ? (Environmentthis
             : Environment.getCurrentEnvironment();
     }
     
     protected TemplateException unknownSettingException(String name) {
         return new UnknownSettingException(namegetEnvironment());
     }
 
     protected TemplateException invalidSettingValueException(String nameString value) {
         return new TemplateException("Invalid value for setting " + name + ": " + valuegetEnvironment());
     }
     
     public static class UnknownSettingException extends TemplateException {
         private UnknownSettingException(String nameEnvironment env) {
             super("Unknown setting: " + nameenv);
         }
     }

    
Set the settings stored in a Properties object.

Throws:
freemarker.template.TemplateException if the Properties object contains invalid keys, or invalid setting values, or any other error occurs while changing the settings.
     
     public void setSettings(Properties propsthrows TemplateException {
         Iterator it = props.keySet().iterator();
         while (it.hasNext()) {
             String key = (Stringit.next();
             setSetting(keyprops.getProperty(key).trim()); 
         }
     }
    
    
Reads a setting list (key and element pairs) from the input stream. The stream has to follow the usual .properties format.

Throws:
freemarker.template.TemplateException if the stream contains invalid keys, or invalid setting values, or any other error occurs while changing the settings.
java.io.IOException if an error occurred when reading from the input stream.
 
     public void setSettings(InputStream propsInthrows TemplateExceptionIOException {
         Properties p = new Properties();
         p.load(propsIn);
         setSettings(p);
     }

    
Internal entry point for setting unnamed custom attributes
 
     void setCustomAttribute(Object keyObject value) {
         synchronized() {
             .put(keyvalue);
         }
     }

    
Internal entry point for getting unnamed custom attributes
 
         synchronized() {
             Object o = .get(key);
             if(o == null && !.containsKey(key)) {
                 o = attr.create();
                 .put(keyo);
             }
             return o;
         }
     }
    
    
Sets a named custom attribute for this configurable.

Parameters:
name the name of the custom attribute
value the value of the custom attribute. You can set the value to null, however note that there is a semantic difference between an attribute set to null and an attribute that is not present, see removeCustomAttribute(java.lang.String).
 
     public void setCustomAttribute(String nameObject value) {
         synchronized() {
             .put(namevalue);
         }
     }
    
    
Returns an array with names of all custom attributes defined directly on this configurable. (That is, it doesn't contain the names of custom attributes defined indirectly on its parent configurables.) The returned array is never null, but can be zero-length. The order of elements in the returned array is not defined and can change between invocations.
 
     public String[] getCustomAttributeNames() {
         synchronized() {
             Collection names = new LinkedList(.keySet());
             for (Iterator iter = names.iterator(); iter.hasNext();) {
                 if(!(iter.next() instanceof String)) {
                     iter.remove();
                 }
             }
             return (String[])names.toArray(new String[names.size()]);
         }
     }
    
    
Removes a named custom attribute for this configurable. Note that this is different than setting the custom attribute value to null. If you set the value to null, getCustomAttribute(java.lang.String) will return null, while if you remove the attribute, it will return the value of the attribute in the parent configurable (if there is a parent configurable, that is).

Parameters:
name the name of the custom attribute
 
     public void removeCustomAttribute(String name) {
         synchronized() {
             .remove(name);
         }
     }

    
Retrieves a named custom attribute for this configurable. If the attribute is not present in the configurable, and the configurable has a parent, then the parent is looked up as well.

Parameters:
name the name of the custom attribute
Returns:
the value of the custom attribute. Note that if the custom attribute was created with <#ftl attributes={...}>, then this value is already unwrapped (i.e. it's a String, or a List, or a Map, ...etc., not a FreeMarker specific class).
 
     public Object getCustomAttribute(String name) {
         Object retval;
         synchronized() {
             retval = .get(name);
             if(retval == null && .containsKey(name)) {
                 return null;
             }
         }
         if(retval == null &&  != null) {
             return .getCustomAttribute(name);
         }
         return retval;
     }
     
    protected void doAutoImportsAndIncludes(Environment env)
    {
        if( != null.doAutoImportsAndIncludes(env);
    }
    protected ArrayList parseAsList(String textthrows ParseException {
        return new SettingStringParser(text).parseAsList();
    }
    protected ArrayList parseAsSegmentedList(String text)
    throws ParseException {
        return new SettingStringParser(text).parseAsSegmentedList();
    }
    
    protected HashMap parseAsImportList(String textthrows ParseException {
        return new SettingStringParser(text).parseAsImportList();
    }
    
    private static class KeyValuePair {
        private final Object key;
        private final Object value;
        
        KeyValuePair(Object keyObject value) {
            this. = key;
            this. = value;
        }
        
        Object getKey() {
            return ;
        }
        
        Object getValue() {
            return ;
        }
    }
    
    
Helper class for parsing setting values given with string.
    private static class SettingStringParser {
        private String text;
        private int p;
        private int ln;
        private SettingStringParser(String text) {
            this. = text;
            this. = 0;
            this. = text.length();
        }
        ArrayList parseAsSegmentedList() throws ParseException {
            ArrayList segments = new ArrayList();
            ArrayList currentSegment = null;
            
            char c;
            while (true) {
                c = skipWS();
                if (c == ' 'break;
                String item = fetchStringValue();
                c = skipWS();
                
                if (c == ':') {
                    currentSegment = new ArrayList();
                    segments.add(new KeyValuePair(itemcurrentSegment));
                } else {
                    if (currentSegment == null) {
                        throw new ParseException(
                                "The very first list item must be followed by \":\" so " +
                                "it will be the key for the following sub-list.",
                                0, 0);
                    }
                    currentSegment.add(item);
                }
                
                if (c == ' 'break;
                if (c != ',' && c != ':'throw new ParseException(
                        "Expected \",\" or \":\" or the end of text but " +
                        "found \"" + c + "\"", 0, 0);
                ++;
            }
            return segments;
        }
        ArrayList parseAsList() throws ParseException {
            char c;
            ArrayList seq = new ArrayList();
            while (true) {
                c = skipWS();
                if (c == ' 'break;
                seq.add(fetchStringValue());
                c = skipWS();
                if (c == ' 'break;
                if (c != ','throw new ParseException(
                        "Expected \",\" or the end of text but " +
                        "found \"" + c + "\"", 0, 0);
                ++;
            }
            return seq;
        }
        HashMap parseAsImportList() throws ParseException {
            char c;
            HashMap map = new HashMap();
            while (true) {
                c = skipWS();
                if (c == ' 'break;
                String lib = fetchStringValue();
                c = skipWS();
                if (c == ' 'throw new ParseException(
                        "Unexpected end of text: expected \"as\"", 0, 0);
                String s = fetchKeyword();
                if (!s.equalsIgnoreCase("as")) throw new ParseException(
                        "Expected \"as\", but found " + StringUtil.jQuote(s), 0, 0);
                c = skipWS();
                if (c == ' 'throw new ParseException(
                        "Unexpected end of text: expected gate hash name", 0, 0);
                String ns = fetchStringValue();
                
                map.put(nslib);
                c = skipWS();
                if (c == ' 'break;
                if (c != ','throw new ParseException(
                        "Expected \",\" or the end of text but "
                        + "found \"" + c + "\"", 0, 0);
                ++;
            }
            return map;
        }
        String fetchStringValue() throws ParseException {
            String w = fetchWord();
            if (w.startsWith("'") || w.startsWith("\"")) {
                w = w.substring(1, w.length() - 1);
            }
            return StringUtil.FTLStringLiteralDec(w);
        }
        String fetchKeyword() throws ParseException {
            String w = fetchWord();
            if (w.startsWith("'") || w.startsWith("\"")) {
                throw new ParseException(
                    "Keyword expected, but a string value found: " + w, 0, 0);
            }
            return w;
        }
        char skipWS() {
            char c;
            while ( < ) {
                c = .charAt();
                if (!Character.isWhitespace(c)) return c;
                ++;
            }
            return ' ';
        }
        private String fetchWord() throws ParseException {
            if ( == throw new ParseException(
                    "Unexpeced end of text", 0, 0);
            char c = .charAt();
            int b = ;
            if (c == '\'' || c == '"') {
                boolean escaped = false;
                char q = c;
                ++;
                while ( < ) {
                    c = .charAt();
                    if (!escaped) {
                        if (c == '\\') {
                            escaped = true;
                        } else if (c == q) {
                            break;
                        }
                    } else {
                        escaped = false;
                    }
                    ++;
                }
                if ( == ) {
                    throw new ParseException("Missing " + q, 0, 0);
                }
                ++;
                return .substring(b);
            } else {
                do {
                    c = .charAt();
                    if (!(Character.isLetterOrDigit(c)
                            || c == '/' || c == '\\' || c == '_'
                            || c == '.' || c == '-' || c == '!'
                            || c == '*' || c == '?')) break;
                    ++;
                } while ( < );
                if (b == ) {
                    throw new ParseException("Unexpected character: " + c, 0, 0);
                } else {
                    return .substring(b);
                }
            }
        }
    }