in freemarker-core/src/main/java/freemarker/core/_ErrorDescriptionBuilder.java [71:168]
public String toString(TemplateElement parentElement, boolean showTips) {
if (blamed == null && tips == null && tip == null && descriptionParts == null) return description;
StringBuilder sb = new StringBuilder(200);
if (parentElement != null && blamed != null && showBlamer) {
try {
Blaming blaming = findBlaming(parentElement, blamed, 0);
if (blaming != null) {
sb.append("For ");
String nss = blaming.blamer.getNodeTypeSymbol();
char q = nss.indexOf('"') == -1 ? '\"' : '`';
sb.append(q).append(nss).append(q);
sb.append(" ").append(blaming.roleOfblamed).append(": ");
}
} catch (Throwable e) {
// Should not happen. But we rather give a not-so-good error message than replace it with another...
// So we ignore this.
LOG.error("Error when searching blamer for better error message.", e);
}
}
if (description != null) {
sb.append(description);
} else {
appendParts(sb, descriptionParts);
}
String extraTip = null;
if (blamed != null) {
// Right-trim:
for (int idx = sb.length() - 1; idx >= 0 && Character.isWhitespace(sb.charAt(idx)); idx --) {
sb.deleteCharAt(idx);
}
char lastChar = sb.length() > 0 ? (sb.charAt(sb.length() - 1)) : 0;
if (lastChar != 0) {
sb.append('\n');
}
if (lastChar != ':') {
sb.append("The blamed expression:\n");
}
String[] lines = splitToLines(blamed.toString());
for (int i = 0; i < lines.length; i++) {
sb.append(i == 0 ? "==> " : "\n ");
sb.append(lines[i]);
}
sb.append(" [");
sb.append(blamed.getStartLocation());
sb.append(']');
if (containsSingleInterpolatoinLiteral(blamed, 0)) {
extraTip = "It has been noticed that you are using ${...} as the sole content of a quoted string. That "
+ "does nothing but forcably converts the value inside ${...} to string (as it inserts it into "
+ "the enclosing string). "
+ "If that's not what you meant, just remove the quotation marks, ${ and }; you don't need "
+ "them. If you indeed wanted to convert to string, use myExpression?string instead.";
}
}
if (showTips) {
int allTipsLen = (tips != null ? tips.length : 0) + (tip != null ? 1 : 0) + (extraTip != null ? 1 : 0);
Object[] allTips;
if (tips != null && allTipsLen == tips.length) {
allTips = tips;
} else {
allTips = new Object[allTipsLen];
int dst = 0;
if (tip != null) allTips[dst++] = tip;
if (tips != null) {
for (int i = 0; i < tips.length; i++) {
allTips[dst++] = tips[i];
}
}
if (extraTip != null) allTips[dst++] = extraTip;
}
if (allTips != null && allTips.length > 0) {
sb.append("\n\n");
for (int i = 0; i < allTips.length; i++) {
if (i != 0) sb.append('\n');
sb.append(_CoreAPI.ERROR_MESSAGE_HR).append('\n');
sb.append("Tip: ");
Object tip = allTips[i];
if (!(tip instanceof Object[])) {
sb.append(allTips[i]);
} else {
appendParts(sb, (Object[]) tip);
}
}
sb.append('\n').append(_CoreAPI.ERROR_MESSAGE_HR);
}
}
return sb.toString();
}