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 <[email protected]> Authored: Tue Aug 8 20:02:53 2017 +0200 Committer: ddekany <[email protected]> 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 <, >, & 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); - } - } -}
