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/07/08 20:35:20 UTC
incubator-freemarker git commit: Continued FM2 to FM3 converter...
Repository: incubator-freemarker
Updated Branches:
refs/heads/3 344b95411 -> 22d3ef2e0
Continued FM2 to FM3 converter...
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/22d3ef2e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/22d3ef2e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/22d3ef2e
Branch: refs/heads/3
Commit: 22d3ef2e089de184e589a288bbe4fa9031487ec0
Parents: 344b954
Author: ddekany <dd...@apache.org>
Authored: Sat Jul 8 22:35:03 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Jul 8 22:35:03 2017 +0200
----------------------------------------------------------------------
.../core/FM2ASTToFM3SourceConverter.java | 271 +++++++++++++++----
.../core/UnexpectedNodeContentException.java | 5 +-
.../apache/freemarker/converter/Converter.java | 39 +--
.../converter/ConverterException.java | 61 ++++-
.../UnconvertableLegacyFeatureException.java | 43 +++
.../converter/FM2ToFM3ConverterTest.java | 58 +++-
.../converter/GenericConverterTest.java | 20 ++
7 files changed, 418 insertions(+), 79 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/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 de31532..6ff6958 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -20,6 +20,7 @@
package freemarker.core;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -32,6 +33,7 @@ import org.apache.freemarker.converter.ConversionMarkers;
import org.apache.freemarker.converter.ConversionMarkers.Type;
import org.apache.freemarker.converter.ConverterException;
import org.apache.freemarker.converter.ConverterUtils;
+import org.apache.freemarker.converter.UnconvertableLegacyFeatureException;
import org.apache.freemarker.core.NamingConvention;
import org.apache.freemarker.core.util.FTLUtil;
import org.apache.freemarker.core.util._ClassUtil;
@@ -214,11 +216,11 @@ public class FM2ASTToFM3SourceConverter {
}
private String convertFtlHeaderParamName(String name) throws ConverterException {
- name = name.indexOf('_') == -1 ? name : ConverterUtils.snakeCaseToCamelCase(name);
- if (name.equals("attributes")) {
- name = "customSettings";
+ String converted = name.indexOf('_') == -1 ? name : ConverterUtils.snakeCaseToCamelCase(name);
+ if (converted.equals("attributes")) {
+ converted = "customSettings";
}
- return name;
+ return converted;
}
private void printNode(TemplateObject node) throws ConverterException {
@@ -257,20 +259,25 @@ public class FM2ASTToFM3SourceConverter {
boolean isNoParseBlock = src.startsWith(tagBeginChar + "#no", startPos);
if (isNoParseBlock) {
- printCoreDirStartTagParameterless(node, "noParse");
+ printDirStartTagNoParamsHasNested(node, "noParse");
}
print(getOnlyParam(node, ParameterRole.CONTENT, String.class));
if (isNoParseBlock) {
- printCoreDirEndTag(node, NO_PARSE_FM_2_TAG_NAMES, "noParse");
+ printDirEndTag(node, NO_PARSE_FM_2_TAG_NAMES, "noParse");
}
}
private static final ImmutableList<String> NO_PARSE_FM_2_TAG_NAMES = ImmutableList.of("noparse", "noParse");
- private void printComment(Comment node) throws UnexpectedNodeContentException {
+ private void printComment(Comment node) throws UnexpectedNodeContentException, UnconvertableLegacyFeatureException {
print(tagBeginChar);
print("#--");
- print(getOnlyParam(node, ParameterRole.CONTENT, String.class));
+ String content = getOnlyParam(node, ParameterRole.CONTENT, String.class);
+ if (content.indexOf("-->") != -1) {
+ throw new UnconvertableLegacyFeatureException("You can't have a \"-->\" inside a comment.",
+ node.getBeginLine(), node.getBeginColumn());
+ }
+ print(content);
print("--");
print(tagEndChar);
}
@@ -385,13 +392,149 @@ public class FM2ASTToFM3SourceConverter {
printDirBreak((BreakInstruction) node);
} else if (node instanceof TrimInstruction) {
printDirTOrNtOrLtOrRt((TrimInstruction) node);
+ } else if (node instanceof PropertySetting) {
+ printDirSetting((PropertySetting) node);
+ } else if (node instanceof StopInstruction) {
+ printDirStop((StopInstruction) node);
+ } else if (node instanceof SwitchBlock) {
+ printDirSwitch((SwitchBlock) node);
+ } else if (node instanceof Case) {
+ printDirCase((Case) node);
+ } else if (node instanceof VisitNode) {
+ printDirVisit((VisitNode) node);
+ } else if (node instanceof RecurseNode) {
+ printDirRecurse((RecurseNode) node);
+ } else if (node instanceof FallbackInstruction) {
+ printDirFallback((FallbackInstruction) node);
} else {
throw new ConverterException("Unhandled AST TemplateElement class: " + node.getClass().getName());
}
}
+ private void printDirFallback(FallbackInstruction node) throws ConverterException {
+ printDirGenericNoParamsNoNested(node, "fallback");
+ }
+
+ private void printDirVisit(VisitNode node) throws ConverterException {
+ printDirVisitLike(node, "visit");
+ }
+
+ private void printDirRecurse(RecurseNode node) throws ConverterException {
+ printDirVisitLike(node, "recurse");
+ }
+
+ private void printDirVisitLike(TemplateElement node, String tagName) throws ConverterException {
+ assertParamCount(node, 2);
+
+ printStartTagPartBeforeParams(node, tagName);
+
+ Expression lastParam;
+
+ Expression nodeExp = getParam(node, 0, ParameterRole.NODE, Expression.class);
+ printExp(nodeExp);
+ lastParam = nodeExp;
+
+ Expression ns = getParam(node, 1, ParameterRole.NAMESPACE, Expression.class);
+ if (ns != null) {
+ printSeparatorAndWSAndExpComments(getEndPositionExclusive(lastParam), "using");
+ printExp(ns);
+ lastParam = ns;
+ }
+
+ printStartTagEnd(node, lastParam, false);
+ }
+
+ private void printDirCase(Case node) throws ConverterException {
+ assertParamCount(node, 2);
+
+ String tagName;
+ Integer subtype = getParam(node, 1, ParameterRole.AST_NODE_SUBTYPE, Integer.class);
+ if (subtype == Case.TYPE_CASE) {
+ tagName = "case";
+ } else if (subtype == Case.TYPE_DEFAULT) {
+ tagName = "default";
+ } else {
+ throw new UnexpectedNodeContentException(node, "Unsupported subtype {}", subtype);
+ }
+
+ int pos = printStartTagPartBeforeParams(node, tagName);
+
+ Expression value = getParam(node, 0, ParameterRole.CONDITION, Expression.class);
+ if (value != null) {
+ printExp(value);
+ pos = getEndPositionExclusive(value);
+ }
+
+ printStartTagEnd(node, pos, false);
+
+ printChildElements(node);
+
+ // Element end tag is always omitted
+ }
+
+ private void printDirSwitch(SwitchBlock node) throws ConverterException {
+ assertParamCount(node, 1);
+
+ printStartTagPartBeforeParams(node, "switch");
+
+ Expression param = getOnlyParam(node, ParameterRole.VALUE, Expression.class);
+ printExp(param);
+
+ printStartTagEnd(node, param, false);
+
+ printChildElements(node);
+
+ printDirEndTag(node, "switch");
+ }
+
+ private void printDirStop(StopInstruction node) throws ConverterException {
+ assertParamCount(node, 1);
+
+ int pos = printStartTagPartBeforeParams(node, "stop");
+ Expression message = getParam(node, 0, ParameterRole.MESSAGE, Expression.class);
+ if (message != null) {
+ printExp(message);
+ pos = getEndPositionExclusive(message);
+ }
+ printStartTagEnd(node, pos, false);
+ }
+
+ private void printDirSetting(PropertySetting node) throws ConverterException {
+ assertParamCount(node, 2);
+
+ int pos = printStartTagPartBeforeParams(node, "setting");
+
+ print(FTLUtil.escapeIdentifier(convertSettingName(
+ getParam(node, 0, ParameterRole.ITEM_KEY, String.class),
+ node)));
+ pos = getPositionAfterIdentifier(pos);
+
+ pos = printSeparatorAndWSAndExpComments(pos, "=");
+
+ Expression paramValue = getParam(node, 1, ParameterRole.ITEM_VALUE, Expression.class);
+ printExp(paramValue);
+
+ printStartTagEnd(node, paramValue, false);
+ }
+
+ private String convertSettingName(String name, TemplateObject node) throws ConverterException {
+ String converted = name.indexOf('_') == -1 ? name : ConverterUtils.snakeCaseToCamelCase(name);
+
+ if (converted.equals("classicCompatible")) {
+ throw new UnconvertableLegacyFeatureException("There \"classicCompatible\" setting doesn't exist in "
+ + "FreeMarker 3. You have to remove it manually before conversion.",
+ node.getBeginLine(), node.getBeginColumn());
+ }
+
+ if (!Arrays.asList(PropertySetting.SETTING_NAMES).contains(converted)) {
+ throw new ConverterException("Couldn't map \"" + name + "\" to a valid FreeMarker 3 setting name "
+ + "(tried: " + converted + ")");
+ }
+ return converted;
+ }
+
private void printDirTOrNtOrLtOrRt(TrimInstruction node) throws ConverterException {
- int subtype= getOnlyParam(node, ParameterRole.AST_NODE_SUBTYPE, Integer.class);
+ int subtype = getOnlyParam(node, ParameterRole.AST_NODE_SUBTYPE, Integer.class);
String tagName;
if (subtype == TrimInstruction.TYPE_T) {
tagName = "t";
@@ -405,11 +548,11 @@ public class FM2ASTToFM3SourceConverter {
throw new UnexpectedNodeContentException(node, "Unhandled subtype {}.", subtype);
}
- printCoreDirStartTagParameterless(node, tagName);
+ printDirStartTagNoParamsNoNested(node, tagName);
}
private void printDirNested(BodyInstruction node) throws ConverterException {
- int pos = printCoreDirStartTagBeforeParams(node, "nested");
+ int pos = printStartTagPartBeforeParams(node, "nested");
int paramCnt = node.getParameterCount();
for (int paramIdx = 0; paramIdx < paramCnt; paramIdx++) {
Expression passedValue = getParam(node, paramIdx, ParameterRole.PASSED_VALUE, Expression.class);
@@ -423,11 +566,11 @@ public class FM2ASTToFM3SourceConverter {
}
private void printDirBreak(BreakInstruction node) throws ConverterException {
- printCoreDirStartTagParameterless(node, "break");
+ printDirStartTagNoParamsNoNested(node, "break");
}
private void printDirItems(Items node) throws ConverterException {
- int pos = printCoreDirStartTagBeforeParams(node, "items");
+ int pos = printStartTagPartBeforeParams(node, "items");
pos = printSeparatorAndWSAndExpComments(pos, "as");
int paramCnt = node.getParameterCount();
@@ -447,7 +590,7 @@ public class FM2ASTToFM3SourceConverter {
printChildElements(node);
- printCoreDirEndTag(node, "items");
+ printDirEndTag(node, "items");
}
private void printDirListElseContainer(ListElseContainer node) throws ConverterException {
@@ -455,25 +598,25 @@ public class FM2ASTToFM3SourceConverter {
printDirListOrForeach((IteratorBlock) node.getChild(0), false);
printDirElseOfList((ElseOfList) node.getChild(1));
- printCoreDirEndTag(node, "list");
+ printDirEndTag(node, "list");
}
private void printDirElseOfList(ElseOfList node) throws ConverterException {
- printCoreDirStartTagParameterless(node, "else");
+ printDirStartTagNoParamsHasNested(node, "else");
printChildElements(node);
}
private void printDirSep(Sep node) throws ConverterException {
- printCoreDirStartTagParameterless(node, "sep");
+ printDirStartTagNoParamsHasNested(node, "sep");
printChildElements(node);
- printCoreDirEndTag(node, Collections.singleton("sep"), "sep", true);
+ printDirEndTag(node, Collections.singleton("sep"), "sep", true);
}
private void printDirListOrForeach(IteratorBlock node, boolean printEndTag) throws ConverterException {
int paramCount = node.getParameterCount();
assertNodeContent(paramCount <= 3, node, "ParameterCount <= 3 was expected");
- int pos = printCoreDirStartTagBeforeParams(node, "list");
+ int pos = printStartTagPartBeforeParams(node, "list");
Expression listSource = getParam(node, 0, ParameterRole.LIST_SOURCE, Expression.class);
// To be future proof, we don't assume that the parameter count of list don't include the null parameters.
@@ -550,7 +693,7 @@ public class FM2ASTToFM3SourceConverter {
printChildElements(node);
if (printEndTag) {
- printCoreDirEndTag(node, LIST_FM_2_TAG_NAMES, "list", false);
+ printDirEndTag(node, LIST_FM_2_TAG_NAMES, "list", false);
}
}
@@ -559,7 +702,7 @@ public class FM2ASTToFM3SourceConverter {
private void printDirInclude(Include node) throws ConverterException {
assertParamCount(node, 4);
- printCoreDirStartTagBeforeParams(node, "include");
+ printStartTagPartBeforeParams(node, "include");
Expression templateName = getParam(node, 0, ParameterRole.TEMPLATE_NAME, Expression.class);
int templateNameEndPos = getEndPositionExclusive(templateName);
@@ -642,7 +785,7 @@ public class FM2ASTToFM3SourceConverter {
private void printDirImport(LibraryLoad node) throws ConverterException {
assertParamCount(node, 2);
- printCoreDirStartTagBeforeParams(node, "import");
+ printStartTagPartBeforeParams(node, "import");
Expression templateName = getParam(node, 0, ParameterRole.TEMPLATE_NAME, Expression.class);
printExp(templateName);
@@ -656,7 +799,7 @@ public class FM2ASTToFM3SourceConverter {
}
private void printDirReturn(ReturnInstruction node) throws ConverterException {
- printCoreDirStartTagBeforeParams(node, "return");
+ printStartTagPartBeforeParams(node, "return");
Expression value = getOnlyParam(node, ParameterRole.VALUE, Expression.class);
printExp(value);
@@ -664,11 +807,11 @@ public class FM2ASTToFM3SourceConverter {
}
private void printDirFlush(FlushInstruction node) throws ConverterException {
- printDirGenericParameterlessWithoutNestedContent(node, "flush");
+ printDirGenericNoParamsNoNested(node, "flush");
}
private void printDirNoEscape(NoEscapeBlock node) throws ConverterException {
- printDirGenericParameterlessWithNestedContent(node, NO_ESCAPE_FM_2_TAG_NAMES, "noEscape");
+ printDirGenericNoParamsHasNested(node, NO_ESCAPE_FM_2_TAG_NAMES, "noEscape");
}
private static final ImmutableList<String> NO_ESCAPE_FM_2_TAG_NAMES = ImmutableList.of("noescape", "noEscape");
@@ -676,7 +819,7 @@ public class FM2ASTToFM3SourceConverter {
private void printDirEscape(EscapeBlock node) throws ConverterException {
assertParamCount(node, 2);
- int pos = printCoreDirStartTagBeforeParams(node, "escape");
+ int pos = printStartTagPartBeforeParams(node, "escape");
pos = getPositionAfterIdentifier(pos);
print(FTLUtil.escapeIdentifier(getParam(node, 0, ParameterRole.PLACEHOLDER_VARIABLE, String.class)));
@@ -689,61 +832,61 @@ public class FM2ASTToFM3SourceConverter {
printChildElements(node);
- printCoreDirEndTag(node, "escape");
+ printDirEndTag(node, "escape");
}
private void printDirCompress(CompressedBlock node) throws ConverterException {
- printDirGenericParameterlessWithNestedContent(node, "compress");
+ printDirGenericNoParamsHasNested(node, "compress");
}
private void printDirAutoEsc(AutoEscBlock node) throws ConverterException {
- printDirGenericParameterlessWithNestedContent(node, AUTO_ESC_FM_2_TAG_NAMES, "autoEsc");
+ printDirGenericNoParamsHasNested(node, AUTO_ESC_FM_2_TAG_NAMES, "autoEsc");
}
private static final ImmutableList<String> AUTO_ESC_FM_2_TAG_NAMES = ImmutableList.of("autoesc", "autoEsc");
private void printDirNoAutoEsc(NoAutoEscBlock node) throws ConverterException {
- printDirGenericParameterlessWithNestedContent(node, NO_AUTO_ESC_FM_2_TAG_NAMES, "noAutoEsc");
+ printDirGenericNoParamsHasNested(node, NO_AUTO_ESC_FM_2_TAG_NAMES, "noAutoEsc");
}
private static final ImmutableList<String> NO_AUTO_ESC_FM_2_TAG_NAMES = ImmutableList.of("noautoesc", "noAutoEsc");
- private void printDirGenericParameterlessWithNestedContent(TemplateElement node, String fm3TagName)
+ private void printDirGenericNoParamsHasNested(TemplateElement node, String fm3TagName)
throws ConverterException {
- printDirGenericParameterlessWithNestedContent(node, Collections.singleton(fm3TagName), fm3TagName);
+ printDirGenericNoParamsHasNested(node, Collections.singleton(fm3TagName), fm3TagName);
}
- private void printDirGenericParameterlessWithNestedContent(TemplateElement node,
+ private void printDirGenericNoParamsHasNested(TemplateElement node,
Collection<String> fm2TagName, String fm3TagName)
throws ConverterException {
assertParamCount(node, 0);
- printCoreDirStartTagParameterless(node, fm3TagName);
+ printDirStartTagNoParamsHasNested(node, fm3TagName);
printChildElements(node);
- printCoreDirEndTag(node, fm2TagName, fm3TagName);
+ printDirEndTag(node, fm2TagName, fm3TagName);
}
- private void printDirGenericParameterlessWithoutNestedContent(TemplateElement node, String name)
+ private void printDirGenericNoParamsNoNested(TemplateElement node, String name)
throws ConverterException {
assertParamCount(node, 0);
- printCoreDirStartTagParameterless(node, name);
+ printDirStartTagNoParamsNoNested(node, name);
}
private void printDirAttemptRecover(AttemptBlock node) throws ConverterException {
assertParamCount(node, 1); // 1: The recovery block
- printCoreDirStartTagParameterless(node, "attempt");
+ printDirStartTagNoParamsHasNested(node, "attempt");
printNode(node.getChild(0));
assertNodeContent(node.getChild(1) instanceof RecoveryBlock, node, "child[1] should be #recover");
RecoveryBlock recoverDir = getOnlyParam(node, ParameterRole.ERROR_HANDLER, RecoveryBlock.class);
- printCoreDirStartTagParameterless(recoverDir, "recover");
+ printDirStartTagNoParamsHasNested(recoverDir, "recover");
printChildElements(recoverDir);
// In FM2 this could be </#recover> as well, but we normalize it
- printCoreDirEndTag(node, ATTEMPT_RECOVER_FM_2_TAG_NAMES, "attempt", false);
+ printDirEndTag(node, ATTEMPT_RECOVER_FM_2_TAG_NAMES, "attempt", false);
}
private static final ImmutableList<String> ATTEMPT_RECOVER_FM_2_TAG_NAMES = ImmutableList.of("attempt", "recover");
@@ -792,7 +935,7 @@ public class FM2ASTToFM3SourceConverter {
printChildElements(node);
- printCoreDirEndTag(node, getAssignmentDirTagName(node, 1));
+ printDirEndTag(node, getAssignmentDirTagName(node, 1));
}
private void printDirAssignmentCommonTagAfterLastAssignmentExp(TemplateElement node, int nsParamIdx, int pos)
@@ -812,7 +955,7 @@ public class FM2ASTToFM3SourceConverter {
private int printDirAssignmentCommonTagTillAssignmentExp(TemplateElement node, int scopeParamIdx)
throws ConverterException {
- return printCoreDirStartTagBeforeParams(node, getAssignmentDirTagName(node, scopeParamIdx));
+ return printStartTagPartBeforeParams(node, getAssignmentDirTagName(node, scopeParamIdx));
}
private String getAssignmentDirTagName(TemplateElement node, int scopeParamIdx)
@@ -868,7 +1011,7 @@ public class FM2ASTToFM3SourceConverter {
throw new UnexpectedNodeContentException(node, "Unhandled node subtype: {}", subtype);
}
- int pos = printCoreDirStartTagBeforeParams(node, tagName);
+ int pos = printStartTagPartBeforeParams(node, tagName);
String assignedName = getParam(node, 0, ParameterRole.ASSIGNMENT_TARGET, String.class);
print(FTLUtil.escapeIdentifier(assignedName));
@@ -935,7 +1078,7 @@ public class FM2ASTToFM3SourceConverter {
printChildElements(node);
- printCoreDirEndTag(node, tagName);
+ printDirEndTag(node, tagName);
}
private void printDirCustom(UnifiedCall node) throws ConverterException {
@@ -1049,24 +1192,24 @@ public class FM2ASTToFM3SourceConverter {
}
if (conditionExp != null) {
- printCoreDirStartTagBeforeParams(node, tagName);
+ printStartTagPartBeforeParams(node, tagName);
printNode(conditionExp);
printStartTagEnd(node, conditionExp, true);
} else {
- printCoreDirStartTagParameterless(node, tagName);
+ printDirStartTagNoParamsHasNested(node, tagName);
}
printChildElements(node);
if (!(node.getParentElement() instanceof IfBlock)) {
- printCoreDirEndTag(node, "if");
+ printDirEndTag(node, "if");
}
}
private void printDirIfElseElseIfContainer(IfBlock node) throws ConverterException {
printChildElements(node);
- printCoreDirEndTag(node, "if");
+ printDirEndTag(node, "if");
}
/**
@@ -1541,7 +1684,7 @@ public class FM2ASTToFM3SourceConverter {
*
* @return The position in the source after the printed part
*/
- private int printCoreDirStartTagBeforeParams(TemplateElement node, String fm3TagName)
+ private int printStartTagPartBeforeParams(TemplateElement node, String fm3TagName)
throws ConverterException {
print(tagBeginChar);
print('#');
@@ -1549,18 +1692,28 @@ public class FM2ASTToFM3SourceConverter {
return printWSAndExpComments(getPositionAfterTagName(node));
}
- private int printCoreDirStartTagParameterless(TemplateElement node, String fm3TagName)
+ private int printDirStartTagNoParamsNoNested(TemplateElement node, String fm3TagName)
throws ConverterException {
- int pos = printCoreDirStartTagBeforeParams(node, fm3TagName);
- printStartTagEnd(node, pos, true);
+ return printDirStartTagNoParams(node, fm3TagName, false);
+ }
+
+ private int printDirStartTagNoParamsHasNested(TemplateElement node, String fm3TagName)
+ throws ConverterException {
+ return printDirStartTagNoParams(node, fm3TagName, true);
+ }
+
+ private int printDirStartTagNoParams(TemplateElement node, String fm3TagName, boolean removeSlash)
+ throws ConverterException {
+ int pos = printStartTagPartBeforeParams(node, fm3TagName);
+ printStartTagEnd(node, pos, removeSlash);
return pos + 1;
}
- private void printCoreDirEndTag(TemplateElement node, String tagName) throws UnexpectedNodeContentException {
- printCoreDirEndTag(node, Collections.singleton(tagName), tagName);
+ private void printDirEndTag(TemplateElement node, String tagName) throws UnexpectedNodeContentException {
+ printDirEndTag(node, Collections.singleton(tagName), tagName);
}
- private void printCoreDirEndTag(TemplateElement node, Collection<String> fm2TagName, String fm3TagName) throws
+ private void printDirEndTag(TemplateElement node, Collection<String> fm2TagName, String fm3TagName) throws
UnexpectedNodeContentException {
if (fm2TagName.size() == 0) {
throw new IllegalArgumentException("You must specify at least 1 FM2 tag names");
@@ -1570,10 +1723,10 @@ public class FM2ASTToFM3SourceConverter {
"You must specify multiple FM2 tag names when the FM3 tag name ("
+ fm3TagName + ") contains upper case letters");
}
- printCoreDirEndTag(node, fm2TagName, fm3TagName, false);
+ printDirEndTag(node, fm2TagName, fm3TagName, false);
}
- private void printCoreDirEndTag(TemplateElement node, Collection<String> fm2TagNames, String fm3TagName,
+ private void printDirEndTag(TemplateElement node, Collection<String> fm2TagNames, String fm3TagName,
boolean optional)
throws UnexpectedNodeContentException {
int tagEndPos = getEndPositionInclusive(node);
@@ -1661,10 +1814,10 @@ public class FM2ASTToFM3SourceConverter {
*/
private int printStartTagEnd(TemplateElement node, Expression lastParam, boolean trimSlash)
throws ConverterException {
+ _NullArgumentException.check("lastParam", lastParam);
return printStartTagEnd(
node,
- lastParam == null ? getPositionAfterTagName(node)
- : getPosition(lastParam.getEndColumn() + 1, lastParam.getEndLine()),
+ getPosition(lastParam.getEndColumn() + 1, lastParam.getEndLine()),
trimSlash);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/freemarker-converter/src/main/java/freemarker/core/UnexpectedNodeContentException.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/freemarker/core/UnexpectedNodeContentException.java b/freemarker-converter/src/main/java/freemarker/core/UnexpectedNodeContentException.java
index c8bfe73..f7c7408 100644
--- a/freemarker-converter/src/main/java/freemarker/core/UnexpectedNodeContentException.java
+++ b/freemarker-converter/src/main/java/freemarker/core/UnexpectedNodeContentException.java
@@ -25,8 +25,9 @@ import org.apache.freemarker.core.util._StringUtil;
public class UnexpectedNodeContentException extends ConverterException {
public UnexpectedNodeContentException(TemplateObject node, String errorMessage, Object msgParam) {
super("Unexpected AST content for " + _StringUtil.jQuote(node.getNodeTypeSymbol()) + " node (class: "
- + node.getClass().getName() + ") " + node.getStartLocation() + ":\n"
- + renderMessage(errorMessage, msgParam));
+ + node.getClass().getName() + "):\n"
+ + renderMessage(errorMessage, msgParam),
+ node.getBeginLine(), node.getBeginColumn());
}
private static String renderMessage(String errorMessage, Object msgParam) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java
index d35a34d..2b3d8eb 100644
--- a/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java
+++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java
@@ -42,6 +42,8 @@ public abstract class Converter {
public static final String PROPERTY_NAME_DESTINATION_DIRECTORY = "destinationDirectory";
public static final String CONVERSION_MARKERS_FILE_NAME = "__conversion-markers.txt";
+ public static ThreadLocal<FileConversionContext> FILE_CONVERSION_CONTEXT_TLS = new ThreadLocal<>();
+
private static final Logger LOG = LoggerFactory.getLogger(Converter.class);
private File source;
@@ -148,29 +150,34 @@ public abstract class Converter {
throw new ConverterException("Failed to open file for reading: " + src, e);
}
try {
- LOG.debug("Converting file: {}", src);
- FileConversionContext ctx = null;
try {
- ctx = new FileConversionContext(srcStream, src, dstDir);
- convertFile(ctx);
- storeConversionMarkers(ctx.getConversionMarkers(), ctx);
- } catch (IOException e) {
- throw new ConverterException("I/O exception while converting " + _StringUtil.jQuote(src) + ".", e);
- } finally {
+ LOG.debug("Converting file: {}", src);
+ FileConversionContext ctx = null;
try {
- if (ctx != null && ctx.outputStream != null) {
- ctx.outputStream.close();
+ ctx = new FileConversionContext(srcStream, src, dstDir);
+ FILE_CONVERSION_CONTEXT_TLS.set(ctx);
+ convertFile(ctx);
+ storeConversionMarkers(ctx.getConversionMarkers(), ctx);
+ } catch (IOException e) {
+ throw new ConverterException("I/O exception while converting " + _StringUtil.jQuote(src) + ".", e);
+ } finally {
+ try {
+ if (ctx != null && ctx.outputStream != null) {
+ ctx.outputStream.close();
+ }
+ } catch (IOException e) {
+ throw new ConverterException("Failed to close destination file", e);
}
+ }
+ } finally {
+ try {
+ srcStream.close();
} catch (IOException e) {
- throw new ConverterException("Failed to close destination file", e);
+ throw new ConverterException("Failed to close file: " + src, e);
}
}
} finally {
- try {
- srcStream.close();
- } catch (IOException e) {
- throw new ConverterException("Failed to close file: " + src, e);
- }
+ FILE_CONVERSION_CONTEXT_TLS.remove();
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/freemarker-converter/src/main/java/org/apache/freemarker/converter/ConverterException.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/ConverterException.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/ConverterException.java
index 4a6533b..d474805 100644
--- a/freemarker-converter/src/main/java/org/apache/freemarker/converter/ConverterException.java
+++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/ConverterException.java
@@ -19,14 +19,73 @@
package org.apache.freemarker.converter;
+import org.apache.freemarker.core.util._NullArgumentException;
+
public class ConverterException extends Exception {
+ private final Integer row;
+ private final Integer column;
+
public ConverterException(String message) {
this(message, null);
}
public ConverterException(String message, Throwable cause) {
- super(message, cause);
+ this(message, null, null, cause);
+ }
+
+ /**
+ * See {@link #ConverterException(String, Integer, Integer, Throwable)}
+ */
+ public ConverterException(String message, Integer row, Integer column) {
+ this(message, row, column, null);
+ }
+
+ /**
+ * @param row The 1-based row in the source file, or {@code null}.
+ * @param column The 1-based column in the source file, or {@code null}.
+ */
+ public ConverterException(String message, Integer row, Integer column, Throwable cause) {
+ super(addLocationToMessage(message, row, column), cause);
+ this.row = row;
+ this.column = column;
+ }
+
+ private static String addLocationToMessage(String message, Integer row, Integer column) {
+ _NullArgumentException.check("message", message);
+
+ StringBuilder sb = new StringBuilder();
+
+ Converter.FileConversionContext ctx = Converter.FILE_CONVERSION_CONTEXT_TLS.get();
+ if (ctx != null || row != null) {
+ sb.append("At ");
+ if (ctx != null) {
+ sb.append(ctx.getSourceFile()).append(':');
+ }
+ if (row != null) {
+ sb.append(row).append(':');
+ if (column != null) {
+ sb.append(column).append(':');
+ }
+ }
+ sb.append(" ");
+ }
+
+ sb.append(message);
+ return sb.toString();
}
+ /**
+ * The 1-based row in the source file, or {@code null}.
+ */
+ public Integer getRow() {
+ return row;
+ }
+
+ /**
+ * The 1-based column in the source file, or {@code null}.
+ */
+ public Integer getColumn() {
+ return column;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/freemarker-converter/src/main/java/org/apache/freemarker/converter/UnconvertableLegacyFeatureException.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/UnconvertableLegacyFeatureException.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/UnconvertableLegacyFeatureException.java
new file mode 100644
index 0000000..c3a4d6e
--- /dev/null
+++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/UnconvertableLegacyFeatureException.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.converter;
+
+/**
+ * The legacy feature has no equivalent in the target format.
+ */
+public class UnconvertableLegacyFeatureException extends ConverterException {
+
+ /**
+ * @param row 1-based
+ * @param column 1-based
+ */
+ public UnconvertableLegacyFeatureException(String message, int row, int column) {
+ this(message, row, column, null);
+ }
+
+ /**
+ * @param row 1-based
+ * @param column 1-based
+ */
+ public UnconvertableLegacyFeatureException(String message, int row, int column, Throwable cause) {
+ super(message, row, column, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
index 1243251..a76350c 100644
--- a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
+++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
@@ -30,6 +30,7 @@ import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.freemarker.converter.ConverterException;
import org.apache.freemarker.converter.FM2ToFM3Converter;
+import org.apache.freemarker.converter.UnconvertableLegacyFeatureException;
import org.freemarker.converter.test.ConverterTest;
import org.junit.Test;
@@ -308,9 +309,64 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
assertConvertedSame("a<#t>\nb");
assertConvertedSame("<#t><#nt><#lt><#rt>");
assertConvertedSame("<#t ><#nt ><#lt ><#rt >");
- assertConverted("<#t><#nt><#lt><#rt>", "<#t /><#nt /><#lt /><#rt />");
+ assertConvertedSame("<#t><#nt><#lt><#rt>");
assertConvertedSame("<#ftl stripText='true'>\n\n<#macro m>\nx\n</#macro>\n");
+
+ assertConvertedSame("<#setting <#--1--> numberFormat <#--2--> = <#--3--> '0.0' <#--4-->>");
+ assertConverted("<#setting numberFormat='0.0' />", "<#setting number_format='0.0' />");
+ try {
+ convert("x<#setting classic_compatible=true>");
+ fail();
+ } catch (UnconvertableLegacyFeatureException e) {
+ assertEquals(1, (Object) e.getRow());
+ assertEquals(2, (Object) e.getColumn());
+ }
+
+ assertConvertedSame("<#stop>");
+ assertConvertedSame("<#stop />");
+ assertConvertedSame("<#stop 'Reason'>");
+ assertConvertedSame("<#stop <#--1--> 'Reason' <#--2-->>");
+
+ assertConvertedSame(""
+ + "<#switch x>\n"
+ + " <#--1-->\n"
+ + " <#case 1>one<#break>\n"
+ + " <#--2-->\n"
+ + " <#case 3>one<#break />\n"
+ + " <#case 3>fall through<#case 4>three<#break>\n"
+ + " <#default>def\n"
+ + "</#switch>");
+ assertConvertedSame(""
+ + "<#switch x>\n"
+ + " <#--1-->\n"
+ + "</#switch>");
+ assertConvertedSame("<#switch x> </#switch>");
+ assertConvertedSame("<#switch x><#-- Empty --></#switch>");
+ assertConverted("<#switch x> <#case 2> </#switch>", "<#switch x> <#case 2> </#switch>");
+
+ assertConvertedSame("<#visit node>");
+ assertConvertedSame("<#visit <#--1--> node <#--2-->>");
+ assertConvertedSame("<#visit node using ns>");
+ assertConvertedSame("<#visit node <#--1--> using <#--2--> ns <#--3-->>");
+ assertConvertedSame("<#recurse node>");
+ assertConvertedSame("<#recurse <#--1--> node <#--2-->>");
+ assertConvertedSame("<#recurse node using ns>");
+ assertConvertedSame("<#recurse node <#--1--> using <#--2--> ns <#--3-->>");
+ assertConvertedSame("<#macro m><#fallback></#macro>");
+ assertConvertedSame("<#macro m><#fallback /></#macro>");
+ }
+
+ @Test
+ public void testLegacyDirectives() throws IOException, ConverterException {
+ assertConverted("<#--<#bar>-->", "<#comment><#bar></#comment>");
+ try {
+ convert("x<#comment>--></#comment>");
+ fail();
+ } catch (UnconvertableLegacyFeatureException e) {
+ assertEquals(1, (Object) e.getRow());
+ assertEquals(2, (Object) e.getColumn());
+ }
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/22d3ef2e/freemarker-converter/src/test/java/org/freemarker/converter/GenericConverterTest.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/GenericConverterTest.java b/freemarker-converter/src/test/java/org/freemarker/converter/GenericConverterTest.java
index 23fae4b..7fcee1a 100644
--- a/freemarker-converter/src/test/java/org/freemarker/converter/GenericConverterTest.java
+++ b/freemarker-converter/src/test/java/org/freemarker/converter/GenericConverterTest.java
@@ -197,12 +197,28 @@ public class GenericConverterTest extends ConverterTest {
assertTrue(markersFile.exists());
}
+ @Test
+ public void testLocationInException() throws IOException, ConverterException {
+ write(new File(srcDir, "error.txt"), "x[trigger error]", UTF_8);
+
+ ToUpperCaseConverter converter = new ToUpperCaseConverter();
+ converter.setSource(srcDir);
+ converter.setDestinationDirectory(dstDir);
+ try {
+ converter.execute();
+ fail();
+ } catch (ConverterException e) {
+ assertThat(e.getMessage(), containsString("error.txt:1:2: Error message"));
+ }
+ }
+
public static class ToUpperCaseConverter extends Converter {
@Override
protected void convertFile(FileConversionContext ctx) throws ConverterException, IOException {
String content = IOUtils.toString(ctx.getSourceStream(), StandardCharsets.UTF_8);
ctx.setDestinationFileName(ctx.getSourceFileName() + ".uc");
+
if (content.contains("[trigger warn]")) {
ctx.getConversionMarkers().markInSource(
1, 2, ConversionMarkers.Type.WARN, "Warn message");
@@ -211,6 +227,10 @@ public class GenericConverterTest extends ConverterTest {
ctx.getConversionMarkers().markInDestination(
1, 2, ConversionMarkers.Type.TIP, "Tip message");
}
+ if (content.contains("[trigger error]")) {
+ throw new ConverterException("Error message", 1, 2);
+ }
+
IOUtils.write(content.toUpperCase(), ctx.getDestinationStream(), StandardCharsets.UTF_8);
}