Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 6774e61fa -> ef9687577


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/BaseNTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/BaseNTemplateNumberFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/BaseNTemplateNumberFormatFactory.java
index f310d30..83e6116 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/BaseNTemplateNumberFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/BaseNTemplateNumberFormatFactory.java
@@ -21,12 +21,12 @@ package org.apache.freemarker.core.userpkg;
 import java.util.Locale;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.TemplateNumberFormat;
-import org.apache.freemarker.core.TemplateNumberFormatFactory;
-import org.apache.freemarker.core.TemplateValueFormatException;
-import org.apache.freemarker.core.UnformattableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateValueFormatException;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.util._NumberUtil;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisDivTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisDivTemplateDateFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisDivTemplateDateFormatFactory.java
index 3eb8969..3404729 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisDivTemplateDateFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisDivTemplateDateFormatFactory.java
@@ -23,13 +23,13 @@ import java.util.Locale;
 import java.util.TimeZone;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateDateFormat;
-import org.apache.freemarker.core.TemplateDateFormatFactory;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.UnformattableValueException;
-import 
org.apache.freemarker.core.UnknownDateTypeFormattingUnsupportedException;
-import org.apache.freemarker.core.UnparsableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateDateFormat;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
+import 
org.apache.freemarker.core.valueformat.UnknownDateTypeFormattingUnsupportedException;
+import org.apache.freemarker.core.valueformat.UnparsableValueException;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.util._StringUtil;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisTemplateDateFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisTemplateDateFormatFactory.java
index 4e3394b..4948722 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisTemplateDateFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/EpochMillisTemplateDateFormatFactory.java
@@ -23,12 +23,12 @@ import java.util.Locale;
 import java.util.TimeZone;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateDateFormat;
-import org.apache.freemarker.core.TemplateDateFormatFactory;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.UnformattableValueException;
-import org.apache.freemarker.core.UnparsableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateDateFormat;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
+import org.apache.freemarker.core.valueformat.UnparsableValueException;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/HTMLISOTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/HTMLISOTemplateDateFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/HTMLISOTemplateDateFormatFactory.java
index a46de8c..6e61053 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/HTMLISOTemplateDateFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/HTMLISOTemplateDateFormatFactory.java
@@ -23,14 +23,14 @@ import java.util.Locale;
 import java.util.TimeZone;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateDateFormat;
-import org.apache.freemarker.core.TemplateDateFormatFactory;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.TemplateValueFormatException;
-import org.apache.freemarker.core.UnformattableValueException;
-import 
org.apache.freemarker.core.UnknownDateTypeFormattingUnsupportedException;
-import org.apache.freemarker.core.UnparsableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateDateFormat;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.TemplateValueFormatException;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
+import 
org.apache.freemarker.core.valueformat.UnknownDateTypeFormattingUnsupportedException;
+import org.apache.freemarker.core.valueformat.UnparsableValueException;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/HexTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/HexTemplateNumberFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/HexTemplateNumberFormatFactory.java
index 06bc6cb..565eedd 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/HexTemplateNumberFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/HexTemplateNumberFormatFactory.java
@@ -21,11 +21,11 @@ package org.apache.freemarker.core.userpkg;
 import java.util.Locale;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.TemplateNumberFormat;
-import org.apache.freemarker.core.TemplateNumberFormatFactory;
-import org.apache.freemarker.core.UnformattableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.util._NumberUtil;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/LocAndTZSensitiveTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/LocAndTZSensitiveTemplateDateFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/LocAndTZSensitiveTemplateDateFormatFactory.java
index fa8fbf7..f8f419d 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/LocAndTZSensitiveTemplateDateFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/LocAndTZSensitiveTemplateDateFormatFactory.java
@@ -23,13 +23,13 @@ import java.util.Locale;
 import java.util.TimeZone;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateDateFormat;
-import org.apache.freemarker.core.TemplateDateFormatFactory;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.UnformattableValueException;
-import 
org.apache.freemarker.core.UnknownDateTypeFormattingUnsupportedException;
-import org.apache.freemarker.core.UnparsableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateDateFormat;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
+import 
org.apache.freemarker.core.valueformat.UnknownDateTypeFormattingUnsupportedException;
+import org.apache.freemarker.core.valueformat.UnparsableValueException;
 import org.apache.freemarker.core.model.TemplateDateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/LocaleSensitiveTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/LocaleSensitiveTemplateNumberFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/LocaleSensitiveTemplateNumberFormatFactory.java
