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:05 UTC
[1/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).
Repository: incubator-freemarker
Updated Branches:
refs/heads/3 ebb39b845 -> a5ac9983f
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
new file mode 100644
index 0000000..061cf51
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
@@ -0,0 +1,872 @@
+/*
+ * 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 TemplateLanguageUtils {
+
+ private static final char[] ESCAPES = createEscapes();
+
+ private TemplateLanguageUtils() {
+ // 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);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/java/org/apache/freemarker/core/util/_StringUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_StringUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_StringUtils.java
index 9218005..e553be1 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_StringUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_StringUtils.java
@@ -1541,18 +1541,18 @@ public class _StringUtils {
}
public static String toFTLIdentifierReferenceAfterDot(String name) {
- return FTLUtil.escapeIdentifier(name);
+ return TemplateLanguageUtils.escapeIdentifier(name);
}
public static String toFTLTopLevelIdentifierReference(String name) {
- return FTLUtil.escapeIdentifier(name);
+ return TemplateLanguageUtils.escapeIdentifier(name);
}
public static String toFTLTopLevelTragetIdentifier(final String name) {
char quotationType = 0;
scanForQuotationType: for (int i = 0; i < name.length(); i++) {
final char c = name.charAt(i);
- if (!(i == 0 ? FTLUtil.isNonEscapedIdentifierStart(c) : FTLUtil.isNonEscapedIdentifierPart(c)) && c != '@') {
+ if (!(i == 0 ? TemplateLanguageUtils.isNonEscapedIdentifierStart(c) : TemplateLanguageUtils.isNonEscapedIdentifierPart(c)) && c != '@') {
if ((quotationType == 0 || quotationType == '\\') && (c == '-' || c == '.' || c == ':')) {
quotationType = '\\';
} else {
@@ -1565,9 +1565,9 @@ public class _StringUtils {
case 0:
return name;
case '"':
- return FTLUtil.toStringLiteral(name);
+ return TemplateLanguageUtils.toStringLiteral(name);
case '\\':
- return FTLUtil.escapeIdentifier(name);
+ return TemplateLanguageUtils.escapeIdentifier(name);
default:
throw new BugException();
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-core/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/javacc/FTL.jj b/freemarker-core/src/main/javacc/FTL.jj
index 952f854..613b252 100644
--- a/freemarker-core/src/main/javacc/FTL.jj
+++ b/freemarker-core/src/main/javacc/FTL.jj
@@ -2165,7 +2165,7 @@ ASTExpStringLiteral ASTExpStringLiteral(boolean interpolate) :
s = t.image.substring(2, t.image.length() -1);
} else {
try {
- s = FTLUtil.unescapeStringLiteralPart(t.image.substring(1, t.image.length() -1));
+ s = TemplateLanguageUtils.unescapeStringLiteralPart(t.image.substring(1, t.image.length() -1));
} catch (GenericParseException e) {
throw new ParseException(e.getMessage(), template, t);
}
@@ -3633,7 +3633,7 @@ ASTDirOutputFormat OutputFormat() :
}
} else {
throw new ParseException(
- "Parameter must be a string, but was: " + FTLUtil.getTypeDescription(paramTM),
+ "Parameter must be a string, but was: " + TemplateLanguageUtils.getTypeDescription(paramTM),
paramExp);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a5ac9983/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
index eb6930b..d833c13 100644
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
@@ -29,7 +29,7 @@ import org.apache.freemarker.core.model.ArgumentArrayLayout;
import org.apache.freemarker.core.model.TemplateBooleanModel;
import org.apache.freemarker.core.model.TemplateDirectiveModel;
import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
public class AssertDirective implements TemplateDirectiveModel {
public static AssertDirective INSTANCE = new AssertDirective();
@@ -45,7 +45,7 @@ public class AssertDirective implements TemplateDirectiveModel {
}
if (!(test instanceof TemplateBooleanModel)) {
throw new AssertationFailedInTemplateException("Assertion failed:\n"
- + "The value had to be boolean, but it was of type" + FTLUtil.getTypeDescription(test),
+ + "The value had to be boolean, but it was of type" + TemplateLanguageUtils.getTypeDescription(test),
env);
}
if (!((TemplateBooleanModel) test).getAsBoolean()) {
[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).
Posted by dd...@apache.org.
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 <, >, & 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);
- }
- }
-}