in freemarker-core/src/main/java/freemarker/core/ParseException.java [373:493]
private String getOrRenderDescription() {
synchronized (this) {
if (description != null) return description; // When we already have it from the constructor
}
if (currentToken == null) {
return null;
}
Token unexpectedTok = currentToken.next;
if (unexpectedTok.kind == EOF) {
Set<String> endTokenDescs = getExpectedEndTokenDescs();
return "Unexpected end of file reached."
+ (endTokenDescs.size() == 0
? ""
: " You have an unclosed " + joinWithAnds(endTokenDescs)
+ ". Check if the FreeMarker end-tags are present, and aren't malformed. "
+ END_TAG_SYNTAX_HINT);
}
int maxExpectedTokenSequenceLength = 0;
for (int i = 0; i < expectedTokenSequences.length; i++) {
int[] expectedTokenSequence = expectedTokenSequences[i];
if (maxExpectedTokenSequenceLength < expectedTokenSequence.length) {
maxExpectedTokenSequenceLength = expectedTokenSequence.length;
}
}
StringBuilder tokenErrDesc = new StringBuilder();
tokenErrDesc.append("Encountered ");
boolean encounteredEndTag = false;
for (int i = 0; i < maxExpectedTokenSequenceLength; i++) {
if (i != 0) {
tokenErrDesc.append(" ");
}
if (unexpectedTok.kind == 0) {
tokenErrDesc.append(tokenImage[0]);
break;
}
String image = unexpectedTok.image;
if (i == 0) {
if (image.startsWith("</") || image.startsWith("[/")) {
encounteredEndTag = true;
}
}
tokenErrDesc.append(StringUtil.jQuote(image));
unexpectedTok = unexpectedTok.next;
}
Set<String> expectedEndTokenDescs;
int unexpTokKind = currentToken.next.kind;
if (getIsEndToken(unexpTokKind) || unexpTokKind == ELSE || unexpTokKind == ELSE_IF) {
expectedEndTokenDescs = new LinkedHashSet<>(getExpectedEndTokenDescs());
if (unexpTokKind == ELSE || unexpTokKind == ELSE_IF) {
// If <\#if> was expected, yet #else or #elseif wasn't, then this isn't nesting related problem.
expectedEndTokenDescs.remove(getEndTokenDescIfIsEndToken(END_IF));
} else {
expectedEndTokenDescs.remove(getEndTokenDescIfIsEndToken(unexpTokKind));
}
} else {
expectedEndTokenDescs = Collections.emptySet();
}
// Generate more helpful error message if this was a nesting related problem:
if (!expectedEndTokenDescs.isEmpty()) {
if (unexpTokKind == ELSE || unexpTokKind == ELSE_IF) {
tokenErrDesc.append(", which can only be used where an #if");
if (unexpTokKind == ELSE) {
tokenErrDesc.append(" or #list");
}
tokenErrDesc.append(" could be closed");
}
tokenErrDesc.append(", but at this place only ");
tokenErrDesc.append(expectedEndTokenDescs.size() > 1 ? "these" : "this");
tokenErrDesc.append(" can be closed: ");
boolean first = true;
for (String expectedEndTokenDesc : expectedEndTokenDescs) {
if (!first) {
tokenErrDesc.append(", ");
} else {
first = false;
}
tokenErrDesc.append(
!expectedEndTokenDesc.startsWith("\"")
? StringUtil.jQuote(expectedEndTokenDesc)
: expectedEndTokenDesc);
}
tokenErrDesc.append(".");
if (encounteredEndTag) {
tokenErrDesc.append(" This usually because of wrong nesting of FreeMarker directives, like a "
+ "missed or malformed end-tag somewhere. " + END_TAG_SYNTAX_HINT);
}
tokenErrDesc.append(eol);
tokenErrDesc.append("Was ");
} else {
tokenErrDesc.append(", but was ");
}
if (expectedTokenSequences.length == 1) {
tokenErrDesc.append("expecting pattern:");
} else {
tokenErrDesc.append("expecting one of these patterns:");
}
tokenErrDesc.append(eol);
for (int i = 0; i < expectedTokenSequences.length; i++) {
if (i != 0) {
tokenErrDesc.append(eol);
}
tokenErrDesc.append(" ");
int[] expectedTokenSequence = expectedTokenSequences[i];
for (int j = 0; j < expectedTokenSequence.length; j++) {
if (j != 0) {
tokenErrDesc.append(' ');
}
tokenErrDesc.append(tokenImage[expectedTokenSequence[j]]);
}
}
return tokenErrDesc.toString();
}