You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/08/08 18:03:06 UTC

[2/2] incubator-freemarker git commit: Renamed `FTLUtil` to `TemplateLanguageUtils` (as the FTL name will be abandoned in FM3, and supporting multiple languages are planned in the future).

Renamed `FTLUtil` to `TemplateLanguageUtils` (as the FTL name will be abandoned in FM3, and supporting multiple languages are planned in the future).


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/a5ac9983
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/a5ac9983
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/a5ac9983

Branch: refs/heads/3
Commit: a5ac9983f22ca59fe465c937b3737badf293ba85
Parents: ebb39b8
Author: ddekany <dd...@apache.org>
Authored: Tue Aug 8 20:02:53 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Tue Aug 8 20:02:53 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |   2 +
 .../core/FM2ASTToFM3SourceConverter.java        |  43 +-
 .../org/apache/freemarker/core/ASTPrinter.java  |   4 +-
 .../templatesuite/models/OverloadedMethods.java |   6 +-
 .../core/userpkg/TestTemplateCallableModel.java |   4 +-
 .../freemarker/core/util/FTLUtilTest.java       | 128 ---
 .../core/util/TemplateLanguageUtilsTest.java    | 128 +++
 .../freemarker/core/ASTDollarInterpolation.java |   4 +-
 .../freemarker/core/ASTExpStringLiteral.java    |   6 +-
 .../core/MutableProcessingConfiguration.java    |   4 +-
 .../apache/freemarker/core/_CallableUtils.java  |  10 +-
 .../core/_DelayedFTLTypeDescription.java        |   4 +-
 .../core/_ObjectBuilderSettingEvaluator.java    |   4 +-
 .../freemarker/core/model/TemplateModel.java    |   4 +-
 .../core/model/impl/OverloadedMethods.java      |   4 +-
 .../apache/freemarker/core/util/FTLUtil.java    | 872 -------------------
 .../core/util/TemplateLanguageUtils.java        | 872 +++++++++++++++++++
 .../freemarker/core/util/_StringUtils.java      |  10 +-
 freemarker-core/src/main/javacc/FTL.jj          |   4 +-
 .../test/templateutil/AssertDirective.java      |   4 +-
 20 files changed, 1060 insertions(+), 1057 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 685160a..0d6a14b 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -469,6 +469,8 @@ Core / Miscellaneous
 - Removed `NestedContentNotSupportedException`, as `TemplateDirectiveModel.isNestedContentSupported()` now takes care
   of that problem.
 - CallPlaceCustomDataInitializationException is not a checked exception anymore (now it extends RuntimeException)
+- Renamed `FTLUtil` to `TemplateLanguageUtils` (as the FTL name will be abandoned in FM3, and supporting multiple
+  languages are planned in the future)
 
 
 Build / development process changes

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
index 1b171bd..2ff86e9 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -35,7 +35,7 @@ import org.apache.freemarker.converter.ConversionMarkers;
 import org.apache.freemarker.converter.ConverterException;
 import org.apache.freemarker.converter.ConverterUtils;
 import org.apache.freemarker.converter.UnconvertableLegacyFeatureException;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._ClassUtils;
 import org.apache.freemarker.core.util._NullArgumentException;
 import org.apache.freemarker.core.util._ObjectHolder;
