You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2015/12/24 02:05:32 UTC
[1/3] incubator-freemarker git commit: More helpful JavaDoc for some
OutputFormat-related parts.
Repository: incubator-freemarker
Updated Branches:
refs/heads/2.3-gae 2153e2309 -> f112ed6ce
More helpful JavaDoc for some OutputFormat-related parts.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/7051e55c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/7051e55c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/7051e55c
Branch: refs/heads/2.3-gae
Commit: 7051e55cfffe244a9ef537c62ec71d45c9d569ad
Parents: 2153e23
Author: ddekany <dd...@apache.org>
Authored: Tue Dec 22 22:17:39 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Tue Dec 22 22:17:39 2015 +0100
----------------------------------------------------------------------
src/main/java/freemarker/template/Configuration.java | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7051e55c/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 4fd347c..7226ef2 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -1800,11 +1800,15 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
}
/**
- * Sets the (default) output format. Usually, you leave this on its default, which is
- * {@link UndefinedOutputFormat#INSTANCE}, and then override it for individual templates based on their name (like
- * based on their "file" extension) with {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. This
- * setting is also overridden by the standard file extensions; see them at
- * {@link #setRecognizeStandardFileExtensions(boolean)}.
+ * Sets the default output format. Usually, you should leave this on its default, which is
+ * {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML output) and
+ * ensure that {@link #setRecognizeStandardFileExtensions(boolean)} is {@code true} (see the description of standard
+ * file extensions there too). Where that approach doesn't fit, like you have no control over the file extensions,
+ * templates can be associated to output formats with patterns matching their name (their path) using
+ * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. Last not least, if all templates will have the
+ * same output format, you may use {@link #setOutputFormat(OutputFormat)} to set a value like
+ * {@link HTMLOutputFormat#INSTANCE}, {@link XMLOutputFormat#INSTANCE}, etc. Also note templates can specify their
+ * own output format like {@code <#ftl output_format="HTML">}, which overrides any configuration settings.
*
* <p>
* The output format is mostly important because of auto-escaping (see {@link #setAutoEscapingPolicy(int)}), but
[2/3] incubator-freemarker git commit: New output formats were in the
wrong source directory
Posted by dd...@apache.org.
New output formats were in the wrong source directory
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/bec1430b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/bec1430b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/bec1430b
Branch: refs/heads/2.3-gae
Commit: bec1430b44ec205f08c8045825c8a0b10a7f87ab
Parents: 7051e55
Author: ddekany <dd...@apache.org>
Authored: Thu Dec 24 01:56:07 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Thu Dec 24 01:56:07 2015 +0100
----------------------------------------------------------------------
.../java/freemarker/core/CSSOutputFormat.java | 34 +++++++++++++++++++
.../java/freemarker/core/JSONOutputFormat.java | 34 +++++++++++++++++++
.../freemarker/core/JavaScriptOutputFormat.java | 35 ++++++++++++++++++++
.../java/freemarker/core/CSSOutputFormat.java | 34 -------------------
.../java/freemarker/core/JSONOutputFormat.java | 34 -------------------
.../freemarker/core/JavaScriptOutputFormat.java | 35 --------------------
6 files changed, 103 insertions(+), 103 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bec1430b/src/main/java/freemarker/core/CSSOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/CSSOutputFormat.java b/src/main/java/freemarker/core/CSSOutputFormat.java
new file mode 100644
index 0000000..e60e1c5
--- /dev/null
+++ b/src/main/java/freemarker/core/CSSOutputFormat.java
@@ -0,0 +1,34 @@
+package freemarker.core;
+
+/**
+ * Represents the CSS output format (MIME type "text/css", name "CSS"). This format doesn't support escaping.
+ *
+ * @since 2.3.24
+ */
+public class CSSOutputFormat extends OutputFormat {
+
+ /**
+ * The only instance (singleton) of this {@link OutputFormat}.
+ */
+ public static final CSSOutputFormat INSTANCE = new CSSOutputFormat();
+
+ private CSSOutputFormat() {
+ // Only to decrease visibility
+ }
+
+ @Override
+ public String getName() {
+ return "CSS";
+ }
+
+ @Override
+ public String getMimeType() {
+ return "text/css";
+ }
+
+ @Override
+ public boolean isOutputFormatMixingAllowed() {
+ return false;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bec1430b/src/main/java/freemarker/core/JSONOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/JSONOutputFormat.java b/src/main/java/freemarker/core/JSONOutputFormat.java
new file mode 100644
index 0000000..7b0d9fe
--- /dev/null
+++ b/src/main/java/freemarker/core/JSONOutputFormat.java
@@ -0,0 +1,34 @@
+package freemarker.core;
+
+/**
+ * Represents the JSON output format (MIME type "application/json", name "JSON"). This format doesn't support escaping.
+ *
+ * @since 2.3.24
+ */
+public class JSONOutputFormat extends OutputFormat {
+
+ /**
+ * The only instance (singleton) of this {@link OutputFormat}.
+ */
+ public static final JSONOutputFormat INSTANCE = new JSONOutputFormat();
+
+ private JSONOutputFormat() {
+ // Only to decrease visibility
+ }
+
+ @Override
+ public String getName() {
+ return "JSON";
+ }
+
+ @Override
+ public String getMimeType() {
+ return "application/json";
+ }
+
+ @Override
+ public boolean isOutputFormatMixingAllowed() {
+ return false;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bec1430b/src/main/java/freemarker/core/JavaScriptOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/JavaScriptOutputFormat.java b/src/main/java/freemarker/core/JavaScriptOutputFormat.java
new file mode 100644
index 0000000..a6375af
--- /dev/null
+++ b/src/main/java/freemarker/core/JavaScriptOutputFormat.java
@@ -0,0 +1,35 @@
+package freemarker.core;
+
+/**
+ * Represents the JavaScript output format (MIME type "application/javascript", name "JavaScript"). This format doesn't
+ * support escaping.
+ *
+ * @since 2.3.24
+ */
+public class JavaScriptOutputFormat extends OutputFormat {
+
+ /**
+ * The only instance (singleton) of this {@link OutputFormat}.
+ */
+ public static final JavaScriptOutputFormat INSTANCE = new JavaScriptOutputFormat();
+
+ private JavaScriptOutputFormat() {
+ // Only to decrease visibility
+ }
+
+ @Override
+ public String getName() {
+ return "JavaScript";
+ }
+
+ @Override
+ public String getMimeType() {
+ return "application/javascript";
+ }
+
+ @Override
+ public boolean isOutputFormatMixingAllowed() {
+ return false;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bec1430b/src/test/java/freemarker/core/CSSOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/CSSOutputFormat.java b/src/test/java/freemarker/core/CSSOutputFormat.java
deleted file mode 100644
index e60e1c5..0000000
--- a/src/test/java/freemarker/core/CSSOutputFormat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package freemarker.core;
-
-/**
- * Represents the CSS output format (MIME type "text/css", name "CSS"). This format doesn't support escaping.
- *
- * @since 2.3.24
- */
-public class CSSOutputFormat extends OutputFormat {
-
- /**
- * The only instance (singleton) of this {@link OutputFormat}.
- */
- public static final CSSOutputFormat INSTANCE = new CSSOutputFormat();
-
- private CSSOutputFormat() {
- // Only to decrease visibility
- }
-
- @Override
- public String getName() {
- return "CSS";
- }
-
- @Override
- public String getMimeType() {
- return "text/css";
- }
-
- @Override
- public boolean isOutputFormatMixingAllowed() {
- return false;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bec1430b/src/test/java/freemarker/core/JSONOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/JSONOutputFormat.java b/src/test/java/freemarker/core/JSONOutputFormat.java
deleted file mode 100644
index 7b0d9fe..0000000
--- a/src/test/java/freemarker/core/JSONOutputFormat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package freemarker.core;
-
-/**
- * Represents the JSON output format (MIME type "application/json", name "JSON"). This format doesn't support escaping.
- *
- * @since 2.3.24
- */
-public class JSONOutputFormat extends OutputFormat {
-
- /**
- * The only instance (singleton) of this {@link OutputFormat}.
- */
- public static final JSONOutputFormat INSTANCE = new JSONOutputFormat();
-
- private JSONOutputFormat() {
- // Only to decrease visibility
- }
-
- @Override
- public String getName() {
- return "JSON";
- }
-
- @Override
- public String getMimeType() {
- return "application/json";
- }
-
- @Override
- public boolean isOutputFormatMixingAllowed() {
- return false;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bec1430b/src/test/java/freemarker/core/JavaScriptOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/JavaScriptOutputFormat.java b/src/test/java/freemarker/core/JavaScriptOutputFormat.java
deleted file mode 100644
index a6375af..0000000
--- a/src/test/java/freemarker/core/JavaScriptOutputFormat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package freemarker.core;
-
-/**
- * Represents the JavaScript output format (MIME type "application/javascript", name "JavaScript"). This format doesn't
- * support escaping.
- *
- * @since 2.3.24
- */
-public class JavaScriptOutputFormat extends OutputFormat {
-
- /**
- * The only instance (singleton) of this {@link OutputFormat}.
- */
- public static final JavaScriptOutputFormat INSTANCE = new JavaScriptOutputFormat();
-
- private JavaScriptOutputFormat() {
- // Only to decrease visibility
- }
-
- @Override
- public String getName() {
- return "JavaScript";
- }
-
- @Override
- public String getMimeType() {
- return "application/javascript";
- }
-
- @Override
- public boolean isOutputFormatMixingAllowed() {
- return false;
- }
-
-}
[3/3] incubator-freemarker git commit: Added custom formatter
examples to the Manual. Fixed/updated JavaDoc and Manual number/date
formatting related parts (mostly).
Posted by dd...@apache.org.
Added custom formatter examples to the Manual. Fixed/updated JavaDoc and Manual number/date formatting related parts (mostly).
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/f112ed6c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/f112ed6c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/f112ed6c
Branch: refs/heads/2.3-gae
Commit: f112ed6ceb84eec5ada63ded39e0efcc2ee546bb
Parents: bec1430
Author: ddekany <dd...@apache.org>
Authored: Thu Dec 24 02:05:11 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Thu Dec 24 02:05:11 2015 +0100
----------------------------------------------------------------------
src/main/java/freemarker/core/Configurable.java | 8 +-
src/main/java/freemarker/core/Environment.java | 4 +-
.../core/TemplateDateFormatFactory.java | 2 +-
.../core/TemplateNumberFormatFactory.java | 2 +-
.../java/freemarker/template/Configuration.java | 16 +-
src/manual/en_US/book.xml | 621 ++++++++++++++++++-
.../core/BaseNTemplateNumberFormatFactory.java | 21 +-
.../EpochMillisTemplateDateFormatFactory.java | 12 +-
.../freemarker/manual/CustomFormatsExample.java | 73 +++
.../UnitAwareTemplateNumberFormatFactory.java | 62 ++
.../manual/UnitAwareTemplateNumberModel.java | 25 +
.../manual/CustomFormatsExample-alias1.ftlh | 4 +
.../manual/CustomFormatsExample-alias1.ftlh.out | 4 +
.../manual/CustomFormatsExample-alias2.ftlh | 1 +
.../manual/CustomFormatsExample-alias2.ftlh.out | 1 +
.../manual/CustomFormatsExample-modelAware.ftlh | 2 +
.../CustomFormatsExample-modelAware.ftlh.out | 2 +
17 files changed, 807 insertions(+), 53 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/main/java/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java
index 9c82428..adba746 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -722,9 +722,9 @@ public class Configurable {
* <li>{@code "computer"}: The number format used by FTL's {@code c} built-in (like in {@code someNumber?c}).</li>
* <li>{@link java.text.DecimalFormat} pattern (like {@code "0.##"}). This syntax has a FreeMarker-specific
* extension, so that you can specify options like the rounding mode and the symbols used in this string. For
- * example, {@code ",000;; rnd=hu grp=_"} 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.
+ * 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, but only if either {@link Configuration#getIncompatibleImprovements()} is at least 2.3.24, or
@@ -779,7 +779,7 @@ public class Configurable {
*
* @param customNumberFormats
* Can't be {@code null}. The name must start with an UNICODE letter, and can only contain UNICODE
- * letters and digits.
+ * letters and digits (not {@code _}).
*
* @since 2.3.24
*/
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/main/java/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java
index ef7a301..5ebf855 100644
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@ -1501,7 +1501,7 @@ public final class Environment extends Configurable {
/**
* Gets a {@link TemplateDateFormat} for the specified parameters. This is mostly meant to be used by
- * {@link TemplateDateFormatFactory} implementations to delegate to a format based on a specific format string. It's
+ * {@link TemplateDateFormatFactory} implementations to delegate to a format based on a specific format string. It
* works well for that, as its parameters are the same low level values as the parameters of
* {@link TemplateDateFormatFactory#get(String, int, Locale, TimeZone, boolean, Environment)}. For other tasks
* consider the other overloads of this method.
@@ -1635,7 +1635,7 @@ public final class Environment extends Configurable {
/**
* Used to get the {@link TemplateDateFormat} according the date/time/datetime format settings, for the current
* locale and time zone. See {@link #getTemplateDateFormat(String, int, Locale, TimeZone, boolean)} for the meaning
- * of some if the parameters.
+ * of some of the parameters.
*/
private TemplateDateFormat getTemplateDateFormat(int dateType, boolean useSQLDTTZ, boolean zonelessInput)
throws TemplateValueFormatException {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/main/java/freemarker/core/TemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateDateFormatFactory.java b/src/main/java/freemarker/core/TemplateDateFormatFactory.java
index 23b88e0..9edda85 100644
--- a/src/main/java/freemarker/core/TemplateDateFormatFactory.java
+++ b/src/main/java/freemarker/core/TemplateDateFormatFactory.java
@@ -59,7 +59,7 @@ public abstract class TemplateDateFormatFactory extends TemplateValueFormatFacto
* The locale to format for. Not {@code null}. The resulting format should be bound to this locale
* forever (i.e. locale changes in the {@link Environment} must not be followed).
* @param timeZone
- * The time zone to format for. Not {@code null}. The resulting format should be bound to this time zone
+ * The time zone to format for. Not {@code null}. The resulting format must be bound to this time zone
* forever (i.e. time zone changes in the {@link Environment} must not be followed).
* @param zonelessInput
* Indicates that the input Java {@link Date} is not from a time zone aware source. When this is
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/main/java/freemarker/core/TemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateNumberFormatFactory.java b/src/main/java/freemarker/core/TemplateNumberFormatFactory.java
index 9a404d3..e0d0bc6 100644
--- a/src/main/java/freemarker/core/TemplateNumberFormatFactory.java
+++ b/src/main/java/freemarker/core/TemplateNumberFormatFactory.java
@@ -48,7 +48,7 @@ public abstract class TemplateNumberFormatFactory extends TemplateValueFormatFac
* {@code "1, 2"} (and {@code "@fooBar"} selects the factory). The format of this string is up to the
* {@link TemplateNumberFormatFactory} implementation. Not {@code null}, often an empty string.
* @param locale
- * The locale to format for. Not {@code null}. The resulting format should be bound to this locale
+ * The locale to format for. Not {@code null}. The resulting format must be bound to this locale
* forever (i.e. locale changes in the {@link Environment} must not be followed).
* @param env
* The runtime environment from which the formatting was called. This is mostly meant to be used for
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index 7226ef2..a913cb7 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -1801,14 +1801,14 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
/**
* Sets the default output format. Usually, you should leave this on its default, which is
- * {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML output) and
- * ensure that {@link #setRecognizeStandardFileExtensions(boolean)} is {@code true} (see the description of standard
- * file extensions there too). Where that approach doesn't fit, like you have no control over the file extensions,
- * templates can be associated to output formats with patterns matching their name (their path) using
- * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. Last not least, if all templates will have the
- * same output format, you may use {@link #setOutputFormat(OutputFormat)} to set a value like
- * {@link HTMLOutputFormat#INSTANCE}, {@link XMLOutputFormat#INSTANCE}, etc. Also note templates can specify their
- * own output format like {@code <#ftl output_format="HTML">}, which overrides any configuration settings.
+ * {@link UndefinedOutputFormat#INSTANCE}, and then use standard file extensions like "ftlh" (for HTML) or "ftlx"
+ * (for XML) and ensure that {@link #setRecognizeStandardFileExtensions(boolean)} is {@code true} (see more there).
+ * Where you can't use standard the file extensions, templates still can be associated to output formats with
+ * patterns matching their name (their path) using {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.
+ * But if all templates will have the same output format, you may use {@link #setOutputFormat(OutputFormat)} after
+ * all, to set 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.
*
* <p>
* The output format is mostly important because of auto-escaping (see {@link #setAutoEscapingPolicy(int)}), but
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index b25ca0c..b68ff4c 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -4119,7 +4119,7 @@ ${("green " + "mouse")?upper_case} <#-- GREEN MOUSE -->
value will be converted to string according the default number
format. This may includes the maximum number of decimals, grouping,
and like. Usually the programmer should set the default number
- format; the template author don't have to deal with it (but he can
+ format; the template author doesn't have to deal with it (but he can
with the <literal>number_format</literal> setting; see in the <link
linkend="ref_directive_setting">documentation of
<literal>setting</literal> directive</link>). Also, you can override
@@ -4168,8 +4168,8 @@ ${("green " + "mouse")?upper_case} <#-- GREEN MOUSE -->
<para>If the expression evaluates to a date-like value then that
will be transformed to a text according to a default format. Usually
- the programmer should set the default format; you don't have to deal
- with it (but if you care, <link
+ the programmer should set the default format; the template author
+ doesn't have to deal with it (but if you care, <link
linkend="topic.dateTimeFormatSettings">see the
<literal>date_format</literal>, <literal>time_format</literal> and
<literal>datetime_format</literal> settings</link> in the
@@ -9163,30 +9163,594 @@ cfg.setTemplateConfigurations(
<section xml:id="pgui_config_custom_formats">
<title>Custom number and date/time formats</title>
- <para>FreeMarker allows you to define your own number and
- date/time/datetime formatter algorithms by implementing
- <literal>freemarker.core.TemplateNumberFormatFactory</literal> and
- <literal>freemarker.core.TemplateDateFormatFactory</literal>. These
- formats can be registered with the
- <literal>custom_number_formats</literal> and
- <literal>custom_date_formats</literal> configuration settings. After
- that, anywhere where you can specify formats with a
- <literal>String</literal>, now you can refer to your custom format as
- <literal>"@<replaceable>name</replaceable>"</literal>. So for example,
- if you have registered your number format implementation with name
- <literal>"smart"</literal>, then you could set the
- <literal>number_format</literal> setting
- (<literal>Configurable.setNumberFormat(String)</literal>) to
- <literal>"@smart"</literal>, or issue
- <literal>${n?string.@smart}</literal> or <literal><#setting
- number_format="@smart"></literal> in a template.</para>
-
- <para>[TODO] What other applications exist (aliasing, model-aware
- formatters)</para>
-
- <para>[TODO] Simple complete example</para>
-
- <para>[TODO] Complex formatter example (base N with fallback)</para>
+ <section>
+ <title>Overview</title>
+
+ <para>FreeMarker allows you to define your own number and
+ date/time/datetime formats, and associate a name to them. This
+ mechanism has several applications:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>Custom formatter algorithms: You can use your own
+ formatter algorithm instead of relying on those provided by
+ FreeMarker. For this, implement
+ <literal>freemarker.core.TemplateNumberFormatFactory</literal>
+ or <literal>freemarker.core.TemplateDateFormatFactory</literal>.
+ You will find a few examples of this <link
+ linkend="pgui_config_custom_formats_ex_cust_alg_simple">below</link>.</para>
+ </listitem>
+
+ <listitem>
+ <para>Aliasing: You can give application-specific names (like
+ <quote>price</quote>, <quote>weight</quote>,
+ <quote>fileDate</quote>, <quote>logEventTime</quote>, etc.) to
+ other formats by using
+ <literal>AliasTemplateNumberFormatFactory</literal> and
+ <literal>AliasTemplateDateFormatFactory</literal>. Thus
+ templates can just refer to that name, like in
+ <literal>${lastModified?string.@fileDate}</literal>, instead of
+ specifying the format directly. Thus the formats can be
+ specified on a single central place (where you configure
+ FreeMarker), instead of being specified repeatedly in templates.
+ Also thus template authors don't have to enter complex and hard
+ to remember formatting patterns. <link
+ linkend="pgui_config_custom_formats_ex_alias">See example
+ below</link>.</para>
+ </listitem>
+
+ <listitem>
+ <para>Model-sensitive formatting: Applications can put custom
+ <literal>freemarker.TemplateModel</literal>-s into the
+ data-model instead of dropping plain values (like
+ <literal>int</literal>-s, <literal>double</literal>-s, etc.)
+ into it, to attach rendering-related information to the value.
+ Custom formatters can utilize this information (for example, to
+ show the unit after numbers), as they receive the
+ <literal>TemplateModel</literal> itself, not the wrapped raw
+ value. <link
+ linkend="pgui_config_custom_formats_ex_model_aware">See example
+ below</link>.</para>
+ </listitem>
+
+ <listitem>
+ <para>Format that prints markup instead of plain text: You might
+ want to use HTML tags (or other markup) in the formatted values,
+ such as coloring negative numbers to red or using HTML
+ <literal>sup</literal> element for exponents. This is possible
+ if you write a custom format as shown in previous cases, but
+ override the <literal>format</literal> method in the formatter
+ class so that it returns a
+ <literal>TemplateMarkupOutputModel</literal> instead of a
+ <literal>String</literal>. (You shouldn't just return the markup
+ as <literal>String</literal>, as then it might will be escaped;
+ see <link
+ linkend="dgui_misc_autoescaping">auto-escaping</link>.)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Custom formats can be registered with the
+ <literal>custom_number_formats</literal> and
+ <literal>custom_date_formats</literal> configuration settings. After
+ that, anywhere where you can specify formats with a
+ <literal>String</literal>, now you can refer to your custom format
+ as <literal>"@<replaceable>name</replaceable>"</literal>. So for
+ example, if you have registered your number format implementation
+ with name <literal>"smart"</literal>, then you could set the
+ <literal>number_format</literal> setting
+ (<literal>Configurable.setNumberFormat(String)</literal>) to
+ <literal>"@smart"</literal>, or issue
+ <literal>${n?string.@smart}</literal> or <literal><#setting
+ number_format="@smart"></literal> in a template. Furthermore, you
+ can define parameters for your custom format, like <literal>"@smart
+ 2"</literal>, and the interpretation of the parameters is up to your
+ formatter implementation.</para>
+ </section>
+
+ <section xml:id="pgui_config_custom_formats_ex_cust_alg_simple">
+ <title>Simple custom number format example</title>
+
+ <para>This custom number format shows numbers in hexadecimal
+ form:</para>
+
+ <programlisting role="unspecified">package com.example;
+
+import java.util.Locale;
+
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+import freemarker.template.utility.NumberUtil;
+
+public class HexTemplateNumberFormatFactory extends TemplateNumberFormatFactory {
+
+ public static final HexTemplateNumberFormatFactory INSTANCE
+ = new HexTemplateNumberFormatFactory();
+
+ private HexTemplateNumberFormatFactory() {
+ // Defined to decrease visibility
+ }
+
+ @Override
+ public TemplateNumberFormat get(String params, Locale locale, Environment env)
+ throws InvalidFormatParametersException {
+ TemplateFormatUtil.checkHasNoParameters(params);
+ return HexTemplateNumberFormat.INSTANCE;
+ }
+
+ private static class HexTemplateNumberFormat extends TemplateNumberFormat {
+
+ private static final HexTemplateNumberFormat INSTANCE = new HexTemplateNumberFormat();
+
+ private HexTemplateNumberFormat() { }
+
+ @Override
+ public String formatToPlainText(TemplateNumberModel numberModel)
+ throws UnformattableValueException, TemplateModelException {
+ Number n = TemplateFormatUtil.getNonNullNumber(numberModel);
+ try {
+ return Integer.toHexString(NumberUtil.toIntExact(n));
+ } catch (ArithmeticException e) {
+ throw new UnformattableValueException(n + " doesn't fit into an int");
+ }
+ }
+
+ @Override
+ public boolean isLocaleBound() {
+ return false;
+ }
+
+ @Override
+ public String getDescription() {
+ return "hexadecimal int";
+ }
+
+ }
+
+}</programlisting>
+
+ <para>We register the above format with name
+ <quote>hex</quote>:</para>
+
+ <programlisting role="unspecified">// Where you initalize the application-wide Configuration singleton:
+Configuration cfg = ...;
+...
+Map<String, TemplateNumberFormatFactory> customNumberFormats = ...;
+...
+customNumberFormats.put("hex", HexTemplateNumberFormatFactory.INSTANCE);
+...
+cfg.setCustomNumberFormats(customNumberFormats);</programlisting>
+
+ <para>Now we can use this format in templates:</para>
+
+ <programlisting role="template">${x?string.@hex}</programlisting>
+
+ <para>or even set it as the default number format:</para>
+
+ <programlisting role="unspecified">cfg.setNumberFormat("@hex");</programlisting>
+ </section>
+
+ <section xml:id="pgui_config_custom_formats_ex_cust_algo_advanced">
+ <title>Advanced custom number format example</title>
+
+ <para>This is a more complex custom number format that shows how to
+ deal with parameters in the format string, also how to delegate to
+ another format:</para>
+
+ <programlisting role="unspecified">package com.example;
+
+import java.util.Locale;
+
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+import freemarker.template.utility.NumberUtil;
+import freemarker.template.utility.StringUtil;
+
+/**
+ * Shows a number in base N number system. Can only format numbers that fit into an {@code int},
+ * however, optionally you can specify a fallback format. This format has one required parameter,
+ * the numerical system base. That can be optionally followed by "|" and a fallback format.
+ */
+public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactory {
+
+ public static final BaseNTemplateNumberFormatFactory INSTANCE
+ = new BaseNTemplateNumberFormatFactory();
+
+ private BaseNTemplateNumberFormatFactory() {
+ // Defined to decrease visibility
+ }
+
+ @Override
+ public TemplateNumberFormat get(String params, Locale locale, Environment env)
+ throws InvalidFormatParametersException {
+ TemplateNumberFormat fallbackFormat;
+ {
+ int barIdx = params.indexOf('|');
+ if (barIdx != -1) {
+ String fallbackFormatStr = params.substring(barIdx + 1);
+ params = params.substring(0, barIdx);
+ try {
+ fallbackFormat = env.getTemplateNumberFormat(fallbackFormatStr, locale);
+ } catch (TemplateValueFormatException e) {
+ throw new InvalidFormatParametersException(
+ "Couldn't get the fallback number format (specified after the \"|\"), "
+ + StringUtil.jQuote(fallbackFormatStr) + ". Reason: " + e.getMessage(),
+ e);
+ }
+ } else {
+ fallbackFormat = null;
+ }
+ }
+
+ int base;
+ try {
+ base = Integer.parseInt(params);
+ } catch (NumberFormatException e) {
+ if (params.length() == 0) {
+ throw new InvalidFormatParametersException(
+ "A format parameter is required to specify the numerical system base.");
+ }
+ throw new InvalidFormatParametersException(
+ "The format paramter must be an integer, but was (shown quoted): "
+ + StringUtil.jQuote(params));
+ }
+ if (base < 2) {
+ throw new InvalidFormatParametersException("A base must be at least 2.");
+ }
+ return new BaseNTemplateNumberFormat(base, fallbackFormat);
+ }
+
+ private static class BaseNTemplateNumberFormat extends TemplateNumberFormat {
+
+ private final int base;
+ private final TemplateNumberFormat fallbackFormat;
+
+ private BaseNTemplateNumberFormat(int base, TemplateNumberFormat fallbackFormat) {
+ this.base = base;
+ this.fallbackFormat = fallbackFormat;
+ }
+
+ @Override
+ public String formatToPlainText(TemplateNumberModel numberModel)
+ throws TemplateModelException, TemplateValueFormatException {
+ Number n = TemplateFormatUtil.getNonNullNumber(numberModel);
+ try {
+ return Integer.toString(NumberUtil.toIntExact(n), base);
+ } catch (ArithmeticException e) {
+ if (fallbackFormat == null) {
+ throw new UnformattableValueException(
+ n + " doesn't fit into an int, and there was no fallback format "
+ + "specified.");
+ } else {
+ return fallbackFormat.formatToPlainText(numberModel);
+ }
+ }
+ }
+
+ @Override
+ public boolean isLocaleBound() {
+ return false;
+ }
+
+ @Override
+ public String getDescription() {
+ return "base " + base;
+ }
+
+ }
+
+}</programlisting>
+
+ <para>We register the above format with name
+ <quote>base</quote>:</para>
+
+ <programlisting role="unspecified">// Where you initalize the application-wide Configuration singleton:
+Configuration cfg = ...;
+...
+Map<String, TemplateNumberFormatFactory> customNumberFormats = ...;
+...
+customNumberFormats.put("hex", BaseNTemplateNumberFormatFactory.INSTANCE);
+...
+cfg.setCustomNumberFormats(customNumberFormats);</programlisting>
+
+ <para>Now we can use this format in templates:</para>
+
+ <programlisting role="template">${x?string.@base_8}</programlisting>
+
+ <para>Above there the parameter string was <literal>"8"</literal>,
+ as FreeMarker allows separating that from the format name with
+ <literal>_</literal> instead of whitespace, so that you don't have
+ to write the longer
+ <literal><replaceable>n</replaceable>?string["@base 8"]</literal>
+ form.</para>
+
+ <para>Of course, we could also set this as the default number format
+ like:</para>
+
+ <programlisting role="unspecified">cfg.setNumberFormat("@base 8");</programlisting>
+
+ <para>Here's an example of using the a fallback number format (which
+ is <literal>"0.0###"</literal>):</para>
+
+ <programlisting role="unspecified">cfg.setNumberFormat("@base 8|0.0###");</programlisting>
+
+ <para>Note that this functionality, with the <literal>|</literal>
+ syntax and all, is purely implemented in the example code
+ earlier.</para>
+ </section>
+
+ <section xml:id="pgui_config_custom_formats_ex_cust_algo_date">
+ <title>Custom date/time format example</title>
+
+ <para>This simple date format formats the date/time value to the
+ milliseconds since the epoch:</para>
+
+ <programlisting role="unspecified">package com.example;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import freemarker.template.TemplateDateModel;
+import freemarker.template.TemplateModelException;
+
+public class EpochMillisTemplateDateFormatFactory extends TemplateDateFormatFactory {
+
+ public static final EpochMillisTemplateDateFormatFactory INSTANCE
+ = new EpochMillisTemplateDateFormatFactory();
+
+ private EpochMillisTemplateDateFormatFactory() {
+ // Defined to decrease visibility
+ }
+
+ @Override
+ public TemplateDateFormat get(String params, int dateType,
+ Locale locale, TimeZone timeZone, boolean zonelessInput,
+ Environment env)
+ throws InvalidFormatParametersException {
+ TemplateFormatUtil.checkHasNoParameters(params);
+ return EpochMillisTemplateDateFormat.INSTANCE;
+ }
+
+ private static class EpochMillisTemplateDateFormat extends TemplateDateFormat {
+
+ private static final EpochMillisTemplateDateFormat INSTANCE
+ = new EpochMillisTemplateDateFormat();
+
+ private EpochMillisTemplateDateFormat() { }
+
+ @Override
+ public String formatToPlainText(TemplateDateModel dateModel)
+ throws UnformattableValueException, TemplateModelException {
+ return String.valueOf(TemplateFormatUtil.getNonNullDate(dateModel).getTime());
+ }
+
+ @Override
+ public boolean isLocaleBound() {
+ return false;
+ }
+
+ @Override
+ public boolean isTimeZoneBound() {
+ return false;
+ }
+
+ @Override
+ public Date parse(String s, int dateType) throws UnparsableValueException {
+ try {
+ return new Date(Long.parseLong(s));
+ } catch (NumberFormatException e) {
+ throw new UnparsableValueException("Malformed long");
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "millis since the epoch";
+ }
+
+ }
+
+}</programlisting>
+
+ <para>We register the above format with name
+ <quote>epoch</quote>:</para>
+
+ <programlisting role="unspecified">// Where you initalize the application-wide Configuration singleton:
+Configuration cfg = ...;
+...
+Map<String, TemplateDateFormatFactory> customDateFormats = ...;
+...
+customDateFormats.put("epoch", EpochMillisTemplateDateFormatFactory.INSTANCE);
+...
+cfg.setCustomDateFormats(customDateFormats);</programlisting>
+
+ <para>Now we can use this format in templates:</para>
+
+ <programlisting role="template">${t?string.@epoch}</programlisting>
+
+ <para>Of course, we could also set this as the default date-time
+ format like:</para>
+
+ <programlisting role="unspecified">cfg.setDateTimeFormat("@epoch");</programlisting>
+
+ <para>For a more complex that for example uses format parameters,
+ refer to the <link
+ linkend="pgui_config_custom_formats_ex_cust_algo_advanced">advanced
+ number format example</link>. Doing that with date formats is very
+ similar.</para>
+ </section>
+
+ <section xml:id="pgui_config_custom_formats_ex_alias">
+ <title>Alias format example</title>
+
+ <para>In this example we specify some number formats and date
+ formats that are aliases to another format:</para>
+
+ <programlisting role="unspecified">// Where you initalize the application-wide Configuration singleton:
+Configuration cfg = ...;
+
+Map<String, TemplateNumberFormatFactory> customNumberFormats
+ = new HashMap<String, TemplateNumberFormatFactory>();
+customNumberFormats.put("price", new AliasTemplateNumberFormatFactory(",000.00"));
+customNumberFormats.put("weight",
+ new AliasTemplateNumberFormatFactory("0.##;; roundingMode=halfUp"));
+cfg.setCustomNumberFormats(customNumberFormats);
+
+Map<String, TemplateDateFormatFactory> customDateFormats
+ = new HashMap<String, TemplateDateFormatFactory>();
+customDateFormats.put("fileDate", new AliasTemplateDateFormatFactory("dd/MMM/yy hh:mm a"));
+customDateFormats.put("logEventTime", new AliasTemplateDateFormatFactory("iso ms u"));
+cfg.setCustomDateFormats(customDateFormats);</programlisting>
+
+ <para>So now you can do this in a template:</para>
+
+ <programlisting role="template">${product.price?string.@price}
+${product.weight?string.@weight}
+${lastModified?string.@fileDate}
+${lastError.timestamp?string.@logEventTime}</programlisting>
+
+ <para>Note that the constructor parameter of
+ <literal>AliasTemplateNumberFormatFactory</literal> can naturally
+ refer to a custom format too:</para>
+
+ <programlisting role="unspecified">Map<String, TemplateNumberFormatFactory> customNumberFormats
+ = new HashMap<String, TemplateNumberFormatFactory>();
+customNumberFormats.put("base", BaseNTemplateNumberFormatFactory.INSTANCE);
+customNumberFormats.put("oct", new AliasTemplateNumberFormatFactory("@base 8"));
+cfg.setCustomNumberFormats(customNumberFormats);</programlisting>
+
+ <para>So now
+ <literal><replaceable>n</replaceable>?string.@oct</literal> will
+ format the number to octal form.</para>
+ </section>
+
+ <section xml:id="pgui_config_custom_formats_ex_model_aware">
+ <title>Model-aware format example</title>
+
+ <para>In this example we specify a number format that automatically
+ show the unit after the number if that was put into the data-model
+ as <literal>UnitAwareTemplateNumberModel</literal>. First let's see
+ <literal>UnitAwareTemplateNumberModel</literal>:</para>
+
+ <programlisting role="unspecified">package com.example;
+
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+
+public class UnitAwareTemplateNumberModel implements TemplateNumberModel {
+
+ private final Number value;
+ private final String unit;
+
+ public UnitAwareTemplateNumberModel(Number value, String unit) {
+ this.value = value;
+ this.unit = unit;
+ }
+
+ @Override
+ public Number getAsNumber() throws TemplateModelException {
+ return value;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+}</programlisting>
+
+ <para>When you fill the data-model, you could do something like
+ this:</para>
+
+ <programlisting role="unspecified">Map<String, Object> dataModel = new HashMap<>();
+dataModel.put("weight", new UnitAwareTemplateNumberModel(1.5, "kg"));
+// Rather than just: dataModel.put("weight", 1.5);</programlisting>
+
+ <para>Then if we have this in the template:</para>
+
+ <programlisting role="template">${weight}</programlisting>
+
+ <para>we want to see this:</para>
+
+ <programlisting role="output">1.5 kg</programlisting>
+
+ <para>To achieve that, we define this custom number format:</para>
+
+ <programlisting role="unspecified">package com.example;
+
+import java.util.Locale;
+
+import freemarker.core.Environment;
+import freemarker.core.TemplateNumberFormat;
+import freemarker.core.TemplateNumberFormatFactory;
+import freemarker.core.TemplateValueFormatException;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+
+/**
+ * A number format that takes any other number format as parameter (specified as a string, as
+ * usual in FreeMarker), then if the model is a {@link UnitAwareTemplateNumberModel}, it shows
+ * the unit after the number formatted with the other format, otherwise it just shows the formatted
+ * number without unit.
+ */
+public class UnitAwareTemplateNumberFormatFactory extends TemplateNumberFormatFactory {
+
+ public static final UnitAwareTemplateNumberFormatFactory INSTANCE
+ = new UnitAwareTemplateNumberFormatFactory();
+
+ private UnitAwareTemplateNumberFormatFactory() {
+ // Defined to decrease visibility
+ }
+
+ @Override
+ public TemplateNumberFormat get(String params, Locale locale, Environment env)
+ throws TemplateValueFormatException {
+ return new UnitAwareNumberFormat(env.getTemplateNumberFormat(params, locale));
+ }
+
+ private static class UnitAwareNumberFormat extends TemplateNumberFormat {
+
+ private final TemplateNumberFormat innerFormat;
+
+ private UnitAwareNumberFormat(TemplateNumberFormat innerFormat) {
+ this.innerFormat = innerFormat;
+ }
+
+ @Override
+ public String formatToPlainText(TemplateNumberModel numberModel)
+ throws TemplateModelException, TemplateValueFormatException {
+ String innerResult = innerFormat.formatToPlainText(numberModel);
+ return numberModel instanceof UnitAwareTemplateNumberModel
+ ? innerResult + " " + ((UnitAwareTemplateNumberModel) numberModel).getUnit()
+ : innerResult;
+ }
+
+ @Override
+ public boolean isLocaleBound() {
+ return innerFormat.isLocaleBound();
+ }
+
+ @Override
+ public String getDescription() {
+ return "unit-aware " + innerFormat.getDescription();
+ }
+
+ }
+
+}</programlisting>
+
+ <para>Finally, we set the above custom format as the default number
+ format:</para>
+
+ <programlisting role="unspecified">// Where you initalize the application-wide Configuration singleton:
+Configuration cfg = ...;
+
+Map<String, TemplateNumberFormatFactory> customNumberFormats = new HashMap<>();
+customNumberFormats.put("ua", UnitAwareTemplateNumberFormatFactory.INSTANCE);
+cfg.setCustomNumberFormats(customNumberFormats);
+
+// Note: "0.####;; roundingMode=halfUp" is a standard format specified in FreeMarker.
+cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");</programlisting>
+ </section>
</section>
<section xml:id="pgui_config_incompatible_improvements">
@@ -15346,7 +15910,8 @@ Tue, Apr 8, '03
</note>
<para>To prevent misunderstandings, the format need not be a string
- literal, it can be a variable or any other expression, like in
+ literal, it can be a variable or any other expression as far as it
+ evaluates to a string. For example, it can be like
<literal>"<replaceable>...</replaceable>"?string[myFormat]</literal>.</para>
<para>See also: <link
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java b/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java
index 3639a38..a669bc8 100644
--- a/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java
+++ b/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java
@@ -25,9 +25,15 @@ import freemarker.template.TemplateNumberModel;
import freemarker.template.utility.NumberUtil;
import freemarker.template.utility.StringUtil;
+/**
+ * Shows a number in base N number system. Can only format numbers that fit into an {@code int},
+ * however, optionally you can specify a fallback format. This format has one required parameter,
+ * the numerical system base. That can be optionally followed by "|" and a fallback format.
+ */
public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactory {
- public static final BaseNTemplateNumberFormatFactory INSTANCE = new BaseNTemplateNumberFormatFactory();
+ public static final BaseNTemplateNumberFormatFactory INSTANCE
+ = new BaseNTemplateNumberFormatFactory();
private BaseNTemplateNumberFormatFactory() {
// Defined to decrease visibility
@@ -61,10 +67,14 @@ public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactor
} catch (NumberFormatException e) {
if (params.length() == 0) {
throw new InvalidFormatParametersException(
- "A format parameter is required, which specifies the numerical system base.");
+ "A format parameter is required to specify the numerical system base.");
}
throw new InvalidFormatParametersException(
- "The format paramter must be an integer, but was (shown quoted): " + StringUtil.jQuote(params));
+ "The format paramter must be an integer, but was (shown quoted): "
+ + StringUtil.jQuote(params));
+ }
+ if (base < 2) {
+ throw new InvalidFormatParametersException("A base must be at least 2.");
}
return new BaseNTemplateNumberFormat(base, fallbackFormat);
}
@@ -88,7 +98,8 @@ public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactor
} catch (ArithmeticException e) {
if (fallbackFormat == null) {
throw new UnformattableValueException(
- n + " doesn't fit into an int, and there was no fallback format specified.");
+ n + " doesn't fit into an int, and there was no fallback format "
+ + "specified.");
} else {
return fallbackFormat.formatToPlainText(numberModel);
}
@@ -102,7 +113,7 @@ public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactor
@Override
public String getDescription() {
- return "hexadecimal int";
+ return "base " + base;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java b/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java
index bbf5cd5..33fbcc3 100644
--- a/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java
+++ b/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java
@@ -27,22 +27,26 @@ import freemarker.template.TemplateModelException;
public class EpochMillisTemplateDateFormatFactory extends TemplateDateFormatFactory {
- public static final EpochMillisTemplateDateFormatFactory INSTANCE = new EpochMillisTemplateDateFormatFactory();
+ public static final EpochMillisTemplateDateFormatFactory INSTANCE
+ = new EpochMillisTemplateDateFormatFactory();
private EpochMillisTemplateDateFormatFactory() {
// Defined to decrease visibility
}
@Override
- public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput,
- Environment env) throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException {
+ public TemplateDateFormat get(String params, int dateType,
+ Locale locale, TimeZone timeZone, boolean zonelessInput,
+ Environment env)
+ throws InvalidFormatParametersException {
TemplateFormatUtil.checkHasNoParameters(params);
return EpochMillisTemplateDateFormat.INSTANCE;
}
private static class EpochMillisTemplateDateFormat extends TemplateDateFormat {
- private static final EpochMillisTemplateDateFormat INSTANCE = new EpochMillisTemplateDateFormat();
+ private static final EpochMillisTemplateDateFormat INSTANCE
+ = new EpochMillisTemplateDateFormat();
private EpochMillisTemplateDateFormat() { }
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/java/freemarker/manual/CustomFormatsExample.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/manual/CustomFormatsExample.java b/src/test/java/freemarker/manual/CustomFormatsExample.java
new file mode 100644
index 0000000..31de217
--- /dev/null
+++ b/src/test/java/freemarker/manual/CustomFormatsExample.java
@@ -0,0 +1,73 @@
+package freemarker.manual;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import freemarker.core.AliasTemplateDateFormatFactory;
+import freemarker.core.AliasTemplateNumberFormatFactory;
+import freemarker.core.BaseNTemplateNumberFormatFactory;
+import freemarker.core.TemplateDateFormatFactory;
+import freemarker.core.TemplateNumberFormatFactory;
+import freemarker.template.Configuration;
+import freemarker.template.TemplateException;
+
+@SuppressWarnings("boxing")
+public class CustomFormatsExample extends ExamplesTest {
+
+ @Test
+ public void aliases1() throws IOException, TemplateException {
+ Configuration cfg = getConfiguration();
+
+ Map<String, TemplateNumberFormatFactory> customNumberFormats
+ = new HashMap<String, TemplateNumberFormatFactory>();
+ customNumberFormats.put("price", new AliasTemplateNumberFormatFactory(",000.00"));
+ customNumberFormats.put("weight", new AliasTemplateNumberFormatFactory("0.##;; roundingMode=halfUp"));
+ cfg.setCustomNumberFormats(customNumberFormats);
+
+ Map<String, TemplateDateFormatFactory> customDateFormats
+ = new HashMap<String, TemplateDateFormatFactory>();
+ customDateFormats.put("fileDate", new AliasTemplateDateFormatFactory("dd/MMM/yy hh:mm a"));
+ customDateFormats.put("logEventTime", new AliasTemplateDateFormatFactory("iso ms u"));
+ cfg.setCustomDateFormats(customDateFormats);
+
+ addToDataModel("p", 10000);
+ addToDataModel("w", 10.305);
+ addToDataModel("fd", new Date(1450904944213L));
+ addToDataModel("let", new Date(1450904944213L));
+
+ assertOutputForNamed("CustomFormatsExample-alias1.ftlh");
+ }
+
+ @Test
+ public void aliases2() throws IOException, TemplateException {
+ Configuration cfg = getConfiguration();
+
+ Map<String, TemplateNumberFormatFactory> customNumberFormats
+ = new HashMap<String, TemplateNumberFormatFactory>();
+ customNumberFormats.put("base", BaseNTemplateNumberFormatFactory.INSTANCE);
+ customNumberFormats.put("oct", new AliasTemplateNumberFormatFactory("@base 8"));
+ cfg.setCustomNumberFormats(customNumberFormats);
+
+ assertOutputForNamed("CustomFormatsExample-alias2.ftlh");
+ }
+
+ @Test
+ public void modelAware() throws IOException, TemplateException {
+ Configuration cfg = getConfiguration();
+
+ Map<String, TemplateNumberFormatFactory> customNumberFormats
+ = new HashMap<String, TemplateNumberFormatFactory>();
+ customNumberFormats.put("ua", UnitAwareTemplateNumberFormatFactory.INSTANCE);
+ cfg.setCustomNumberFormats(customNumberFormats);
+ cfg.setNumberFormat("@ua 0.####;; roundingMode=halfUp");
+
+ addToDataModel("weight", new UnitAwareTemplateNumberModel(1.5, "kg"));
+
+ assertOutputForNamed("CustomFormatsExample-modelAware.ftlh");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/java/freemarker/manual/UnitAwareTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/manual/UnitAwareTemplateNumberFormatFactory.java b/src/test/java/freemarker/manual/UnitAwareTemplateNumberFormatFactory.java
new file mode 100644
index 0000000..df179d4
--- /dev/null
+++ b/src/test/java/freemarker/manual/UnitAwareTemplateNumberFormatFactory.java
@@ -0,0 +1,62 @@
+package freemarker.manual;
+
+import java.util.Locale;
+
+import freemarker.core.Environment;
+import freemarker.core.TemplateNumberFormat;
+import freemarker.core.TemplateNumberFormatFactory;
+import freemarker.core.TemplateValueFormatException;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+
+/**
+ * A number format that takes any other number format as parameter (specified as a string, as
+ * usual in FreeMarker), then if the model is a {@link UnitAwareTemplateNumberModel}, it shows
+ * the unit after the number formatted with the other format, otherwise it just shows the formatted
+ * number without unit.
+ */
+public class UnitAwareTemplateNumberFormatFactory extends TemplateNumberFormatFactory {
+
+ public static final UnitAwareTemplateNumberFormatFactory INSTANCE
+ = new UnitAwareTemplateNumberFormatFactory();
+
+ private UnitAwareTemplateNumberFormatFactory() {
+ // Defined to decrease visibility
+ }
+
+ @Override
+ public TemplateNumberFormat get(String params, Locale locale, Environment env)
+ throws TemplateValueFormatException {
+ return new UnitAwareNumberFormat(env.getTemplateNumberFormat(params, locale));
+ }
+
+ private static class UnitAwareNumberFormat extends TemplateNumberFormat {
+
+ private final TemplateNumberFormat innerFormat;
+
+ private UnitAwareNumberFormat(TemplateNumberFormat innerFormat) {
+ this.innerFormat = innerFormat;
+ }
+
+ @Override
+ public String formatToPlainText(TemplateNumberModel numberModel)
+ throws TemplateModelException, TemplateValueFormatException {
+ String innerResult = innerFormat.formatToPlainText(numberModel);
+ return numberModel instanceof UnitAwareTemplateNumberModel
+ ? innerResult + " " + ((UnitAwareTemplateNumberModel) numberModel).getUnit()
+ : innerResult;
+ }
+
+ @Override
+ public boolean isLocaleBound() {
+ return innerFormat.isLocaleBound();
+ }
+
+ @Override
+ public String getDescription() {
+ return "unit-aware " + innerFormat.getDescription();
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/java/freemarker/manual/UnitAwareTemplateNumberModel.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/manual/UnitAwareTemplateNumberModel.java b/src/test/java/freemarker/manual/UnitAwareTemplateNumberModel.java
new file mode 100644
index 0000000..fc58431
--- /dev/null
+++ b/src/test/java/freemarker/manual/UnitAwareTemplateNumberModel.java
@@ -0,0 +1,25 @@
+package freemarker.manual;
+
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+
+public class UnitAwareTemplateNumberModel implements TemplateNumberModel {
+
+ private final Number value;
+ private final String unit;
+
+ public UnitAwareTemplateNumberModel(Number value, String unit) {
+ this.value = value;
+ this.unit = unit;
+ }
+
+ @Override
+ public Number getAsNumber() throws TemplateModelException {
+ return value;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh b/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh
new file mode 100644
index 0000000..abf6dfe
--- /dev/null
+++ b/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh
@@ -0,0 +1,4 @@
+${p?string.@price}
+${w?string.@weight}
+${fd?string.@fileDate}
+${let?datetime?string.@logEventTime}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh.out
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh.out b/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh.out
new file mode 100644
index 0000000..a15bd01
--- /dev/null
+++ b/src/test/resources/freemarker/manual/CustomFormatsExample-alias1.ftlh.out
@@ -0,0 +1,4 @@
+10,000.00
+10.31
+23/Dec/15 10:09 PM
+2015-12-23T21:09:04.213Z
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh b/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh
new file mode 100644
index 0000000..ae64acb
--- /dev/null
+++ b/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh
@@ -0,0 +1 @@
+${10?string.@oct}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh.out
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh.out b/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh.out
new file mode 100644
index 0000000..3cacc0b
--- /dev/null
+++ b/src/test/resources/freemarker/manual/CustomFormatsExample-alias2.ftlh.out
@@ -0,0 +1 @@
+12
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh b/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh
new file mode 100644
index 0000000..49ce264
--- /dev/null
+++ b/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh
@@ -0,0 +1,2 @@
+${10.12356}
+${weight}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f112ed6c/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh.out
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh.out b/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh.out
new file mode 100644
index 0000000..22e0890
--- /dev/null
+++ b/src/test/resources/freemarker/manual/CustomFormatsExample-modelAware.ftlh.out
@@ -0,0 +1,2 @@
+10.1236
+1.5 kg
\ No newline at end of file