blob: 2b49798a86239697e1b2eee78741093a7cdb391d [file] [log] [blame]
package freemarker.core;
import java.util.ArrayList;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.StringUtil;
/**
* Utilities for creating error messages (and other messages).
*/
class MessageUtil {
static final String[] UNKNOWN_DATE_TYPE_ERROR_TIPS = new String[] {
"Use ?time, ?date or ?datetime to tell FreeMarker which parts of the date is used.",
"For programmers: Use java.sql.Date/Time/Timestamp instead of java.util.Date in the "
+ "data-model to avoid this ambiguity."
};
static final String[] UNKNOWN_DATE_TO_STRING_TIPS = new String[] {
"Use ?string(format) to specify which parts to display.",
UNKNOWN_DATE_TYPE_ERROR_TIPS[0],
UNKNOWN_DATE_TYPE_ERROR_TIPS[1]
};
static final String EMBEDDED_MESSAGE_BEGIN = "---begin-message---\n";
static final String EMBEDDED_MESSAGE_END = "\n---end-message---";
// Can't be instantiated
private MessageUtil() { }
static String formatLocationForSimpleParsingError(Template template, int line, int column) {
return formatLocation("in", template, line, column);
}
static String formatLocationForSimpleParsingError(String templateName, int line, int column) {
return formatLocation("in", templateName, line, column);
}
static String formatLocationForDependentParsingError(Template template, int line, int column) {
return formatLocation("on", template, line, column);
}
static String formatLocationForDependentParsingError(String templateName, int line, int column) {
return formatLocation("on", templateName, line, column);
}
static String formatLocationForEvaluationError(Template template, int line, int column) {
return formatLocation("at", template, line, column);
}
static String formatLocationForEvaluationError(Macro macro, int line, int column) {
Template t = macro.getTemplate();
return formatLocation("at", t != null ? t.getName() : null, macro.getName(), macro.isFunction(), line, column);
}
static String formatLocationForEvaluationError(String templateName, int line, int column) {
return formatLocation("at", templateName, line, column);
}
private static String formatLocation(String preposition, Template template, int line, int column) {
return formatLocation(preposition, template != null ? template.getName() : null, line, column);
}
private static String formatLocation(String preposition, String templateName, int line, int column) {
return formatLocation(
preposition, templateName,
null, false,
line, column);
}
private static String formatLocation(
String preposition, String templateName,
String macroOrFuncName, boolean isFunction,
int line, int column) {
String templateDesc;
if (line < 0) {
templateDesc = "?eval-ed string";
line -= TemplateObject.RUNTIME_EVAL_LINE_DISPLACEMENT - 1;
macroOrFuncName = null;
} else {
templateDesc = templateName != null
? "template " + StringUtil.jQuoteNoXSS(templateName)
: "nameless template";
}
return "in " + templateDesc
+ (macroOrFuncName != null
? " in " + (isFunction ? "function " : "macro ") + StringUtil.jQuote(macroOrFuncName)
: "")
+ " "
+ preposition + " line " + line + ", column " + column;
}
/**
* Returns a single line string that is no longer than {@code maxLength}.
* If will truncate the string at line-breaks too.
* The truncation is always signaled with a a {@code "..."} at the end of the result string.
*/
static String shorten(String s, int maxLength) {
if (maxLength < 5) maxLength = 5;
boolean isTruncated = false;
int brIdx = s.indexOf('\n');
if (brIdx != -1) {
s = s.substring(0, brIdx);
isTruncated = true;
};
brIdx = s.indexOf('\r');
if (brIdx != -1) {
s = s.substring(0, brIdx);
isTruncated = true;
}
if (s.length() > maxLength) {
s = s.substring(0, maxLength - 3);
isTruncated = true;
}
if (!isTruncated) {
return s;
} else {
if (s.endsWith(".")) {
if (s.endsWith("..")) {
if (s.endsWith("...")) {
return s;
} else {
return s + ".";
}
} else {
return s + "..";
}
} else {
return s + "...";
}
}
}
static StringBuffer appendExpressionAsUntearable(StringBuffer sb, Expression argExp) {
boolean needParen =
!(argExp instanceof NumberLiteral)
&& !(argExp instanceof StringLiteral)
&& !(argExp instanceof BooleanLiteral)
&& !(argExp instanceof ListLiteral)
&& !(argExp instanceof HashLiteral)
&& !(argExp instanceof Identifier)
&& !(argExp instanceof Dot)
&& !(argExp instanceof DynamicKeyName)
&& !(argExp instanceof MethodCall)
&& !(argExp instanceof BuiltIn);
if (needParen) sb.append('(');
sb.append(argExp.getCanonicalForm());
if (needParen) sb.append(')');
return sb;
}
static TemplateModelException newArgCntError(String methodName, int argCnt, int expectedCnt) {
return newArgCntError(methodName, argCnt, expectedCnt, expectedCnt);
}
static TemplateModelException newArgCntError(String methodName, int argCnt, int minCnt, int maxCnt) {
ArrayList/*<Object>*/ desc = new ArrayList(20);
desc.add(methodName);
desc.add("(");
if (maxCnt != 0) desc.add("...");
desc.add(") expects ");
if (minCnt == maxCnt) {
if (maxCnt == 0) {
desc.add("no");
} else {
desc.add(new Integer(maxCnt));
}
} else if (maxCnt - minCnt == 1) {
desc.add(new Integer(minCnt));
desc.add(" or ");
desc.add(new Integer(maxCnt));
} else {
desc.add(new Integer(minCnt));
if (maxCnt != Integer.MAX_VALUE) {
desc.add(" to ");
desc.add(new Integer(maxCnt));
} else {
desc.add(" or more (unlimited)");
}
}
desc.add(" argument");
if (maxCnt > 1) desc.add("s");
desc.add(" but has received ");
if (argCnt == 0) {
desc.add("none");
} else {
desc.add(new Integer(argCnt));
}
desc.add(".");
return new _TemplateModelException(desc.toArray());
}
static TemplateModelException newMethodArgMustBeStringException(String methodName, int argIdx, TemplateModel arg) {
return newMethodArgUnexpectedTypeException(methodName, argIdx, "string", arg);
}
static TemplateModelException newMethodArgMustBeNumberException(String methodName, int argIdx, TemplateModel arg) {
return newMethodArgUnexpectedTypeException(methodName, argIdx, "number", arg);
}
static TemplateModelException newMethodArgMustBeBooleanException(String methodName, int argIdx, TemplateModel arg) {
return newMethodArgUnexpectedTypeException(methodName, argIdx, "boolean", arg);
}
static TemplateModelException newMethodArgMustBeExtendedHashException(
String methodName, int argIdx, TemplateModel arg) {
return newMethodArgUnexpectedTypeException(methodName, argIdx, "extended hash", arg);
}
static TemplateModelException newMethodArgMustBeSequenceException(
String methodName, int argIdx, TemplateModel arg) {
return newMethodArgUnexpectedTypeException(methodName, argIdx, "sequence", arg);
}
static TemplateModelException newMethodArgMustBeSequenceOrCollectionException(
String methodName, int argIdx, TemplateModel arg) {
return newMethodArgUnexpectedTypeException(methodName, argIdx, "sequence or collection", arg);
}
static TemplateModelException newMethodArgUnexpectedTypeException(
String methodName, int argIdx, String expectedType, TemplateModel arg) {
return new _TemplateModelException(new Object[] {
methodName, "(...) expects ", new _DelayedAOrAn(expectedType), " as argument #", new Integer(argIdx + 1),
", but received ", new _DelayedAOrAn(new _DelayedFTLTypeDescription(arg)), "." });
}
static TemplateException newInstantiatingClassNotAllowedException(String className, Environment env) {
return new _MiscTemplateException(env, new Object[] {
"Instantiating ", className, " is not allowed in the template for security reasons." });
}
/**
* @return "a" or "an" or "a(n)" (or "" for empty string) for an FTL type name
*/
static String getAOrAn(String s) {
if (s == null) return null;
if (s.length() == 0) return "";
char fc = Character.toLowerCase(s.charAt(0));
if (fc == 'a' || fc == 'e' || fc == 'i') {
return "an";
} else if (fc == 'h') {
String ls = s.toLowerCase();
if (ls.startsWith("has") || ls.startsWith("hi")) {
return "a";
} else if (ls.startsWith("ht")) {
return "an";
} else {
return "a(n)";
}
} else if (fc == 'u' || fc == 'o') {
return "a(n)";
} else {
char sc = (s.length() > 1) ? s.charAt(1) : '\0';
if (fc == 'x' && !(sc == 'a' || sc == 'e' || sc == 'i' || sc == 'a' || sc == 'o' || sc == 'u')) {
return "an";
} else {
return "a";
}
}
}
}