http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ParseException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ParseException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ParseException.java new file mode 100644 index 0000000..9e5dad3 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ParseException.java @@ -0,0 +1,518 @@ +/* + * 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.core; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.freemarker.core.util._NullArgumentException; +import org.apache.freemarker.core.util._SecurityUtil; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Parsing-time exception in a template (as opposed to a runtime exception, a {@link TemplateException}). This usually + * signals syntactical/lexical errors. + * + * Note that on JavaCC-level lexical errors throw {@link TokenMgrError} instead of this, however with the API-s that + * most users use those will be wrapped into {@link ParseException}-s. + * + * This is a modified version of file generated by JavaCC from FTL.jj. + * You can modify this class to customize the error reporting mechanisms so long as the public interface + * remains compatible with the original. + * + * @see TokenMgrError + */ +public class ParseException extends IOException implements FMParserConstants { + + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * following this token will (therefore) be the first error token. + */ + public Token currentToken; + + private static volatile Boolean jbossToolsMode; + + private boolean messageAndDescriptionRendered; + private String message; + private String description; + + public int columnNumber, lineNumber; + public int endColumnNumber, endLineNumber; + + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * The end of line string for this machine. + */ + protected String eol = _SecurityUtil.getSystemProperty("line.separator", "\n"); + + private String templateSourceName; + private String templateLookupName; + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. + * This constructor calls its super class with the empty string + * to force the "toString" method of parent class "Throwable" to + * print the error message in the form: + * ParseException: <result of getMessage> + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) { + super(""); + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + lineNumber = currentToken.next.beginLine; + columnNumber = currentToken.next.beginColumn; + endLineNumber = currentToken.next.endLine; + endColumnNumber = currentToken.next.endColumn; + } + + /** + * Used by JavaCC generated code. + */ + protected ParseException() { + super(); + } + + /** + * @since 2.3.21 + */ + public ParseException(String description, Template template, + int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber) { + this(description, template, lineNumber, columnNumber, endLineNumber, endColumnNumber, null); + } + + /** + * @since 2.3.21 + */ + public ParseException(String description, Template template, + int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + Throwable cause) { + super(description); // but we override getMessage, so it will be different + try { + initCause(cause); + } catch (Exception e) { + // Suppressed; we can't do more + } + this.description = description; + if (template != null) { // Allowed because sometimes the template is set later via setTemplate(Template) + templateSourceName = template.getSourceName(); + templateLookupName = template.getLookupName(); + } + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.endLineNumber = endLineNumber; + this.endColumnNumber = endColumnNumber; + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, Template template, Token tk) { + this(description, template, tk, null); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, Template template, Token tk, Throwable cause) { + this(description, + template, + tk.beginLine, tk.beginColumn, + tk.endLine, tk.endColumn, + cause); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, ASTNode astNode) { + this(description, astNode, null); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, ASTNode astNode, Throwable cause) { + this(description, + astNode.getTemplate(), + astNode.beginLine, astNode.beginColumn, + astNode.endLine, astNode.endColumn, + cause); + } + + /** + * Should be used internally only; sets the name of the template that contains the error. + * This is needed as the constructor that JavaCC automatically calls doesn't pass in the template, so we + * set it somewhere later in an exception handler. + */ + public void setTemplate(Template template) { + _NullArgumentException.check("template", template); + templateSourceName = template.getSourceName(); + templateLookupName = template.getLookupName(); + synchronized (this) { + messageAndDescriptionRendered = false; + message = null; + } + } + + /** + * Returns the error location plus the error description. + * + * @see #getDescription() + * @see #getTemplateSourceName() + * @see #getTemplateLookupName() + * @see #getLineNumber() + * @see #getColumnNumber() + */ + @Override + public String getMessage() { + synchronized (this) { + if (messageAndDescriptionRendered) return message; + } + renderMessageAndDescription(); + synchronized (this) { + return message; + } + } + + private String getDescription() { + synchronized (this) { + if (messageAndDescriptionRendered) return description; + } + renderMessageAndDescription(); + synchronized (this) { + return description; + } + } + + /** + * Returns the description of the error without error location or source quotations, or {@code null} if there's no + * description available. This is useful in editors (IDE-s) where the error markers and the editor window itself + * already carry this information, so it's redundant the repeat in the error dialog. + */ + public String getEditorMessage() { + return getDescription(); + } + + /** + * Returns the {@linkplain Template#getLookupName()} lookup name} of the template whose parsing was failed. + * Maybe {@code null}, for example if this is a non-stored template. + */ + public String getTemplateLookupName() { + return templateLookupName; + } + + /** + * Returns the {@linkplain Template#getSourceName()} source name} of the template whose parsing was failed. + * Maybe {@code null}, for example if this is a non-stored template. + */ + public String getTemplateSourceName() { + return templateSourceName; + } + + /** + * Returns the {@linkplain #getTemplateSourceName() template source name}, or if that's {@code null} then the + * {@linkplain #getTemplateLookupName() template lookup name}. This name is primarily meant to be used in error + * messages. + */ + public String getTemplateSourceOrLookupName() { + return getTemplateSourceName() != null ? getTemplateSourceName() : getTemplateLookupName(); + } + + /** + * 1-based line number of the failing section, or 0 is the information is not available. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * 1-based column number of the failing section, or 0 is the information is not available. + */ + public int getColumnNumber() { + return columnNumber; + } + + /** + * 1-based line number of the last line that contains the failing section, or 0 if the information is not available. + * + * @since 2.3.21 + */ + public int getEndLineNumber() { + return endLineNumber; + } + + /** + * 1-based column number of the last character of the failing section, or 0 if the information is not available. + * Note that unlike with Java string API-s, this column number is inclusive. + * + * @since 2.3.21 + */ + public int getEndColumnNumber() { + return endColumnNumber; + } + + private void renderMessageAndDescription() { + String desc = getOrRenderDescription(); + + String prefix; + if (!isInJBossToolsMode()) { + prefix = "Syntax error " + + MessageUtil.formatLocationForSimpleParsingError(getTemplateSourceOrLookupName(), lineNumber, + columnNumber) + + ":\n"; + } else { + prefix = "[col. " + columnNumber + "] "; + } + + String msg = prefix + desc; + desc = msg.substring(prefix.length()); // so we reuse the backing char[] + + synchronized (this) { + message = msg; + description = desc; + messageAndDescriptionRendered = true; + } + } + + private boolean isInJBossToolsMode() { + if (jbossToolsMode == null) { + try { + jbossToolsMode = Boolean.valueOf( + ParseException.class.getClassLoader().toString().indexOf( + "[org.jboss.ide.eclipse.freemarker:") != -1); + } catch (Throwable e) { + jbossToolsMode = Boolean.FALSE; + } + } + return jbossToolsMode.booleanValue(); + } + + /** + * Returns the description of the error without the error location, or {@code null} if there's no description + * available. + */ + private String getOrRenderDescription() { + synchronized (this) { + if (description != null) return description; // When we already have it from the constructor + } + + String tokenErrDesc; + if (currentToken != null) { + tokenErrDesc = getCustomTokenErrorDescription(); + if (tokenErrDesc == null) { + // The default JavaCC message generation stuff follows. + StringBuilder expected = new StringBuilder(); + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (i != 0) { + expected.append(eol); + } + expected.append(" "); + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + if (j != 0) expected.append(' '); + expected.append(tokenImage[expectedTokenSequences[i][j]]); + } + } + tokenErrDesc = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) tokenErrDesc += " "; + if (tok.kind == 0) { + tokenErrDesc += tokenImage[0]; + break; + } + tokenErrDesc += add_escapes(tok.image); + tok = tok.next; + } + tokenErrDesc += "\", but "; + + if (expectedTokenSequences.length == 1) { + tokenErrDesc += "was expecting:" + eol; + } else { + tokenErrDesc += "was expecting one of:" + eol; + } + tokenErrDesc += expected; + } + } else { + tokenErrDesc = null; + } + return tokenErrDesc; + } + + private String getCustomTokenErrorDescription() { + final Token nextToken = currentToken.next; + final int kind = nextToken.kind; + if (kind == EOF) { + Set/*<String>*/ endNames = new HashSet(); + for (int[] sequence : expectedTokenSequences) { + for (int aSequence : sequence) { + switch (aSequence) { + case END_LIST: + endNames.add("#list"); + break; + case END_SWITCH: + endNames.add("#switch"); + break; + case END_IF: + endNames.add("#if"); + break; + case END_COMPRESS: + endNames.add("#compress"); + break; + case END_MACRO: + endNames.add("#macro"); + case END_FUNCTION: + endNames.add("#function"); + break; + case END_ESCAPE: + endNames.add("#escape"); + break; + case END_NOESCAPE: + endNames.add("#noescape"); + break; + case END_ASSIGN: + endNames.add("#assign"); + break; + case END_LOCAL: + endNames.add("#local"); + break; + case END_GLOBAL: + endNames.add("#global"); + break; + case END_ATTEMPT: + endNames.add("#attempt"); + break; + case CLOSING_CURLY_BRACKET: + endNames.add("\"{\""); + break; + case CLOSE_BRACKET: + endNames.add("\"[\""); + break; + case CLOSE_PAREN: + endNames.add("\"(\""); + break; + case UNIFIED_CALL_END: + endNames.add("@..."); + break; + } + } + } + return "Unexpected end of file reached." + + (endNames.size() == 0 ? "" : " You have an unclosed " + concatWithOrs(endNames) + "."); + } else if (kind == ELSE) { + return "Unexpected directive, \"#else\". " + + "Check if you have a valid #if-#elseif-#else or #list-#else structure."; + } else if (kind == END_IF || kind == ELSE_IF) { + return "Unexpected directive, " + + _StringUtil.jQuote(nextToken) + + ". Check if you have a valid #if-#elseif-#else structure."; + } + return null; + } + + private String concatWithOrs(Set/*<String>*/ endNames) { + StringBuilder sb = new StringBuilder(); + for (Iterator/*<String>*/ it = endNames.iterator(); it.hasNext(); ) { + String endName = (String) it.next(); + if (sb.length() != 0) { + sb.append(" or "); + } + sb.append(endName); + } + return sb.toString(); + } + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuilder retval = new StringBuilder(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java new file mode 100644 index 0000000..719af93 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java @@ -0,0 +1,29 @@ +/* + * 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.core; + +/** + * <b>Don't implement this interface yourself</b>; use the existing implementation(s). This interface is the union of + * {@link ProcessingConfiguration} and {@link ParsingConfiguration}, which is useful for declaring types for values + * that must implement both interfaces. + */ +public interface ParsingAndProcessingConfiguration extends ParsingConfiguration, ProcessingConfiguration { + // No additional method +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java new file mode 100644 index 0000000..0eb9569 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java @@ -0,0 +1,299 @@ +/* + * 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.core; + +import java.nio.charset.Charset; + +import org.apache.freemarker.core.arithmetic.ArithmeticEngine; +import org.apache.freemarker.core.outputformat.MarkupOutputFormat; +import org.apache.freemarker.core.outputformat.OutputFormat; +import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat; +import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat; +import org.apache.freemarker.core.outputformat.impl.XMLOutputFormat; + +/** + * Implemented by FreeMarker core classes (not by you) that provide configuration settings that affect template parsing + * (as opposed to {@linkplain Template#process (Object, Writer) template processing}). <b>New methods may be added + * any time in future FreeMarker versions, so don't try to implement this interface yourself!</b> + * + * @see ProcessingConfiguration + * @see ParsingAndProcessingConfiguration + */ +public interface ParsingConfiguration { + + int AUTO_DETECT_NAMING_CONVENTION = 10; + int LEGACY_NAMING_CONVENTION = 11; + int CAMEL_CASE_NAMING_CONVENTION = 12; + + int AUTO_DETECT_TAG_SYNTAX = 0; + int ANGLE_BRACKET_TAG_SYNTAX = 1; + int SQUARE_BRACKET_TAG_SYNTAX = 2; + + /** + * Don't enable auto-escaping, regardless of what the {@link OutputFormat} is. Note that a {@code + * <#ftl auto_esc=true>} in the template will override this. + */ + int DISABLE_AUTO_ESCAPING_POLICY = 20; + /** + * Enable auto-escaping if the output format supports it and {@link MarkupOutputFormat#isAutoEscapedByDefault()} is + * {@code true}. + */ + int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 21; + /** Enable auto-escaping if the {@link OutputFormat} supports it. */ + int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 22; + + /** + * The template language used; this is often overridden for certain file extension with the + * {@link Configuration#getTemplateConfigurations() templateConfigurations} setting of the {@link Configuration}. + */ + TemplateLanguage getTemplateLanguage(); + + boolean isTemplateLanguageSet(); + + /** + * Determines the syntax of the template files (angle bracket VS square bracket) + * that has no {@code #ftl} in it. The {@code tagSyntax} + * parameter must be one of: + * <ul> + * <li>{@link #AUTO_DETECT_TAG_SYNTAX}: + * use the syntax of the first FreeMarker tag (can be anything, like <tt>#list</tt>, + * <tt>#include</tt>, user defined, etc.) + * <li>{@link #ANGLE_BRACKET_TAG_SYNTAX}: + * use the angle bracket syntax (the normal syntax) + * <li>{@link #SQUARE_BRACKET_TAG_SYNTAX}: + * use the square bracket syntax + * </ul> + * + * <p>In FreeMarker 2.3.x {@link #ANGLE_BRACKET_TAG_SYNTAX} is the + * default for better backward compatibility. Starting from 2.4.x {@link + * ParsingConfiguration#AUTO_DETECT_TAG_SYNTAX} is the default, so it's recommended to use + * that even for 2.3.x. + * + * <p>This setting is ignored for the templates that have {@code ftl} directive in + * it. For those templates the syntax used for the {@code ftl} directive determines + * the syntax. + */ + int getTagSyntax(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isTagSyntaxSet(); + + /** + * The naming convention used for the identifiers that are part of the template language. The available naming + * conventions are legacy (directive (tag) names are all-lower-case {@code likethis}, others are snake case + * {@code like_this}), and camel case ({@code likeThis}). The default is auto-detect, which detects the naming + * convention used and enforces that same naming convention for the whole template. + * + * <p> + * This setting doesn't influence what naming convention is used for the setting names outside templates. Also, it + * won't ever convert the names of user-defined things, like of data-model members, or the names of user defined + * macros/functions. It only influences the names of the built-in directives ({@code #elseIf} VS {@code elseif}), + * built-ins ({@code ?upper_case} VS {@code ?upperCase} ), special variables ({@code .data_model} VS + * {@code .dataModel}). + * + * <p> + * Which convention to use: FreeMarker prior to 2.3.23 has only supported + * {@link #LEGACY_NAMING_CONVENTION}, so that's how most templates and examples out there are written + * as of 2015. But as templates today are mostly written by programmers and often access Java API-s which already + * use camel case, {@link #CAMEL_CASE_NAMING_CONVENTION} is the recommended option for most projects. + * However, it's no necessary to make a application-wide decision; see auto-detection below. + * + * <p> + * FreeMarker will decide the naming convention automatically for each template individually when this setting is + * set to {@link #AUTO_DETECT_NAMING_CONVENTION} (which is the default). The naming convention of a template is + * decided when the first core (non-user-defined) identifier is met during parsing (not during processing) where the + * naming convention is relevant (like for {@code s?upperCase} or {@code s?upper_case} it's relevant, but for + * {@code s?length} it isn't). At that point, the naming convention of the template is decided, and any later core + * identifier that uses a different convention will be a parsing error. As the naming convention is decided per + * template, it's not a problem if a template and the other template it {@code #include}-s/{@code #import} uses a + * different convention. + * + * <p> + * FreeMarker always enforces the same naming convention to be used consistently within the same template "file". + * Additionally, when this setting is set to non-{@link #AUTO_DETECT_NAMING_CONVENTION}, the selected naming + * convention is enforced on all templates. Thus such a setup can be used to enforce an application-wide naming + * convention. + * + * @return + * One of the {@link #AUTO_DETECT_NAMING_CONVENTION} or + * {@link #LEGACY_NAMING_CONVENTION} or {@link #CAMEL_CASE_NAMING_CONVENTION}. + */ + int getNamingConvention(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isNamingConventionSet(); + + /** + * Whether the template parser will try to remove superfluous white-space around certain tags. + */ + boolean getWhitespaceStripping(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isWhitespaceStrippingSet(); + + /** + * Overlaps with {@link ProcessingConfiguration#getArithmeticEngine()}; the parser needs this for creating numerical + * literals. + */ + ArithmeticEngine getArithmeticEngine(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isArithmeticEngineSet(); + + /** + * See {@link Configuration#getAutoEscapingPolicy()}. + */ + int getAutoEscapingPolicy(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isAutoEscapingPolicySet(); + + /** + * The output format to use, which among others influences auto-escaping (see {@link #getAutoEscapingPolicy} + * autoEscapingPolicy}), and possibly the MIME type of the output. + * <p> + * On the {@link Configuration} level, usually, you should leave this on its default, which is + * {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML) or "ftlx" + * (for XML) (and ensure that {@link #getRecognizeStandardFileExtensions() recognizeStandardFileExtensions} is + * {@code true}; see more there). Where you can't use the standard extensions, templates still can be associated + * to output formats with patterns matching their name (their path) using the + * {@link Configuration#getTemplateConfigurations() templateConfigurations} setting of the {@link Configuration}. + * But if all templates will have the same output format, you may set the + * {@link #getOutputFormat() outputFormat} setting of the {@link Configuration} + * after all, to a value like {@link HTMLOutputFormat#INSTANCE}, {@link XMLOutputFormat#INSTANCE}, etc. Also + * note that templates can specify their own output format like {@code <#ftl output_format="HTML">}, which + * overrides any configuration settings. + * + * @see Configuration#getRegisteredCustomOutputFormats() + * @see Configuration#getTemplateConfigurations() + * @see #getRecognizeStandardFileExtensions() + * @see #getAutoEscapingPolicy() + */ + OutputFormat getOutputFormat(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isOutputFormatSet(); + + /** + * Tells if the "file" extension part of the source name ({@link Template#getSourceName()}) will influence certain + * parsing settings. For backward compatibility, it defaults to {@code false} if + * {@link #getIncompatibleImprovements()} is less than 2.3.24. Starting from {@code incompatibleImprovements} + * 2.3.24, it defaults to {@code true}, so the following standard file extensions take their effect: + * + * <ul> + * <li>{@code ftlh}: Sets the {@link #getOutputFormat() outputFormat} setting to {@code "HTML"} + * (i.e., {@link HTMLOutputFormat#INSTANCE}, unless the {@code "HTML"} name is overridden by + * the {@link Configuration#getRegisteredCustomOutputFormats registeredOutputFormats} setting) and + * the {@link #getAutoEscapingPolicy() autoEscapingPolicy} setting to + * {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. + * <li>{@code ftlx}: Sets the {@link #getOutputFormat() outputFormat} setting to + * {@code "XML"} (i.e., {@link XMLOutputFormat#INSTANCE}, unless the {@code "XML"} name is overridden by + * the {@link Configuration#getRegisteredCustomOutputFormats registeredOutputFormats} setting) and + * the {@link #getAutoEscapingPolicy() autoEscapingPolicy} setting to + * {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}. + * </ul> + * + * <p>These file extensions are not case sensitive. The file extension is the part after the last dot in the source + * name. If the source name contains no dot, then it has no file extension. + * + * <p>The settings activated by these file extensions override the setting values dictated by the + * {@link Configuration#getTemplateConfigurations templateConfigurations} setting of the {@link Configuration}. + */ + boolean getRecognizeStandardFileExtensions(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isRecognizeStandardFileExtensionsSet(); + + /** + * See {@link TopLevelConfiguration#getIncompatibleImprovements()}; this is normally directly delegates to + * {@link Configuration#getIncompatibleImprovements()}, and it's always set. + */ + Version getIncompatibleImprovements(); + + /** + * The assumed display width of the tab character (ASCII 9), which influences the column number shown in error + * messages (or the column number you get through other API-s). So for example if the users edit templates in an + * editor where the tab width is set to 4, you should set this to 4 so that the column numbers printed by FreeMarker + * will match the column number shown in the editor. This setting doesn't affect the output of templates, as a tab + * in the template will remain a tab in the output too. + * It's value is at least 1, at most 256. + */ + int getTabSize(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isTabSizeSet(); + + /** + * Sets the charset used for decoding template files. + * <p> + * Defaults to the default system {@code fileEncoding}, which can change from one server to + * another, so <b>you should always set this setting</b>. If you don't know what charset your should chose, + * {@code "UTF-8"} is usually a good choice. + * <p> + * When a project contains groups (like folders) of template files where the groups use different encodings, + * consider using the {@link Configuration#getTemplateConfigurations() templateConfigurations} setting on the + * {@link Configuration} level. + * <p> + * Individual templates may specify their own charset by starting with + * <tt><#ftl sourceEncoding="..."></tt>. However, before that's detected, at least part of template must be + * decoded with some charset first, so this setting (and + * {@link Configuration#getTemplateConfigurations() templateConfigurations}) still have role. + */ + Charset getSourceEncoding(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting might returns a default value, or returns the value of the setting from a parent parsing + * configuration or throws a {@link SettingValueNotSetException}. + */ + boolean isSourceEncodingSet(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java new file mode 100644 index 0000000..545a313 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java @@ -0,0 +1,704 @@ +/* + * 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.core; + +import java.io.Writer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.freemarker.core.arithmetic.ArithmeticEngine; +import org.apache.freemarker.core.arithmetic.impl.BigDecimalArithmeticEngine; +import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory; +import org.apache.freemarker.core.valueformat.TemplateNumberFormat; +import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory; + +/** + * Implemented by FreeMarker core classes (not by you) that provide configuration settings that affect {@linkplain + * Template#process(Object, Writer) template processing} (as opposed to template parsing). <b>New methods may be added + * any time in future FreeMarker versions, so don't try to implement this interface yourself!</b> + * + * @see ParsingConfiguration + * @see ParsingAndProcessingConfiguration + */ +public interface ProcessingConfiguration { + + /** + * The locale used for number and date formatting (among others), also the locale used for searching localized + * template variations when no locale was explicitly specified where the template is requested. + * + * @see Configuration#getTemplate(String, Locale) + */ + Locale getLocale(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isLocaleSet(); + + /** + * The time zone to use when formatting date/time values. It {@link Configuration}-level default + * is the system time zone ({@link TimeZone#getDefault()}), regardless of the "locale" FreeMarker setting, + * so in a server application you probably want to set it explicitly in the {@link Environment} to match the + * preferred time zone of the target audience (like the Web page visitor). + * + * <p>If you or the templates set the time zone, you should probably also set + * {@link #getSQLDateAndTimeTimeZone()}! + * + * @see #getSQLDateAndTimeTimeZone() + */ + TimeZone getTimeZone(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isTimeZoneSet(); + + /** + * The time zone used when dealing with {@link java.sql.Date java.sql.Date} and + * {@link java.sql.Time java.sql.Time} values. Its {@link Configuration}-level defaults is {@code null} for + * backward compatibility, but in most applications this should be set to the JVM default time zone (server + * default time zone), because that's what most JDBC drivers will use when constructing the + * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values. If this setting is {@code + * null} FreeMarker will use the value of ({@link #getTimeZone()}) for {@link java.sql.Date java.sql.Date} and + * {@link java.sql.Time java.sql.Time} values, which often gives bad results. + * + * <p>This setting doesn't influence the formatting of other kind of values (like of + * {@link java.sql.Timestamp java.sql.Timestamp} or plain {@link java.util.Date java.util.Date} values). + * + * <p>To decide what value you need, a few things has to be understood: + * <ul> + * <li>Date-only and time-only values in SQL-oriented databases are usually store calendar and clock field + * values directly (year, month, day, or hour, minute, seconds (with decimals)), as opposed to a set of points + * on the physical time line. Thus, unlike SQL timestamps, these values usually aren't meant to be shown + * differently depending on the time zone of the audience. + * + * <li>When a JDBC query has to return a date-only or time-only value, it has to convert it to a point on the + * physical time line, because that's what {@link java.util.Date} and its subclasses store (milliseconds since + * the epoch). Obviously, this is impossible to do. So JDBC just chooses a physical time which, when rendered + * <em>with the JVM default time zone</em>, will give the same field values as those stored + * in the database. (Actually, you can give JDBC a calendar, and so it can use other time zones too, but most + * application won't care using those overloads.) For example, assume that the system time zone is GMT+02:00. + * Then, 2014-07-12 in the database will be translated to physical time 2014-07-11 22:00:00 UTC, because that + * rendered in GMT+02:00 gives 2014-07-12 00:00:00. Similarly, 11:57:00 in the database will be translated to + * physical time 1970-01-01 09:57:00 UTC. Thus, the physical time stored in the returned value depends on the + * default system time zone of the JDBC client, not just on the content of the database. (This used to be the + * default behavior of ORM-s, like Hibernate, too.) + * + * <li>The value of the {@code time_zone} FreeMarker configuration setting sets the time zone used for the + * template output. For example, when a web page visitor has a preferred time zone, the web application framework + * may calls {@link Environment#setTimeZone(TimeZone)} with that time zone. Thus, the visitor will + * see {@link java.sql.Timestamp java.sql.Timestamp} and plain {@link java.util.Date java.util.Date} values as + * they look in his own time zone. While + * this is desirable for those types, as they meant to represent physical points on the time line, this is not + * necessarily desirable for date-only and time-only values. When {@code sql_date_and_time_time_zone} is + * {@code null}, {@code time_zone} is used for rendering all kind of date/time/dateTime values, including + * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time}, and then if, for example, + * {@code time_zone} is GMT+00:00, the + * values from the earlier examples will be shown as 2014-07-11 (one day off) and 09:57:00 (2 hours off). While + * those are the time zone correct renderings, those values are probably meant to be shown "as is". + * + * <li>You may wonder why this setting isn't simply "SQL time zone", that is, why's this time zone not applied to + * {@link java.sql.Timestamp java.sql.Timestamp} values as well. Timestamps in databases refer to a point on + * the physical time line, and thus doesn't have the inherent problem of date-only and time-only values. + * FreeMarker assumes that the JDBC driver converts time stamps coming from the database so that they store + * the distance from the epoch (1970-01-01 00:00:00 UTC), as requested by the {@link java.util.Date} API. + * Then time stamps can be safely rendered in different time zones, and thus need no special treatment. + * </ul> + * + * @see #getTimeZone() + */ + TimeZone getSQLDateAndTimeTimeZone(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isSQLDateAndTimeTimeZoneSet(); + + /** + * The number format used to convert numbers to strings (where no number format is explicitly given). Its + * {@link Configuration}-level default is {@code "number"}. The possible values are: + * <ul> + * <li>{@code "number"}: The number format returned by {@link NumberFormat#getNumberInstance(Locale)}</li> + * <li>{@code "currency"}: The number format returned by {@link NumberFormat#getCurrencyInstance(Locale)}</li> + * <li>{@code "percent"}: The number format returned by {@link NumberFormat#getPercentInstance(Locale)}</li> + * <li>{@code "computer"}: The number format used by FTL's {@code c} built-in (like in {@code someNumber?c}).</li> + * <li>A {@link java.text.DecimalFormat} pattern (like {@code "0.##"}). This syntax is extended by FreeMarker + * so that you can specify options like the rounding mode and the symbols used after a 2nd semicolon. For + * example, {@code ",000;; roundingMode=halfUp groupingSeparator=_"} will format numbers like {@code ",000"} + * would, but with half-up rounding mode, and {@code _} as the group separator. See more about "extended Java + * decimal format" in the FreeMarker Manual. + * </li> + * <li>If the string starts with {@code @} character followed by a letter then it's interpreted as a custom number + * format. The format of a such string is <code>"@<i>name</i>"</code> or <code>"@<i>name</i> + * <i>parameters</i>"</code>, where <code><i>name</i></code> is the key in the {@link Map} set by + * {@link MutableProcessingConfiguration#setCustomNumberFormats(Map)}, and <code><i>parameters</i></code> is + * parsed by the custom {@link TemplateNumberFormat}. + * </li> + * </ul> + */ + String getNumberFormat(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isNumberFormatSet(); + + /** + * A {@link Map} that associates {@link TemplateNumberFormatFactory}-es to names, which then can be referred by the + * {@link #getNumberFormat() number_format} setting with values starting with <code>@<i>name</i></code>. The keys in + * the {@link Map} should start with an UNICODE letter, and should only contain UNICODE letters and digits (not + * {@code _}), otherwise accessing the custom format from templates can be difficult or impossible. The + * {@link Configuration}-level default of this setting is an empty {@link Map}. + * <p> + * When the {@link ProcessingConfiguration} is part of a setting inheritance chain ({@link Environment} inherits + * settings from the main {@link Template}, which inherits from the {@link Configuration}), you still only get the + * {@link Map} from the closest {@link ProcessingConfiguration} where it was set, not a {@link Map} that respects + * inheritance. Thus, to get a custom format you shouldn't use this {@link Map} directly, but + * {@link #getCustomNumberFormat(String)}, which will search the format in the inheritance chain. + * + * @return Never {@code null}. Unless the method was called on a builder class, the returned {@link Map} shouldn't + * be modified. + */ + Map<String, TemplateNumberFormatFactory> getCustomNumberFormats(); + + /** + * Gets the custom number format registered for the name. This differs from calling {@link #getCustomNumberFormats() + * getCustomNumberFormats().get(name)}, because if there's {@link ProcessingConfiguration} from which setting values + * are inherited then this method will search the custom format there as well if it isn't found here. For example, + * {@link Environment#getCustomNumberFormat(String)} will check if the {@link Environment} contains the custom + * format with the name, and if not, it will try {@link Template#getCustomNumberFormat(String)} on the main + * template, which in turn might falls back to calling {@link Configuration#getCustomNumberFormat(String)}. + */ + TemplateNumberFormatFactory getCustomNumberFormat(String name); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isCustomNumberFormatsSet(); + + /** + * The string value for the boolean {@code true} and {@code false} values, intended for human audience (not for a + * computer language), separated with comma. For example, {@code "yes,no"}. Note that white-space is significant, + * so {@code "yes, no"} is WRONG (unless you want that leading space before "no"). + * + * <p>For backward compatibility the default is {@code "true,false"}, but using that value is denied for automatic + * boolean-to-string conversion (like <code>${myBoolean}</code> will fail with it), only {@code myBool?string} will + * allow it, which is deprecated since FreeMarker 2.3.20. + */ + String getBooleanFormat(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isBooleanFormatSet(); + + /** + * The format used to convert {@link java.util.Date}-s that are time (no date part) values to string-s, also the + * format that {@code someString?time} will use to parse strings. + * + * <p>For the possible values see {@link #getDateTimeFormat()}. + * + * <p>Its {@link Configuration}-level default is {@code ""}, which is equivalent to {@code "medium"}. + */ + String getTimeFormat(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isTimeFormatSet(); + + /** + * The format used to convert {@link java.util.Date}-s that are date-only (no time part) values to string-s, + * also the format that {@code someString?date} will use to parse strings. + * + * <p>For the possible values see {@link #getDateTimeFormat()}. + * + * <p>Its {@link Configuration}-level default is {@code ""} which is equivalent to {@code "medium"}. + */ + String getDateFormat(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isDateFormatSet(); + + /** + * The format used to convert {@link java.util.Date}-s that are date-time (timestamp) values to string-s, + * also the format that {@code someString?datetime} will use to parse strings. + * + * <p>The possible setting values are (the quotation marks aren't part of the value itself): + * + * <ul> + * <li><p>Patterns accepted by Java's {@link SimpleDateFormat}, for example {@code "dd.MM.yyyy HH:mm:ss"} (where + * {@code HH} means 24 hours format) or {@code "MM/dd/yyyy hh:mm:ss a"} (where {@code a} prints AM or PM, if + * the current language is English). + * + * <li><p>{@code "xs"} for XML Schema format, or {@code "iso"} for ISO 8601:2004 format. + * These formats allow various additional options, separated with space, like in + * {@code "iso m nz"} (or with {@code _}, like in {@code "iso_m_nz"}; this is useful in a case like + * {@code lastModified?string.iso_m_nz}). The options and their meanings are: + * + * <ul> + * <li><p>Accuracy options:<br> + * {@code ms} = Milliseconds, always shown with all 3 digits, even if it's all 0-s. + * Example: {@code 13:45:05.800}<br> + * {@code s} = Seconds (fraction seconds are dropped even if non-0), like {@code 13:45:05}<br> + * {@code m} = Minutes, like {@code 13:45}. This isn't allowed for "xs".<br> + * {@code h} = Hours, like {@code 13}. This isn't allowed for "xs".<br> + * Neither = Up to millisecond accuracy, but trailing millisecond 0-s are removed, also the whole + * milliseconds part if it would be 0 otherwise. Example: {@code 13:45:05.8} + * + * <li><p>Time zone offset visibility options:<br> + * {@code fz} = "Force Zone", always show time zone offset (even for for + * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values). + * But, because ISO 8601 doesn't allow for dates (means date without time of the day) to + * show the zone offset, this option will have no effect in the case of {@code "iso"} with + * dates.<br> + * {@code nz} = "No Zone", never show time zone offset<br> + * Neither = always show time zone offset, except for {@link java.sql.Date java.sql.Date} + * and {@link java.sql.Time java.sql.Time}, and for {@code "iso"} date values. + * + * <li><p>Time zone options:<br> + * {@code u} = Use UTC instead of what the {@code time_zone} setting suggests. However, + * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} aren't affected + * by this (see {@link #getSQLDateAndTimeTimeZone()} to understand why)<br> + * {@code fu} = "Force UTC", that is, use UTC instead of what the {@code time_zone} or the + * {@code sql_date_and_time_time_zone} setting suggests. This also effects + * {@link java.sql.Date java.sql.Date} and {@link java.sql.Time java.sql.Time} values<br> + * Neither = Use the time zone suggested by the {@code time_zone} or the + * {@code sql_date_and_time_time_zone} configuration setting ({@link #getTimeZone()} and + * {@link #getSQLDateAndTimeTimeZone()}). + * </ul> + * + * <p>The options can be specified in any order.</p> + * + * <p>Options from the same category are mutually exclusive, like using {@code m} and {@code s} + * together is an error. + * + * <p>The accuracy and time zone offset visibility options don't influence parsing, only formatting. + * For example, even if you use "iso m nz", "2012-01-01T15:30:05.125+01" will be parsed successfully and with + * milliseconds accuracy. + * The time zone options (like "u") influence what time zone is chosen only when parsing a string that doesn't + * contain time zone offset. + * + * <p>Parsing with {@code "iso"} understands both extend format and basic format, like + * {@code 20141225T235018}. It doesn't, however, support the parsing of all kind of ISO 8601 strings: if + * there's a date part, it must use year, month and day of the month values (not week of the year), and the + * day can't be omitted. + * + * <p>The output of {@code "iso"} is deliberately so that it's also a good representation of the value with + * XML Schema format, except for 0 and negative years, where it's impossible. Also note that the time zone + * offset is omitted for date values in the {@code "iso"} format, while it's preserved for the {@code "xs"} + * format. + * + * <li><p>{@code "short"}, {@code "medium"}, {@code "long"}, or {@code "full"}, which that has locale-dependent + * meaning defined by the Java platform (see in the documentation of {@link java.text.DateFormat}). + * For date-time values, you can specify the length of the date and time part independently, be separating + * them with {@code _}, like {@code "short_medium"}. ({@code "medium"} means + * {@code "medium_medium"} for date-time values.) + * + * <li><p>Anything that starts with {@code "@"} followed by a letter is interpreted as a custom + * date/time/dateTime format, but only if either {@link Configuration#getIncompatibleImprovements()} + * is at least 2.3.24, or there's any custom formats defined (even if custom number format). The format of + * such string is <code>"@<i>name</i>"</code> or <code>"@<i>name</i> <i>parameters</i>"</code>, where + * <code><i>name</i></code> is the name parameter to {@link #getCustomDateFormat(String)}, and + * <code><i>parameters</i></code> is parsed by the custom number format. + * + * </ul> + * + * <p>Its {@link Configuration}-level default is {@code ""}, which is equivalent to {@code "medium_medium"}. + */ + String getDateTimeFormat(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isDateTimeFormatSet(); + + /** + * A {@link Map} that associates {@link TemplateDateFormatFactory}-es to names, which then can be referred by the + * {@link #getDateFormat() date_format}/{@link #getDateFormat() date_format }/{@link #getDateTimeFormat() + * datetime_format} settings with values starting with <code>@<i>name</i></code>. The keys in the {@link Map} should + * start with an UNICODE letter, and should only contain UNICODE letters and digits (not {@code _}), otherwise + * accessing the custom format from templates can be difficult or impossible. The {@link Configuration}-level + * default of this setting is an empty {@link Map}. + * <p> + * When the {@link ProcessingConfiguration} is part of a setting inheritance chain ({@link Environment} inherits + * settings from the main {@link Template}, which inherits from the {@link Configuration}), you still only get the + * {@link Map} from the closest {@link ProcessingConfiguration} where it was set, not a {@link Map} that respects + * inheritance. Thus, to get a custom format you shouldn't use this {@link Map} directly, but {@link + * #getCustomDateFormat(String)}, which will search the format in the inheritance chain. + * + * @return Never {@code null}. Unless the method was called on a builder class, the returned {@link Map} shouldn't + * be modified. + */ + Map<String, TemplateDateFormatFactory> getCustomDateFormats(); + + /** + * Gets the custom date or time or date-time format registered for the name. This differs from calling {@link + * #getCustomDateFormats() getCustomDateFormats.get(name)}, because if there's {@link ProcessingConfiguration} from + * which setting values are inherited then this method will search the custom format there as well if it isn't found + * here. For example, {@link Environment#getCustomNumberFormat(String)} will check if the {@link Environment} + * contains the custom format with the name, and if not, it will try {@link Template#getCustomDateFormat(String)} on + * the main template, which in turn might falls back to calling {@link Configuration#getCustomDateFormat(String)}. + */ + TemplateDateFormatFactory getCustomDateFormat(String name); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isCustomDateFormatsSet(); + + /** + * The exception handler used to handle exceptions occurring inside templates. + * Its {@link Configuration}-level default is {@link TemplateExceptionHandler#DEBUG_HANDLER}. The recommended + * values are: + * + * <ul> + * <li>In production systems: {@link TemplateExceptionHandler#RETHROW_HANDLER} + * <li>During development of HTML templates: {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER} + * <li>During development of non-HTML templates: {@link TemplateExceptionHandler#DEBUG_HANDLER} + * </ul> + * + * <p>All of these will let the exception propagate further, so that you can catch it around + * {@link Template#process(Object, Writer)} for example. The difference is in what they print on the output before + * they do that. + * + * <p>Note that the {@link TemplateExceptionHandler} is not meant to be used for generating HTTP error pages. + * Neither is it meant to be used to roll back the printed output. These should be solved outside template + * processing when the exception raises from {@link Template#process(Object, Writer) Template.process}. + * {@link TemplateExceptionHandler} meant to be used if you want to include special content <em>in</em> the template + * output, or if you want to suppress certain exceptions. + */ + TemplateExceptionHandler getTemplateExceptionHandler(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isTemplateExceptionHandlerSet(); + + /** + * The arithmetic engine used to perform arithmetic operations. + * Its {@link Configuration}-level default is {@link BigDecimalArithmeticEngine#INSTANCE}. + * Note that this setting overlaps with {@link ParsingConfiguration#getArithmeticEngine()}. + */ + ArithmeticEngine getArithmeticEngine(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isArithmeticEngineSet(); + + /** + * The object wrapper used to wrap objects to {@link TemplateModel}-s. + * Its {@link Configuration}-level default is a {@link DefaultObjectWrapper} with all its setting on default + * values, and {@code incompatibleImprovements} set to {@link Configuration#getIncompatibleImprovements()}. + */ + ObjectWrapper getObjectWrapper(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isObjectWrapperSet(); + + /** + * Informs FreeMarker about the charset used for the output. As FreeMarker outputs character stream (not + * byte stream), it's not aware of the output charset unless the software that encloses it tells it + * with this setting. Some templates may use FreeMarker features that require this information. + * Setting this to {@code null} means that the output encoding is not known. + * + * <p>Its {@link Configuration}-level default is {@code null}. + */ + Charset getOutputEncoding(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isOutputEncodingSet(); + + /** + * The URL escaping (URL encoding, percentage encoding) charset. If ({@code null}), the output encoding + * ({@link #getOutputEncoding()}) will be used. Its {@link Configuration}-level default is {@code null}. + */ + Charset getURLEscapingCharset(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isURLEscapingCharsetSet(); + + /** + * The {@link TemplateClassResolver} that is used when the <code>new</code> built-in is called in a template. That + * is, when a template contains the <code>"com.example.SomeClassName"?new</code> expression, this object will be + * called to resolve the <code>"com.example.SomeClassName"</code> string to a class. The default value is {@link + * TemplateClassResolver#UNRESTRICTED_RESOLVER}. If you allow users to upload templates, it's important to use a + * custom restrictive {@link TemplateClassResolver} or {@link TemplateClassResolver#ALLOWS_NOTHING_RESOLVER}. + */ + TemplateClassResolver getNewBuiltinClassResolver(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isNewBuiltinClassResolverSet(); + + /** + * Specifies if {@code ?api} can be used in templates. Its {@link Configuration}-level is {@code false} (which + * is the safest option). + */ + boolean getAPIBuiltinEnabled(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isAPIBuiltinEnabledSet(); + + /** + * Whether the output {@link Writer} is automatically flushed at the end of {@link Template#process(Object, Writer)} + * (and its overloads). Its {@link Configuration}-level default is {@code true}. + * <p> + * Using {@code false} is needed for example when a Web page is composed from several boxes (like portlets, GUI + * panels, etc.) that aren't inserted with <tt>#include</tt> (or with similar directives) into a master FreeMarker + * template, rather they are all processed with a separate {@link Template#process(Object, Writer)} call. In a such + * scenario the automatic flushes would commit the HTTP response after each box, hence interfering with full-page + * buffering, and also possibly decreasing performance with too frequent and too early response buffer flushes. + */ + boolean getAutoFlush(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isAutoFlushSet(); + + /** + * Whether tips should be shown in error messages of errors arising during template processing. + * Its {@link Configuration}-level default is {@code true}. + */ + boolean getShowErrorTips(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isShowErrorTipsSet(); + + /** + * Specifies if {@link TemplateException}-s thrown by template processing are logged by FreeMarker or not. The + * default is {@code true} for backward compatibility, but that results in logging the exception twice in properly + * written applications, because there the {@link TemplateException} thrown by the public FreeMarker API is also + * logged by the caller (even if only as the cause exception of a higher level exception). Hence, in modern + * applications it should be set to {@code false}. Note that this setting has no effect on the logging of exceptions + * caught by {@code #attempt}; those are always logged, no mater what (because those exceptions won't bubble up + * until the API caller). + */ + boolean getLogTemplateExceptions(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isLogTemplateExceptionsSet(); + + /** + * Specifies if {@code <#import ...>} (and {@link Environment#importLib(String, String)}) should delay the loading + * and processing of the imported templates until the content of the imported namespace is actually accessed. This + * makes the overhead of <em>unused</em> imports negligible. A drawback is that importing a missing or otherwise + * broken template will be successful, and the problem will remain hidden until (and if) the namespace content is + * actually used. Also, you lose the strict control over when the namespace initializing code in the imported + * template will be executed, though it shouldn't mater for well written imported templates anyway. Note that the + * namespace initializing code will run with the same {@linkplain #getLocale() locale} as it was at the + * point of the {@code <#import ...>} call (other settings won't be handled specially like that). + * <p> + * The default is {@code false} (and thus imports are eager) for backward compatibility, which can cause + * perceivable overhead if you have many imports and only a few of them is used. + * <p> + * This setting also affects {@linkplain #getAutoImports() auto-imports}, unless you have set a non-{@code null} + * value with {@link #getLazyAutoImports()}. + * + * @see #getLazyAutoImports() + */ + boolean getLazyImports(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isLazyImportsSet(); + + /** + * Specifies if {@linkplain #getAutoImports() auto-imports} will be + * {@link #getLazyImports() lazy imports}. This is useful to make the overhead of <em>unused</em> + * auto-imports negligible. If this is set to {@code null}, {@link #getLazyImports()} specifies the behavior of + * auto-imports too. The default value is {@code null}. + */ + Boolean getLazyAutoImports(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isLazyAutoImportsSet(); + + /** + * Adds invisible <code>#import <i>templateName</i> as <i>namespaceVarName</i></code> statements at the beginning of + * the main template (that's the top-level template that wasn't included/imported from another template). While + * it only affects the main template directly, as the imports will create a global variable there, the imports + * will be visible from the further imported templates too. + * <p> + * It's recommended to set the {@link Configuration#getLazyAutoImports() lazyAutoImports} setting to {@code true} + * when using this, so that auto-imports that are unused in a template won't degrade performance by unnecessary + * loading and initializing the imported library. + * <p> + * If the imports aren't lazy, the order of the imports will be the same as the order in which the {@link Map} + * iterates through its entries. + * <p> + * When the {@link ProcessingConfiguration} is part of a setting inheritance chain ({@link Environment} inherits + * settings from the main {@link Template}, which inherits from the {@link Configuration}), you still only get the + * {@link Map} from the closest {@link ProcessingConfiguration} where it was set, not a {@link Map} that respects + * inheritance. But FreeMarker will walk the whole inheritance chain, executing all auto-imports starting + * from the ancestors. If, however, the same auto-import <code><i>namespaceVarName</i></code> occurs in multiple + * {@link ProcessingConfiguration}-s of the chain, only the one in the last (child) + * {@link ProcessingConfiguration} will be executed. + * <p> + * If there are also auto-includes (see {@link #getAutoIncludes()}), those will be executed after the auto-imports. + * <p> + * The {@link Configuration}-level default of this setting is an empty {@link Map}. + * + * @return Never {@code null} + */ + Map<String, String> getAutoImports(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isAutoImportsSet(); + + /** + * Adds an invisible <code>#include <i>templateName</i></code> at the beginning of the main template (that's the + * top-level template that wasn't included/imported from another template). + * <p> + * The order of the inclusions will be the same as the order in this {@link List}. + * <p> + * When the {@link ProcessingConfiguration} is part of a setting inheritance chain ({@link Environment} inherits + * settings from the main {@link Template}, which inherits from the {@link Configuration}), you still only get the + * {@link List} from the closest {@link ProcessingConfiguration} where it was set, not a {@link List} that respects + * inheritance. But FreeMarker will walk the whole inheritance chain, executing all auto-imports starting + * from the ancestors. If, however, the same auto-included template name occurs in multiple + * {@link ProcessingConfiguration}-s of the chain, only the one in the last (child) + * {@link ProcessingConfiguration} will be executed. + * <p> + * If there are also auto-imports ({@link #getAutoImports()}), those imports will be executed before + * the auto-includes, hence the namespace variables are alrady accessible for the auto-included templates. + */ + List<String> getAutoIncludes(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isAutoIncludesSet(); + + /** + * The {@code Map} of custom attributes. Custom attributes are key-value pairs associated to a + * {@link ProcessingConfiguration} objects, which meant to be used for storing application or framework specific + * configuration settings. The FreeMarker core doesn't define any attributes. Note that to store + * {@link ProcessingConfiguration}-scoped state (such as application or framework specific caches) you should use + * the methods provided by the {@link CustomStateScope} instead. + * <p> + * When the {@link ProcessingConfiguration} is part of a setting inheritance chain ({@link Environment} inherits + * settings from the main {@link Template}, which inherits from the {@link Configuration}), you still only get the + * {@link Map} from the closest {@link ProcessingConfiguration} where it was set, not a {@link Map} that respects + * inheritance. Thus to get attributes, you shouldn't use this {@link Map} directly, but + * {@link #getCustomAttribute(Object)} that will search the custom attribute in the whole inheritance chain. + */ + Map<Object, Object> getCustomAttributes(); + + /** + * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading + * the setting mights returns a default value, or returns the value of the setting from a parent object, or throws + * an {@link SettingValueNotSetException}. + */ + boolean isCustomAttributesSet(); + + /** + * Retrieves a custom attribute for this {@link ProcessingConfiguration}. If the attribute is not present in the + * {@link ProcessingConfiguration}, but it inherits from another {@link ProcessingConfiguration}, then the attribute + * is searched the as well. + * + * @param key + * the identifier (usually a name) of the custom attribute + * + * @return the value of the custom attribute. Note that if the custom attribute was created with + * <tt><#ftl attributes={...}></tt>, then this value is already unwrapped (i.e. it's a + * <code>String</code>, or a <code>List</code>, or a <code>Map</code>, ...etc., not a FreeMarker specific class). + */ + Object getCustomAttribute(Object key); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java new file mode 100644 index 0000000..45f9345 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java @@ -0,0 +1,59 @@ +/* + * 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.core; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.apache.freemarker.core.model.impl.SimpleNumber; + +abstract class RangeModel implements TemplateSequenceModel, java.io.Serializable { + + private final int begin; + + public RangeModel(int begin) { + this.begin = begin; + } + + final int getBegining() { + return begin; + } + + @Override + final public TemplateModel get(int index) throws TemplateModelException { + if (index < 0 || index >= size()) { + throw new _TemplateModelException("Range item index ", Integer.valueOf(index), " is out of bounds."); + } + long value = begin + getStep() * (long) index; + return value <= Integer.MAX_VALUE ? new SimpleNumber((int) value) : new SimpleNumber(value); + } + + /** + * @return {@code 1} or {@code -1}; other return values need not be properly handled until FTL supports other steps. + */ + abstract int getStep(); + + abstract boolean isRightUnbounded(); + + abstract boolean isRightAdaptive(); + + abstract boolean isAffactedByStringSlicingBug(); + +}
