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.*;
  import java.text.*;
  import java.util.*;
  
Object that represents the runtime environment during template processing. For every invocation of a Template.process() method, a new instance of this object is created, and then discarded when process() returns. This object stores the set of temporary variables created by the template, the value of settings set by the template, the reference to the data model root, etc. Everything that is needed to fulfill the template processing job.

Data models that need to access the Environment object that represents the template processing on the current thread can use the getCurrentEnvironment() method.

If you need to modify or read this object before or after the process call, use freemarker.template.Template.createProcessingEnvironment(java.lang.Object,java.io.Writer,freemarker.template.ObjectWrapper)

Author(s):
Jonathan Revusky
Attila Szegedi
  
  public final class Environment extends Configurable {
  
      private static final ThreadLocal threadEnv = new ThreadLocal();
  
      private static final Logger logger = Logger.getLogger("freemarker.runtime");
      private static final Logger attemptLogger = Logger.getLogger("freemarker.runtime.attempt");
  
      private static final Map localizedNumberFormats = new HashMap();
      private static final Map localizedDateFormats = new HashMap();
  
      // Do not use this object directly; clone it first! DecimalFormat isn't
      // thread-safe.
      private static final DecimalFormat C_NUMBER_FORMAT
              = new DecimalFormat(
                      "0.################",
                     new DecimalFormatSymbols(.));
     static {
         .setGroupingUsed(false);
     }
 
     private final TemplateHashModel rootDataModel;
     private final ArrayList elementStack = new ArrayList();
     private final ArrayList recoveredErrorStack = new ArrayList();
 
     private NumberFormat numberFormat;
     private Map numberFormats;
 
     private Map[] dateFormats;
     private NumberFormat cNumberFormat;
    
    
Used by the "iso_" built-ins to accelerate formatting.

 
 
     private Collator collator;
 
     private Writer out;
     private Macro.Context currentMacroContext;
     private ArrayList localContextStack
     private HashMap loadedLibs;
 
     private Throwable lastThrowable;
     
     private TemplateModel lastReturnValue;
     private HashMap macroToNamespaceLookup = new HashMap();
 
     private TemplateNodeModel currentVisitorNode;    
     // Things we keep track of for the fallback mechanism.
     private int nodeNamespaceIndex;
     private String currentNodeNamecurrentNodeNS;
     
     private String cachedURLEscapingCharset;
     private boolean urlEscapingCharsetCached;

    
Retrieves the environment object associated with the current thread. Data model implementations that need access to the environment can call this method to obtain the environment object that represents the template processing that is currently running on the current thread.
 
     public static Environment getCurrentEnvironment()
     {
         return (Environment).get();
     }
 
     public Environment(Template templatefinal TemplateHashModel rootDataModelWriter out)
     {
         super(template);
         this. = new Namespace(null);
         this. =  = new Namespace(template);
         this. = out;
         this. = rootDataModel;
         importMacros(template);
     }

    
Retrieves the currently processed template.
 
     public Template getTemplate()
     {
         return (Template)getParent();
     }

    
Deletes cached values that meant to be valid only during a single template execution.
 
     private void clearCachedValues() {
          = null;
          = null;
          = null;
          = null;
          = null;
          = false;
     }
    
    
Processes the template to which this environment belongs.
 
     public void process() throws TemplateExceptionIOException {
         Object savedEnv = .get();
         .set(this);
         try {
             // Cached values from a previous execution are possibly outdated.
             clearCachedValues();
             try {
                 doAutoImportsAndIncludes(this);
                 visit(getTemplate().getRootTreeNode());
                 // It's here as we must not flush if there was an exception.
                 if (getAutoFlush()) {
                     .flush();
                 }
             } finally {
                 // It's just to allow the GC to free memory...
                 clearCachedValues();
             }
         } finally {
             .set(savedEnv);
         }
     }
    
    
"Visit" the template element.
 
     void visit(TemplateElement element)
     throws TemplateExceptionIOException
     {
         pushElement(element);
         try {
             element.accept(this);
         }
         catch (TemplateException te) {
             handleTemplateException(te);
         }
         finally {
             popElement();
         }
     }
 
     private static final TemplateModel[] NO_OUT_ARGS = new TemplateModel[0];
     
     public void visit(final TemplateElement element,
             TemplateDirectiveModel directiveModelMap args
             final List bodyParameterNamesthrows TemplateExceptionIOException {
         TemplateDirectiveBody nested;
         if(element == null) {
             nested = null;
         }
         else {
             nested = new TemplateDirectiveBody() {
                 public void render(Writer newOutthrows TemplateExceptionIOException {
                     Writer prevOut = ;
                      = newOut;
                     try {
                         Environment.this.visit(element);
                     }
                     finally {
                          = prevOut;
                     }
                 }
             };
         }
         final TemplateModel[] outArgs;
         if(bodyParameterNames == null || bodyParameterNames.isEmpty()) {
             outArgs = ;
         }
         else {
             outArgs = new TemplateModel[bodyParameterNames.size()];
         }
         if(outArgs.length > 0) {
             pushLocalContext(new LocalContext() {
                 public TemplateModel getLocalVariable(String name) {
                     int index = bodyParameterNames.indexOf(name);
                     return index != -1 ? outArgs[index] : null;
                 }
 
                 public Collection getLocalVariableNames() {
                     return bodyParameterNames;
                 }
             });
         }
         try {
             directiveModel.execute(thisargsoutArgsnested);
         }
         finally {
             if(outArgs.length > 0) {
                 popLocalContext();
             }
         }
     }
    
    
"Visit" the template element, passing the output through a TemplateTransformModel

Parameters:
element the element to visit through a transform
transform the transform to pass the element output through
args optional arguments fed to the transform
 
     void visit(TemplateElement element,
                TemplateTransformModel transform,
                Map args)
     throws TemplateExceptionIOException
     {
         try {
             Writer tw = transform.getWriter(args);
             if (tw == nulltw = ;
             TransformControl tc =
                 tw instanceof TransformControl
                 ? (TransformControl)tw
                 : null;
 
             Writer prevOut = ;
              = tw;
             try {
                 if(tc == null || tc.onStart() != .) {
                     do {
                         if(element != null) {
                             visit(element);
                         }
                     } while(tc != null && tc.afterBody() == .);
                 }
             }
             catch(Throwable t) {
                 try {
                     if(tc != null) {
                         tc.onError(t);
                     }
                     else {
                         throw t;
                     }
                 }
                 catch(TemplateException e) {
                     throw e;
                 }
                 catch(IOException e) {
                     throw e;
                 }
                 catch(RuntimeException e) {
                     throw e;
                 }
                 catch(Error e) {
                     throw e;
                 }
                 catch(Throwable e) {
                     throw new UndeclaredThrowableException(e);
                 }
             }
             finally {
                  = prevOut;
                 tw.close();
             }
         }
         catch(TemplateException te) {
             handleTemplateException(te);
         }
     }
    
    
Visit a block using buffering/recovery
 
     
      void visit(TemplateElement attemptBlockTemplateElement recoveryBlock
      throws TemplateExceptionIOException {
          Writer prevOut = this.;
          StringWriter sw = new StringWriter();
          this. = sw;
          TemplateException thrownException = null;
          try {
              visit(attemptBlock);
          } catch (TemplateException te) {
              thrownException = te;
          } finally {
              this. = prevOut;
          }
          if (thrownException != null) {
              if (.isDebugEnabled()) {
                  .debug("Error in attempt block " + 
                          attemptBlock.getStartLocationQuoted(), thrownException);
              }
              try {
                  .add(thrownException.getMessage());
                  visit(recoveryBlock);
              } finally {
                  .remove(.size() -1);
              }
          } else {
              .write(sw.toString());
          }
      }
      
          if(.isEmpty()) {
              throw new TemplateException(
                  ".error is not available outside of a <#recover> block"this);
          }
          return (String.get(.size() -1);
      }
 
 
     void visit(BodyInstruction.Context bctxtthrows TemplateExceptionIOException {
         Macro.Context invokingMacroContext = getCurrentMacroContext();
         ArrayList prevLocalContextStack = ;
         TemplateElement body = invokingMacroContext.body;
         if (body != null) {
             this. = invokingMacroContext.prevMacroContext;
              = invokingMacroContext.bodyNamespace;
             Configurable prevParent = getParent();
             setParent(.getTemplate());
             this. = invokingMacroContext.prevLocalContextStack;
             if (invokingMacroContext.bodyParameterNames != null) {
                 pushLocalContext(bctxt);
             }
             try {
                 visit(body);
             }
             finally {
                 if (invokingMacroContext.bodyParameterNames != null) {
                     popLocalContext();
                 }
                 this. = invokingMacroContext;
                  = getMacroNamespace(invokingMacroContext.getMacro());
                 setParent(prevParent);
                 this. = prevLocalContextStack;
             }
         }
     }

    
"visit" an IteratorBlock
 
     void visit(IteratorBlock.Context ictxt)
     throws TemplateExceptionIOException
     {
         pushLocalContext(ictxt);
         try {
             ictxt.runLoop(this);
         }
         catch (BreakInstruction.Break br) {
         }
         catch (TemplateException te) {
             handleTemplateException(te);
         }
         finally {
             popLocalContext();
         }
     }
    
    
"Visit" A TemplateNodeModel
 
     
     void visit(TemplateNodeModel nodeTemplateSequenceModel namespaces
     throws TemplateExceptionIOException 
     {
         if ( == null) {
             SimpleSequence ss = new SimpleSequence(1);
             ss.add();
              = ss;
         }
         int prevNodeNamespaceIndex = this.;
         String prevNodeName = this.;
         String prevNodeNS = this.;
         TemplateSequenceModel prevNodeNamespaces = ;
         TemplateNodeModel prevVisitorNode = ;
          = node;
         if (namespaces != null) {
             this. = namespaces;
         }
         try {
             TemplateModel macroOrTransform = getNodeProcessor(node);
             if (macroOrTransform instanceof Macro) {
                 visit((MacromacroOrTransformnullnullnullnull);
             }
             else if (macroOrTransform instanceof TemplateTransformModel) {
                 visit(null, (TemplateTransformModelmacroOrTransformnull); 
             }
             else {
                 String nodeType = node.getNodeType();
                 if (nodeType != null) {
                     // If the node's type is 'text', we just output it.
                     if ((nodeType.equals("text") && node instanceof TemplateScalarModel)) 
                     {
                            .write(((TemplateScalarModelnode).getAsString());
                     }
                     else if (nodeType.equals("document")) {
                         recurse(nodenamespaces);
                     }
                     // We complain here, unless the node's type is 'pi', or "comment" or "document_type", in which case
                     // we just ignore it.
                     else if (!nodeType.equals("pi"
                          && !nodeType.equals("comment"
                          && !nodeType.equals("document_type")) 
                     {
                         String nsBit = "";
                         String ns = node.getNodeNamespace();
                         if (ns != null) {
                             if (ns.length() >0) {
                                 nsBit = " and namespace " + ns;
                             } else {
                                 nsBit = " and no namespace";
                             }
                         }
                         throw new TemplateException("No macro or transform defined for node named "  
                                     + node.getNodeName() + nsBit
                                     + ", and there is no fallback handler called @" + nodeType + " either.",
                                     this);
                     }
                 }
                 else {
                     String nsBit = "";
                     String ns = node.getNodeNamespace();
                     if (ns != null) {
                         if (ns.length() >0) {
                             nsBit = " and namespace " + ns;
                         } else {
                             nsBit = " and no namespace";
                         }
                     }
                     throw new TemplateException("No macro or transform defined for node with name " 
                                 + node.getNodeName() + nsBit 
                                 + ", and there is no macro or transform called @default either.",
                                 this);
                 }
             }
         } 
         finally {
             this. = prevVisitorNode;
             this. = prevNodeNamespaceIndex;
             this. = prevNodeName;
             this. = prevNodeNS;
             this. = prevNodeNamespaces;
         }
     }
     
     void fallback() throws TemplateExceptionIOException {
         if (macroOrTransform instanceof Macro) {
             visit((MacromacroOrTransformnullnullnullnull);
         }
         else if (macroOrTransform instanceof TemplateTransformModel) {
             visit(null, (TemplateTransformModelmacroOrTransformnull); 
         }
     }
    
    
"visit" a macro.
 
     
     void visit(Macro macro
                Map namedArgs
                List positionalArgs
                List bodyParameterNames,
                TemplateElement nestedBlock
        throws TemplateExceptionIOException 
     {
         if (macro == .) {
             return;
         }
         pushElement(macro);
         try {
             Macro.Context previousMacroContext = ;
             Macro.Context mc = macro.new Context(thisnestedBlockbodyParameterNames);
 
             String catchAll = macro.getCatchAll();
             TemplateModel unknownVars = null;
             
             if (namedArgs != null) {
                 if (catchAll != null)
                     unknownVars = new SimpleHash();
                 for (Iterator it = namedArgs.entrySet().iterator(); it.hasNext();) {
                     Map.Entry entry = (Map.Entryit.next();
                     String varName = (Stringentry.getKey();
                     boolean hasVar = macro.hasArgNamed(varName);
                     if (hasVar || catchAll != null) {
                         Expression arg = (Expressionentry.getValue();
                         TemplateModel value = arg.getAsTemplateModel(this);
                         if (hasVar) {
                             mc.setLocalVar(varNamevalue);
                         } else {
                             ((SimpleHash)unknownVars).put(varNamevalue);
                         }
                     } else {
                         String msg = "Macro " + macro.getName() + " has no such argument: " + varName;
                         throw new TemplateException(msgthis);
                     }
                 }
             }
             else if (positionalArgs != null) {
                 if (catchAll != null)
                     unknownVars = new SimpleSequence();
                 String[] argumentNames = macro.getArgumentNamesInternal();
                 int size = positionalArgs.size();
                 if (argumentNames.length < size && catchAll == null) {
                     throw new TemplateException("Macro " + macro.getName() 
                       + " only accepts " + argumentNames.length + " parameters."this);
                 }
                 for (int i = 0; i < sizei++) {
                     Expression argExp = (ExpressionpositionalArgs.get(i);
                     TemplateModel argModel = argExp.getAsTemplateModel(this);
                     try {
                         if (i < argumentNames.length) {
                             String argName = argumentNames[i];
                             mc.setLocalVar(argNameargModel);
                         } else {
                             ((SimpleSequence)unknownVars).add(argModel);
                         }
                     } catch (RuntimeException re) {
                         throw new TemplateException(rethis);
                     }
                 }
             }
             if (catchAll != null) {
                 mc.setLocalVar(catchAllunknownVars);
             }
             ArrayList prevLocalContextStack = ;
              = null;
             Namespace prevNamespace = ;
             Configurable prevParent = getParent();
              = (Namespace.get(macro);
              = mc;
             try {
                 mc.runMacro(this);
             }
             catch (ReturnInstruction.Return re) {
             }
             catch (TemplateException te) {
                 handleTemplateException(te);
             } finally {
                  = previousMacroContext;
                  = prevLocalContextStack;
                  = prevNamespace;
                 setParent(prevParent);
             }
         } finally {
             popElement();
         }
     }
     
     void visitMacroDef(Macro macro) {
         .put(macro);
         .put(macro.getName(), macro);
     }
     
     Namespace getMacroNamespace(Macro macro) {
         return (Namespace.get(macro);
     }
     
     void recurse(TemplateNodeModel nodeTemplateSequenceModel namespaces)
     throws TemplateExceptionIOException 
     {
         if (node == null) {
             node = this.getCurrentVisitorNode();
             if (node == null) {
                 throw new TemplateModelException(
                         "The target node of recursion is missing or null.");
             }
         }
         TemplateSequenceModel children = node.getChildNodes();
         if (children == nullreturn;
         for (int i=0; i<children.size(); i++) {
             TemplateNodeModel child = (TemplateNodeModelchildren.get(i);
             if (child != null) {
                 visit(childnamespaces);
             }
         }
     }
 
         return ;
     }
     
     private void handleTemplateException(TemplateException te)
         throws TemplateException
     {
         // Logic to prevent double-handling of the exception in
         // nested visit() calls.
         if( == te) {
             throw te;
         }
          = te;
 
         // Log the exception
         if(.isErrorEnabled()) {
             .error("Template processing error: " + StringUtil.jQuoteNoXSS(te.getMessage()), te);
         }
 
         // Stop exception is not passed to the handler, but
         // explicitly rethrown.
         if(te instanceof StopException) {
             throw te;
         }
 
         // Finally, pass the exception to the handler
     }
 
     public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) {
         super.setTemplateExceptionHandler(templateExceptionHandler);
          = null;
     }
     
     public void setLocale(Locale locale) {
         super.setLocale(locale);
         // Clear local format cache
          = null;
          = null;
 
          = null;
          =  =  = null;
 
          = null;
     }
 
     public void setTimeZone(TimeZone timeZone) {
         super.setTimeZone(timeZone);
         // Clear local date format cache
          = null;
          =  =  = null;
     }
     
     public void setURLEscapingCharset(String urlEscapingCharset) {
          = false;
         super.setURLEscapingCharset(urlEscapingCharset);
     }
     
     /*
      * Note that altough it is not allowed to set this setting with the
      * <tt>setting</tt> directive, it still must be allowed to set it from Java
      * code while the template executes, since some frameworks allow templates
      * to actually change the output encoding on-the-fly.
      */
     public void setOutputEncoding(String outputEncoding) {
          = false;
         super.setOutputEncoding(outputEncoding);
     }
    
    
Returns the name of the charset that should be used for URL encoding. This will be null if the information is not available. The function caches the return value, so it is quick to call it repeately.
 
         if (!) {
              = getURLEscapingCharset();
             if ( == null) {
                  = getOutputEncoding();
             }
              = true;
         }
         return ;
     }
 
     Collator getCollator() {
         if( == null) {
              = Collator.getInstance(getLocale());
         }
         return ;
     }
 
     public void setOut(Writer out) {
         this. = out;
     }
 
     public Writer getOut() {
         return ;
     }
 
     String formatNumber(Number number) {
         if( == null) {
              = getNumberFormatObject(getNumberFormat());
         }
         return .format(number);
     }
 
     public void setNumberFormat(String formatName) {
         super.setNumberFormat(formatName);
          = null;
     }
 
     String formatDate(Date dateint typethrows TemplateModelException {
         DateFormat df = getDateFormatObject(type);
         if(df == null) {
             throw new TemplateModelException("Can't convert the date to string, because it is not known which parts of the date variable are in use. Use ?date, ?time or ?datetime built-in, or ?string.<format> or ?string(format) built-in with this date.");
         }
         return df.format(date);
     }
 
     public void setTimeFormat(String formatName) {
         super.setTimeFormat(formatName);
          = null;
     }
 
     public void setDateFormat(String formatName) {
         super.setDateFormat(formatName);
          = null;
     }
 
     public void setDateTimeFormat(String formatName) {
         super.setDateTimeFormat(formatName);
          = null;
     }
 
     public Configuration getConfiguration() {
         return getTemplate().getConfiguration();
     }
     
         return ;
     }
     
     void setLastReturnValue(TemplateModel lastReturnValue) {
         this. = lastReturnValue;
     }
     
     void clearLastReturnValue() {
         this. = null;
     }
 
     {
         if( == null) {
              = new HashMap();
         }
 
         NumberFormat format = (NumberFormat.get(pattern);
         if(format != null)
         {
             return format;
         }
 
         // Get format from global format cache
         synchronized()
         {
             Locale locale = getLocale();
             NumberFormatKey fk = new NumberFormatKey(patternlocale);
             format = (NumberFormat).get(fk);
             if(format == null)
             {
                 // Add format to global format cache. Note this is
                 // globally done once per locale per pattern.
                 if("number".equals(pattern))
                 {
                     format = NumberFormat.getNumberInstance(locale);
                 }
                 else if("currency".equals(pattern))
                 {
                     format = NumberFormat.getCurrencyInstance(locale);
                 }
                 else if("percent".equals(pattern))
                 {
                     format = NumberFormat.getPercentInstance(locale);
                 }
                 else if ("computer".equals(pattern))
                 {
                     format = getCNumberFormat();
                 }
                 else
                 {
                     format = new DecimalFormat(patternnew DecimalFormatSymbols(getLocale()));
                 }
                 .put(fkformat);
             }
         }
 
         // Clone it and store the clone in the local cache
         format = (NumberFormat)format.clone();
         .put(patternformat);
         return format;
     }
 
     DateFormat getDateFormatObject(int dateType)
     throws
         TemplateModelException
     {
         switch(dateType) {
             case .: {
                 return null;
             }
             case .: {
                 if( == null) {
                      = getDateFormatObject(dateTypegetTimeFormat());
                 }
                 return ;
             }
             case .: {
                 if( == null) {
                      = getDateFormatObject(dateTypegetDateFormat());
                 }
                 return ;
             }
             case .: {
                 if( == null) {
                      = getDateFormatObject(dateTypegetDateTimeFormat());
                 }
                 return ;
             }
             default: {
                 throw new TemplateModelException("Unrecognized date type " + dateType);
             }
         }
     }
     
     DateFormat getDateFormatObject(int dateTypeString pattern)
     throws
         TemplateModelException
     {
         if( == null) {
              = new Map[4];
             [.] = new HashMap();
             [.] = new HashMap();
             [.] = new HashMap();
             [.] = new HashMap();
         }
         Map typedDateFormat = [dateType];
 
         DateFormat format = (DateFormattypedDateFormat.get(pattern);
         if(format != null) {
             return format;
         }
 
         // Get format from global format cache
         synchronized() {
             Locale locale = getLocale();
             TimeZone timeZone = getTimeZone();
             DateFormatKey fk = new DateFormatKey(dateTypepatternlocaletimeZone);
             format = (DateFormat).get(fk);
             if(format == null) {
                 // Add format to global format cache. Note this is
                 // globally done once per locale per pattern.
                 StringTokenizer tok = new StringTokenizer(pattern"_");
                 int style = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : .;
                 if(style != -1) {
                     switch(dateType) {
                         case .: {
                             throw new TemplateModelException(
                                 "Can't convert the date to string using a " +
                                 "built-in format, because it is not known which " +
                                 "parts of the date variable are in use. Use " +
                                 "?date, ?time or ?datetime built-in, or " +
                                 "?string.<format> or ?string(<format>) built-in "+
                                 "with explicit formatting pattern with this date.");
                         }
                         case .: {
                             format = DateFormat.getTimeInstance(stylelocale);
                             break;
                         }
                         case .: {
                             format = DateFormat.getDateInstance(stylelocale);
                             break;
                         }
                         case .: {
                             int timestyle = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : style;
                             if(timestyle != -1) {
                                 format = DateFormat.getDateTimeInstance(styletimestylelocale);
                             }
                             break;
                         }
                     }
                 }
                 if(format == null) {
                     try {
                         format = new SimpleDateFormat(patternlocale);
                     }
                     catch(IllegalArgumentException e) {
                         throw new TemplateModelException("Can't parse " + pattern + " to a date format."e);
                     }
                 }
                 format.setTimeZone(timeZone);
                 .put(fkformat);
             }
         }
 
         // Clone it and store the clone in the local cache
         format = (DateFormat)format.clone();
         typedDateFormat.put(patternformat);
         return format;
     }
 
     int parseDateStyleToken(String token) {
         if("short".equals(token)) {
             return .;
         }
         if("medium".equals(token)) {
             return .;
         }
         if("long".equals(token)) {
             return .;
         }
         if("full".equals(token)) {
             return .;
         }
         return -1;
     }
    

    
 
         if ( == null) {
         }
         return ;
    }

    
Returns the java.text.NumberFormat used for the c built-in. This is always US English "0.################", without grouping and without superfluous decimal separator.
    public NumberFormat getCNumberFormat() {
        // It can't be cached in a static field, because DecimalFormat-s aren't
        // thread-safe.
        if ( == null) {
             = (DecimalFormat.clone();
        }
        return ;
    }
        TemplateTransformModel ttm = null;
        TemplateModel tm = exp.getAsTemplateModel(this);
        if (tm instanceof TemplateTransformModel) {
            ttm = (TemplateTransformModeltm;
        }
        else if (exp instanceof Identifier) {
            tm = getConfiguration().getSharedVariable(exp.toString());
            if (tm instanceof TemplateTransformModel) {
                ttm = (TemplateTransformModeltm;
            }
        }
        return ttm;
    }

    
Returns the loop or macro local variable corresponding to this variable name. Possibly null. (Note that the misnomer is kept for backward compatibility: loop variables are not local variables according to our terminology.)
        if ( != null) {
            for (int i = .size()-1; i>=0; i--) {
                LocalContext lc = (LocalContext.get(i);
                TemplateModel tm = lc.getLocalVariable(name);
                if (tm != null) {
                    return tm;
                }
            }
        }
        return  == null ? null : .getLocalVariable(name);
    }

    
Returns the variable that is visible in this context. This is the correspondent to an FTL top-level variable reading expression. That is, it tries to find the the variable in this order:
  1. An loop variable (if we're in a loop or user defined directive body) such as foo_has_next
  2. A local variable (if we're in a macro)
  3. A variable defined in the current namespace (say, via <#assign ...>)
  4. A variable defined globally (say, via <#global ....>)
  5. Variable in the data model:
    1. A variable in the root hash that was exposed to this rendering environment in the Template.process(...) call
    2. A shared variable set in the configuration via a call to Configuration.setSharedVariable(...)
    public TemplateModel getVariable(String namethrows TemplateModelException {
        TemplateModel result = getLocalVariable(name);
        if (result == null) {
            result = .get(name);
        }
        if (result == null) {
            result = getGlobalVariable(name);
        }
        return result;
    }

    
Returns the globally visible variable of the given name (or null). This is correspondent to FTL .globals.name. This will first look at variables that were assigned globally via: <#global ...> and then at the data model exposed to the template.
        TemplateModel result = .get(name);
        if (result == null) {
            result = .get(name);
        }
        if (result == null) {
            result = getConfiguration().getSharedVariable(name);
        }
        return result;
    }

    
Sets a variable that is visible globally. This is correspondent to FTL <#global name=model>. This can be considered a convenient shorthand for: getGlobalNamespace().put(name, model)
    public void setGlobalVariable(String nameTemplateModel model) {
        .put(namemodel);
    }

    
Sets a variable in the current namespace. This is correspondent to FTL <#assign name=model>. This can be considered a convenient shorthand for: getCurrentNamespace().put(name, model)
    public void setVariable(String nameTemplateModel model) {
        .put(namemodel);
    }

    
Sets a local variable (one effective only during a macro invocation). This is correspondent to FTL <#local name=model>.

Parameters:
name the identifier of the variable
model the value of the variable.
Throws:
java.lang.IllegalStateException if the environment is not executing a macro body.
    public void setLocalVariable(String nameTemplateModel model) {
        if( == null) {
            throw new IllegalStateException("Not executing macro body");
        }
        .setLocalVar(namemodel);
    }

    
Returns a set of variable names that are known at the time of call. This includes names of all shared variables in the freemarker.template.Configuration, names of all global variables that were assigned during the template processing, names of all variables in the current name-space, names of all local variables and loop variables. If the passed root data model implements the freemarker.template.TemplateHashModelEx interface, then all names it retrieves through a call to freemarker.template.TemplateHashModelEx.keys() method are returned as well. The method returns a new Set object on each call that is completely disconnected from the Environment. That is, modifying the set will have no effect on the Environment object.
        // shared vars.
        Set set = getConfiguration().getSharedVariableNames();
        
        // root hash
        if ( instanceof TemplateHashModelEx) {
            TemplateModelIterator rootNames =
                ((TemplateHashModelEx).keys().iterator();
            while(rootNames.hasNext()) {
                set.add(((TemplateScalarModel)rootNames.next()).getAsString());
            }
        }
        
        // globals
        for (TemplateModelIterator tmi = .keys().iterator(); tmi.hasNext();) {
            set.add(((TemplateScalarModeltmi.next()).getAsString());
        }
        
        // current name-space
        for (TemplateModelIterator tmi = .keys().iterator(); tmi.hasNext();) {
            set.add(((TemplateScalarModeltmi.next()).getAsString());
        }
        
        // locals and loop vars
        if( != null) {
            set.addAll(.getLocalVariableNames());
        }
        if ( != null) {
            for (int i = .size()-1; i>=0; i--) {
                LocalContext lc = (LocalContext.get(i);
                set.addAll(lc.getLocalVariableNames());
            }
        }
        return set;
    }

    
Outputs the instruction stack. Useful for debugging. freemarker.template.TemplateExceptions incorporate this information in their stack traces.
    public void outputInstructionStack(PrintWriter pw) {
        pw.println("----------");
        ListIterator