freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [35/54] [partial] incubator-freemarker git commit: Unifying the o.a.f.core and o.a.f.core.ast
Date Thu, 23 Feb 2017 21:36:02 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java b/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java
new file mode 100644
index 0000000..35a22f5
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.Template.WrongEncodingException;
+
+/**
+ * Specifies the behavior when the template specifies its own encoding (via {@code <#ftl encoding=...>}) in the template
+ * content itself, and also when it doesn't do that.
+ */
+public interface TemplateSpecifiedEncodingHandler {
+    
+    TemplateSpecifiedEncodingHandler DEFAULT = new TemplateSpecifiedEncodingHandler() {
+
+        @Override
+        public void handle(String templateSpecificEncoding, String constructorSpecifiedEncoding)
+                throws WrongEncodingException {
+            if (constructorSpecifiedEncoding != null && templateSpecificEncoding != null
+                    && !constructorSpecifiedEncoding.equalsIgnoreCase(templateSpecificEncoding)) {
+                throw new Template.WrongEncodingException(templateSpecificEncoding, constructorSpecifiedEncoding);
+            }
+        }
+        
+    };
+
+    /**
+     * Called once during template parsing, either when the {@code #ftl} directive is processed, or near the beginning
+     * of the template processing when there's no {@code #ftl} directive in the template.
+     * 
+     * @param templateSpecificEncoding
+     *            The encoding specified via {@code <#ftl encoding=...>}, or {@code null} if that was missing (either
+     *            the {@code encoding} parameter or the whole {@code #ftl} directive).
+     * @param constructorSpecifiedEncoding
+     *            The encoding specified to the {@link Template} constructor; also the value of
+     *            {@link Template#getEncoding()}. If there was an encoding used for decoding the template file, it
+     *            should be that, or if there was no encoding, it should be {@code null}.
+     * 
+     * @throws WrongEncodingException
+     *             If the template "file" has to be re-read and the {@link Template} re-created with the encoding
+     *             specified in the constructor of {@link WrongEncodingException}.
+     */
+    void handle(String templateSpecificEncoding, String constructorSpecifiedEncoding) throws WrongEncodingException;
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java b/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java
new file mode 100644
index 0000000..cc2833c
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * Superclass of all value format objects; objects that convert values to strings, or parse strings.
+ * 
+ * @since 2.3.24
+ */
+public abstract class TemplateValueFormat {
+
+    /**
+     * Meant to be used in error messages to tell what format the parsed string didn't fit.
+     */
+    public abstract String getDescription();
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java b/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java
new file mode 100644
index 0000000..f952369
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * Error while getting, creating or applying {@link TemplateValueFormat}-s (including its subclasses, like
+ * {@link TemplateNumberFormat}).
+ * 
+ * @since 2.3.24
+ */
+public abstract class TemplateValueFormatException extends Exception {
+
+    public TemplateValueFormatException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public TemplateValueFormatException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java b/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java
new file mode 100644
index 0000000..3c504de
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * Superclass of all format factories.
+ * 
+ * @since 2.3.24
+ */
+public abstract class TemplateValueFormatFactory {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java b/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java
new file mode 100644
index 0000000..3871e58
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * Stores HTML markup to be printed; used with {@link HTMLOutputFormat}.
+ * 
+ * @since 2.3.24
+ */
+public final class TemplateXHTMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateXHTMLOutputModel> {
+    
+    /**
+     * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}.
+     */
+    TemplateXHTMLOutputModel(String plainTextContent, String markupContent) {
+        super(plainTextContent, markupContent);
+    }
+
+    @Override
+    public XHTMLOutputFormat getOutputFormat() {
+        return XHTMLOutputFormat.INSTANCE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java b/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java
new file mode 100644
index 0000000..38d2fe1
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * Stores XML markup to be printed; used with {@link XMLOutputFormat}.
+ * 
+ * @since 2.3.24
+ */
+public final class TemplateXMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateXMLOutputModel> {
+    
+    /**
+     * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}.
+     */
+    TemplateXMLOutputModel(String plainTextContent, String markupContent) {
+        super(plainTextContent, markupContent);
+    }
+
+    @Override
+    public XMLOutputFormat getOutputFormat() {
+        return XMLOutputFormat.INSTANCE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java b/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
new file mode 100644
index 0000000..c24f2f2
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateDirectiveBody;
+
+/**
+ * Not yet public; subject to change.
+ * 
+ * <p>
+ * Known compatibility risks when using this post-processor:
+ * <ul>
+ * <li>{@link TemplateDateModel}-s that care to explicitly check if their nested content is {@code null} might start to
+ *   complain that you have specified a body despite that the directive doesn't support that. Directives should use
+ *   {@link NestedContentNotSupportedException#check(TemplateDirectiveBody)} instead of a simple
+ *   {@code null}-check to avoid this problem.</li>
+ * <li>
+ *   Software that uses {@link DirectiveCallPlace#isNestedOutputCacheable()} will always get {@code false}, because
+ *   interruption checks ({@link ASTThreadInterruptionCheck} elements) are, obviously, not cacheable. This should only
+ *   impact the performance.
+ * <li>
+ *   Software that investigates the AST will see the injected {@link ASTThreadInterruptionCheck} elements. As of this
+ *   writing the AST API-s aren't published, also such software need to be able to deal with new kind of elements
+ *   anyway, so this shouldn't be a problem.
+ * </ul>
+ */
+class ThreadInterruptionSupportTemplatePostProcessor extends TemplatePostProcessor {
+
+    @Override
+    public void postProcess(Template t) throws TemplatePostProcessorException {
+        final _ASTElement te = t.getRootTreeNode();
+        addInterruptionChecks(te);
+    }
+
+    private void addInterruptionChecks(final _ASTElement te) throws TemplatePostProcessorException {
+        if (te == null) {
+            return;
+        }
+        
+        final int childCount = te.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            addInterruptionChecks(te.getChild(i));
+        }
+        
+        if (te.isNestedBlockRepeater()) {
+            try {
+                te.addChild(0, new ASTThreadInterruptionCheck(te));
+            } catch (ParseException e) {
+                throw new TemplatePostProcessorException("Unexpected error; see cause", e);
+            }
+        }
+    }
+
+    /**
+     * AST directive-like node: Checks if the current thread's "interrupted" flag is set, and throws
+     * {@link TemplateProcessingThreadInterruptedException} if it is. We inject this to some points into the AST.
+     */
+    static class ASTThreadInterruptionCheck extends _ASTElement {
+        
+        private ASTThreadInterruptionCheck(_ASTElement te) throws ParseException {
+            setLocation(te.getTemplate(), te.beginColumn, te.beginLine, te.beginColumn, te.beginLine);
+        }
+
+        @Override
+        _ASTElement[] accept(Environment env) throws TemplateException, IOException {
+            // As the API doesn't allow throwing InterruptedException here (nor anywhere else, most importantly,
+            // Template.process can't throw it), we must not clear the "interrupted" flag of the thread.
+            if (Thread.currentThread().isInterrupted()) {
+                throw new TemplateProcessingThreadInterruptedException();
+            }
+            return null;
+        }
+
+        @Override
+        protected String dump(boolean canonical) {
+            return canonical ? "" : "<#--" + getNodeTypeSymbol() + "--#>";
+        }
+
+        @Override
+        String getNodeTypeSymbol() {
+            return "##threadInterruptionCheck";
+        }
+
+        @Override
+        int getParameterCount() {
+            return 0;
+        }
+
+        @Override
+        Object getParameterValue(int idx) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        @Override
+        ParameterRole getParameterRole(int idx) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        @Override
+        boolean isNestedBlockRepeater() {
+            return false;
+        }
+        
+    }
+    
+    /**
+     * Indicates that the template processing thread's "interrupted" flag was found to be set.
+     * 
+     * <p>ATTENTION: This is used by https://github.com/kenshoo/freemarker-online. Don't break backward
+     * compatibility without updating that project too! 
+     */
+    static class TemplateProcessingThreadInterruptedException extends RuntimeException {
+        
+        TemplateProcessingThreadInterruptedException() {
+            super("Template processing thread \"interrupted\" flag was set.");
+        }
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TokenMgrError.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TokenMgrError.java b/src/main/java/org/apache/freemarker/core/TokenMgrError.java
new file mode 100644
index 0000000..adddcf7
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/TokenMgrError.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/**
+ * Exception thrown on lower (lexical) level parsing errors. Shouldn't reach normal FreeMarker users, as FreeMarker
+ * usually catches this and wraps it into a {@link ParseException}.
+ * 
+ * This is a modified version of file generated by JavaCC from FTL.jj.
+ * You can modify this class to customize the error reporting mechanisms so long as the public interface
+ * remains compatible with the original.
+ * 
+ * @see ParseException
+ */
+public class TokenMgrError extends Error {
+   /*
+    * Ordinals for various reasons why an Error of this type can be thrown.
+    */
+
+   /**
+    * Lexical error occurred.
+    */
+   static final int LEXICAL_ERROR = 0;
+
+   /**
+    * An attempt was made to create a second instance of a static token manager.
+    */
+   static final int STATIC_LEXER_ERROR = 1;
+
+   /**
+    * Tried to change to an invalid lexical state.
+    */
+   static final int INVALID_LEXICAL_STATE = 2;
+
+   /**
+    * Detected (and bailed out of) an infinite loop in the token manager.
+    */
+   static final int LOOP_DETECTED = 3;
+
+   /**
+    * Indicates the reason why the exception is thrown. It will have
+    * one of the above 4 values.
+    */
+   int errorCode;
+
+   private String detail;
+   private Integer lineNumber, columnNumber;
+   private Integer endLineNumber, endColumnNumber;
+
+   /**
+    * Replaces unprintable characters by their espaced (or unicode escaped)
+    * equivalents in the given string
+    */
+   protected static final String addEscapes(String str) {
+      StringBuilder retval = new StringBuilder();
+      char ch;
+      for (int i = 0; i < str.length(); i++) {
+        switch (str.charAt(i))
+        {
+           case 0 :
+              continue;
+           case '\b':
+              retval.append("\\b");
+              continue;
+           case '\t':
+              retval.append("\\t");
+              continue;
+           case '\n':
+              retval.append("\\n");
+              continue;
+           case '\f':
+              retval.append("\\f");
+              continue;
+           case '\r':
+              retval.append("\\r");
+              continue;
+           case '\"':
+              retval.append("\\\"");
+              continue;
+           case '\'':
+              retval.append("\\\'");
+              continue;
+           case '\\':
+              retval.append("\\\\");
+              continue;
+           default:
+              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                 String s = "0000" + Integer.toString(ch, 16);
+                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+              } else {
+                 retval.append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.toString();
+   }
+
+   /**
+    * Returns a detailed message for the Error when it's thrown by the
+    * token manager to indicate a lexical error.
+    * Parameters : 
+    *    EOFSeen     : indicates if EOF caused the lexicl error
+    *    curLexState : lexical state in which this error occurred
+    *    errorLine   : line number when the error occurred
+    *    errorColumn : column number when the error occurred
+    *    errorAfter  : prefix that was seen before this error occurred
+    *    curchar     : the offending character
+    * Note: You can customize the lexical error message by modifying this method.
+    */
+   protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+      return("Lexical error: encountered " +
+           (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int) curChar + "), ") +
+           "after \"" + addEscapes(errorAfter) + "\".");
+   }
+
+   /**
+    * You can also modify the body of this method to customize your error messages.
+    * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+    * of end-users concern, so you can return something like : 
+    *
+    *     "Internal Error : Please file a bug report .... "
+    *
+    * from this method for such cases in the release version of your parser.
+    */
+   @Override
+public String getMessage() {
+      return super.getMessage();
+   }
+
+   /*
+    * Constructors of various flavors follow.
+    */
+
+   public TokenMgrError() {
+   }
+
+   public TokenMgrError(String detail, int reason) {
+      super(detail);  // the "detail" must not contain location information, the "message" might does
+      this.detail = detail;
+      errorCode = reason;
+   }
+   
+   /**
+    * @since 2.3.21
+    */
+   public TokenMgrError(String detail, int reason,
+           int errorLine, int errorColumn,
+           int endLineNumber, int endColumnNumber) {
+       super(detail);  // the "detail" must not contain location information, the "message" might does
+       this.detail = detail;
+       errorCode = reason;
+
+      lineNumber = Integer.valueOf(errorLine);  // In J2SE there was no Integer.valueOf(int)
+      columnNumber = Integer.valueOf(errorColumn);
+       this.endLineNumber = Integer.valueOf(endLineNumber); 
+       this.endColumnNumber = Integer.valueOf(endColumnNumber); 
+    }
+
+   /**
+    * Overload for JavaCC 6 compatibility.
+    * 
+    * @since 2.3.24
+    */
+   TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) {
+       this(EOFSeen, lexState, errorLine, errorColumn, errorAfter, (char) curChar, reason);
+   }
+   
+   public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+      this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+
+      lineNumber = Integer.valueOf(errorLine);  // In J2SE there was no Integer.valueOf(int)
+      columnNumber = Integer.valueOf(errorColumn);
+      // We blame the single character that can't be the start of a legal token: 
+      endLineNumber = lineNumber;
+      endColumnNumber = columnNumber;
+   }
+
+   /**
+    * 1-based line number of the unexpected character(s).
+    * 
+    * @since 2.3.20
+    */
+   public Integer getLineNumber() {
+      return lineNumber;
+   }
+    
+   /**
+    * 1-based column number of the unexpected character(s).
+    * 
+    * @since 2.3.20
+    */
+   public Integer getColumnNumber() {
+      return columnNumber;
+   }
+   
+   /**
+    * Returns the 1-based line at which the last character of the wrong section is. This will be usually (but not
+    * always) the same as {@link #getLineNumber()} because the lexer can only point to the single character that
+    * doesn't match any patterns.
+    * 
+    * @since 2.3.21
+    */
+   public Integer getEndLineNumber() {
+      return endLineNumber;
+   }
+
+   /**
+    * Returns the 1-based column at which the last character of the wrong section is. This will be usually (but not
+    * always) the same as {@link #getColumnNumber()} because the lexer can only point to the single character that
+    * doesn't match any patterns.
+    * 
+    * @since 2.3.21
+    */
+   public Integer getEndColumnNumber() {
+      return endColumnNumber;
+   }
+
+   public String getDetail() {
+       return detail;
+   }
+
+   public ParseException toParseException(Template template) {
+       return new ParseException(getDetail(),
+               template,
+               getLineNumber() != null ? getLineNumber().intValue() : 0,
+               getColumnNumber() != null ? getColumnNumber().intValue() : 0,
+               getEndLineNumber() != null ? getEndLineNumber().intValue() : 0,
+               getEndColumnNumber() != null ? getEndColumnNumber().intValue() : 0);
+   }
+   
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java b/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java
new file mode 100644
index 0000000..26561f3
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * @since 2.3.24
+ */
+public class UndefinedCustomFormatException extends InvalidFormatStringException {
+
+    public UndefinedCustomFormatException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UndefinedCustomFormatException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java b/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java
new file mode 100644
index 0000000..6b6d46e
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * Represents the output format used when the template output format is undecided. This is the default output format if
+ * FreeMarker can't select anything more specific (see
+ * {@link Configuration#setTemplateConfigurations(org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory)}). This format doesn't
+ * support auto-escaping ({@link Configuration#setAutoEscapingPolicy(int)}). It will print
+ * {@link TemplateMarkupOutputModel}-s as is (doesn't try to convert them).
+ * 
+ * @see PlainTextOutputFormat
+ * 
+ * @since 2.3.24
+ */
+public final class UndefinedOutputFormat extends OutputFormat {
+
+    public static final UndefinedOutputFormat INSTANCE = new UndefinedOutputFormat();
+    
+    private UndefinedOutputFormat() {
+        // Only to decrease visibility
+    }
+
+    @Override
+    public boolean isOutputFormatMixingAllowed() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "undefined";
+    }
+
+    @Override
+    public String getMimeType() {
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java b/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java
new file mode 100644
index 0000000..638b0c5
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * The type of a value differs from what was expected.
+ * 
+ * @since 2.3.20
+ */
+public class UnexpectedTypeException extends TemplateException {
+    
+    public UnexpectedTypeException(Environment env, String description) {
+        super(description, env);
+    }
+
+    UnexpectedTypeException(Environment env, _ErrorDescriptionBuilder description) {
+        super(null, env, null, description);
+    }
+
+    UnexpectedTypeException(
+            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Environment env)
+            throws InvalidReferenceException {
+        super(null, env, blamed, newDesciptionBuilder(blamed, null, model, expectedTypesDesc, expectedTypes, env));
+    }
+
+    UnexpectedTypeException(
+            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, String tip,
+            Environment env)
+            throws InvalidReferenceException {
+        super(null, env, blamed, newDesciptionBuilder(blamed, null, model, expectedTypesDesc, expectedTypes, env)
+                .tip(tip));
+    }
+
+    UnexpectedTypeException(
+            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Object[] tips,
+            Environment env)
+            throws InvalidReferenceException {
+        super(null, env, blamed, newDesciptionBuilder(blamed, null, model, expectedTypesDesc, expectedTypes, env)
+                .tips(tips));
+    }
+
+    UnexpectedTypeException(
+            String blamedAssignmentTargetVarName, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes,
+            Object[] tips,
+            Environment env)
+            throws InvalidReferenceException {
+        super(null, env, null, newDesciptionBuilder(
+                null, blamedAssignmentTargetVarName, model, expectedTypesDesc, expectedTypes, env).tips(tips));
+    }
+    
+    /**
+     * @param blamedAssignmentTargetVarName
+     *            Used for assignments that use {@code +=} and such, in which case the {@code blamed} expression
+     *            parameter will be null {@code null} and this parameter will be non-{null}.
+     */
+    private static _ErrorDescriptionBuilder newDesciptionBuilder(
+            ASTExpression blamed, String blamedAssignmentTargetVarName,
+            TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Environment env)
+            throws InvalidReferenceException {
+        if (model == null) throw InvalidReferenceException.getInstance(blamed, env);
+
+        _ErrorDescriptionBuilder errorDescBuilder = new _ErrorDescriptionBuilder(
+                unexpectedTypeErrorDescription(expectedTypesDesc, blamed, blamedAssignmentTargetVarName, model))
+                .blame(blamed).showBlamer(true);
+        if (model instanceof _UnexpectedTypeErrorExplainerTemplateModel) {
+            Object[] tip = ((_UnexpectedTypeErrorExplainerTemplateModel) model).explainTypeError(expectedTypes);
+            if (tip != null) {
+                errorDescBuilder.tip(tip);
+            }
+        }
+        return errorDescBuilder;
+    }
+
+    private static Object[] unexpectedTypeErrorDescription(
+            String expectedTypesDesc,
+            ASTExpression blamed, String blamedAssignmentTargetVarName,
+            TemplateModel model) {
+        return new Object[] {
+                "Expected ", new _DelayedAOrAn(expectedTypesDesc), ", but ",
+                (blamedAssignmentTargetVarName == null
+                        ? blamed != null ? "this" : "the expression"
+                        : new Object[] {
+                                "assignment target variable ",
+                                new _DelayedJQuote(blamedAssignmentTargetVarName) }), 
+                " has evaluated to ",
+                new _DelayedAOrAn(new _DelayedFTLTypeDescription(model)),
+                (blamedAssignmentTargetVarName == null ? ":" : ".")};
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnformattableValueException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UnformattableValueException.java b/src/main/java/org/apache/freemarker/core/UnformattableValueException.java
new file mode 100644
index 0000000..cb30c92
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UnformattableValueException.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * Thrown when a {@link TemplateModel} can't be formatted because of the value/properties of it are outside of that the
+ * {@link TemplateValueFormat} supports. For example, a formatter may not support dates before year 1, or can't format
+ * NaN. The most often used subclass is {@link UnknownDateTypeFormattingUnsupportedException}.
+ * 
+ * @since 2.3.24
+ */
+public class UnformattableValueException extends TemplateValueFormatException {
+
+    public UnformattableValueException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UnformattableValueException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java b/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java
new file mode 100644
index 0000000..aceca07
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.model.TemplateDateModel;
+
+/**
+ * Thrown when a {@link TemplateDateModel} can't be formatted because its type is {@link TemplateDateModel#UNKNOWN}.
+ * 
+ * @since 2.3.24
+ */
+public final class UnknownDateTypeFormattingUnsupportedException extends UnformattableValueException {
+
+    public UnknownDateTypeFormattingUnsupportedException() {
+        super(MessageUtil.UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java b/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java
new file mode 100644
index 0000000..a1173b6
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.model.TemplateDateModel;
+
+/**
+ * Thrown when a string can't be parsed to {@link TemplateDateModel}, because the provided target type is
+ * {@link TemplateDateModel#UNKNOWN}.
+ * 
+ * @since 2.3.24
+ */
+public final class UnknownDateTypeParsingUnsupportedException extends UnformattableValueException {
+
+    public UnknownDateTypeParsingUnsupportedException() {
+        super(MessageUtil.UNKNOWN_DATE_PARSING_ERROR_MESSAGE);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnparsableValueException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UnparsableValueException.java b/src/main/java/org/apache/freemarker/core/UnparsableValueException.java
new file mode 100644
index 0000000..37d979d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UnparsableValueException.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/**
+ * Thrown when the content of the string that should be parsed by the {@link TemplateValueFormat} doesn't match what the
+ * format expects.
+ * 
+ * @since 2.3.24
+ */
+public class UnparsableValueException extends TemplateValueFormatException {
+
+    public UnparsableValueException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UnparsableValueException(String message) {
+        this(message, null);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java b/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java
new file mode 100644
index 0000000..dd5a18a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+/**
+ * @since 2.3.24
+ */
+public class UnregisteredOutputFormatException extends Exception {
+
+    public UnregisteredOutputFormatException(String message) {
+        this(message, null);
+    }
+    
+    public UnregisteredOutputFormatException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java b/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java
new file mode 100644
index 0000000..101d056
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * Represents the XML output format (MIME type "application/xhtml+xml", name "XHTML"). This format escapes by default
+ * (via {@link _StringUtil#XHTMLEnc(String)}). The {@code ?xml} built-in silently bypasses template output values of the
+ * type produced by this output format ({@link TemplateXHTMLOutputModel}).
+ * 
+ * @since 2.3.24
+ */
+public final class XHTMLOutputFormat extends CommonMarkupOutputFormat<TemplateXHTMLOutputModel> {
+
+    /**
+     * The only instance (singleton) of this {@link OutputFormat}.
+     */
+    public static final XHTMLOutputFormat INSTANCE = new XHTMLOutputFormat();
+    
+    private XHTMLOutputFormat() {
+        // Only to decrease visibility
+    }
+    
+    @Override
+    public String getName() {
+        return "XHTML";
+    }
+
+    @Override
+    public String getMimeType() {
+        return "application/xhtml+xml";
+    }
+
+    @Override
+    public void output(String textToEsc, Writer out) throws IOException, TemplateModelException {
+        _StringUtil.XHTMLEnc(textToEsc, out);
+    }
+
+    @Override
+    public String escapePlainText(String plainTextContent) {
+        return _StringUtil.XHTMLEnc(plainTextContent);
+    }
+
+    @Override
+    public boolean isLegacyBuiltInBypassed(String builtInName) {
+        return builtInName.equals("html") || builtInName.equals("xml") || builtInName.equals("xhtml");
+    }
+
+    @Override
+    protected TemplateXHTMLOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) {
+        return new TemplateXHTMLOutputModel(plainTextContent, markupContent);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java b/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java
new file mode 100644
index 0000000..898b569
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * Represents the XML output format (MIME type "application/xml", name "XML"). This format escapes by default (via
+ * {@link _StringUtil#XMLEnc(String)}). The {@code ?html}, {@code ?xhtml} and {@code ?xml} built-ins silently bypass
+ * template output values of the type produced by this output format ({@link TemplateXHTMLOutputModel}).
+ * 
+ * @since 2.3.24
+ */
+public final class XMLOutputFormat extends CommonMarkupOutputFormat<TemplateXMLOutputModel> {
+
+    /**
+     * The only instance (singleton) of this {@link OutputFormat}.
+     */
+    public static final XMLOutputFormat INSTANCE = new XMLOutputFormat();
+
+    private XMLOutputFormat() {
+        // Only to decrease visibility
+    }
+
+    @Override
+    public String getName() {
+        return "XML";
+    }
+
+    @Override
+    public String getMimeType() {
+        return "application/xml";
+    }
+
+    @Override
+    public void output(String textToEsc, Writer out) throws IOException, TemplateModelException {
+        _StringUtil.XMLEnc(textToEsc, out);
+    }
+
+    @Override
+    public String escapePlainText(String plainTextContent) {
+        return _StringUtil.XMLEnc(plainTextContent);
+    }
+
+    @Override
+    public boolean isLegacyBuiltInBypassed(String builtInName) {
+        return builtInName.equals("xml");
+    }
+
+    @Override
+    protected TemplateXMLOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) {
+        return new TemplateXMLOutputModel(plainTextContent, markupContent);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java
new file mode 100644
index 0000000..56135f3
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.freemarker.core.util._DateUtil;
+import org.apache.freemarker.core.util._DateUtil.CalendarFieldsToDateConverter;
+import org.apache.freemarker.core.util._DateUtil.DateParseException;
+import org.apache.freemarker.core.util._DateUtil.DateToISO8601CalendarFactory;
+
+/**
+ * XML Schema format.
+ */
+final class XSTemplateDateFormat extends ISOLikeTemplateDateFormat {
+
+    XSTemplateDateFormat(
+            String settingValue, int parsingStart,
+            int dateType,
+            boolean zonelessInput,
+            TimeZone timeZone,
+            ISOLikeTemplateDateFormatFactory factory,
+            Environment env)
+            throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException {
+        super(settingValue, parsingStart, dateType, zonelessInput, timeZone, factory, env);
+    }
+    
+    @Override
+    protected String format(Date date, boolean datePart, boolean timePart, boolean offsetPart, int accuracy,
+            TimeZone timeZone, DateToISO8601CalendarFactory calendarFactory) {
+        return _DateUtil.dateToXSString(
+                date, datePart, timePart, offsetPart, accuracy, timeZone, calendarFactory);
+    }
+
+    @Override
+    protected Date parseDate(String s, TimeZone tz, CalendarFieldsToDateConverter calToDateConverter)
+            throws DateParseException {
+        return _DateUtil.parseXSDate(s, tz, calToDateConverter);
+    }
+
+    @Override
+    protected Date parseTime(String s, TimeZone tz, CalendarFieldsToDateConverter calToDateConverter)
+            throws DateParseException {
+        return _DateUtil.parseXSTime(s, tz, calToDateConverter);
+    }
+
+    @Override
+    protected Date parseDateTime(String s, TimeZone tz,
+            CalendarFieldsToDateConverter calToDateConverter) throws DateParseException {
+        return _DateUtil.parseXSDateTime(s, tz, calToDateConverter);
+    }
+
+    @Override
+    protected String getDateDescription() {
+        return "W3C XML Schema date";
+    }
+
+    @Override
+    protected String getTimeDescription() {
+        return "W3C XML Schema time";
+    }
+
+    @Override
+    protected String getDateTimeDescription() {
+        return "W3C XML Schema dateTime";
+    }
+
+    @Override
+    protected boolean isXSMode() {
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java
new file mode 100644
index 0000000..9421cc0
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+class XSTemplateDateFormatFactory extends ISOLikeTemplateDateFormatFactory {
+    
+    static final XSTemplateDateFormatFactory INSTANCE = new XSTemplateDateFormatFactory();
+
+    private XSTemplateDateFormatFactory() {
+        // Not meant to be instantiated
+    }
+
+    @Override
+    public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput,
+            Environment env) throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException {
+        // We don't cache these as creating them is cheap (only 10% speedup of ${d?string.xs} with caching)
+        return new XSTemplateDateFormat(
+                params, 2,
+                dateType, zonelessInput,
+                timeZone, this, env);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java b/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java
new file mode 100644
index 0000000..00f1ef7
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+
+import org.apache.freemarker.core.debug.impl.DebuggerService;
+
+/**
+ * Don't use this; used internally by FreeMarker, might changes without notice.
+ * A debug breakpoint inserted into the template 
+ */
+public class _ASTDebugBreak extends _ASTElement {
+    public _ASTDebugBreak(_ASTElement nestedBlock) {
+        addChild(nestedBlock);
+        copyLocationFrom(nestedBlock);
+    }
+    
+    @Override
+    protected _ASTElement[] accept(Environment env) throws TemplateException, IOException {
+        if (!DebuggerService.suspendEnvironment(
+                env, getTemplate().getSourceName(), getChild(0).getBeginLine())) {
+            return getChild(0).accept(env);
+        } else {
+            throw new StopException(env, "Stopped by debugger");
+        }
+    }
+
+    @Override
+    protected String dump(boolean canonical) {
+        if (canonical) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("<#-- ");
+            sb.append("debug break");
+            if (getChildCount() == 0) {
+                sb.append(" /-->");
+            } else {
+                sb.append(" -->");
+                sb.append(getChild(0).getCanonicalForm());                
+                sb.append("<#--/ debug break -->");
+            }
+            return sb.toString();
+        } else {
+            return "debug break";
+        }
+    }
+    
+    @Override
+    String getNodeTypeSymbol() {
+        return "#debug_break";
+    }
+
+    @Override
+    int getParameterCount() {
+        return 0;
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        throw new IndexOutOfBoundsException();
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        throw new IndexOutOfBoundsException();
+    }
+
+    @Override
+    boolean isNestedBlockRepeater() {
+        return false;
+    }
+        
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_ASTElement.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ASTElement.java b/src/main/java/org/apache/freemarker/core/_ASTElement.java
new file mode 100644
index 0000000..af7e9c1
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_ASTElement.java
@@ -0,0 +1,473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.apache.freemarker.core.model.TemplateNodeModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
+import org.apache.freemarker.core.util._ArrayEnumeration;
+
+/**
+ * AST non-expression node superclass: Superclass of directive calls, interpolations, static text, top-level comments,
+ * or other such non-expression node in the parsed template. Some information that can be found here can be accessed
+ * through the {@link Environment#getCurrentDirectiveCallPlace()}, which is a published API, and thus promises backward
+ * compatibility.
+ */
+// TODO [FM3] Get rid of "public" and thus the "_" prefix
+abstract public class _ASTElement extends ASTNode {
+
+    private static final int INITIAL_CHILD_BUFFER_CAPACITY = 6;
+
+    private _ASTElement parent;
+
+    /**
+     * Contains 1 or more nested elements with optional trailing {@code null}-s, or is {@code null} exactly if there are
+     * no nested elements.
+     */
+    private _ASTElement[] childBuffer;
+
+    /**
+     * Contains the number of elements in the {@link #childBuffer}, not counting the trailing {@code null}-s. If this is
+     * 0, then and only then {@link #childBuffer} must be {@code null}.
+     */
+    private int childCount;
+
+    /**
+     * The index of the element in the parent's {@link #childBuffer} array.
+     * 
+     * @since 2.3.23
+     */
+    private int index;
+
+    /**
+     * Executes this {@link _ASTElement}. Usually should not be called directly, but through
+     * {@link Environment#visit(_ASTElement)} or a similar {@link Environment} method.
+     *
+     * @param env
+     *            The runtime environment
+     * 
+     * @return The template elements to execute (meant to be used for nested elements), or {@code null}. Can have
+     *         <em>trailing</em> {@code null}-s (unused buffer capacity). Returning the nested elements instead of
+     *         executing them inside this method is a trick used for decreasing stack usage when there's nothing to do
+     *         after the children was processed anyway.
+     */
+    abstract _ASTElement[] accept(Environment env) throws TemplateException, IOException;
+
+    /**
+     * One-line description of the element, that contain all the information that is used in {@link #getCanonicalForm()}
+     * , except the nested content (elements) of the element. The expressions inside the element (the parameters) has to
+     * be shown. Meant to be used for stack traces, also for tree views that don't go down to the expression-level.
+     * There are no backward-compatibility guarantees regarding the format used ATM, but it must be regular enough to be
+     * machine-parseable, and it must contain all information necessary for restoring an AST equivalent to the original.
+     * 
+     * This final implementation calls {@link #dump(boolean) dump(false)}.
+     * 
+     * @see #getCanonicalForm()
+     * @see #getNodeTypeSymbol()
+     */
+    public final String getDescription() {
+        return dump(false);
+    }
+
+    /**
+     * This final implementation calls {@link #dump(boolean) dump(false)}.
+     */
+    @Override
+    public final String getCanonicalForm() {
+        return dump(true);
+    }
+
+    final String getChildrenCanonicalForm() {
+        return getChildrenCanonicalForm(childBuffer);
+    }
+    
+    static String getChildrenCanonicalForm(_ASTElement[] children) {
+        if (children == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (_ASTElement child : children) {
+            if (child == null) {
+                break;
+            }
+            sb.append(child.getCanonicalForm());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Tells if the element should show up in error stack traces. Note that this will be ignored for the top (current)
+     * element of a stack trace, as that's always shown.
+     */
+    boolean isShownInStackTrace() {
+        return false;
+    }
+
+    /**
+     * Tells if this element possibly executes its nested content for many times. This flag is useful when a template
+     * AST is modified for running time limiting (see {@link ThreadInterruptionSupportTemplatePostProcessor}). Elements
+     * that use {@link #childBuffer} should not need this, as the insertion of the timeout checks is impossible there,
+     * given their rigid nested element schema.
+     */
+    abstract boolean isNestedBlockRepeater();
+
+    /**
+     * Brings the implementation of {@link #getCanonicalForm()} and {@link #getDescription()} to a single place. Don't
+     * call those methods in method on {@code this}, because that will result in infinite recursion!
+     * 
+     * @param canonical
+     *            if {@code true}, it calculates the return value of {@link #getCanonicalForm()}, otherwise of
+     *            {@link #getDescription()}.
+     */
+    abstract protected String dump(boolean canonical);
+
+    // Methods to implement TemplateNodeModel
+
+    public TemplateNodeModel getParentNode() {
+        // return parent;
+        return null;
+    }
+
+    public String getNodeNamespace() {
+        return null;
+    }
+
+    public String getNodeType() {
+        return "element";
+    }
+
+    public TemplateSequenceModel getChildNodes() {
+        if (childBuffer != null) {
+            final SimpleSequence seq = new SimpleSequence(childCount);
+            for (int i = 0; i < childCount; i++) {
+                seq.add(childBuffer[i]);
+            }
+            return seq;
+        } else {
+            return new SimpleSequence(0);
+        }
+    }
+
+    public String getNodeName() {
+        String className = getClass().getName();
+        int shortNameOffset = className.lastIndexOf('.') + 1;
+        return className.substring(shortNameOffset);
+    }
+
+    // Methods so that we can implement the Swing TreeNode API.
+
+    public boolean isLeaf() {
+        return childCount == 0;
+    }
+
+    public int getIndex(_ASTElement node) {
+        for (int i = 0; i < childCount; i++) {
+            if (childBuffer[i].equals(node)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public int getChildCount() {
+        return childCount;
+    }
+
+    /**
+     * Note: For element with {@code #nestedBlock}, this will hide the {@code #nestedBlock} when that's a
+     * {@link ASTImplicitParent}.
+     */
+    public Enumeration children() {
+        return childBuffer != null
+                ? new _ArrayEnumeration(childBuffer, childCount)
+                : Collections.enumeration(Collections.EMPTY_LIST);
+    }
+
+    public void setChildAt(int index, _ASTElement element) {
+        if (index < childCount && index >= 0) {
+            childBuffer[index] = element;
+            element.index = index;
+            element.parent = this;
+        } else {
+            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + childCount);
+        }
+    }
+    
+    /**
+     * The element whose child this element is, or {@code null} if this is the root node.
+     */
+    final _ASTElement getParentElement() {
+        return parent;
+    }
+
+    final void setChildBufferCapacity(int capacity) {
+        int ln = childCount;
+        _ASTElement[] newChildBuffer = new _ASTElement[capacity];
+        for (int i = 0; i < ln; i++) {
+            newChildBuffer[i] = childBuffer[i];
+        }
+        childBuffer = newChildBuffer;
+    }
+
+    /**
+     * Inserts a new nested element after the last nested element.
+     */
+    final void addChild(_ASTElement nestedElement) {
+        addChild(childCount, nestedElement);
+    }
+
+    /**
+     * Inserts a new nested element at the given index, which can also be one higher than the current highest index.
+     */
+    final void addChild(int index, _ASTElement nestedElement) {
+        final int lChildCount = childCount;
+
+        _ASTElement[] lChildBuffer = childBuffer;
+        if (lChildBuffer == null) {
+            lChildBuffer = new _ASTElement[INITIAL_CHILD_BUFFER_CAPACITY];
+            childBuffer = lChildBuffer;
+        } else if (lChildCount == lChildBuffer.length) {
+            setChildBufferCapacity(lChildCount != 0 ? lChildCount * 2 : 1);
+            lChildBuffer = childBuffer;
+        }
+        // At this point: nestedElements == this.nestedElements, and has sufficient capacity.
+
+        for (int i = lChildCount; i > index; i--) {
+            _ASTElement movedElement = lChildBuffer[i - 1];
+            movedElement.index = i;
+            lChildBuffer[i] = movedElement;
+        }
+        nestedElement.index = index;
+        nestedElement.parent = this;
+        lChildBuffer[index] = nestedElement;
+        childCount = lChildCount + 1;
+    }
+
+    final _ASTElement getChild(int index) {
+        return childBuffer[index];
+    }
+
+    /**
+     * @return Array containing 1 or more nested elements with optional trailing {@code null}-s, or is {@code null}
+     *         exactly if there are no nested elements.
+     */
+    final _ASTElement[] getChildBuffer() {
+        return childBuffer;
+    }
+
+    /**
+     * @param buffWithCnt Maybe {@code null}
+     * 
+     * @since 2.3.24
+     */
+    final void setChildren(TemplateElements buffWithCnt) {
+        _ASTElement[] childBuffer = buffWithCnt.getBuffer();
+        int childCount = buffWithCnt.getCount();
+        for (int i = 0; i < childCount; i++) {
+            _ASTElement child = childBuffer[i];
+            child.index = i;
+            child.parent = this;
+        }
+        this.childBuffer = childBuffer;
+        this.childCount = childCount;
+    }
+
+    final int getIndex() {
+        return index;
+    }
+
+    /**
+     * This is a special case, because a root element is not contained in another element, so we couldn't set the
+     * private fields.
+     */
+    final void setFieldsForRootElement() {
+        index = 0;
+        parent = null;
+    }
+
+    /**
+     * Walk the AST subtree rooted by this element, and do simplifications where possible, also removes superfluous
+     * whitespace.
+     * 
+     * @param stripWhitespace
+     *            whether to remove superfluous whitespace
+     * 
+     * @return The element this element should be replaced with in the parent. If it's the same as this element, no
+     *         actual replacement will happen. Note that adjusting the {@link #parent} and {@link #index} of the result
+     *         is the duty of the caller, not of this method.
+     */
+    _ASTElement postParseCleanup(boolean stripWhitespace) throws ParseException {
+        int childCount = this.childCount;
+        if (childCount != 0) {
+            for (int i = 0; i < childCount; i++) {
+                _ASTElement te = childBuffer[i];
+                
+                /*
+                // Assertion:
+                if (te.getIndex() != i) {
+                    throw new BugException("Invalid index " + te.getIndex() + " (expected: "
+                            + i + ") for: " + te.dump(false));
+                }
+                if (te.getParent() != this) {
+                    throw new BugException("Invalid parent " + te.getParent() + " (expected: "
+                            + this.dump(false) + ") for: " + te.dump(false));
+                }
+                */
+                
+                te = te.postParseCleanup(stripWhitespace);
+                childBuffer[i] = te;
+                te.parent = this;
+                te.index = i;
+            }
+            for (int i = 0; i < childCount; i++) {
+                _ASTElement te = childBuffer[i];
+                if (te.isIgnorable(stripWhitespace)) {
+                    childCount--;
+                    // As later isIgnorable calls might investigates the siblings, we have to move all the items now. 
+                    for (int j = i; j < childCount; j++) {
+                        final _ASTElement te2 = childBuffer[j + 1];
+                        childBuffer[j] = te2;
+                        te2.index = j;
+                    }
+                    childBuffer[childCount] = null;
+                    this.childCount = childCount;
+                    i--;
+                }
+            }
+            if (childCount == 0) {
+                childBuffer = null;
+            } else if (childCount < childBuffer.length
+                    && childCount <= childBuffer.length * 3 / 4) {
+                _ASTElement[] trimmedChildBuffer = new _ASTElement[childCount];
+                for (int i = 0; i < childCount; i++) {
+                    trimmedChildBuffer[i] = childBuffer[i];
+                }
+                childBuffer = trimmedChildBuffer;
+            }
+        }
+        return this;
+    }
+
+    boolean isIgnorable(boolean stripWhitespace) {
+        return false;
+    }
+
+    // The following methods exist to support some fancier tree-walking
+    // and were introduced to support the whitespace cleanup feature in 2.2
+
+    _ASTElement prevTerminalNode() {
+        _ASTElement prev = previousSibling();
+        if (prev != null) {
+            return prev.getLastLeaf();
+        } else if (parent != null) {
+            return parent.prevTerminalNode();
+        }
+        return null;
+    }
+
+    _ASTElement nextTerminalNode() {
+        _ASTElement next = nextSibling();
+        if (next != null) {
+            return next.getFirstLeaf();
+        } else if (parent != null) {
+            return parent.nextTerminalNode();
+        }
+        return null;
+    }
+
+    _ASTElement previousSibling() {
+        if (parent == null) {
+            return null;
+        }
+        return index > 0 ? parent.childBuffer[index - 1] : null;
+    }
+
+    _ASTElement nextSibling() {
+        if (parent == null) {
+            return null;
+        }
+        return index + 1 < parent.childCount ? parent.childBuffer[index + 1] : null;
+    }
+
+    private _ASTElement getFirstChild() {
+        return childCount == 0 ? null : childBuffer[0];
+    }
+
+    private _ASTElement getLastChild() {
+        final int childCount = this.childCount;
+        return childCount == 0 ? null : childBuffer[childCount - 1];
+    }
+
+    private _ASTElement getFirstLeaf() {
+        _ASTElement te = this;
+        while (!te.isLeaf() && !(te instanceof ASTDirMacro) && !(te instanceof ASTDirCapturingAssignment)) {
+            // A macro or macro invocation is treated as a leaf here for special reasons
+            te = te.getFirstChild();
+        }
+        return te;
+    }
+
+    private _ASTElement getLastLeaf() {
+        _ASTElement te = this;
+        while (!te.isLeaf() && !(te instanceof ASTDirMacro) && !(te instanceof ASTDirCapturingAssignment)) {
+            // A macro or macro invocation is treated as a leaf here for special reasons
+            te = te.getLastChild();
+        }
+        return te;
+    }
+
+    /**
+     * Tells if executing this element has output that only depends on the template content and that has no side
+     * effects.
+     */
+    boolean isOutputCacheable() {
+        return false;
+    }
+
+    boolean isChildrenOutputCacheable() {
+        int ln = childCount;
+        for (int i = 0; i < ln; i++) {
+            if (!childBuffer[i].isOutputCacheable()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * determines whether this element's presence on a line indicates that we should not strip opening whitespace in the
+     * post-parse whitespace gobbling step.
+     */
+    boolean heedsOpeningWhitespace() {
+        return false;
+    }
+
+    /**
+     * determines whether this element's presence on a line indicates that we should not strip trailing whitespace in
+     * the post-parse whitespace gobbling step.
+     */
+    boolean heedsTrailingWhitespace() {
+        return false;
+    }
+}


Mime
View raw message