/*
 * 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 freemarker.core;

import java.util.ArrayList;

import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModelEx;
import freemarker.template.TemplateHashModelEx2;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.StringUtil;

Used internally only, might changes without notice! Utilities for creating error messages (and other messages).
/** * Used internally only, might changes without notice! * Utilities for creating error messages (and other messages). */
public class _MessageUtil { static final String UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE = "Can't convert the date-like value to string because it isn't " + "known if it's a date (no time part), time or date-time value."; static final String UNKNOWN_DATE_PARSING_ERROR_MESSAGE = "Can't parse the string to date-like value because it isn't " + "known if it's desired result should be a date (no time part), a time, or a date-time value."; static final String UNKNOWN_DATE_TYPE_ERROR_TIP = "Use ?date, ?time, or ?datetime to tell FreeMarker the exact type."; static final Object[] UNKNOWN_DATE_TO_STRING_TIPS = { UNKNOWN_DATE_TYPE_ERROR_TIP, "If you need a particular format only once, use ?string(pattern), like ?string('dd.MM.yyyy HH:mm:ss'), " + "to specify which fields to display. " }; 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 templateSourceName, int line, int column) { return formatLocation("in", templateSourceName, line, column); } static String formatLocationForDependentParsingError(Template template, int line, int column) { return formatLocation("on", template, line, column); } static String formatLocationForDependentParsingError(String templateSourceName, int line, int column) { return formatLocation("on", templateSourceName, 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.getSourceName() : null, macro.getName(), macro.isFunction(), line, column); } static String formatLocationForEvaluationError(String templateSourceName, int line, int column) { return formatLocation("at", templateSourceName, line, column); } private static String formatLocation(String preposition, Template template, int line, int column) { return formatLocation(preposition, template != null ? template.getSourceName() : null, line, column); } private static String formatLocation(String preposition, String templateSourceName, int line, int column) { return formatLocation( preposition, templateSourceName, null, false, line, column); } private static String formatLocation( String preposition, String templateSourceName, String macroOrFuncName, boolean isFunction, int line, int column) { String templateDesc; if (line < 0) { templateDesc = "?eval-ed string"; macroOrFuncName = null; } else { templateDesc = templateSourceName != null ? "template " + StringUtil.jQuoteNoXSS(templateSourceName) : "nameless template"; } return "in " + templateDesc + (macroOrFuncName != null ? " in " + (isFunction ? "function " : "macro ") + StringUtil.jQuote(macroOrFuncName) : "") + " " + preposition + " " + formatPosition(line, column); } static String formatPosition(int line, int column) { return "line " + (line >= 0 ? line : line - (TemplateObject.RUNTIME_EVAL_LINE_DISPLACEMENT - 1)) + ", column " + column; }
Returns a single line string that is no longer than maxLength. If will truncate the string at line-breaks too. The truncation is always signaled with a a "..." at the end of the result string.
/** * 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. */
public 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 + "..."; } } } public static StringBuilder appendExpressionAsUntearable(StringBuilder 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) && !(argExp instanceof ExistsExpression) && !(argExp instanceof ParentheticalExpression); if (needParen) sb.append('('); sb.append(argExp.getCanonicalForm()); if (needParen) sb.append(')'); return sb; } public static TemplateModelException newArgCntError(String methodName, int argCnt, int expectedCnt) { return newArgCntError(methodName, argCnt, expectedCnt, expectedCnt); } public 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(Integer.valueOf(maxCnt)); } } else if (maxCnt - minCnt == 1) { desc.add(Integer.valueOf(minCnt)); desc.add(" or "); desc.add(Integer.valueOf(maxCnt)); } else { desc.add(Integer.valueOf(minCnt)); if (maxCnt != Integer.MAX_VALUE) { desc.add(" to "); desc.add(Integer.valueOf(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(Integer.valueOf(argCnt)); } desc.add("."); return new _TemplateModelException(desc.toArray()); } public static TemplateModelException newMethodArgMustBeStringException(String methodName, int argIdx, TemplateModel arg) { return newMethodArgUnexpectedTypeException(methodName, argIdx, "string", arg); } public static TemplateModelException newMethodArgMustBeNumberException(String methodName, int argIdx, TemplateModel arg) { return newMethodArgUnexpectedTypeException(methodName, argIdx, "number", arg); } public static TemplateModelException newMethodArgMustBeBooleanException(String methodName, int argIdx, TemplateModel arg) { return newMethodArgUnexpectedTypeException(methodName, argIdx, "boolean", arg); } public static TemplateModelException newMethodArgMustBeExtendedHashException( String methodName, int argIdx, TemplateModel arg) { return newMethodArgUnexpectedTypeException(methodName, argIdx, "extended hash", arg); } public static TemplateModelException newMethodArgMustBeSequenceException( String methodName, int argIdx, TemplateModel arg) { return newMethodArgUnexpectedTypeException(methodName, argIdx, "sequence", arg); } public static TemplateModelException newMethodArgMustBeSequenceOrCollectionException( String methodName, int argIdx, TemplateModel arg) { return newMethodArgUnexpectedTypeException(methodName, argIdx, "sequence or collection", arg); } public static TemplateModelException newMethodArgUnexpectedTypeException( String methodName, int argIdx, String expectedType, TemplateModel arg) { return new _TemplateModelException( methodName, "(...) expects ", new _DelayedAOrAn(expectedType), " as argument #", Integer.valueOf(argIdx + 1), ", but received ", new _DelayedAOrAn(new _DelayedFTLTypeDescription(arg)), "."); }
The type of the argument was good, but it's value wasn't.
/** * The type of the argument was good, but it's value wasn't. */
public static TemplateModelException newMethodArgInvalidValueException( String methodName, int argIdx, Object... details) { return new _TemplateModelException( methodName, "(...) argument #", Integer.valueOf(argIdx + 1), " had invalid value: ", details); }
The type of the argument was good, but the values of two or more arguments are inconsistent with each other.
/** * The type of the argument was good, but the values of two or more arguments are inconsistent with each other. */
public static TemplateModelException newMethodArgsInvalidValueException( String methodName, Object... details) { return new _TemplateModelException(methodName, "(...) arguments have invalid value: ", details); } public static TemplateException newInstantiatingClassNotAllowedException(String className, Environment env) { return new _MiscTemplateException(env, "Instantiating ", className, " is not allowed in the template for security reasons."); } public static _TemplateModelException newCantFormatUnknownTypeDateException( Expression dateSourceExpr, UnknownDateTypeFormattingUnsupportedException cause) { return new _TemplateModelException(cause, null, new _ErrorDescriptionBuilder( _MessageUtil.UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE) .blame(dateSourceExpr) .tips(_MessageUtil.UNKNOWN_DATE_TO_STRING_TIPS)); } public static TemplateException newCantFormatDateException(TemplateDateFormat format, Expression dataSrcExp, TemplateValueFormatException e, boolean useTempModelExc) { _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder( "Failed to format date/time/datetime with format ", new _DelayedJQuote(format.getDescription()), ": ", e.getMessage()) .blame(dataSrcExp); return useTempModelExc ? new _TemplateModelException(e, (Environment) null, desc) : new _MiscTemplateException(e, (Environment) null, desc); } public static TemplateException newCantFormatNumberException(TemplateNumberFormat format, Expression dataSrcExp, TemplateValueFormatException e, boolean useTempModelExc) { _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder( "Failed to format number with format ", new _DelayedJQuote(format.getDescription()), ": ", e.getMessage()) .blame(dataSrcExp); return useTempModelExc ? new _TemplateModelException(e, (Environment) null, desc) : new _MiscTemplateException(e, (Environment) null, desc); } public static TemplateModelException newKeyValuePairListingNonStringKeyExceptionMessage( TemplateModel key, TemplateHashModelEx listedHashEx) { return new _TemplateModelException(new _ErrorDescriptionBuilder( "When listing key-value pairs of traditional hash " + "implementations, all keys must be strings, but one of them " + "was ", new _DelayedAOrAn(new _DelayedFTLTypeDescription(key)), "." ).tip("The listed value's TemplateModel class was ", new _DelayedShortClassName(listedHashEx.getClass()), ", which doesn't implement ", new _DelayedShortClassName(TemplateHashModelEx2.class), ", which leads to this restriction.")); }
Returns:"a" or "an" or "a(n)" (or "" for empty string) for an FTL type name
/** * @return "a" or "an" or "a(n)" (or "" for empty string) for an FTL type name */
static public 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"; } } } }