@@ -461,7 +461,7 @@ public class FM2ASTToFM3SourceConverter {
         int paramIdx = 1;
         while (paramIdx < node.getParameterCount()) {
             String paramName = getParam(node, paramIdx++, ParameterRole.ARGUMENT_NAME, String.class);
-            print(FTLUtil.escapeIdentifier(paramName));
+            print(TemplateLanguageUtils.escapeIdentifier(paramName));
             pos = getPositionAfterIdentifier(pos);
 
             pos = printSeparatorAndWSAndExpComments(pos, "=");
@@ -592,7 +592,7 @@ public class FM2ASTToFM3SourceConverter {
 
         int pos = printDirStartTagPartBeforeParams(node, "setting");
 
-        print(FTLUtil.escapeIdentifier(convertSettingName(
+        print(TemplateLanguageUtils.escapeIdentifier(convertSettingName(
                 getParam(node, 0, ParameterRole.ITEM_KEY, String.class),
                 node)));
         pos = getPositionAfterIdentifier(pos);
@@ -676,11 +676,11 @@ public class FM2ASTToFM3SourceConverter {
                 paramCnt >= 2 ? getParam(node, 1, ParameterRole.TARGET_LOOP_VARIABLE, String.class)
                 : null;
 
-        print(FTLUtil.escapeIdentifier(nestedContParamName1));
+        print(TemplateLanguageUtils.escapeIdentifier(nestedContParamName1));
         pos = getPositionAfterIdentifier(pos);
         if (nestedContParamName2 != null) {
             pos = printSeparatorAndWSAndExpComments(pos, ",");
-            print(FTLUtil.escapeIdentifier(nestedContParamName2));
+            print(TemplateLanguageUtils.escapeIdentifier(nestedContParamName2));
             pos = getPositionAfterIdentifier(pos);
         }
 
@@ -734,13 +734,13 @@ public class FM2ASTToFM3SourceConverter {
             if (loopVal1 != null) { // #list xs as <v1 | v1, v2>
                 pos = printSeparatorAndWSAndExpComments(getEndPositionExclusive(listSource), "as");
 
-                print(FTLUtil.escapeIdentifier(loopVal1));
+                print(TemplateLanguageUtils.escapeIdentifier(loopVal1));
                 pos = getPositionAfterAssignmentTargetIdentifier(pos);
 
                 if (loopVal2 != null) { // #list xs as <v1, v2>
                     pos = printSeparatorAndWSAndExpComments(pos, ",");
 
-                    print(FTLUtil.escapeIdentifier(loopVal2));
+                    print(TemplateLanguageUtils.escapeIdentifier(loopVal2));
                     pos = getPositionAfterAssignmentTargetIdentifier(pos);
                 }
 
@@ -780,7 +780,7 @@ public class FM2ASTToFM3SourceConverter {
             printExp(listSource);
             printWithConvertedExpComments(ConverterUtils.rightTrim(postVar2WSAndComment));
             print(" as ");
-            print(FTLUtil.escapeIdentifier(loopVal1));
+            print(TemplateLanguageUtils.escapeIdentifier(loopVal1));
             printWithConvertedExpComments(ConverterUtils.rightTrim(postVar1WSAndComment));
             printWithConvertedExpComments(ConverterUtils.rightTrim(postInWSAndComment));
         } else {
@@ -892,7 +892,7 @@ public class FM2ASTToFM3SourceConverter {
 
         int pos = printSeparatorAndWSAndExpComments(getEndPositionExclusive(templateName), "as");
 
-        print(FTLUtil.escapeIdentifier(getParam(node, 1, ParameterRole.NAMESPACE, String.class)));
+        print(TemplateLanguageUtils.escapeIdentifier(getParam(node, 1, ParameterRole.NAMESPACE, String.class)));
         pos = getPositionAfterIdentifier(pos);
 
         printDirStartTagEnd(node, pos, false);
@@ -925,7 +925,7 @@ public class FM2ASTToFM3SourceConverter {
         int pos = printDirStartTagPartBeforeParams(node, "escape");
 
         pos = getPositionAfterIdentifier(pos);
-        print(FTLUtil.escapeIdentifier(getParam(node, 0, ParameterRole.PLACEHOLDER_VARIABLE, String.class)));
+        print(TemplateLanguageUtils.escapeIdentifier(getParam(node, 0, ParameterRole.PLACEHOLDER_VARIABLE, String.class)));
 
         pos = printSeparatorAndWSAndExpComments(pos, "as");
 
@@ -1023,7 +1023,7 @@ public class FM2ASTToFM3SourceConverter {
 
         int pos = printDirAssignmentCommonTagTillAssignmentExp(node, 1);
 
-        print(FTLUtil.escapeIdentifier(getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class)));
+        print(TemplateLanguageUtils.escapeIdentifier(getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class)));
         pos = getPositionAfterAssignmentTargetIdentifier(pos);
 
         Expression namespace = getParam(node, 2, ParameterRole.NAMESPACE, Expression.class);
@@ -1075,7 +1075,7 @@ public class FM2ASTToFM3SourceConverter {
 
     private int printDirAssignmentCommonExp(Assignment node, int pos) throws ConverterException {
         String target = getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class);
-        print(FTLUtil.escapeIdentifier(target));
+        print(TemplateLanguageUtils.escapeIdentifier(target));
         pos = getPositionAfterAssignmentTargetIdentifier(pos);
 
         pos = printWSAndExpComments(pos);
@@ -1118,7 +1118,7 @@ public class FM2ASTToFM3SourceConverter {
         int pos = printDirStartTagPartBeforeParams(node, tagName);
 
         String assignedName = getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class);
-        print(FTLUtil.escapeIdentifier(assignedName));
+        print(TemplateLanguageUtils.escapeIdentifier(assignedName));
         pos = getPositionAfterAssignmentTargetIdentifier(pos);
 
         {
@@ -1154,7 +1154,7 @@ public class FM2ASTToFM3SourceConverter {
         int paramIdx = 1;
         while (node.getParameterRole(paramIdx) == ParameterRole.PARAMETER_NAME) {
             String paramName = getParam(node, paramIdx++, ParameterRole.PARAMETER_NAME, String.class);
-            print(FTLUtil.escapeIdentifier(paramName));
+            print(TemplateLanguageUtils.escapeIdentifier(paramName));
             pos = getPositionAfterIdentifier(pos);
 
             Expression paramDefault = getParam(node, paramIdx++, ParameterRole.PARAMETER_DEFAULT, Expression.class);
@@ -1204,7 +1204,7 @@ public class FM2ASTToFM3SourceConverter {
         }
         String paramName = getParam(node, paramIdx++, ParameterRole.CATCH_ALL_PARAMETER_NAME, String.class);
         if (paramName != null) {
-            print(FTLUtil.escapeIdentifier(paramName));
+            print(TemplateLanguageUtils.escapeIdentifier(paramName));
             pos = getPositionAfterIdentifier(pos);
             pos = printWSAndExpComments(pos);
             assertNodeContent(src.startsWith("...", pos), node,
@@ -1312,7 +1312,7 @@ public class FM2ASTToFM3SourceConverter {
             if (ftlDirMode) {
                 paramName = convertFtlHeaderParamName(paramName);
             }
-            print(FTLUtil.escapeIdentifier(paramName));
+            print(TemplateLanguageUtils.escapeIdentifier(paramName));
             printSeparatorAndWSAndExpComments(pos, "=");
             printExp(argValue);
 
@@ -1620,7 +1620,7 @@ public class FM2ASTToFM3SourceConverter {
         printSeparatorAndWSAndExpComments(getEndPositionExclusive(lho), ".");
 
         rho = mapStringHashKey(rho);
-        print(rho.startsWith("*") ? rho : FTLUtil.escapeIdentifier(rho));
+        print(rho.startsWith("*") ? rho : TemplateLanguageUtils.escapeIdentifier(rho));
     }
 
     private String mapStringHashKey(String key) {
@@ -1794,7 +1794,7 @@ public class FM2ASTToFM3SourceConverter {
     }
 
     private void printExpIdentifier(Identifier node) {
-        print(FTLUtil.escapeIdentifier(node.getName()));
+        print(TemplateLanguageUtils.escapeIdentifier(node.getName()));
     }
 
     private void printExpMethodCall(MethodCall node) throws ConverterException {
@@ -1911,7 +1911,7 @@ public class FM2ASTToFM3SourceConverter {
         if (parameterCount == 0) {
             _NullArgumentException.check("value", value);
             if (!rawString) {
-                print(FTLUtil.escapeStringLiteralPart(value, quote, escapeAmp, escapeLT, escapeGT));
+                print(TemplateLanguageUtils.escapeStringLiteralPart(value, quote, escapeAmp, escapeLT, escapeGT));
             } else {
                 print(value);
             }
@@ -1925,7 +1925,8 @@ public class FM2ASTToFM3SourceConverter {
             for (int paramIdx = 0; paramIdx < parameterCount; paramIdx++) {
                 Object param = getParam(node, paramIdx, ParameterRole.VALUE_PART, Object.class);
                 if (param instanceof String) {
-                    print(FTLUtil.escapeStringLiteralPart((String) param, quote, escapeAmp, escapeLT, escapeGT));
+                    print(TemplateLanguageUtils
+                            .escapeStringLiteralPart((String) param, quote, escapeAmp, escapeLT, escapeGT));
                 } else {
                     assertNodeContent(param instanceof Interpolation, node,
                             "Unexpected parameter type: {}", param.getClass().getName());
@@ -1944,7 +1945,7 @@ public class FM2ASTToFM3SourceConverter {
                     String interp = out.substring(interpStartPos, interpEndPos);
                     out.setLength(interpStartPos + 2); // +2 to keep the "${"
                     String inerpInside = interp.substring(2, interp.length() - 1);
-                    print(FTLUtil.escapeStringLiteralPart(inerpInside, quote)); // For now we escape as FTL2
+                    print(TemplateLanguageUtils.escapeStringLiteralPart(inerpInside, quote)); // For now we escape as FTL2
                     print(interp.charAt(interp.length() - 1)); // "}"
                 }
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
index 2bc1eea..8157c19 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
@@ -40,7 +40,7 @@ import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
 
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._ClassUtils;
 import org.apache.freemarker.core.util._StringUtils;
 import org.apache.freemarker.test.TestConfigurationBuilder;
@@ -330,7 +330,7 @@ public class ASTPrinter {
                     out.write(INDENTATION);
                     out.write(ind);
                     out.write("= const ");
-                    out.write(FTLUtil.getTypeDescription(tm));
+                    out.write(TemplateLanguageUtils.getTypeDescription(tm));
                     out.write(' ');
                     out.write(tm.toString());
                     out.write('\n');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/OverloadedMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/OverloadedMethods.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/OverloadedMethods.java
index 4eac8cc..1e7d3a2 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/OverloadedMethods.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/OverloadedMethods.java
@@ -26,7 +26,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 
 /**
  * For testing overloaded method selection.
@@ -149,9 +149,9 @@ public class OverloadedMethods {
 		if (value == null) {
 		    return "null";
 		} else if (value instanceof Character) {
-			return "'" + FTLUtil.escapeStringLiteralPart(value.toString()) + "'";
+			return "'" + TemplateLanguageUtils.escapeStringLiteralPart(value.toString()) + "'";
 		} else if (value instanceof String) {
-			return "\"" + FTLUtil.escapeStringLiteralPart((String) value) + "\"";
+			return "\"" + TemplateLanguageUtils.escapeStringLiteralPart((String) value) + "\"";
 		} else if (value instanceof Map) {
 			StringBuilder sb = new StringBuilder(); 
 			sb.append("{");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
index 9cedddd..6b36cf8 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/TestTemplateCallableModel.java
@@ -28,7 +28,7 @@ import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._StringUtils;
 
 public abstract class TestTemplateCallableModel implements TemplateCallableModel {
@@ -64,7 +64,7 @@ public abstract class TestTemplateCallableModel implements TemplateCallableModel
         } else if (value instanceof TemplateNumberModel) {
             sb.append(((TemplateNumberModel) value).getAsNumber().toString());
         } else if (value instanceof TemplateScalarModel) {
-            sb.append(FTLUtil.toStringLiteral(((TemplateScalarModel) value).getAsString()));
+            sb.append(TemplateLanguageUtils.toStringLiteral(((TemplateScalarModel) value).getAsString()));
         } else if (value instanceof TemplateSequenceModel) {
             int len = ((TemplateSequenceModel) value).size();
             sb.append('[');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/FTLUtilTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/FTLUtilTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/FTLUtilTest.java
deleted file mode 100644
index aab0dbe..0000000
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/FTLUtilTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.util;
-
-import static junit.framework.TestCase.assertFalse;
-import static junit.framework.TestCase.assertNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-public class FTLUtilTest {
-
-    @Test
-    public void testEscapeStringLiteralPart() {
-        assertEquals("", FTLUtil.escapeStringLiteralPart(""));
-        assertEquals("abc", FTLUtil.escapeStringLiteralPart("abc"));
-        assertEquals("{", FTLUtil.escapeStringLiteralPart("{"));
-        assertEquals("a{b}c", FTLUtil.escapeStringLiteralPart("a{b}c"));
-        assertEquals("a#b", FTLUtil.escapeStringLiteralPart("a#b"));
-        assertEquals("a$b", FTLUtil.escapeStringLiteralPart("a$b"));
-        // Find related: [interpolation prefixes]
-        assertEquals("a#{b}c", FTLUtil.escapeStringLiteralPart("a#{b}c"));
-        assertEquals("a$\\{b}c", FTLUtil.escapeStringLiteralPart("a${b}c"));
-        assertEquals("a'c\\\"d", FTLUtil.escapeStringLiteralPart("a'c\"d", '"'));
-        assertEquals("a\\'c\"d", FTLUtil.escapeStringLiteralPart("a'c\"d", '\''));
-        assertEquals("a\\'c\"d", FTLUtil.escapeStringLiteralPart("a'c\"d", '\''));
-        assertEquals("\\n\\r\\t\\f\\x0002\\\\", FTLUtil.escapeStringLiteralPart("\n\r\t\f\u0002\\"));
-        assertEquals("\\l\\g\\a", FTLUtil.escapeStringLiteralPart("<>&"));
-    }
-
-    @Test
-    public void testEscapeStringLiteralAll() {
-        assertFTLEsc("", "", "", "", "\"\"");
-        assertFTLEsc("\'", "\\'", "'", "\\'", "\"'\"");
-        assertFTLEsc("\"", "\\\"", "\\\"", "\"", "'\"'");
-        assertFTLEsc("\"", "\\\"", "\\\"", "\"", "'\"'");
-        assertFTLEsc("foo", "foo", "foo", "foo", "\"foo\"");
-        assertFTLEsc("foo's", "foo\\'s", "foo's", "foo\\'s", "\"foo's\"");
-        assertFTLEsc("foo \"", "foo \\\"", "foo \\\"", "foo \"", "'foo \"'");
-        assertFTLEsc("foo's \"", "foo\\'s \\\"", "foo's \\\"", "foo\\'s \"", "\"foo's \\\"\"");
-        assertFTLEsc("foo\nb\u0000c", "foo\\nb\\x0000c", "foo\\nb\\x0000c", "foo\\nb\\x0000c", "\"foo\\nb\\x0000c\"");
-    }
-
-    private void assertFTLEsc(String s, String partAny, String partQuot, String partApos, String quoted) {
-        assertEquals(partAny, FTLUtil.escapeStringLiteralPart(s));
-        assertEquals(partQuot, FTLUtil.escapeStringLiteralPart(s, '\"'));
-        assertEquals(partApos, FTLUtil.escapeStringLiteralPart(s, '\''));
-        assertEquals(quoted, FTLUtil.toStringLiteral(s));
-    }
-
-    @Test
-    public void testUnescapeStringLiteralPart() throws Exception {
-        assertEquals("", FTLUtil.unescapeStringLiteralPart(""));
-        assertEquals("1", FTLUtil.unescapeStringLiteralPart("1"));
-        assertEquals("123", FTLUtil.unescapeStringLiteralPart("123"));
-        assertEquals("1&2&3", FTLUtil.unescapeStringLiteralPart("1\\a2\\a3"));
-        assertEquals("&", FTLUtil.unescapeStringLiteralPart("\\a"));
-        assertEquals("&&&", FTLUtil.unescapeStringLiteralPart("\\a\\a\\a"));
-        assertEquals(
-                "\u0000\u0000&\u0000\u0000\u0000\u0000",
-                FTLUtil.unescapeStringLiteralPart("\\x0000\\x0000\\a\\x0000\\x000\\x00\\x0"));
-        assertEquals(
-                "'\"\n\b\u0000c><&{\\",
-                FTLUtil.unescapeStringLiteralPart("\\'\\\"\\n\\b\\x0000c\\g\\l\\a\\{\\\\"));
-    }
-
-    @Test
-    public void testEscapeIdentifier() {
-        assertNull(FTLUtil.escapeIdentifier(null));
-        assertEquals("", FTLUtil.escapeIdentifier(""));
-        assertEquals("a", FTLUtil.escapeIdentifier("a"));
-        assertEquals("ab", FTLUtil.escapeIdentifier("ab"));
-        assertEquals("\\.", FTLUtil.escapeIdentifier("."));
-        assertEquals("\\.\\:\\-", FTLUtil.escapeIdentifier(".:-"));
-        assertEquals("a\\.b", FTLUtil.escapeIdentifier("a.b"));
-        assertEquals("a\\.b\\:c\\-d", FTLUtil.escapeIdentifier("a.b:c-d"));
-    }
-
-    @Test
-    public void testIsNonEscapedIdentifierStart() {
-        assertTrue(FTLUtil.isNonEscapedIdentifierPart('a'));
-        assertTrue(FTLUtil.isNonEscapedIdentifierPart('á'));
-        assertTrue(FTLUtil.isNonEscapedIdentifierPart('1'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierPart('-'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierPart(' '));
-        assertFalse(FTLUtil.isNonEscapedIdentifierPart('\u0000'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierPart('\\'));
-    }
-
-    @Test
-    public void testisNonEscapedIdentifierStart() {
-        assertTrue(FTLUtil.isNonEscapedIdentifierStart('a'));
-        assertTrue(FTLUtil.isNonEscapedIdentifierStart('á'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierStart('1'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierStart('-'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierStart(' '));
-        assertFalse(FTLUtil.isNonEscapedIdentifierStart('\u0000'));
-        assertFalse(FTLUtil.isNonEscapedIdentifierStart('\\'));
-    }
-
-    @Test
-    public void testToStringLiteral() {
-        assertNull(FTLUtil.toStringLiteral(null));
-        assertEquals("\"\"", FTLUtil.toStringLiteral(""));
-        assertEquals("'foo\"bar\"baaz\\''", FTLUtil.toStringLiteral("foo\"bar\"baaz'"));
-        assertEquals("\"foo'bar'baaz\\\"\"", FTLUtil.toStringLiteral("foo'bar'baaz\""));
-        assertEquals("r\"\\d\"", FTLUtil.toStringLiteral("\\d"));
-        assertEquals("r'\\d\"'", FTLUtil.toStringLiteral("\\d\""));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/TemplateLanguageUtilsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/TemplateLanguageUtilsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/TemplateLanguageUtilsTest.java
new file mode 100644
index 0000000..aae4699
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/util/TemplateLanguageUtilsTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.util;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class TemplateLanguageUtilsTest {
+
+    @Test
+    public void testEscapeStringLiteralPart() {
+        assertEquals("", TemplateLanguageUtils.escapeStringLiteralPart(""));
+        assertEquals("abc", TemplateLanguageUtils.escapeStringLiteralPart("abc"));
+        assertEquals("{", TemplateLanguageUtils.escapeStringLiteralPart("{"));
+        assertEquals("a{b}c", TemplateLanguageUtils.escapeStringLiteralPart("a{b}c"));
+        assertEquals("a#b", TemplateLanguageUtils.escapeStringLiteralPart("a#b"));
+        assertEquals("a$b", TemplateLanguageUtils.escapeStringLiteralPart("a$b"));
+        // Find related: [interpolation prefixes]
+        assertEquals("a#{b}c", TemplateLanguageUtils.escapeStringLiteralPart("a#{b}c"));
+        assertEquals("a$\\{b}c", TemplateLanguageUtils.escapeStringLiteralPart("a${b}c"));
+        assertEquals("a'c\\\"d", TemplateLanguageUtils.escapeStringLiteralPart("a'c\"d", '"'));
+        assertEquals("a\\'c\"d", TemplateLanguageUtils.escapeStringLiteralPart("a'c\"d", '\''));
+        assertEquals("a\\'c\"d", TemplateLanguageUtils.escapeStringLiteralPart("a'c\"d", '\''));
+        assertEquals("\\n\\r\\t\\f\\x0002\\\\", TemplateLanguageUtils.escapeStringLiteralPart("\n\r\t\f\u0002\\"));
+        assertEquals("\\l\\g\\a", TemplateLanguageUtils.escapeStringLiteralPart("<>&"));
+    }
+
+    @Test
+    public void testEscapeStringLiteralAll() {
+        assertFTLEsc("", "", "", "", "\"\"");
+        assertFTLEsc("\'", "\\'", "'", "\\'", "\"'\"");
+        assertFTLEsc("\"", "\\\"", "\\\"", "\"", "'\"'");
+        assertFTLEsc("\"", "\\\"", "\\\"", "\"", "'\"'");
+        assertFTLEsc("foo", "foo", "foo", "foo", "\"foo\"");
+        assertFTLEsc("foo's", "foo\\'s", "foo's", "foo\\'s", "\"foo's\"");
+        assertFTLEsc("foo \"", "foo \\\"", "foo \\\"", "foo \"", "'foo \"'");
+        assertFTLEsc("foo's \"", "foo\\'s \\\"", "foo's \\\"", "foo\\'s \"", "\"foo's \\\"\"");
+        assertFTLEsc("foo\nb\u0000c", "foo\\nb\\x0000c", "foo\\nb\\x0000c", "foo\\nb\\x0000c", "\"foo\\nb\\x0000c\"");
+    }
+
+    private void assertFTLEsc(String s, String partAny, String partQuot, String partApos, String quoted) {
+        assertEquals(partAny, TemplateLanguageUtils.escapeStringLiteralPart(s));
+        assertEquals(partQuot, TemplateLanguageUtils.escapeStringLiteralPart(s, '\"'));
+        assertEquals(partApos, TemplateLanguageUtils.escapeStringLiteralPart(s, '\''));
+        assertEquals(quoted, TemplateLanguageUtils.toStringLiteral(s));
+    }
+
+    @Test
+    public void testUnescapeStringLiteralPart() throws Exception {
+        assertEquals("", TemplateLanguageUtils.unescapeStringLiteralPart(""));
+        assertEquals("1", TemplateLanguageUtils.unescapeStringLiteralPart("1"));
+        assertEquals("123", TemplateLanguageUtils.unescapeStringLiteralPart("123"));
+        assertEquals("1&2&3", TemplateLanguageUtils.unescapeStringLiteralPart("1\\a2\\a3"));
+        assertEquals("&", TemplateLanguageUtils.unescapeStringLiteralPart("\\a"));
+        assertEquals("&&&", TemplateLanguageUtils.unescapeStringLiteralPart("\\a\\a\\a"));
+        assertEquals(
+                "\u0000\u0000&\u0000\u0000\u0000\u0000",
+                TemplateLanguageUtils.unescapeStringLiteralPart("\\x0000\\x0000\\a\\x0000\\x000\\x00\\x0"));
+        assertEquals(
+                "'\"\n\b\u0000c><&{\\",
+                TemplateLanguageUtils.unescapeStringLiteralPart("\\'\\\"\\n\\b\\x0000c\\g\\l\\a\\{\\\\"));
+    }
+
+    @Test
+    public void testEscapeIdentifier() {
+        assertNull(TemplateLanguageUtils.escapeIdentifier(null));
+        assertEquals("", TemplateLanguageUtils.escapeIdentifier(""));
+        assertEquals("a", TemplateLanguageUtils.escapeIdentifier("a"));
+        assertEquals("ab", TemplateLanguageUtils.escapeIdentifier("ab"));
+        assertEquals("\\.", TemplateLanguageUtils.escapeIdentifier("."));
+        assertEquals("\\.\\:\\-", TemplateLanguageUtils.escapeIdentifier(".:-"));
+        assertEquals("a\\.b", TemplateLanguageUtils.escapeIdentifier("a.b"));
+        assertEquals("a\\.b\\:c\\-d", TemplateLanguageUtils.escapeIdentifier("a.b:c-d"));
+    }
+
+    @Test
+    public void testIsNonEscapedIdentifierStart() {
+        assertTrue(TemplateLanguageUtils.isNonEscapedIdentifierPart('a'));
+        assertTrue(TemplateLanguageUtils.isNonEscapedIdentifierPart('á'));
+        assertTrue(TemplateLanguageUtils.isNonEscapedIdentifierPart('1'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierPart('-'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierPart(' '));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierPart('\u0000'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierPart('\\'));
+    }
+
+    @Test
+    public void testisNonEscapedIdentifierStart() {
+        assertTrue(TemplateLanguageUtils.isNonEscapedIdentifierStart('a'));
+        assertTrue(TemplateLanguageUtils.isNonEscapedIdentifierStart('á'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierStart('1'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierStart('-'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierStart(' '));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierStart('\u0000'));
+        assertFalse(TemplateLanguageUtils.isNonEscapedIdentifierStart('\\'));
+    }
+
+    @Test
+    public void testToStringLiteral() {
+        assertNull(TemplateLanguageUtils.toStringLiteral(null));
+        assertEquals("\"\"", TemplateLanguageUtils.toStringLiteral(""));
+        assertEquals("'foo\"bar\"baaz\\''", TemplateLanguageUtils.toStringLiteral("foo\"bar\"baaz'"));
+        assertEquals("\"foo'bar'baaz\\\"\"", TemplateLanguageUtils.toStringLiteral("foo'bar'baaz\""));
+        assertEquals("r\"\\d\"", TemplateLanguageUtils.toStringLiteral("\\d"));
+        assertEquals("r'\\d\"'", TemplateLanguageUtils.toStringLiteral("\\d\""));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
index 9003b07..c422d93 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
@@ -25,7 +25,7 @@ import java.io.Writer;
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
 import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
 import org.apache.freemarker.core.outputformat.OutputFormat;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 
 /**
  * AST interpolation node: <tt>${exp}</tt>
@@ -103,7 +103,7 @@ final class ASTDollarInterpolation extends ASTInterpolation {
         StringBuilder sb = new StringBuilder();
         sb.append("${");
         final String exprCF = expression.getCanonicalForm();
-        sb.append(inStringLiteral ? FTLUtil.escapeStringLiteralPart(exprCF, '"') : exprCF);
+        sb.append(inStringLiteral ? TemplateLanguageUtils.escapeStringLiteralPart(exprCF, '"') : exprCF);
         sb.append("}");
         if (!canonical && expression != escapedExpression) {
             sb.append(" auto-escaped");            

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
index 0dbcfb1..194ccef 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
@@ -27,7 +27,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.outputformat.OutputFormat;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 
 /**
  * AST expression node: string literal
@@ -149,7 +149,7 @@ final class ASTExpStringLiteral extends ASTExpression implements TemplateScalarM
     @Override
     public String getCanonicalForm() {
         if (dynamicValue == null) {
-            return FTLUtil.toStringLiteral(value);
+            return TemplateLanguageUtils.toStringLiteral(value);
         } else {
             StringBuilder sb = new StringBuilder();
             sb.append('"');
@@ -157,7 +157,7 @@ final class ASTExpStringLiteral extends ASTExpression implements TemplateScalarM
                 if (child instanceof ASTInterpolation) {
                     sb.append(((ASTInterpolation) child).getCanonicalFormInStringLiteral());
                 } else {
-                    sb.append(FTLUtil.escapeStringLiteralPart((String) child, '"'));
+                    sb.append(TemplateLanguageUtils.escapeStringLiteralPart((String) child, '"'));
                 }
             }
             sb.append('"');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
index d1684d3..2f2db0e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
@@ -58,7 +58,7 @@ import org.apache.freemarker.core.templateresolver.OrMatcher;
 import org.apache.freemarker.core.templateresolver.PathGlobMatcher;
 import org.apache.freemarker.core.templateresolver.PathRegexMatcher;
 import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util.GenericParseException;
 import org.apache.freemarker.core.util.OptInTemplateClassResolver;
 import org.apache.freemarker.core.util._ClassUtils;
@@ -2209,7 +2209,7 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
             if (w.startsWith("'") || w.startsWith("\"")) {
                 w = w.substring(1, w.length() - 1);
             }
-            return FTLUtil.unescapeStringLiteralPart(w);
+            return TemplateLanguageUtils.unescapeStringLiteralPart(w);
         }
 
         String fetchKeyword() throws GenericParseException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
index 81a5aa0..fb2143a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
@@ -34,7 +34,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util._CollectionUtils;
 
@@ -238,7 +238,7 @@ public final class _CallableUtils {
             checkSupportsAnyParameters(callableValue, argsLayout, env);
             List<String> validPredefNames = argsLayout.getPredefinedNamedArgumentsMap().getKeys();
             _ErrorDescriptionBuilder errorDesc = new _ErrorDescriptionBuilder(
-                    "The called ", FTLUtil.getCallableTypeName(callableValue), " ",
+                    "The called ", TemplateLanguageUtils.getCallableTypeName(callableValue), " ",
                     (predefPosArgCnt != 0
                             ? new Object[]{ "can only have ", predefPosArgCnt }
                             : "can't have"
@@ -286,7 +286,7 @@ public final class _CallableUtils {
                                     validNames == null || validNames.isEmpty()
                                             ? getNamedArgumentsNotSupportedMessage(callableValue, namedArg)
                                             : new Object[] {
-                                                    "The called ", FTLUtil.getCallableTypeName(callableValue),
+                                                    "The called ", TemplateLanguageUtils.getCallableTypeName(callableValue),
                                                     " has no parameter that's passed by name and is called ",
                                                     new _DelayedJQuote(namedArg.name),
                                                     ". The supported parameter names are:\n",
@@ -309,7 +309,7 @@ public final class _CallableUtils {
     private static Object[] getNamedArgumentsNotSupportedMessage(TemplateCallableModel callableValue,
             NamedArgument namedArg) {
         return new Object[] {
-                "The called ", FTLUtil.getCallableTypeName(callableValue),
+                "The called ", TemplateLanguageUtils.getCallableTypeName(callableValue),
                 " can't have arguments that are passed by name (like ",
                 new _DelayedJQuote(namedArg.name), "). Try to pass arguments by position "
                 + "(i.e, without name, as in ",
@@ -325,7 +325,7 @@ public final class _CallableUtils {
             throws TemplateException {
         if (argsLayout.getTotalLength() == 0) {
             throw new _MiscTemplateException(env,
-                    "The called ", FTLUtil.getCallableTypeName(callableValue), " doesn't support any parameters.");
+                    "The called ", TemplateLanguageUtils.getCallableTypeName(callableValue), " doesn't support any parameters.");
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
index 21b6d55..2bdaa6d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
@@ -20,7 +20,7 @@
 package org.apache.freemarker.core;
 
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 
 /** Don't use this; used internally by FreeMarker, might changes without notice. */
 public class _DelayedFTLTypeDescription extends _DelayedConversionToString {
@@ -31,7 +31,7 @@ public class _DelayedFTLTypeDescription extends _DelayedConversionToString {
 
     @Override
     protected String doConversion(Object obj) {
-        return FTLUtil.getTypeDescription((TemplateModel) obj);
+        return TemplateLanguageUtils.getTypeDescription((TemplateModel) obj);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
index 7be4511..7fcf25d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -58,7 +58,7 @@ import org.apache.freemarker.core.templateresolver.OrMatcher;
 import org.apache.freemarker.core.templateresolver.PathGlobMatcher;
 import org.apache.freemarker.core.templateresolver.PathRegexMatcher;
 import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util.GenericParseException;
 import org.apache.freemarker.core.util._ClassUtils;
 import org.apache.freemarker.core.util._StringUtils;
@@ -533,7 +533,7 @@ public class _ObjectBuilderSettingEvaluator {
         final String sInside = src.substring(startPos + (raw ? 2 : 1), pos);
         try {
             pos++; // skip closing quotation mark
-            return raw ? sInside : FTLUtil.unescapeStringLiteralPart(sInside);
+            return raw ? sInside : TemplateLanguageUtils.unescapeStringLiteralPart(sInside);
         } catch (GenericParseException e) {
             throw new _ObjectBuilderSettingEvaluationException("Malformed string literal: " + sInside, e);
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModel.java
index bbe3c03..b4247c7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModel.java
@@ -20,7 +20,7 @@
 package org.apache.freemarker.core.model;
 
 import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 
 /**
  * The common super-interface of the interfaces that stand for the FreeMarker Template Language (FTL) data types.
@@ -42,7 +42,7 @@ import org.apache.freemarker.core.util.FTLUtil;
  * these types: string, number, boolean, date. The intended applications are like string+hash, string+method,
  * hash+sequence, etc.
  * 
- * @see FTLUtil#getTypeDescription(TemplateModel)
+ * @see TemplateLanguageUtils#getTypeDescription(TemplateModel)
  */
 public interface TemplateModel {
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
index 5912424..11a45bd 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
@@ -30,7 +30,7 @@ import org.apache.freemarker.core._TemplateModelException;
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._ClassUtils;
 
 /**
@@ -216,7 +216,7 @@ final class OverloadedMethods {
     private _DelayedConversionToString getTMActualParameterTypes(TemplateModel[] args) {
         final String[] argumentTypeDescs = new String[args.length];
         for (int i = 0; i < args.length; i++) {
-            argumentTypeDescs[i] = FTLUtil.getTypeDescription(args[i]);
+            argumentTypeDescs[i] = TemplateLanguageUtils.getTypeDescription(args[i]);
         }
         
         return new DelayedCallSignatureToString(argumentTypeDescs) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
deleted file mode 100644
index 1b5e04a..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/FTLUtil.java
+++ /dev/null
@@ -1,872 +0,0 @@
-/*
- * 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.util;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core._CoreAPI;
-import org.apache.freemarker.core.model.AdapterTemplateModel;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCallableModel;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateCollectionModelEx;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateNodeModel;
-import org.apache.freemarker.core.model.TemplateNodeModelEx;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.impl.BeanAndStringModel;
-import org.apache.freemarker.core.model.impl.BeanModel;
-import org.apache.freemarker.core.model.impl.JavaMethodModel;
-
-/**
- * Static utility methods that perform tasks specific to the FreeMarker Template Language (FTL).
- * This is meant to be used from outside FreeMarker (i.e., it's an official, published API), not just from inside it.
- */
-// TODO [FM3] This name won't be good if the template language isn't called "FTL"...
-public final class FTLUtil {
-
-    private static final char[] ESCAPES = createEscapes();
-
-    private FTLUtil() {
-        // Not meant to be instantiated
-    }
-
-    private static char[] createEscapes() {
-        char[] escapes = new char['\\' + 1];
-        for (int i = 0; i < 32; ++i) {
-            escapes[i] = 1;
-        }
-        escapes['\\'] = '\\';
-        escapes['\''] = '\'';
-        escapes['"'] = '"';
-        escapes['<'] = 'l';
-        escapes['>'] = 'g';
-        escapes['&'] = 'a';
-        escapes['\b'] = 'b';
-        escapes['\t'] = 't';
-        escapes['\n'] = 'n';
-        escapes['\f'] = 'f';
-        escapes['\r'] = 'r';
-        return escapes;
-    }
-
-    /**
-     * Escapes a string according the FTL string literal escaping rules, assuming the literal is quoted with
-     * {@code quotation}; it doesn't add the quotation marks themselves. {@code '&'}, {@code '<'}, and {@code '>'}
-     * characters will be escaped.
-     *
-     * @param quotation Either {@code '"'} or {@code '\''}. It's assumed that the string literal whose part we calculate is
-     *                  enclosed within this kind of quotation mark. Thus, the other kind of quotation character will not be
-     *                  escaped in the result.
-     */
-    public static String escapeStringLiteralPart(String s, char quotation) {
-        return escapeStringLiteralPart(s, quotation, false, true, true, true);
-    }
-
-    /**
-     * Escapes a string according the FTL string literal escaping rules, assuming the literal is quoted with
-     * {@code quotation}; it doesn't add the quotation marks themselves.
-     *
-     * @param quotation See in {@link #escapeStringLiteralPart(String, char)}
-     * @param escapeAmp Whether to escape {@code '&'}
-     * @param escapeLT Whether to escape {@code '<'}
-     * @param escapeGT Whether to escape {@code '>'}
-     */
-    public static String escapeStringLiteralPart(String s, char quotation,
-            boolean escapeAmp, boolean escapeLT, boolean escapeGT) {
-        return escapeStringLiteralPart(s, quotation, false, escapeAmp, escapeLT, escapeGT);
-    }
-
-    /**
-     * Escapes a string according the FTL string literal escaping rules; it doesn't add the quotation marks themselves.
-     * As this method doesn't know if the string literal is quoted with regular quotation marks or apostrophe quote, it
-     * will escape both.
-     *
-     * @see #escapeStringLiteralPart(String, char)
-     */
-    public static String escapeStringLiteralPart(String s) {
-        return escapeStringLiteralPart(s, (char) 0, false);
-    }
-
-    private static String escapeStringLiteralPart(String s, char quotation, boolean addQuotation) {
-        return escapeStringLiteralPart(s, quotation, addQuotation, true, true, true);
-    }
-
-    private static String escapeStringLiteralPart(String s, char quotation, boolean addQuotation,
-            boolean escapeAmp, boolean escapeLT, boolean escapeGT) {
-        final int ln = s.length();
-
-        final char otherQuotation;
-        if (quotation == 0) {
-            otherQuotation = 0;
-        } else if (quotation == '"') {
-            otherQuotation = '\'';
-        } else if (quotation == '\'') {
-            otherQuotation = '"';
-        } else {
-            throw new IllegalArgumentException("Unsupported quotation character: " + quotation);
-        }
-
-        final int escLn = ESCAPES.length;
-        StringBuilder buf = null;
-        for (int i = 0; i < ln; i++) {
-            char c = s.charAt(i);
-            char escape =
-                    c < escLn ? ESCAPES[c] :
-                            c == '{' && i > 0 && isInterpolationStart(s.charAt(i - 1)) ? '{' :
-                                    0;
-            if (escape == 0 || escape == otherQuotation
-                    || c == '&' && !escapeAmp || c == '<' && !escapeLT || c == '>' && !escapeGT) {
-                if (buf != null) {
-                    buf.append(c);
-                }
-            } else {
-                if (buf == null) {
-                    buf = new StringBuilder(s.length() + 4 + (addQuotation ? 2 : 0));
-                    if (addQuotation) {
-                        buf.append(quotation);
-                    }
-                    buf.append(s.substring(0, i));
-                }
-                if (escape == 1) {
-                    // hex encoding for characters below 0x20
-                    // that have no other escape representation
-                    buf.append("\\x00");
-                    int c2 = (c >> 4) & 0x0F;
-                    c = (char) (c & 0x0F);
-                    buf.append((char) (c2 < 10 ? c2 + '0' : c2 - 10 + 'A'));
-                    buf.append((char) (c < 10 ? c + '0' : c - 10 + 'A'));
-                } else {
-                    buf.append('\\');
-                    buf.append(escape);
-                }
-            }
-        }
-
-        if (buf == null) {
-            return addQuotation ? quotation + s + quotation : s;
-        } else {
-            if (addQuotation) {
-                buf.append(quotation);
-            }
-            return buf.toString();
-        }
-    }
-
-    private static boolean isInterpolationStart(char c) {
-        // Find related: [interpolation prefixes]
-        return c == '$';
-    }
-
-    /**
-     * Unescapes a string that was escaped to be part of an FTL string literal. The string to unescape most not include
-     * the two quotation marks or two apostrophe-quotes that delimit the literal.
-     * <p>
-     * \\, \", \', \n, \t, \r, \b and \f will be replaced according to
-     * Java rules. In additional, it knows \g, \l, \a and \{ which are
-     * replaced with &lt;, &gt;, &amp; and { respectively.
-     * \x works as hexadecimal character code escape. The character
-     * codes are interpreted according to UCS basic plane (Unicode).
-     * "f\x006Fo", "f\x06Fo" and "f\x6Fo" will be "foo".
-     * "f\x006F123" will be "foo123" as the maximum number of digits is 4.
-     * <p>
-     * All other \X (where X is any character not mentioned above or End-of-string)
-     * will cause a ParseException.
-     *
-     * @param s String literal <em>without</em> the surrounding quotation marks
-     * @return String with all escape sequences resolved
-     * @throws GenericParseException if there string contains illegal escapes
-     */
-    public static String unescapeStringLiteralPart(String s) throws GenericParseException {
-
-        int idx = s.indexOf('\\');
-        if (idx == -1) {
-            return s;
-        }
-
-        int lidx = s.length() - 1;
-        int bidx = 0;
-        StringBuilder buf = new StringBuilder(lidx);
-        do {
-            buf.append(s.substring(bidx, idx));
-            if (idx >= lidx) {
-                throw new GenericParseException("The last character of string literal is backslash");
-            }
-            char c = s.charAt(idx + 1);
-            switch (c) {
-                case '"':
-                    buf.append('"');
-                    bidx = idx + 2;
-                    break;
-                case '\'':
-                    buf.append('\'');
-                    bidx = idx + 2;
-                    break;
-                case '\\':
-                    buf.append('\\');
-                    bidx = idx + 2;
-                    break;
-                case 'n':
-                    buf.append('\n');
-                    bidx = idx + 2;
-                    break;
-                case 'r':
-                    buf.append('\r');
-                    bidx = idx + 2;
-                    break;
-                case 't':
-                    buf.append('\t');
-                    bidx = idx + 2;
-                    break;
-                case 'f':
-                    buf.append('\f');
-                    bidx = idx + 2;
-                    break;
-                case 'b':
-                    buf.append('\b');
-                    bidx = idx + 2;
-                    break;
-                case 'g':
-                    buf.append('>');
-                    bidx = idx + 2;
-                    break;
-                case 'l':
-                    buf.append('<');
-                    bidx = idx + 2;
-                    break;
-                case 'a':
-                    buf.append('&');
-                    bidx = idx + 2;
-                    break;
-                case '{':
-                    buf.append('{');
-                    bidx = idx + 2;
-                    break;
-                case 'x': {
-                    idx += 2;
-                    int x = idx;
-                    int y = 0;
-                    int z = lidx > idx + 3 ? idx + 3 : lidx;
-                    while (idx <= z) {
-                        char b = s.charAt(idx);
-                        if (b >= '0' && b <= '9') {
-                            y <<= 4;
-                            y += b - '0';
-                        } else if (b >= 'a' && b <= 'f') {
-                            y <<= 4;
-                            y += b - 'a' + 10;
-                        } else if (b >= 'A' && b <= 'F') {
-                            y <<= 4;
-                            y += b - 'A' + 10;
-                        } else {
-                            break;
-                        }
-                        idx++;
-                    }
-                    if (x < idx) {
-                        buf.append((char) y);
-                    } else {
-                        throw new GenericParseException("Invalid \\x escape in a string literal");
-                    }
-                    bidx = idx;
-                    break;
-                }
-                default:
-                    throw new GenericParseException("Invalid escape sequence (\\" + c + ") in a string literal");
-            }
-            idx = s.indexOf('\\', bidx);
-        } while (idx != -1);
-        buf.append(s.substring(bidx));
-
-        return buf.toString();
-    }
-
-    /**
-     * Creates a <em>quoted</em> FTL string literal from a string, using escaping where necessary. The result either
-     * uses regular quotation marks (UCS 0x22) or apostrophe-quotes (UCS 0x27), or it will be a raw string literal
-     * (like {@code r"can contain backslash anywhere"}).
-     * This is decided based on the number of regular quotation marks, apostrophe-quotes, and backslashes.
-     *
-     * @param s The value that should be converted to an FTL string literal whose evaluated value equals to {@code s}
-     */
-    public static String toStringLiteral(String s) {
-        if (s == null) {
-            return null;
-        }
-
-        int aposCnt = 0;
-        int quotCnt = 0;
-        int backslashCnt = 0;
-        for (int i = 0; i < s.length(); i++) {
-            char c = s.charAt(i);
-            if (c == '\'') {
-                aposCnt++;
-            } else if (c == '"') {
-                quotCnt++;
-            } else if (c == '\\') {
-                backslashCnt++;
-            }
-        }
-
-        if (backslashCnt != 0) {
-            if (quotCnt == 0) {
-                return "r\"" + s + "\"";
-            } else if (aposCnt == 0) {
-                return "r\'" + s + "\'";
-            }
-        }
-
-        char quotation;
-        if (aposCnt < quotCnt ) {
-            quotation = '\'';
-        } else {
-            quotation = '\"';
-        }
-        return escapeStringLiteralPart(s, quotation, true);
-    }
-
-    /**
-     * Tells if a character can occur on the beginning of an FTL identifier expression (without escaping).
-     */
-    public static boolean isNonEscapedIdentifierStart(final char c) {
-        // This code was generated on JDK 1.8.0_20 Win64 with src/main/misc/identifierChars/IdentifierCharGenerator.java
-        if (c < 0xAA) { // This branch was edited for speed.
-            if (c >= 'a' && c <= 'z' || c >= '@' && c <= 'Z') {
-                return true;
-            } else {
-                return c == '$' || c == '_';
-            }
-        } else { // c >= 0xAA
-            if (c < 0xA7F8) {
-                if (c < 0x2D6F) {
-                    if (c < 0x2128) {
-                        if (c < 0x2090) {
-                            if (c < 0xD8) {
-                                if (c < 0xBA) {
-                                    return c == 0xAA || c == 0xB5;
-                                } else { // c >= 0xBA
-                                    return c == 0xBA || c >= 0xC0 && c <= 0xD6;
-                                }
-                            } else { // c >= 0xD8
-                                if (c < 0x2071) {
-                                    return c >= 0xD8 && c <= 0xF6 || c >= 0xF8 && c <= 0x1FFF;
-                                } else { // c >= 0x2071
-                                    return c == 0x2071 || c == 0x207F;
-                                }
-                            }
-                        } else { // c >= 0x2090
-                            if (c < 0x2115) {
-                                if (c < 0x2107) {
-                                    return c >= 0x2090 && c <= 0x209C || c == 0x2102;
-                                } else { // c >= 0x2107
-                                    return c == 0x2107 || c >= 0x210A && c <= 0x2113;
-                                }
-                            } else { // c >= 0x2115
-                                if (c < 0x2124) {
-                                    return c == 0x2115 || c >= 0x2119 && c <= 0x211D;
-                                } else { // c >= 0x2124
-                                    return c == 0x2124 || c == 0x2126;
-                                }
-                            }
-                        }
-                    } else { // c >= 0x2128
-                        if (c < 0x2C30) {
-                            if (c < 0x2145) {
-                                if (c < 0x212F) {
-                                    return c == 0x2128 || c >= 0x212A && c <= 0x212D;
-                                } else { // c >= 0x212F
-                                    return c >= 0x212F && c <= 0x2139 || c >= 0x213C && c <= 0x213F;
-                                }
-                            } else { // c >= 0x2145
-                                if (c < 0x2183) {
-                                    return c >= 0x2145 && c <= 0x2149 || c == 0x214E;
-                                } else { // c >= 0x2183
-                                    return c >= 0x2183 && c <= 0x2184 || c >= 0x2C00 && c <= 0x2C2E;
-                                }
-                            }
-                        } else { // c >= 0x2C30
-                            if (c < 0x2D00) {
-                                if (c < 0x2CEB) {
-                                    return c >= 0x2C30 && c <= 0x2C5E || c >= 0x2C60 && c <= 0x2CE4;
-                                } else { // c >= 0x2CEB
-                                    return c >= 0x2CEB && c <= 0x2CEE || c >= 0x2CF2 && c <= 0x2CF3;
-                                }
-                            } else { // c >= 0x2D00
-                                if (c < 0x2D2D) {
-                                    return c >= 0x2D00 && c <= 0x2D25 || c == 0x2D27;
-                                } else { // c >= 0x2D2D
-                                    return c == 0x2D2D || c >= 0x2D30 && c <= 0x2D67;
-                                }
-                            }
-                        }
-                    }
-                } else { // c >= 0x2D6F
-                    if (c < 0x31F0) {
-                        if (c < 0x2DD0) {
-                            if (c < 0x2DB0) {
-                                if (c < 0x2DA0) {
-                                    return c == 0x2D6F || c >= 0x2D80 && c <= 0x2D96;
-                                } else { // c >= 0x2DA0
-                                    return c >= 0x2DA0 && c <= 0x2DA6 || c >= 0x2DA8 && c <= 0x2DAE;
-                                }
-                            } else { // c >= 0x2DB0
-                                if (c < 0x2DC0) {
-                                    return c >= 0x2DB0 && c <= 0x2DB6 || c >= 0x2DB8 && c <= 0x2DBE;
-                                } else { // c >= 0x2DC0
-                                    return c >= 0x2DC0 && c <= 0x2DC6 || c >= 0x2DC8 && c <= 0x2DCE;
-                                }
-                            }
-                        } else { // c >= 0x2DD0
-                            if (c < 0x3031) {
-                                if (c < 0x2E2F) {
-                                    return c >= 0x2DD0 && c <= 0x2DD6 || c >= 0x2DD8 && c <= 0x2DDE;
-                                } else { // c >= 0x2E2F
-                                    return c == 0x2E2F || c >= 0x3005 && c <= 0x3006;
-                                }
-                            } else { // c >= 0x3031
-                                if (c < 0x3040) {
-                                    return c >= 0x3031 && c <= 0x3035 || c >= 0x303B && c <= 0x303C;
-                                } else { // c >= 0x3040
-                                    return c >= 0x3040 && c <= 0x318F || c >= 0x31A0 && c <= 0x31BA;
-                                }
-                            }
-                        }
-                    } else { // c >= 0x31F0
-                        if (c < 0xA67F) {
-                            if (c < 0xA4D0) {
-                                if (c < 0x3400) {
-                                    return c >= 0x31F0 && c <= 0x31FF || c >= 0x3300 && c <= 0x337F;
-                                } else { // c >= 0x3400
-                                    return c >= 0x3400 && c <= 0x4DB5 || c >= 0x4E00 && c <= 0xA48C;
-                                }
-                            } else { // c >= 0xA4D0
-                                if (c < 0xA610) {
-                                    return c >= 0xA4D0 && c <= 0xA4FD || c >= 0xA500 && c <= 0xA60C;
-                                } else { // c >= 0xA610
-                                    return c >= 0xA610 && c <= 0xA62B || c >= 0xA640 && c <= 0xA66E;
-                                }
-                            }
-                        } else { // c >= 0xA67F
-                            if (c < 0xA78B) {
-                                if (c < 0xA717) {
-                                    return c >= 0xA67F && c <= 0xA697 || c >= 0xA6A0 && c <= 0xA6E5;
-                                } else { // c >= 0xA717
-                                    return c >= 0xA717 && c <= 0xA71F || c >= 0xA722 && c <= 0xA788;
-                                }
-                            } else { // c >= 0xA78B
-                                if (c < 0xA7A0) {
-                                    return c >= 0xA78B && c <= 0xA78E || c >= 0xA790 && c <= 0xA793;
-                                } else { // c >= 0xA7A0
-                                    return c >= 0xA7A0 && c <= 0xA7AA;
-                                }
-                            }
-                        }
-                    }
-                }
-            } else { // c >= 0xA7F8
-                if (c < 0xAB20) {
-                    if (c < 0xAA44) {
-                        if (c < 0xA8FB) {
-                            if (c < 0xA840) {
-                                if (c < 0xA807) {
-                                    return c >= 0xA7F8 && c <= 0xA801 || c >= 0xA803 && c <= 0xA805;
-                                } else { // c >= 0xA807
-                                    return c >= 0xA807 && c <= 0xA80A || c >= 0xA80C && c <= 0xA822;
-                                }
-                            } else { // c >= 0xA840
-                                if (c < 0xA8D0) {
-                                    return c >= 0xA840 && c <= 0xA873 || c >= 0xA882 && c <= 0xA8B3;
-                                } else { // c >= 0xA8D0
-                                    return c >= 0xA8D0 && c <= 0xA8D9 || c >= 0xA8F2 && c <= 0xA8F7;
-                                }
-                            }
-                        } else { // c >= 0xA8FB
-                            if (c < 0xA984) {
-                                if (c < 0xA930) {
-                                    return c == 0xA8FB || c >= 0xA900 && c <= 0xA925;
-                                } else { // c >= 0xA930
-                                    return c >= 0xA930 && c <= 0xA946 || c >= 0xA960 && c <= 0xA97C;
-                                }
-                            } else { // c >= 0xA984
-                                if (c < 0xAA00) {
-                                    return c >= 0xA984 && c <= 0xA9B2 || c >= 0xA9CF && c <= 0xA9D9;
-                                } else { // c >= 0xAA00
-                                    return c >= 0xAA00 && c <= 0xAA28 || c >= 0xAA40 && c <= 0xAA42;
-                                }
-                            }
-                        }
-                    } else { // c >= 0xAA44
-                        if (c < 0xAAC0) {
-                            if (c < 0xAA80) {
-                                if (c < 0xAA60) {
-                                    return c >= 0xAA44 && c <= 0xAA4B || c >= 0xAA50 && c <= 0xAA59;
-                                } else { // c >= 0xAA60
-                                    return c >= 0xAA60 && c <= 0xAA76 || c == 0xAA7A;
-                                }
-                            } else { // c >= 0xAA80
-                                if (c < 0xAAB5) {
-                                    return c >= 0xAA80 && c <= 0xAAAF || c == 0xAAB1;
-                                } else { // c >= 0xAAB5
-                                    return c >= 0xAAB5 && c <= 0xAAB6 || c >= 0xAAB9 && c <= 0xAABD;
-                                }
-                            }
-                        } else { // c >= 0xAAC0
-                            if (c < 0xAAF2) {
-                                if (c < 0xAADB) {
-                                    return c == 0xAAC0 || c == 0xAAC2;
-                                } else { // c >= 0xAADB
-                                    return c >= 0xAADB && c <= 0xAADD || c >= 0xAAE0 && c <= 0xAAEA;
-                                }
-                            } else { // c >= 0xAAF2
-                                if (c < 0xAB09) {
-                                    return c >= 0xAAF2 && c <= 0xAAF4 || c >= 0xAB01 && c <= 0xAB06;
-                                } else { // c >= 0xAB09
-                                    return c >= 0xAB09 && c <= 0xAB0E || c >= 0xAB11 && c <= 0xAB16;
-                                }
-                            }
-                        }
-                    }
-                } else { // c >= 0xAB20
-                    if (c < 0xFB46) {
-                        if (c < 0xFB13) {
-                            if (c < 0xAC00) {
-                                if (c < 0xABC0) {
-                                    return c >= 0xAB20 && c <= 0xAB26 || c >= 0xAB28 && c <= 0xAB2E;
-                                } else { // c >= 0xABC0
-                                    return c >= 0xABC0 && c <= 0xABE2 || c >= 0xABF0 && c <= 0xABF9;
-                                }
-                            } else { // c >= 0xAC00
-                                if (c < 0xD7CB) {
-                                    return c >= 0xAC00 && c <= 0xD7A3 || c >= 0xD7B0 && c <= 0xD7C6;
-                                } else { // c >= 0xD7CB
-                                    return c >= 0xD7CB && c <= 0xD7FB || c >= 0xF900 && c <= 0xFB06;
-                                }
-                            }
-                        } else { // c >= 0xFB13
-                            if (c < 0xFB38) {
-                                if (c < 0xFB1F) {
-                                    return c >= 0xFB13 && c <= 0xFB17 || c == 0xFB1D;
-                                } else { // c >= 0xFB1F
-                                    return c >= 0xFB1F && c <= 0xFB28 || c >= 0xFB2A && c <= 0xFB36;
-                                }
-                            } else { // c >= 0xFB38
-                                if (c < 0xFB40) {
-                                    return c >= 0xFB38 && c <= 0xFB3C || c == 0xFB3E;
-                                } else { // c >= 0xFB40
-                                    return c >= 0xFB40 && c <= 0xFB41 || c >= 0xFB43 && c <= 0xFB44;
-                                }
-                            }
-                        }
-                    } else { // c >= 0xFB46
-                        if (c < 0xFF21) {
-                            if (c < 0xFDF0) {
-                                if (c < 0xFD50) {
-                                    return c >= 0xFB46 && c <= 0xFBB1 || c >= 0xFBD3 && c <= 0xFD3D;
-                                } else { // c >= 0xFD50
-                                    return c >= 0xFD50 && c <= 0xFD8F || c >= 0xFD92 && c <= 0xFDC7;
-                                }
-                            } else { // c >= 0xFDF0
-                                if (c < 0xFE76) {
-                                    return c >= 0xFDF0 && c <= 0xFDFB || c >= 0xFE70 && c <= 0xFE74;
-                                } else { // c >= 0xFE76
-                                    return c >= 0xFE76 && c <= 0xFEFC || c >= 0xFF10 && c <= 0xFF19;
-                                }
-                            }
-                        } else { // c >= 0xFF21
-                            if (c < 0xFFCA) {
-                                if (c < 0xFF66) {
-                                    return c >= 0xFF21 && c <= 0xFF3A || c >= 0xFF41 && c <= 0xFF5A;
-                                } else { // c >= 0xFF66
-                                    return c >= 0xFF66 && c <= 0xFFBE || c >= 0xFFC2 && c <= 0xFFC7;
-                                }
-                            } else { // c >= 0xFFCA
-                                if (c < 0xFFDA) {
-                                    return c >= 0xFFCA && c <= 0xFFCF || c >= 0xFFD2 && c <= 0xFFD7;
-                                } else { // c >= 0xFFDA
-                                    return c >= 0xFFDA && c <= 0xFFDC;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Tells if a character can occur in an FTL identifier expression (without escaping) as other than the first
-     * character.
-     */
-    public static boolean isNonEscapedIdentifierPart(final char c) {
-        return isNonEscapedIdentifierStart(c) || (c >= '0' && c <= '9');
-    }
-
-    /**
-     * Tells if a given character, for which {@link #isNonEscapedIdentifierStart(char)} and
-     * {@link #isNonEscapedIdentifierPart(char)} is {@code false}, can occur in an identifier if it's preceded by a
-     * backslash. Currently it return {@code true} for these: {@code '-'}, {@code '.'} and {@code ':'}.
-     */
-    public static boolean isEscapedIdentifierCharacter(final char c) {
-        return c == '-' || c == '.' || c == ':';
-    }
-
-    /**
-     * Escapes characters in the string that can only occur in FTL identifiers (variable names) escaped.
-     * This means adding a backslash before any character for which {@link #isEscapedIdentifierCharacter(char)}
-     * is {@code true}. Other characters will be left unescaped, even if they aren't valid in FTL identifiers.
-     *
-     * @param s The identifier to escape. If {@code null}, {@code null} is returned.
-     */
-    public static String escapeIdentifier(String s) {
-        if (s == null) {
-            return null;
-        }
-
-        int ln = s.length();
-
-        // First we find out if we need to escape, and if so, what the length of the output will be:
-        int firstEscIdx = -1;
-        int lastEscIdx = 0;
-        int plusOutLn = 0;
-        for (int i = 0; i < ln; i++) {
-            char c = s.charAt(i);
-            if (isEscapedIdentifierCharacter(c)) {
-                if (firstEscIdx == -1) {
-                    firstEscIdx = i;
-                }
-                lastEscIdx = i;
-                plusOutLn++;
-            } else if (i == 0 && !isNonEscapedIdentifierStart(c)
-                    || i > 0 && !isNonEscapedIdentifierPart(c)) {
-                // TODO [FM3] But quoting is only allowed for target variables... that's a strange syntax anyway.
-                return toStringLiteral(s);
-            }
-        }
-
-        if (firstEscIdx == -1) {
-            return s; // Nothing to escape
-        } else {
-            char[] esced = new char[ln + plusOutLn];
-            if (firstEscIdx != 0) {
-                s.getChars(0, firstEscIdx, esced, 0);
-            }
-            int dst = firstEscIdx;
-            for (int i = firstEscIdx; i <= lastEscIdx; i++) {
-                char c = s.charAt(i);
-                if (isEscapedIdentifierCharacter(c)) {
-                    esced[dst++] = '\\';
-                }
-                esced[dst++] = c;
-            }
-            if (lastEscIdx != ln - 1) {
-                s.getChars(lastEscIdx + 1, ln, esced, dst);
-            }
-
-            return String.valueOf(esced);
-        }
-    }
-
-    /**
-     * Returns the type description of a value with FTL terms (not plain class name), as it should be used in
-     * type-related error messages and for debugging purposes. The exact format is not specified and might change over
-     * time, but currently it's something like {@code "string (wrapper: f.t.SimpleScalar)"} or
-     * {@code "sequence+hash+string (ArrayList wrapped into f.e.b.CollectionModel)"}.
-     *
-     * @param tm The value whose type we will describe. If {@code null}, then {@code "Null"} is returned (without the
-     *           quotation marks).
-     */
-    public static String getTypeDescription(TemplateModel tm) {
-        if (tm == null) {
-            return "Null";
-        } else {
-            Set typeNamesAppended = new HashSet();
-
-            StringBuilder sb = new StringBuilder();
-
-            Class primaryInterface = getPrimaryTemplateModelInterface(tm);
-            if (primaryInterface != null) {
-                appendTemplateModelTypeName(sb, typeNamesAppended, primaryInterface);
-            }
-
-            appendTemplateModelTypeName(sb, typeNamesAppended, tm.getClass());
-
-            String javaClassName;
-            Class unwrappedClass = getUnwrappedClass(tm);
-            if (unwrappedClass != null) {
-                javaClassName = _ClassUtils.getShortClassName(unwrappedClass, true);
-            } else {
-                javaClassName = null;
-            }
-
-            sb.append(" (");
-            String modelClassName = _ClassUtils.getShortClassName(tm.getClass(), true);
-            if (javaClassName == null) {
-                sb.append("wrapper: ");
-                sb.append(modelClassName);
-            } else {
-                sb.append(javaClassName);
-                sb.append(" wrapped into ");
-                sb.append(modelClassName);
-            }
-            sb.append(")");
-
-            return sb.toString();
-        }
-    }
-
-    /**
-     * Return the template language type name of callable class, as it should be shown in error messages.
-     *
-     * @param callable
-     *         Can't be {@code null}.
-     */
-    public static String getCallableTypeName(TemplateCallableModel callable) {
-        _NullArgumentException.check("callable", callable);
-
-        String result = "callable-of-unknown-kind";
-
-        String d = null;
-        if (callable instanceof TemplateDirectiveModel) {
-            d = _CoreAPI.isMacro(callable.getClass()) ? "macro" : "directive";
-            result = d;
-        }
-
-        if (callable instanceof TemplateFunctionModel) {
-            String f = callable instanceof JavaMethodModel ? "method" : "function";
-            result = d == null ? f : d + "+" + f;
-        }
-
-        return result;
-    }
-
-    /**
-     * Returns the {@link TemplateModel} interface that is the most characteristic of the object, or {@code null}.
-     */
-    private static Class getPrimaryTemplateModelInterface(TemplateModel tm) {
-        if (tm instanceof BeanModel) {
-            if (tm instanceof BeanAndStringModel) {
-                Object wrapped = ((BeanModel) tm).getWrappedObject();
-                return wrapped instanceof String
-                        ? TemplateScalarModel.class
-                        : (tm instanceof TemplateHashModelEx ? TemplateHashModelEx.class : null);
-            } else {
-                return null;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    private static void appendTemplateModelTypeName(StringBuilder sb, Set typeNamesAppended, Class cl) {
-        int initalLength = sb.length();
-
-        if (TemplateNodeModelEx.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "extended node");
-        } else if (TemplateNodeModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "node");
-        }
-
-        if (TemplateCallableModel.class.isAssignableFrom(cl)) {
-            if (TemplateDirectiveModel.class.isAssignableFrom(cl)) {
-                appendTypeName(sb, typeNamesAppended, _CoreAPI.isMacro(cl) ? "macro" : "directive");
-            }
-            if (TemplateFunctionModel.class.isAssignableFrom(cl)) {
-                appendTypeName(sb, typeNamesAppended,
-                        JavaMethodModel.class.isAssignableFrom(cl) ? "method" : "function");
-            }
-        }
-
-        if (TemplateSequenceModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "sequence");
-        } else if (TemplateCollectionModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended,
-                    TemplateCollectionModelEx.class.isAssignableFrom(cl) ? "extended_collection" : "collection");
-        } else if (TemplateModelIterator.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "iterator");
-        }
-
-        if (Environment.Namespace.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "namespace");
-        } else if (TemplateHashModelEx.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "extendedHash");
-        } else if (TemplateHashModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "hash");
-        }
-
-        if (TemplateNumberModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "number");
-        }
-
-        if (TemplateDateModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "date_or_time_or_dateTime");
-        }
-
-        if (TemplateBooleanModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "boolean");
-        }
-
-        if (TemplateScalarModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "string");
-        }
-
-        if (TemplateMarkupOutputModel.class.isAssignableFrom(cl)) {
-            appendTypeName(sb, typeNamesAppended, "markupOutput");
-        }
-
-        if (sb.length() == initalLength) {
-            appendTypeName(sb, typeNamesAppended, "miscTemplateModel");
-        }
-    }
-
-    private static Class getUnwrappedClass(TemplateModel tm) {
-        Object unwrapped;
-        try {
-            if (tm instanceof WrapperTemplateModel) {
-                unwrapped = ((WrapperTemplateModel) tm).getWrappedObject();
-            } else if (tm instanceof AdapterTemplateModel) {
-                unwrapped = ((AdapterTemplateModel) tm).getAdaptedObject(Object.class);
-            } else {
-                unwrapped = null;
-            }
-        } catch (Throwable e) {
-            unwrapped = null;
-        }
-        return unwrapped != null ? unwrapped.getClass() : null;
-    }
-
-    private static void appendTypeName(StringBuilder sb, Set typeNamesAppended, String name) {
-        if (!typeNamesAppended.contains(name)) {
-            if (sb.length() != 0) sb.append("+");
-            sb.append(name);
-            typeNamesAppended.add(name);
-        }
-    }
-}