Repository: incubator-freemarker Updated Branches: refs/heads/3 0356b30b0 -> 18a4ace82
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/ParserConfiguration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParserConfiguration.java b/src/main/java/org/apache/freemarker/core/ParserConfiguration.java deleted file mode 100644 index 87bab74..0000000 --- a/src/main/java/org/apache/freemarker/core/ParserConfiguration.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 org.apache.freemarker.core.arithmetic.ArithmeticEngine; -import org.apache.freemarker.core.outputformat.OutputFormat; - -/** - * <b>Don't implement this interface yourself</b>; use the existing implementation(s). This interface is implemented by - * classes that hold settings that affect template parsing (as opposed to {@linkplain Template#process(Object, Writer) - * template processing}). New parser settings can be added in new FreeMarker versions, which will break your - * implementation. - * - * @see ProcessingConfiguration - */ -public interface ParserConfiguration { - - TemplateLanguage getTemplateLanguage(); - - boolean isTemplateLanguageSet(); - - /** - * See {@link Configuration#getTagSyntax()}. - */ - int getTagSyntax(); - - /** - * 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 isTagSyntaxSet(); - - /** - * See {@link Configuration#getNamingConvention()}. - */ - int getNamingConvention(); - - /** - * 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 isNamingConventionSet(); - - /** - * See {@link Configuration#getWhitespaceStripping()}. - */ - boolean getWhitespaceStripping(); - - /** - * 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 isWhitespaceStrippingSet(); - - /** - * Overlaps with {@link MutableProcessingConfiguration#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 mights returns a default value, or returns the value of the setting from a parent object, or throws - * an {@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 mights returns a default value, or returns the value of the setting from a parent object, or throws - * an {@link SettingValueNotSetException}. - */ - boolean isAutoEscapingPolicySet(); - - /** - * See {@link Configuration#getOutputEncoding()}. - */ - OutputFormat getOutputFormat(); - - /** - * 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 isOutputFormatSet(); - - /** - * See {@link Configuration#getRecognizeStandardFileExtensions()}. - */ - boolean getRecognizeStandardFileExtensions(); - - /** - * 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 isRecognizeStandardFileExtensionsSet(); - - /** - * See {@link Configuration#getIncompatibleImprovements()}; as this is normally directly delegates to - * {@link Configuration#getIncompatibleImprovements()}, it's always set. - */ - Version getIncompatibleImprovements(); - - /** - * See {@link Configuration#getTabSize()}. - * - * @since 2.3.25 - */ - int getTabSize(); - - /** - * 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 isTabSizeSet(); - - /** - * Gets the default encoding for converting bytes to characters when - * reading template files in a locale for which no explicit encoding - * was specified. Defaults to the default system encoding. - */ - Charset getSourceEncoding(); - - boolean isSourceEncodingSet(); - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java b/src/main/java/org/apache/freemarker/core/ParsingAndProcessingConfiguration.java new file mode 100644 index 0000000..719af93 --- /dev/null +++ b/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/18a4ace8/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java b/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java new file mode 100644 index 0000000..b62daac --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java @@ -0,0 +1,154 @@ +/* + * 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.OutputFormat; + +/** + * 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 + * anytime in future FreeMarker versions, so don't try to implement this interface yourself!</b> + * + * @see ProcessingConfiguration + */ +// TODO Clean up API docs like in ProcessingConfiguration, when Configuration is done +public interface ParsingConfiguration { + + TemplateLanguage getTemplateLanguage(); + + boolean isTemplateLanguageSet(); + + /** + * See {@link Configuration#getTagSyntax()}. + */ + 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(); + + /** + * See {@link Configuration#getNamingConvention()}. + */ + 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(); + + /** + * See {@link Configuration#getWhitespaceStripping()}. + */ + 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(); + + /** + * See {@link Configuration#getOutputEncoding()}. + */ + 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(); + + /** + * See {@link Configuration#getRecognizeStandardFileExtensions()}. + */ + 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 Configuration#getIncompatibleImprovements()}; as this is normally directly delegates to + * {@link Configuration#getIncompatibleImprovements()}, it's always set. + */ + Version getIncompatibleImprovements(); + + /** + * See {@link Configuration#getTabSize()}. + * + * @since 2.3.25 + */ + 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(); + + /** + * Gets the default encoding for converting bytes to characters when + * reading template files in a locale for which no explicit encoding + * was specified. Defaults to the default system encoding. + */ + Charset getSourceEncoding(); + + boolean isSourceEncodingSet(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java b/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java index b224a01..9dd8dc9 100644 --- a/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java +++ b/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java @@ -21,29 +21,36 @@ 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; /** - * <b>Don't implement this interface yourself</b>; use the existing implementation(s). This interface is implemented by - * classes that hold settings that affect {@linkplain Template#process(Object, Writer) template processing} (as opposed - * to template parsing). New parser settings can be added in new FreeMarker versions, which will break your - * implementation. + * 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 + * anytime in future FreeMarker versions, so don't try to implement this interface yourself!</b> * - * @see ParserConfiguration + * @see ParsingConfiguration */ -// TODO [FM3] JavaDoc public interface ProcessingConfiguration { - /** - * Getter pair of {@link MutableProcessingConfiguration#setLocale(Locale)}. + /** + * 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(); @@ -55,7 +62,15 @@ public interface ProcessingConfiguration { boolean isLocaleSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setTimeZone(TimeZone)}. + * 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(); @@ -67,11 +82,58 @@ public interface ProcessingConfiguration { boolean isTimeZoneSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setSQLDateAndTimeTimeZone(TimeZone)}. + * 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.) * - * @return {@code null} if the value of {@link #getTimeZone()} should be used for formatting {@link java.sql.Date - * java.sql.Date} and {@link java.sql.Time java.sql.Time} values, otherwise the time zone that should be used to - * format the values of those two types. + * <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(); @@ -83,7 +145,26 @@ public interface ProcessingConfiguration { boolean isSQLDateAndTimeTimeZoneSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setNumberFormat(String)}. + * 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(); @@ -95,10 +176,31 @@ public interface ProcessingConfiguration { boolean isNumberFormatSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setCustomNumberFormats(Map)}. + * 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); /** @@ -108,10 +210,21 @@ public interface ProcessingConfiguration { */ boolean isCustomNumberFormatsSet(); + /** + * Tells if {@link #getCustomNumberFormat(String)} or {@link #getCustomDateFormat(String)} (or any other future + * method that returns custom formats) will return a custom format for some name. (Knowing that it's not the case + * allows certain optimizations.) + */ boolean hasCustomFormats(); /** - * Getter pair of {@link MutableProcessingConfiguration#setBooleanFormat(String)}. + * 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(); @@ -123,7 +236,12 @@ public interface ProcessingConfiguration { boolean isBooleanFormatSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setTimeFormat(String)}. + * 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(); @@ -135,7 +253,12 @@ public interface ProcessingConfiguration { boolean isTimeFormatSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setDateFormat(String)}. + * 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(); @@ -147,7 +270,90 @@ public interface ProcessingConfiguration { boolean isDateFormatSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setDateTimeFormat(String)}. + * 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(); @@ -159,10 +365,32 @@ public interface ProcessingConfiguration { boolean isDateTimeFormatSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setCustomDateFormats(Map)}. + * 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); /** @@ -173,7 +401,25 @@ public interface ProcessingConfiguration { boolean isCustomDateFormatsSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setTemplateExceptionHandler(TemplateExceptionHandler)}. + * 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(); @@ -185,7 +431,9 @@ public interface ProcessingConfiguration { boolean isTemplateExceptionHandlerSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setArithmeticEngine(ArithmeticEngine)}. + * 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(); @@ -197,7 +445,9 @@ public interface ProcessingConfiguration { boolean isArithmeticEngineSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setObjectWrapper(ObjectWrapper)}. + * 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(); @@ -209,7 +459,12 @@ public interface ProcessingConfiguration { boolean isObjectWrapperSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setOutputEncoding(Charset)}. + * 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(); @@ -221,7 +476,8 @@ public interface ProcessingConfiguration { boolean isOutputEncodingSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setURLEscapingCharset(Charset)}. + * 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(); @@ -233,7 +489,11 @@ public interface ProcessingConfiguration { boolean isURLEscapingCharsetSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setNewBuiltinClassResolver(TemplateClassResolver)}. + * 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(); @@ -244,12 +504,28 @@ public interface ProcessingConfiguration { */ 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(); /** - * Getter pair of {@link MutableProcessingConfiguration#setAutoFlush(boolean)}. + * 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(); @@ -261,7 +537,8 @@ public interface ProcessingConfiguration { boolean isAutoFlushSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setShowErrorTips(boolean)}. + * Whether tips should be shown in error messages of errors arising during template processing. + * Its {@link Configuration}-level default is {@code true}. */ boolean getShowErrorTips(); @@ -273,7 +550,13 @@ public interface ProcessingConfiguration { boolean isShowErrorTipsSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setLogTemplateExceptions(boolean)}. + * 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(); @@ -285,7 +568,22 @@ public interface ProcessingConfiguration { boolean isLogTemplateExceptionsSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setLazyImports(boolean)}. + * 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(); @@ -297,7 +595,10 @@ public interface ProcessingConfiguration { boolean isLazyImportsSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setLazyAutoImports(Boolean)}. + * 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(); @@ -309,7 +610,31 @@ public interface ProcessingConfiguration { boolean isLazyAutoImportsSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setAutoImports(Map)}. + * 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 {@code lazy_auto_imports} setting ({@link Configuration#setLazyAutoImports(Boolean)}) + * 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(); @@ -321,7 +646,21 @@ public interface ProcessingConfiguration { boolean isAutoImportsSet(); /** - * Getter pair of {@link MutableProcessingConfiguration#setAutoIncludes(List)}. + * 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(); @@ -332,6 +671,19 @@ public interface ProcessingConfiguration { */ 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(); /** @@ -341,6 +693,18 @@ public interface ProcessingConfiguration { */ boolean isCustomAttributesSet(); - Object getCustomAttribute(Object name); + /** + * 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/18a4ace8/src/main/java/org/apache/freemarker/core/Template.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/Template.java b/src/main/java/org/apache/freemarker/core/Template.java index 8671dc1..726cd52 100644 --- a/src/main/java/org/apache/freemarker/core/Template.java +++ b/src/main/java/org/apache/freemarker/core/Template.java @@ -102,7 +102,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { // Inherited settings: private final transient Configuration cfg; private final transient TemplateConfiguration tCfg; - private final transient ParserConfiguration parserConfiguration; + private final transient ParsingConfiguration parsingConfiguration; // Values from the template content (#ftl header parameters usually), as opposed to from the TemplateConfiguration: private transient OutputFormat outputFormat; // TODO Deserialization: use the name of the output format @@ -281,7 +281,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { _NullArgumentException.check("configuration", configuration); this.cfg = configuration; this.tCfg = templateConfiguration; - this.parserConfiguration = tCfg != null ? new TemplateParserConfigurationWithFallback(cfg, tCfg) : cfg; + this.parsingConfiguration = tCfg != null ? new TemplateParsingConfigurationWithFallback(cfg, tCfg) : cfg; this.name = name; this.sourceName = sourceName; @@ -294,13 +294,13 @@ public class Template implements ProcessingConfiguration, CustomStateScope { reader = new BufferedReader(reader, READER_BUFFER_SIZE); } - ltbReader = new LineTableBuilder(reader, parserConfiguration); + ltbReader = new LineTableBuilder(reader, parsingConfiguration); reader = ltbReader; try { FMParser parser = new FMParser( this, reader, - parserConfiguration, contextOutputFormat, contextAutoEscapingPolicy, + parsingConfiguration, contextOutputFormat, contextAutoEscapingPolicy, streamToUnmarkWhenEncEstabd); try { rootElement = parser.Root(); @@ -599,8 +599,8 @@ public class Template implements ProcessingConfiguration, CustomStateScope { return tCfg; } - public ParserConfiguration getParserConfiguration() { - return parserConfiguration; + public ParsingConfiguration getParsingConfiguration() { + return parsingConfiguration; } @@ -672,7 +672,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { /** * Returns the output format (see {@link Configuration#setOutputFormat(OutputFormat)}) used for this template. * The output format of a template can come from various places, in order of increasing priority: - * {@link Configuration#getOutputFormat()}, {@link ParserConfiguration#getOutputFormat()} (which is usually + * {@link Configuration#getOutputFormat()}, {@link ParsingConfiguration#getOutputFormat()} (which is usually * provided by {@link Configuration#getTemplateConfigurations()}) and the {@code #ftl} header's * {@code output_format} option in the template. * @@ -692,7 +692,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { /** * Returns if the auto-escaping policy (see {@link Configuration#setAutoEscapingPolicy(int)}) that this template * uses. This is decided from these, in increasing priority: - * {@link Configuration#getAutoEscapingPolicy()}, {@link ParserConfiguration#getAutoEscapingPolicy()}, + * {@link Configuration#getAutoEscapingPolicy()}, {@link ParsingConfiguration#getAutoEscapingPolicy()}, * {@code #ftl} header's {@code auto_esc} option in the template. */ public int getAutoEscapingPolicy() { @@ -733,7 +733,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { /** * Returns the template source at the location specified by the coordinates given, or {@code null} if unavailable. * A strange legacy in the behavior of this method is that it replaces tab characters with spaces according the - * value of {@link Template#getParserConfiguration()}/{@link ParserConfiguration#getTabSize()} (which usually + * value of {@link Template#getParsingConfiguration()}/{@link ParsingConfiguration#getTabSize()} (which usually * comes from {@link Configuration#getTabSize()}), because tab characters move the column number with more than * 1 in error messages. However, if you set the tab size to 1, this method leaves the tab characters as is. * @@ -1128,7 +1128,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { /** * @param r the character stream to wrap */ - LineTableBuilder(Reader r, ParserConfiguration parserConfiguration) { + LineTableBuilder(Reader r, ParsingConfiguration parserConfiguration) { super(r); tabSize = parserConfiguration.getTabSize(); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java b/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java index f2a5fc6..efb8152 100644 --- a/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java +++ b/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java @@ -51,7 +51,7 @@ import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory; * * @see Template#Template(String, String, Reader, Configuration, TemplateConfiguration, Charset) */ -public final class TemplateConfiguration implements ParserAndProcessingConfiguration { +public final class TemplateConfiguration implements ParsingAndProcessingConfiguration { private final Locale locale; private final String numberFormat; @@ -669,7 +669,7 @@ public final class TemplateConfiguration implements ParserAndProcessingConfigura return null; } - public static final class Builder extends MutableProcessingAndParseConfiguration<Builder> + public static final class Builder extends MutableParsingAndProcessingConfiguration<Builder> implements CommonBuilder<TemplateConfiguration> { public Builder() { @@ -826,7 +826,7 @@ public final class TemplateConfiguration implements ParserAndProcessingConfigura * {@link TemplateConfiguration}, possibly overwriting the earlier value in this object. (A setting is said to be * set in a {@link TemplateConfiguration} if it was explicitly set via a setter method, as opposed to be inherited.) */ - public void merge(ParserAndProcessingConfiguration tc) { + public void merge(ParsingAndProcessingConfiguration tc) { if (tc.isAPIBuiltinEnabledSet()) { setAPIBuiltinEnabled(tc.getAPIBuiltinEnabled()); } @@ -935,7 +935,7 @@ public final class TemplateConfiguration implements ParserAndProcessingConfigura } if (tc.isCustomAttributesSet()) { - setCustomAttributes(mergeMaps( + setCustomAttributesWithoutCopying(mergeMaps( isCustomAttributesSet() ? getCustomAttributes() : null, tc.isCustomAttributesSet() ? tc.getCustomAttributes() : null, true)); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/TemplateParserConfigurationWithFallback.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateParserConfigurationWithFallback.java b/src/main/java/org/apache/freemarker/core/TemplateParserConfigurationWithFallback.java deleted file mode 100644 index e9758cf..0000000 --- a/src/main/java/org/apache/freemarker/core/TemplateParserConfigurationWithFallback.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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.OutputFormat; - -/** - * Adds {@link Configuration} fallback to the {@link ParserConfiguration} part of a {@link TemplateConfiguration}. - */ -final class TemplateParserConfigurationWithFallback implements ParserConfiguration { - - private final Configuration cfg; - private final TemplateConfiguration tCfg; - - TemplateParserConfigurationWithFallback(Configuration cfg, TemplateConfiguration tCfg) { - this.cfg = cfg; - this.tCfg = tCfg; - } - - @Override - public TemplateLanguage getTemplateLanguage() { - return tCfg.isTemplateLanguageSet() ? tCfg.getTemplateLanguage() : cfg.getTemplateLanguage(); - } - - @Override - public boolean isTemplateLanguageSet() { - return true; - } - - @Override - public int getTagSyntax() { - return tCfg.isTagSyntaxSet() ? tCfg.getTagSyntax() : cfg.getTagSyntax(); - } - - @Override - public boolean isTagSyntaxSet() { - return true; - } - - @Override - public int getNamingConvention() { - return tCfg.isNamingConventionSet() ? tCfg.getNamingConvention() : cfg.getNamingConvention(); - } - - @Override - public boolean isNamingConventionSet() { - return true; - } - - @Override - public boolean getWhitespaceStripping() { - return tCfg.isWhitespaceStrippingSet() ? tCfg.getWhitespaceStripping() : cfg.getWhitespaceStripping(); - } - - @Override - public boolean isWhitespaceStrippingSet() { - return true; - } - - @Override - public ArithmeticEngine getArithmeticEngine() { - return tCfg.isArithmeticEngineSet() ? tCfg.getArithmeticEngine() : cfg.getArithmeticEngine(); - } - - @Override - public boolean isArithmeticEngineSet() { - return true; - } - - @Override - public int getAutoEscapingPolicy() { - return tCfg.isAutoEscapingPolicySet() ? tCfg.getAutoEscapingPolicy() : cfg.getAutoEscapingPolicy(); - } - - @Override - public boolean isAutoEscapingPolicySet() { - return true; - } - - @Override - public OutputFormat getOutputFormat() { - return tCfg.isOutputFormatSet() ? tCfg.getOutputFormat() : cfg.getOutputFormat(); - } - - @Override - public boolean isOutputFormatSet() { - return true; - } - - @Override - public boolean getRecognizeStandardFileExtensions() { - return tCfg.isRecognizeStandardFileExtensionsSet() ? tCfg.getRecognizeStandardFileExtensions() - : cfg.getRecognizeStandardFileExtensions(); - } - - @Override - public boolean isRecognizeStandardFileExtensionsSet() { - return true; - } - - @Override - public Version getIncompatibleImprovements() { - // This can be only set on the Configuration-level - return cfg.getIncompatibleImprovements(); - } - - @Override - public int getTabSize() { - return tCfg.isTabSizeSet() ? tCfg.getTabSize() : cfg.getTabSize(); - } - - @Override - public boolean isTabSizeSet() { - return true; - } - - @Override - public Charset getSourceEncoding() { - return tCfg.isSourceEncodingSet() ? tCfg.getSourceEncoding() : cfg.getSourceEncoding(); - } - - @Override - public boolean isSourceEncodingSet() { - return true; - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/TemplateParsingConfigurationWithFallback.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateParsingConfigurationWithFallback.java b/src/main/java/org/apache/freemarker/core/TemplateParsingConfigurationWithFallback.java new file mode 100644 index 0000000..93a5840 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateParsingConfigurationWithFallback.java @@ -0,0 +1,146 @@ +/* + * 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.OutputFormat; + +/** + * Adds {@link Configuration} fallback to the {@link ParsingConfiguration} part of a {@link TemplateConfiguration}. + */ +final class TemplateParsingConfigurationWithFallback implements ParsingConfiguration { + + private final Configuration cfg; + private final TemplateConfiguration tCfg; + + TemplateParsingConfigurationWithFallback(Configuration cfg, TemplateConfiguration tCfg) { + this.cfg = cfg; + this.tCfg = tCfg; + } + + @Override + public TemplateLanguage getTemplateLanguage() { + return tCfg.isTemplateLanguageSet() ? tCfg.getTemplateLanguage() : cfg.getTemplateLanguage(); + } + + @Override + public boolean isTemplateLanguageSet() { + return true; + } + + @Override + public int getTagSyntax() { + return tCfg.isTagSyntaxSet() ? tCfg.getTagSyntax() : cfg.getTagSyntax(); + } + + @Override + public boolean isTagSyntaxSet() { + return true; + } + + @Override + public int getNamingConvention() { + return tCfg.isNamingConventionSet() ? tCfg.getNamingConvention() : cfg.getNamingConvention(); + } + + @Override + public boolean isNamingConventionSet() { + return true; + } + + @Override + public boolean getWhitespaceStripping() { + return tCfg.isWhitespaceStrippingSet() ? tCfg.getWhitespaceStripping() : cfg.getWhitespaceStripping(); + } + + @Override + public boolean isWhitespaceStrippingSet() { + return true; + } + + @Override + public ArithmeticEngine getArithmeticEngine() { + return tCfg.isArithmeticEngineSet() ? tCfg.getArithmeticEngine() : cfg.getArithmeticEngine(); + } + + @Override + public boolean isArithmeticEngineSet() { + return true; + } + + @Override + public int getAutoEscapingPolicy() { + return tCfg.isAutoEscapingPolicySet() ? tCfg.getAutoEscapingPolicy() : cfg.getAutoEscapingPolicy(); + } + + @Override + public boolean isAutoEscapingPolicySet() { + return true; + } + + @Override + public OutputFormat getOutputFormat() { + return tCfg.isOutputFormatSet() ? tCfg.getOutputFormat() : cfg.getOutputFormat(); + } + + @Override + public boolean isOutputFormatSet() { + return true; + } + + @Override + public boolean getRecognizeStandardFileExtensions() { + return tCfg.isRecognizeStandardFileExtensionsSet() ? tCfg.getRecognizeStandardFileExtensions() + : cfg.getRecognizeStandardFileExtensions(); + } + + @Override + public boolean isRecognizeStandardFileExtensionsSet() { + return true; + } + + @Override + public Version getIncompatibleImprovements() { + // This can be only set on the Configuration-level + return cfg.getIncompatibleImprovements(); + } + + @Override + public int getTabSize() { + return tCfg.isTabSizeSet() ? tCfg.getTabSize() : cfg.getTabSize(); + } + + @Override + public boolean isTabSizeSet() { + return true; + } + + @Override + public Charset getSourceEncoding() { + return tCfg.isSourceEncodingSet() ? tCfg.getSourceEncoding() : cfg.getSourceEncoding(); + } + + @Override + public boolean isSourceEncodingSet() { + return true; + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/_EvalUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_EvalUtil.java b/src/main/java/org/apache/freemarker/core/_EvalUtil.java index c89af2f..727085f 100644 --- a/src/main/java/org/apache/freemarker/core/_EvalUtil.java +++ b/src/main/java/org/apache/freemarker/core/_EvalUtil.java @@ -539,7 +539,7 @@ public class _EvalUtil { static ArithmeticEngine getArithmeticEngine(Environment env, ASTNode tObj) { return env != null ? env.getArithmeticEngine() - : tObj.getTemplate().getParserConfiguration().getArithmeticEngine(); + : tObj.getTemplate().getParsingConfiguration().getArithmeticEngine(); } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/java/org/apache/freemarker/core/util/_KeyValuePair.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/util/_KeyValuePair.java b/src/main/java/org/apache/freemarker/core/util/_KeyValuePair.java new file mode 100644 index 0000000..d88d8e4 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/util/_KeyValuePair.java @@ -0,0 +1,61 @@ +/* + * 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.util; + +public class _KeyValuePair<K, V> { + private final K key; + private final V value; + + public _KeyValuePair(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + _KeyValuePair<?, ?> that = (_KeyValuePair<?, ?>) o; + + if (key != null ? !key.equals(that.key) : that.key != null) return false; + return value != null ? value.equals(that.value) : that.value == null; + } + + @Override + public int hashCode() { + int result = key != null ? key.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "_KeyValuePair{key=" + key + ", value=" + value + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/main/javacc/FTL.jj ---------------------------------------------------------------------- diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj index 661cb5d..14a2fe7 100644 --- a/src/main/javacc/FTL.jj +++ b/src/main/javacc/FTL.jj @@ -80,7 +80,7 @@ public class FMParser { private OutputFormat outputFormat; private int autoEscapingPolicy; private boolean autoEscaping; - private ParserConfiguration pCfg; + private ParsingConfiguration pCfg; private InputStream streamToUnmarkWhenEncEstabd; /** Keeps track of #list nesting. */ @@ -96,21 +96,21 @@ public class FMParser { private int mixedContentNesting; // for stripText FMParser(Template template, Reader reader, - ParserConfiguration pCfg, OutputFormat outputFormat, Integer autoEscapingPolicy, + ParsingConfiguration pCfg, OutputFormat outputFormat, Integer autoEscapingPolicy, InputStream streamToUnmarkWhenEncEstabd) { this(template, true, readerToTokenManager(reader, pCfg), pCfg, outputFormat, autoEscapingPolicy, streamToUnmarkWhenEncEstabd); } - private static FMParserTokenManager readerToTokenManager(Reader reader, ParserConfiguration pCfg) { + private static FMParserTokenManager readerToTokenManager(Reader reader, ParsingConfiguration pCfg) { SimpleCharStream simpleCharStream = new SimpleCharStream(reader, 1, 1); simpleCharStream.setTabSize(pCfg.getTabSize()); return new FMParserTokenManager(simpleCharStream); } FMParser(Template template, boolean newTemplate, FMParserTokenManager tkMan, - ParserConfiguration pCfg, OutputFormat contextOutputFormat, Integer contextAutoEscapingPolicy, + ParsingConfiguration pCfg, OutputFormat contextOutputFormat, Integer contextAutoEscapingPolicy, InputStream streamToUnmarkWhenEncEstabd) { this(tkMan); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/manual/en_US/FM3-CHANGE-LOG.txt ---------------------------------------------------------------------- diff --git a/src/manual/en_US/FM3-CHANGE-LOG.txt b/src/manual/en_US/FM3-CHANGE-LOG.txt index ba0267b..e74a951 100644 --- a/src/manual/en_US/FM3-CHANGE-LOG.txt +++ b/src/manual/en_US/FM3-CHANGE-LOG.txt @@ -190,6 +190,7 @@ the FreeMarer 3 changelog here: - Made TemplateConfiguration immutable, added a TemplateConfiguration.Builder. - Made Template immutable (via public API-s). Template-specific settings now can only come from the TemplateConfiguration associated to the template, or from the #ftl header for some settings (most notably for custom attributes). + - Renamed ParserConfiguration to ParsingConfiguration, so that the name is more consistent with the new ProcessingConfiguration. - Settings that have contained a charset name (sourceEncoding, outputEncoding, URLEscapingCharset) are now of type Charset, not String. For string based configuration sources (such as .properties files) this means that: - Unrecognized charset names are now errors http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/test/java/org/apache/freemarker/core/OutputFormatTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/OutputFormatTest.java b/src/test/java/org/apache/freemarker/core/OutputFormatTest.java index 64fad5f..d651b9d 100644 --- a/src/test/java/org/apache/freemarker/core/OutputFormatTest.java +++ b/src/test/java/org/apache/freemarker/core/OutputFormatTest.java @@ -827,8 +827,9 @@ public class OutputFormatTest extends TemplateTest { @Test public void testDynamicParsingBIsInherticContextOutputFormat() throws Exception { - // Dynamic parser BI-s are supposed to use the parserConfiguration of the calling template, and ignore anything - // inside the calling template itself. Except, the outputFormat has to come from the calling lexical context. + // Dynamic parser BI-s are supposed to use the ParsingConfiguration of the calling template, and ignore anything + // inside the calling template itself. Except, the outputFormat and autoEscapingPolicy has to come from the + // calling lexical context. String commonFTL = "Eval: ${'.outputFormat'?eval}; " http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/18a4ace8/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java b/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java index 5d57e48..593b912 100644 --- a/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java +++ b/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java @@ -254,7 +254,7 @@ public class TemplateConfigurationTest { static { PARSER_PROP_NAMES = new HashSet<>(); // It's an interface; can't use standard Inrospector - for (Method m : ParserConfiguration.class.getMethods()) { + for (Method m : ParsingConfiguration.class.getMethods()) { String propertyName; String name = m.getName(); if (name.startsWith("get")) {