index 7bc23ba..231246b 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/LocaleSensitiveTemplateNumberFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/LocaleSensitiveTemplateNumberFormatFactory.java
@@ -21,11 +21,11 @@ package org.apache.freemarker.core.userpkg;
 import java.util.Locale;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.TemplateNumberFormat;
-import org.apache.freemarker.core.TemplateNumberFormatFactory;
-import org.apache.freemarker.core.UnformattableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/userpkg/PrintfGTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/userpkg/PrintfGTemplateNumberFormatFactory.java
 
b/src/test/java/org/apache/freemarker/core/userpkg/PrintfGTemplateNumberFormatFactory.java
index b81f043..7364df2 100644
--- 
a/src/test/java/org/apache/freemarker/core/userpkg/PrintfGTemplateNumberFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/core/userpkg/PrintfGTemplateNumberFormatFactory.java
@@ -23,11 +23,11 @@ import java.math.BigInteger;
 import java.util.Locale;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.InvalidFormatParametersException;
-import org.apache.freemarker.core.TemplateFormatUtil;
-import org.apache.freemarker.core.TemplateNumberFormat;
-import org.apache.freemarker.core.TemplateNumberFormatFactory;
-import org.apache.freemarker.core.UnformattableValueException;
+import org.apache.freemarker.core.valueformat.InvalidFormatParametersException;
+import org.apache.freemarker.core.valueformat.TemplateFormatUtil;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.UnformattableValueException;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java 
b/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
new file mode 100644
index 0000000..d5c8adb
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/valueformat/NumberFormatTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.valueformat;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.freemarker.core.*;
+import org.apache.freemarker.core.model.TemplateDirectiveBody;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+import 
org.apache.freemarker.core.templateresolver.ConditionalTemplateConfigurationFactory;
+import org.apache.freemarker.core.templateresolver.FileNameGlobMatcher;
+import org.apache.freemarker.core.userpkg.BaseNTemplateNumberFormatFactory;
+import org.apache.freemarker.core.userpkg.HexTemplateNumberFormatFactory;
+import 
org.apache.freemarker.core.userpkg.LocaleSensitiveTemplateNumberFormatFactory;
+import org.apache.freemarker.core.userpkg.PrintfGTemplateNumberFormatFactory;
+import 
org.apache.freemarker.core.valueformat.impl.AliasTemplateNumberFormatFactory;
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+@SuppressWarnings("boxing")
+public class NumberFormatTest extends TemplateTest {
+    
+    @Before
+    public void setup() {
+        Configuration cfg = getConfiguration();
+        cfg.setLocale(Locale.US);
+        
+        cfg.setCustomNumberFormats(ImmutableMap.of(
+                "hex", HexTemplateNumberFormatFactory.INSTANCE,
+                "loc", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE,
+                "base", BaseNTemplateNumberFormatFactory.INSTANCE,
+                "printfG", PrintfGTemplateNumberFormatFactory.INSTANCE));
+    }
+
+    @Test
+    public void testUnknownCustomFormat() throws Exception {
+        {
+            getConfiguration().setNumberFormat("@noSuchFormat");
+            Throwable exc = assertErrorContains("${1}", "\"@noSuchFormat\"", 
"\"noSuchFormat\"");
+            assertThat(exc.getCause(), 
instanceOf(UndefinedCustomFormatException.class));
+        }
+
+        {
+            getConfiguration().setNumberFormat("number");
+            Throwable exc = 
assertErrorContains("${1?string('@noSuchFormat2')}",
+                    "\"@noSuchFormat2\"", "\"noSuchFormat2\"");
+            assertThat(exc.getCause(), 
instanceOf(UndefinedCustomFormatException.class));
+        }
+    }
+    
+    @Test
+    public void testStringBI() throws Exception {
+        assertOutput("${11} ${11?string.@hex} ${12} ${12?string.@hex}", "11 b 
12 c");
+    }
+
+    @Test
+    public void testSetting() throws Exception {
+        getConfiguration().setNumberFormat("@hex");
+        assertOutput("${11?string.number} ${11} ${12?string.number} ${12}", 
"11 b 12 c");
+    }
+
+    @Test
+    public void testSetting2() throws Exception {
+        assertOutput(
+                "<#setting numberFormat='@hex'>${11?string.number} ${11} 
${12?string.number} ${12} ${13?string}"
+                + "<#setting numberFormat='@loc'>${11?string.number} ${11} 
${12?string.number} ${12} ${13?string}",
+                "11 b 12 c d"
+                + "11 11_en_US 12 12_en_US 13_en_US");
+    }
+    
+    @Test
+    public void testUnformattableNumber() throws Exception {
+        getConfiguration().setNumberFormat("@hex");
+        assertErrorContains("${1.1}", "hexadecimal int", "doesn't fit into an 
int");
+    }
+
+    @Test
+    public void testLocaleSensitive() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setNumberFormat("@loc");
+        assertOutput("${1.1}", "1.1_en_US");
+        cfg.setLocale(Locale.GERMANY);
+        assertOutput("${1.1}", "1.1_de_DE");
+    }
+
+    @Test
+    public void testLocaleSensitive2() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setNumberFormat("@loc");
+        assertOutput("${1.1} <#setting locale='de_DE'>${1.1}", "1.1_en_US 
1.1_de_DE");
+    }
+
+    @Test
+    public void testCustomParameterized() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setNumberFormat("@base 2");
+        assertOutput("${11}", "1011");
+        assertOutput("${11?string}", "1011");
+        assertOutput("${11?string.@base_3}", "102");
+        
+        assertErrorContains("${11?string.@base_xyz}", "\"@base_xyz\"", 
"\"xyz\"");
+        cfg.setNumberFormat("@base");
+        assertErrorContains("${11}", "\"@base\"", "format parameter is 
required");
+    }
+
+    @Test
+    public void testCustomWithFallback() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setNumberFormat("@base 2|0.0#");
+        assertOutput("${11}", "1011");
+        assertOutput("${11.34}", "11.34");
+        assertOutput("${11?string('@base 3|0.00')}", "102");
+        assertOutput("${11.2?string('@base 3|0.00')}", "11.20");
+    }
+
+    @Test
+    public void testEnvironmentGetters() throws Exception {
+        Template t = new Template(null, "", getConfiguration());
+        Environment env = t.createProcessingEnvironment(null, null);
+        
+        TemplateNumberFormat defF = env.getTemplateNumberFormat();
+        //
+        TemplateNumberFormat explF = env.getTemplateNumberFormat("0.00");
+        assertEquals("1.25", explF.formatToPlainText(new SimpleNumber(1.25)));
+        //
+        TemplateNumberFormat expl2F = env.getTemplateNumberFormat("@loc");
+        assertEquals("1.25_en_US", expl2F.formatToPlainText(new 
SimpleNumber(1.25)));
+        
+        TemplateNumberFormat explFFr = env.getTemplateNumberFormat("0.00", 
Locale.FRANCE);
+        assertNotSame(explF, explFFr);
+        assertEquals("1,25", explFFr.formatToPlainText(new 
SimpleNumber(1.25)));
+        //
+        TemplateNumberFormat expl2FFr = env.getTemplateNumberFormat("@loc", 
Locale.FRANCE);
+        assertEquals("1.25_fr_FR", expl2FFr.formatToPlainText(new 
SimpleNumber(1.25)));
+        
+        assertSame(env.getTemplateNumberFormat(), defF);
+        //
+        assertSame(env.getTemplateNumberFormat("0.00"), explF);
+        //
+        assertSame(env.getTemplateNumberFormat("@loc"), expl2F);
+    }
+    
+    /**
+     * ?string formats lazily (at least in 2.3.x), so it must make a snapshot 
of the format inputs when it's called.
+     */
+    @Test
+    @Ignore // [FM3] We want to rework BI-s so that lazy evaluation won't be 
needed. Then this will go away too.
+    public void testStringBIDoesSnapshot() throws Exception {
+        // TemplateNumberModel-s shouldn't change, but we have to keep BC when 
that still happens.
+        final MutableTemplateNumberModel nm = new MutableTemplateNumberModel();
+        nm.setNumber(123);
+        addToDataModel("n", nm);
+        addToDataModel("incN", new TemplateDirectiveModel() {
+            
+            @Override
+            public void execute(Environment env, Map params, TemplateModel[] 
loopVars, TemplateDirectiveBody body)
+                    throws TemplateException, IOException {
+                nm.setNumber(nm.getAsNumber().intValue() + 1);
+            }
+        });
+        assertOutput(
+                "<#assign s1 = n?string>"
+                + "<#setting numberFormat='@loc'>"
+                + "<#assign s2 = n?string>"
+                + "<#setting numberFormat='@hex'>"
+                + "<#assign s3 = n?string>"
+                + "${s1} ${s2} ${s3}",
+                "123 123_en_US 7b");
+        assertOutput(
+                "<#assign s1 = n?string>"
+                + "<@incN />"
+                + "<#assign s2 = n?string>"
+                + "${s1} ${s2}",
+                "123 124");
+    }
+
+    @Test
+    public void testNullInModel() throws Exception {
+        addToDataModel("n", new MutableTemplateNumberModel());
+        assertErrorContains("${n}", "nothing inside it");
+        assertErrorContains("${n?string}", "nothing inside it");
+    }
+    
+    @Test
+    public void testAtPrefix() throws Exception {
+        Configuration cfg = getConfiguration();
+        
+        cfg.setNumberFormat("@hex");
+        assertOutput("${10}", "a");
+        cfg.setNumberFormat("'@'0");
+        assertOutput("${10}", "@10");
+        cfg.setNumberFormat("@@0");
+        assertOutput("${10}", "@@10");
+        
+        cfg.setCustomNumberFormats(Collections.<String, 
TemplateNumberFormatFactory>emptyMap());
+        cfg.setNumberFormat("@hex");
+        assertErrorContains("${10}", "custom", "\"hex\"");
+        cfg.setNumberFormat("'@'0");
+        assertOutput("${10}", "@10");
+        cfg.setNumberFormat("@@0");
+        assertOutput("${10}", "@@10");
+    }
+
+    @Test
+    public void testAlieses() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setCustomNumberFormats(ImmutableMap.of(
+                "f", new AliasTemplateNumberFormatFactory("0.#'f'"),
+                "d", new AliasTemplateNumberFormatFactory("0.0#"),
+                "hex", HexTemplateNumberFormatFactory.INSTANCE));
+        
+        TemplateConfiguration tc = new TemplateConfiguration();
+        tc.setCustomNumberFormats(ImmutableMap.of(
+                "d", new AliasTemplateNumberFormatFactory("0.#'d'"),
+                "i", new AliasTemplateNumberFormatFactory("@hex")));
+        cfg.setTemplateConfigurations(new 
ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*2*"), tc));
+        
+        String commonFtl = "${1?string.@f} ${1?string.@d} "
+                + "<#setting locale='fr_FR'>${1.5?string.@d} "
+                + "<#attempt>${10?string.@i}<#recover>E</#attempt>";
+        addTemplate("t1.ftl", commonFtl);
+        addTemplate("t2.ftl", commonFtl);
+        
+        assertOutputForNamed("t1.ftl", "1f 1.0 1,5 E");
+        assertOutputForNamed("t2.ftl", "1f 1d 1,5d a");
+    }
+
+    @Test
+    public void testAlieses2() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setCustomNumberFormats(ImmutableMap.of(
+                "n", new AliasTemplateNumberFormatFactory("0.0",
+                        ImmutableMap.of(
+                                new Locale("en"), "0.0'_en'",
+                                Locale.UK, "0.0'_en_GB'",
+                                Locale.FRANCE, "0.0'_fr_FR'"))));
+        cfg.setNumberFormat("@n");
+        assertOutput(
+                "<#setting locale='en_US'>${1} "
+                + "<#setting locale='en_GB'>${1} "
+                + "<#setting locale='en_GB_Win'>${1} "
+                + "<#setting locale='fr_FR'>${1} "
+                + "<#setting locale='hu_HU'>${1}",
+                "1.0_en 1.0_en_GB 1.0_en_GB 1,0_fr_FR 1,0");
+    }
+    
+    @Test
+    public void testMarkupFormat() throws IOException, TemplateException {
+        getConfiguration().setNumberFormat("@printfG_3");
+
+        String commonFTL = "${1234567} ${'cat:' + 1234567} ${0.0000123}";
+        String commonOutput = "1.23*10<sup>6</sup> cat:1.23*10<sup>6</sup> 
1.23*10<sup>-5</sup>";
+        assertOutput(commonFTL, commonOutput);
+        assertOutput("<#ftl outputFormat='HTML'>" + commonFTL, commonOutput);
+        assertOutput("<#escape x as x?html>" + commonFTL + "</#escape>", 
commonOutput);
+        assertOutput("<#escape x as x?xhtml>" + commonFTL + "</#escape>", 
commonOutput);
+        assertOutput("<#escape x as x?xml>" + commonFTL + "</#escape>", 
commonOutput);
+        assertOutput("${\"" + commonFTL + "\"}", "1.23*10<sup>6</sup> 
cat:1.23*10<sup>6</sup> 1.23*10<sup>-5</sup>");
+        assertErrorContains("<#ftl outputFormat='plainText'>" + commonFTL, 
"HTML", "plainText", "conversion");
+    }
+
+    @Test
+    public void testPrintG() throws IOException, TemplateException {
+        for (Number n : new Number[] {
+                1234567, 1234567L, 1234567d, 1234567f, 
BigInteger.valueOf(1234567), BigDecimal.valueOf(1234567) }) {
+            addToDataModel("n", n);
+            
+            assertOutput("${n?string.@printfG}", "1.23457E+06");
+            assertOutput("${n?string.@printfG_3}", "1.23E+06");
+            assertOutput("${n?string.@printfG_7}", "1234567");
+            assertOutput("${0.0000123?string.@printfG}", "1.23000E-05");
+        }
+    }
+    
+    private static class MutableTemplateNumberModel implements 
TemplateNumberModel {
+        
+        private Number number;
+
+        public void setNumber(Number number) {
+            this.number = number;
+        }
+
+        @Override
+        public Number getAsNumber() throws TemplateModelException {
+            return number;
+        }
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/core/valueformat/impl/ExtendedDecimalFormatTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/core/valueformat/impl/ExtendedDecimalFormatTest.java
 
b/src/test/java/org/apache/freemarker/core/valueformat/impl/ExtendedDecimalFormatTest.java
new file mode 100644
index 0000000..29bbc6a
--- /dev/null
+++ 
b/src/test/java/org/apache/freemarker/core/valueformat/impl/ExtendedDecimalFormatTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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.valueformat.impl;
+
+import static 
org.apache.freemarker.test.hamcerst.Matchers.containsStringIgnoringCase;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Test;
+
+public class ExtendedDecimalFormatTest extends TemplateTest {
+    
+    private static final Locale LOC = Locale.US;
+    
+    @Test
+    public void testNonExtended() throws ParseException {
+        for (String fStr : new String[] { "0.00", "0.###", "#,#0.###", 
"#0.####", "0.0;m", "0.0;",
+                "0'x'", "0'x';'m'", "0';'", "0';';m", "0';';'#'m';'", "0';;'", 
"" }) {
+            assertFormatsEquivalent(new DecimalFormat(fStr), 
ExtendedDecimalFormatParser.parse(fStr, LOC));
+        }
+        
+        try {
+            new DecimalFormat(";");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";", LOC);
+        } catch (ParseException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testNonExtended2() throws ParseException {
+        assertFormatsEquivalent(new DecimalFormat("0.0"), 
ExtendedDecimalFormatParser.parse("0.0;", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0.0"), 
ExtendedDecimalFormatParser.parse("0.0;;", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0.0;m"), 
ExtendedDecimalFormatParser.parse("0.0;m;", LOC));
+        assertFormatsEquivalent(new DecimalFormat(""), 
ExtendedDecimalFormatParser.parse(";;", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0'x'"), 
ExtendedDecimalFormatParser.parse("0'x';;", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0'x';'m'"), 
ExtendedDecimalFormatParser.parse("0'x';'m';", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0';'"), 
ExtendedDecimalFormatParser.parse("0';';;", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0';';m"), 
ExtendedDecimalFormatParser.parse("0';';m;", LOC));
+        assertFormatsEquivalent(new DecimalFormat("0';';'#'m';'"), 
ExtendedDecimalFormatParser.parse("0';';'#'m';';",
+                LOC));
+        assertFormatsEquivalent(new DecimalFormat("0';;'"), 
ExtendedDecimalFormatParser.parse("0';;';;", LOC));
+        
+        try {
+            new DecimalFormat(";m");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+        try {
+            new DecimalFormat("; ;");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+        try {
+            ExtendedDecimalFormatParser.parse("; ;", LOC);
+            fail();
+        } catch (ParseException e) {
+            // Expected
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";m", LOC);
+            fail();
+        } catch (ParseException e) {
+            // Expected
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";m;", LOC);
+            fail();
+        } catch (ParseException e) {
+            // Expected
+        }
+    }
+    
+    @SuppressWarnings("boxing")
+    @Test
+    public void testExtendedParamsParsing() throws ParseException {
+        for (String fs : new String[] {
+                "00.##;; decimalSeparator='D'",
+                "00.##;;decimalSeparator=D",
+                "00.##;;  decimalSeparator  =  D ", "00.##;; decimalSeparator 
= 'D' " }) {
+            assertFormatted(fs, 1.125, "01D12");
+        }
+        for (String fs : new String[] {
+                ",#0.0;; decimalSeparator=D, groupingSeparator=_",
+                ",#0.0;;decimalSeparator=D,groupingSeparator=_",
+                ",#0.0;; decimalSeparator = D , groupingSeparator = _ ",
+                ",#0.0;; decimalSeparator='D', groupingSeparator='_'"
+                }) {
+            assertFormatted(fs, 12345, "1_23_45D0");
+        }
+        
+        assertFormatted("0.0;;infinity=infinity", Double.POSITIVE_INFINITY, 
"infinity");
+        assertFormatted("0.0;;infinity='infinity'", Double.POSITIVE_INFINITY, 
"infinity");
+        assertFormatted("0.0;;infinity=\"infinity\"", 
Double.POSITIVE_INFINITY, "infinity");
+        assertFormatted("0.0;;infinity=''", Double.POSITIVE_INFINITY, "");
+        assertFormatted("0.0;;infinity=\"\"", Double.POSITIVE_INFINITY, "");
+        assertFormatted("0.0;;infinity='x''y'", Double.POSITIVE_INFINITY, 
"x'y");
+        assertFormatted("0.0;;infinity=\"x'y\"", Double.POSITIVE_INFINITY, 
"x'y");
+        assertFormatted("0.0;;infinity='x\"\"y'", Double.POSITIVE_INFINITY, 
"x\"\"y");
+        assertFormatted("0.0;;infinity=\"x''y\"", Double.POSITIVE_INFINITY, 
"x''y");
+        assertFormatted("0.0;;decimalSeparator=''''", 1, "1'0");
+        assertFormatted("0.0;;decimalSeparator=\"'\"", 1, "1'0");
+        assertFormatted("0.0;;decimalSeparator='\"'", 1, "1\"0");
+        assertFormatted("0.0;;decimalSeparator=\"\"\"\"", 1, "1\"0");
+        
+        try {
+            ExtendedDecimalFormatParser.parse(";;decimalSeparator=D,", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(),
+                    allOf(containsStringIgnoringCase("expected a(n) name"), 
containsString(" end of ")));
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";;foo=D,", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(),
+                    allOf(containsString("\"foo\""), containsString("name")));
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";;decimalSeparator='D", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(),
+                    allOf(containsString("quotation"), 
containsString("closed")));
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";;decimalSeparator=\"D", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(),
+                    allOf(containsString("quotation"), 
containsString("closed")));
+        }
+        try {
+            
ExtendedDecimalFormatParser.parse(";;decimalSeparator='D'groupingSeparator=G", 
LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(), allOf(
+                    containsString("separator"), containsString("whitespace"), 
containsString("comma")));
+        }
+        try {
+            ExtendedDecimalFormatParser.parse(";;decimalSeparator=., 
groupingSeparator=G", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(), allOf(
+                    containsStringIgnoringCase("expected a(n) value"), 
containsString("., gr[...]")));
+        }
+        try {
+            ExtendedDecimalFormatParser.parse("0.0;;decimalSeparator=''", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(), allOf(
+                    containsStringIgnoringCase("\"decimalSeparator\""), 
containsString("exactly 1 char")));
+        }
+        try {
+            ExtendedDecimalFormatParser.parse("0.0;;multipier=ten", LOC);
+            fail();
+        } catch (java.text.ParseException e) {
+            assertThat(e.getMessage(), allOf(
+                    containsString("\"multipier\""), 
containsString("\"ten\""), containsString("integer")));
+        }
+    }
+    
+    @SuppressWarnings("boxing")
+    @Test
+    public void testExtendedParamsEffect() throws ParseException {
+        assertFormatted("0",
+                1.5, "2", 2.5, "2", 3.5, "4", 1.4, "1", 1.6, "2", -1.4, "-1", 
-1.5, "-2", -2.5, "-2", -1.6, "-2");
+        assertFormatted("0;; roundingMode=halfEven",
+                1.5, "2", 2.5, "2", 3.5, "4", 1.4, "1", 1.6, "2", -1.4, "-1", 
-1.5, "-2", -2.5, "-2", -1.6, "-2");
+        assertFormatted("0;; roundingMode=halfUp",
+                1.5, "2", 2.5, "3", 3.5, "4", 1.4, "1", 1.6, "2", -1.4, "-1", 
-1.5, "-2", -2.5, "-3", -1.6, "-2");
+        assertFormatted("0;; roundingMode=halfDown",
+                1.5, "1", 2.5, "2", 3.5, "3", 1.4, "1", 1.6, "2", -1.4, "-1", 
-1.5, "-1", -2.5, "-2", -1.6, "-2");
+        assertFormatted("0;; roundingMode=floor",
+                1.5, "1", 2.5, "2", 3.5, "3", 1.4, "1", 1.6, "1", -1.4, "-2", 
-1.5, "-2", -2.5, "-3", -1.6, "-2");
+        assertFormatted("0;; roundingMode=ceiling",
+                1.5, "2", 2.5, "3", 3.5, "4", 1.4, "2", 1.6, "2", -1.4, "-1", 
-1.5, "-1", -2.5, "-2", -1.6, "-1");
+        assertFormatted("0;; roundingMode=up",
+                1.5, "2", 2.5, "3", 3.5, "4", 1.4, "2", 1.6, "2", -1.4, "-2", 
-1.5, "-2", -2.5, "-3", -1.6, "-2");
+        assertFormatted("0;; roundingMode=down",
+                1.5, "1", 2.5, "2", 3.5, "3", 1.4, "1", 1.6, "1", -1.4, "-1", 
-1.5, "-1", -2.5, "-2", -1.6, "-1");
+        assertFormatted("0;; roundingMode=unnecessary", 2, "2");
+        try {
+            assertFormatted("0;; roundingMode=unnecessary", 2.5, "2");
+            fail();
+        } catch (ArithmeticException e) {
+            // Expected
+        }
+
+        assertFormatted("0.##;; multipier=100", 12.345, "1234.5");
+        assertFormatted("0.##;; multipier=1000", 12.345, "12345");
+        
+        assertFormatted(",##0.##;; groupingSeparator=_ decimalSeparator=D", 
12345.1, "12_345D1", 1, "1");
+        
+        assertFormatted("0.##E0;; exponentSeparator='*10^'", 12345.1, 
"1.23*10^4");
+        
+        assertFormatted("0.##;; minusSign=m", -1, "m1", 1, "1");
+        
+        assertFormatted("0.##;; infinity=foo", Double.POSITIVE_INFINITY, 
"foo", Double.NEGATIVE_INFINITY, "-foo");
+        
+        assertFormatted("0.##;; nan=foo", Double.NaN, "foo");
+        
+        assertFormatted("0%;; percent='c'", 0.75, "75c");
+        
+        assertFormatted("0\u2030;; perMill='m'", 0.75, "750m");
+        
+        assertFormatted("0.00;; zeroDigit='@'", 10.5, "[email protected]@");
+        
+        assertFormatted("0;; currencyCode=USD", 10, "10");
+        assertFormatted("0 \u00A4;; currencyCode=USD", 10, "10 $");
+        assertFormatted("0 \u00A4\u00A4;; currencyCode=USD", 10, "10 USD");
+        assertFormatted(Locale.GERMANY, "0 \u00A4;; currencyCode=EUR", 10, "10 
\u20AC");
+        assertFormatted(Locale.GERMANY, "0 \u00A4\u00A4;; currencyCode=EUR", 
10, "10 EUR");
+        try {
+            assertFormatted("0;; currencyCode=USDX", 10, "10");
+        } catch (ParseException e) {
+            assertThat(e.getMessage(), containsString("ISO 4217"));
+        }
+        assertFormatted("0 \u00A4;; currencyCode=USD currencySymbol=bucks", 
10, "10 bucks");
+     // Order doesn't mater:
+        assertFormatted("0 \u00A4;; currencySymbol=bucks currencyCode=USD", 
10, "10 bucks");
+        // International symbol isn't affected:
+        assertFormatted("0 \u00A4\u00A4;; currencyCode=USD 
currencySymbol=bucks", 10, "10 USD");
+        
+        assertFormatted("0.0 \u00A4;; monetaryDecimalSeparator=m", 10.5, "10m5 
$");
+        assertFormatted("0.0 kg;; monetaryDecimalSeparator=m", 10.5, "10.5 
kg");
+        assertFormatted("0.0 \u00A4;; decimalSeparator=d", 10.5, "10.5 $");
+        assertFormatted("0.0 kg;; decimalSeparator=d", 10.5, "10d5 kg");
+        assertFormatted("0.0 \u00A4;; monetaryDecimalSeparator=m 
decimalSeparator=d", 10.5, "10m5 $");
+        assertFormatted("0.0 kg;; monetaryDecimalSeparator=m 
decimalSeparator=d", 10.5, "10d5 kg");
+    }
+    
+    @Test
+    public void testLocale() throws ParseException {
+        assertEquals("1000.0", ExtendedDecimalFormatParser.parse("0.0", 
Locale.US).format(1000));
+        assertEquals("1000,0", ExtendedDecimalFormatParser.parse("0.0", 
Locale.FRANCE).format(1000));
+        assertEquals("1_000.0", 
ExtendedDecimalFormatParser.parse(",000.0;;groupingSeparator=_", 
Locale.US).format(1000));
+        assertEquals("1_000,0", 
ExtendedDecimalFormatParser.parse(",000.0;;groupingSeparator=_", 
Locale.FRANCE).format(1000));
+    }
+    
+    @Test
+    public void testTemplates() throws IOException, TemplateException {
+        Configuration cfg = getConfiguration();
+        cfg.setLocale(Locale.US);
+        
+        cfg.setNumberFormat(",000.#");
+        assertOutput("${1000.15} ${1000.25}", "1,000.2 1,000.2");
+        cfg.setNumberFormat(",000.#;; roundingMode=halfUp 
groupingSeparator=_");
+        assertOutput("${1000.15} ${1000.25}", "1_000.2 1_000.3");
+        cfg.setLocale(Locale.GERMANY);
+        assertOutput("${1000.15} ${1000.25}", "1_000,2 1_000,3");
+        cfg.setLocale(Locale.US);
+        assertOutput(
+                "${1000.15}; "
+                + "${1000.15?string(',##.#;;groupingSeparator=\" \"')}; "
+                + "<#setting locale='de_DE'>${1000.15}; "
+                + "<#setting numberFormat='0.0;;roundingMode=down'>${1000.15}",
+                "1_000.2; 10 00.2; 1_000,2; 1000,1");
+        assertErrorContains("${1?string('#E')}",
+                TemplateException.class, "\"#E\"", "format string", 
"exponential");
+        assertErrorContains("<#setting numberFormat='#E'>${1}",
+                TemplateException.class, "\"#E\"", "format string", 
"exponential");
+        assertErrorContains("<#setting numberFormat=';;foo=bar'>${1}",
+                TemplateException.class, "\"foo\"", "supported");
+        assertErrorContains("<#setting 
numberFormat='0;;roundingMode=unnecessary'>${1.5}",
+                TemplateException.class, "can't format", "1.5", "UNNECESSARY");
+    }
+
+    private void assertFormatted(String formatString, Object... 
numberAndExpectedOutput) throws ParseException {
+        assertFormatted(LOC, formatString, numberAndExpectedOutput);
+    }
+    
+    private void assertFormatted(Locale loc, String formatString, Object... 
numberAndExpectedOutput) throws ParseException {
+        if (numberAndExpectedOutput.length % 2 != 0) {
+            throw new IllegalArgumentException();
+        }
+        
+        DecimalFormat df = ExtendedDecimalFormatParser.parse(formatString, 
loc);
+        Number num = null;
+        for (int i = 0; i < numberAndExpectedOutput.length; i++) {
+            if (i % 2 == 0) {
+                num = (Number) numberAndExpectedOutput[i];
+            } else {
+                assertEquals(numberAndExpectedOutput[i], df.format(num));
+            }
+        }
+    }
+    
+    private void assertFormatsEquivalent(DecimalFormat dfExpected, 
DecimalFormat dfActual) {
+        for (int signum : new int[] { 1, -1 }) {
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 0);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 0.5);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 0.25);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 0.125);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 1);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 10);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 100);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 1000);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 10000);
+            assertFormatsEquivalent(dfExpected, dfActual, signum * 100000);
+        }
+    }
+
+    private void assertFormatsEquivalent(DecimalFormat dfExpected, 
DecimalFormat dfActual, double n) {
+        assertEquals(dfExpected.format(n), dfActual.format(n));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/manualtest/CustomFormatsExample.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/manualtest/CustomFormatsExample.java 
b/src/test/java/org/apache/freemarker/manualtest/CustomFormatsExample.java
index 5441609..0f4cee5 100644
--- a/src/test/java/org/apache/freemarker/manualtest/CustomFormatsExample.java
+++ b/src/test/java/org/apache/freemarker/manualtest/CustomFormatsExample.java
@@ -24,12 +24,12 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.freemarker.core.AliasTemplateDateFormatFactory;
-import org.apache.freemarker.core.AliasTemplateNumberFormatFactory;
+import 
org.apache.freemarker.core.valueformat.impl.AliasTemplateDateFormatFactory;
+import 
org.apache.freemarker.core.valueformat.impl.AliasTemplateNumberFormatFactory;
 import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.TemplateDateFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.TemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
 import org.apache.freemarker.core.userpkg.BaseNTemplateNumberFormatFactory;
 import org.junit.Test;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/test/java/org/apache/freemarker/manualtest/UnitAwareTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/freemarker/manualtest/UnitAwareTemplateNumberFormatFactory.java
 
b/src/test/java/org/apache/freemarker/manualtest/UnitAwareTemplateNumberFormatFactory.java
index 862c5f1..7013b30 100644
--- 
a/src/test/java/org/apache/freemarker/manualtest/UnitAwareTemplateNumberFormatFactory.java
+++ 
b/src/test/java/org/apache/freemarker/manualtest/UnitAwareTemplateNumberFormatFactory.java
@@ -21,9 +21,9 @@ package org.apache.freemarker.manualtest;
 import java.util.Locale;
 
 import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.TemplateNumberFormat;
-import org.apache.freemarker.core.TemplateNumberFormatFactory;
-import org.apache.freemarker.core.TemplateValueFormatException;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
+import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
+import org.apache.freemarker.core.valueformat.TemplateValueFormatException;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 


Reply via email to