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/09/29 02:05:57 UTC

[1/5] incubator-freemarker git commit: Added checks against null returning (custom) TemplateValueFormat results. Minor cleanup and documentation additions in formatting related parts.

Repository: incubator-freemarker
Updated Branches:
  refs/heads/2.3 84927d91a -> 4b1550e78


Added checks against null returning (custom) TemplateValueFormat results. Minor cleanup and documentation additions in formatting 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/9ce036f5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/9ce036f5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/9ce036f5

Branch: refs/heads/2.3
Commit: 9ce036f5d78fef56a03b49f83e88379366f04c3e
Parents: e90143c
Author: ddekany <dd...@apache.org>
Authored: Sat Sep 26 09:19:52 2015 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Sep 26 09:19:52 2015 +0200

----------------------------------------------------------------------
 .../freemarker/core/AddConcatExpression.java    |  1 -
 .../core/BuiltInsForMultipleTypes.java          |  2 +-
 .../java/freemarker/core/DollarVariable.java    |  2 +-
 src/main/java/freemarker/core/Environment.java  |  6 +--
 src/main/java/freemarker/core/EvalUtil.java     | 42 ++++++++++++++++----
 .../freemarker/core/TemplateDateFormat.java     |  2 +
 .../freemarker/core/TemplateNumberFormat.java   |  2 +
 .../java/freemarker/ext/beans/BeanModel.java    |  8 +++-
 8 files changed, 50 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/core/AddConcatExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/AddConcatExpression.java b/src/main/java/freemarker/core/AddConcatExpression.java
index c90cb19..f92b933 100644
--- a/src/main/java/freemarker/core/AddConcatExpression.java
+++ b/src/main/java/freemarker/core/AddConcatExpression.java
@@ -82,7 +82,6 @@ final class AddConcatExpression extends Expression {
                         leftModel, leftExp, (String) null, markupOutputFormat, env);
                 Object rightOMOrStr = EvalUtil.coerceModelToMarkupOutputOrString(
                         rightModel, rightExp, (String) null, markupOutputFormat, env);
-                // TODO prove that neither can be null
 
                 if (leftOMOrStr instanceof String) {
                     if (rightOMOrStr instanceof String) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java b/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
index aaaaf97..deb88b2 100644
--- a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
@@ -578,7 +578,7 @@ class BuiltInsForMultipleTypes {
                                 throw new BugException();
                             }
                         }
-                        cachedValue = defaultFormat.formatToString(dateModel);
+                        cachedValue = EvalUtil.formatResultNotNull(defaultFormat.formatToString(dateModel));
                     } catch (TemplateValueFormatException e) {
                         try {
                             throw MessageUtil.newCantFormatDateException(defaultFormat, target, e, true);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/core/DollarVariable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/DollarVariable.java b/src/main/java/freemarker/core/DollarVariable.java
index 4331b51..23a995b 100644
--- a/src/main/java/freemarker/core/DollarVariable.java
+++ b/src/main/java/freemarker/core/DollarVariable.java
@@ -68,7 +68,7 @@ final class DollarVariable extends Interpolation {
             } else {
                 out.write(s);
             }
-        } else { // moOrStr wasn't output yet
+        } else {
             final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) moOrStr;
             final MarkupOutputFormat moOF = mo.getOutputFormat();
             // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/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 a1073c2..ca6d7b1 100644
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@ -1049,7 +1049,7 @@ public final class Environment extends Configurable {
             boolean useTempModelExc)
             throws TemplateException {
         try {
-            return format.formatToString(number);
+            return EvalUtil.formatResultNotNull(format.formatToString(number));
         } catch (TemplateValueFormatException e) {
             throw MessageUtil.newCantFormatNumberException(format, exp, e, useTempModelExc);
         }
@@ -1324,7 +1324,7 @@ public final class Environment extends Configurable {
         TemplateDateFormat format = getTemplateDateFormat(tdm, tdmSourceExpr, useTempModelExc);
         
         try {
-            return format.formatToString(tdm);
+            return EvalUtil.formatResultNotNull(format.formatToString(tdm));
         } catch (TemplateValueFormatException e) {
             throw MessageUtil.newCantFormatDateException(format, tdmSourceExpr, e, useTempModelExc);
         }
@@ -1347,7 +1347,7 @@ public final class Environment extends Configurable {
                 useTempModelExc);
         
         try {
-            return format.formatToString(tdm);
+            return EvalUtil.formatResultNotNull(format.formatToString(tdm));
         } catch (TemplateValueFormatException e) {
             throw MessageUtil.newCantFormatDateException(format, blamedDateSourceExp, e, useTempModelExc);
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/core/EvalUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/EvalUtil.java b/src/main/java/freemarker/core/EvalUtil.java
index c20dcff..6dfc68d 100644
--- a/src/main/java/freemarker/core/EvalUtil.java
+++ b/src/main/java/freemarker/core/EvalUtil.java
@@ -341,26 +341,32 @@ class EvalUtil {
         }
     }
 
+    /**
+     * @return Never {@code null}
+     */
     static String coerceModelToString(TemplateModel tm, Expression exp, String seqHint,
             Environment env) throws TemplateException {
         if (tm instanceof TemplateNumberModel) {
-            return env.formatNumberToString((TemplateNumberModel) tm, exp, false);
+            return formatResultNotNull(env.formatNumberToString((TemplateNumberModel) tm, exp, false));
         } else if (tm instanceof TemplateDateModel) {
-            return env.formatDateToString((TemplateDateModel) tm, exp, false);
+            return formatResultNotNull(env.formatDateToString((TemplateDateModel) tm, exp, false));
         } else {
             return coerceModelToStringCommon(tm, exp, seqHint, false, env);
         }
     }
 
+    /**
+     * @return Never {@code null}
+     */
     static Object coerceModelToMarkupOutputOrString(TemplateModel tm, Expression exp, String seqHint,
             MarkupOutputFormat markupOutputFormat, Environment env) throws TemplateException {
         if (tm instanceof TemplateNumberModel) {
             TemplateNumberModel tnm = (TemplateNumberModel) tm; 
             TemplateNumberFormat format = env.getTemplateNumberFormat(exp, false);
             try {
-                return markupOutputFormat != null 
+                return formatResultNotNull(markupOutputFormat != null 
                         ? format.formatToMarkupOrString(tnm, markupOutputFormat)
-                        : format.formatToString(tnm);
+                        : format.formatToString(tnm));
             } catch (TemplateValueFormatException e) {
                 throw MessageUtil.newCantFormatNumberException(format, exp, e, false);
             }
@@ -368,9 +374,9 @@ class EvalUtil {
             TemplateDateModel tdm = (TemplateDateModel) tm;
             TemplateDateFormat format = env.getTemplateDateFormat(tdm, exp, false);
             try {
-                return markupOutputFormat != null
+                return formatResultNotNull(markupOutputFormat != null
                         ? format.formatToMarkupOrString(tdm, markupOutputFormat)
-                        : format.formatToString(tdm);
+                        : format.formatToString(tdm));
             } catch (TemplateValueFormatException e) {
                 throw MessageUtil.newCantFormatDateException(format, exp, e, false);
             }
@@ -382,11 +388,17 @@ class EvalUtil {
     }
 
     /**
+     * @param tm
+     *            If {@code null} that's an exception, unless we are in classic compatible mode.
+     * 
      * @param supportsTOM
      *            Whether the caller {@code coerceModelTo...} method could handle a {@link TemplateMarkupOutputModel}.
+     *            
+     * @return Never {@code null}
      */
-    private static String coerceModelToStringCommon(TemplateModel tm, Expression exp, String seqHint, boolean supportsTOM,
-            Environment env) throws TemplateModelException, InvalidReferenceException, TemplateException,
+    private static String coerceModelToStringCommon(
+            TemplateModel tm, Expression exp, String seqHint, boolean supportsTOM, Environment env)
+            throws TemplateModelException, InvalidReferenceException, TemplateException,
                     NonStringOrTemplateOutputException, NonStringException {
         if (tm instanceof TemplateScalarModel) {
             return modelToString((TemplateScalarModel) tm, exp, env);
@@ -444,6 +456,20 @@ class EvalUtil {
         }
     }
 
+    static String formatResultNotNull(String r) {
+        if (r != null) {
+            return r;
+        }
+        throw new NullPointerException("TemplateValueFormatter result can't be null");
+    }
+
+    static Object formatResultNotNull(Object r) {
+        if (r != null) {
+            return r;
+        }
+        throw new NullPointerException("TemplateValueFormatter result can't be null");
+    }
+
     /**
      * Returns an {@link ArithmeticEngine} even if {@code env} is {@code null}, because we are in parsing phase.
      */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/core/TemplateDateFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateDateFormat.java b/src/main/java/freemarker/core/TemplateDateFormat.java
index 6a6d66b..2f49d2d 100644
--- a/src/main/java/freemarker/core/TemplateDateFormat.java
+++ b/src/main/java/freemarker/core/TemplateDateFormat.java
@@ -62,6 +62,8 @@ public abstract class TemplateDateFormat extends TemplateValueFormat {
      * {@link #formatToString(TemplateDateModel)} escaped, it must return the {@link String} that
      * {@link #formatToString(TemplateDateModel)} does.
      * 
+     * <p>The implementation in {@link TemplateDateFormat} simply calls {@link #formatToString(TemplateDateModel)}.
+     * 
      * @param outputFormat
      *            When the result is a {@link TemplateMarkupOutputModel} result, it must be exactly of this output
      *            format.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/core/TemplateNumberFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateNumberFormat.java b/src/main/java/freemarker/core/TemplateNumberFormat.java
index eb75ea7..69a5da0 100644
--- a/src/main/java/freemarker/core/TemplateNumberFormat.java
+++ b/src/main/java/freemarker/core/TemplateNumberFormat.java
@@ -61,6 +61,8 @@ public abstract class TemplateNumberFormat extends TemplateValueFormat {
      * {@link #formatToString(TemplateNumberModel)} escaped, it must return the {@link String} that
      * {@link #formatToString(TemplateNumberModel)} does.
      * 
+     * <p>The implementation in {@link TemplateNumberFormat} simply calls {@link #formatToString(TemplateNumberModel)}.
+     * 
      * @param outputFormat
      *            When the result is a {@link TemplateMarkupOutputModel} result, it must be exactly of this output
      *            format. Not {@code null}.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9ce036f5/src/main/java/freemarker/ext/beans/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeanModel.java b/src/main/java/freemarker/ext/beans/BeanModel.java
index 090deab..26eb1f5 100644
--- a/src/main/java/freemarker/ext/beans/BeanModel.java
+++ b/src/main/java/freemarker/ext/beans/BeanModel.java
@@ -337,9 +337,15 @@ implements
      * In FreeMarker 1.7 (and also at least in 2.1) {@link BeanModel} was a {@link TemplateScalarModel}. Some internal
      * FreeMarker code tries to emulate FreeMarker classic by calling this method when a {@link TemplateScalarModel} is
      * expected.
+     * 
+     * @return Never {@code null}
      */
     String getAsClassicCompatibleString() {
-        return object == null ? "null" : object.toString();        
+        if (object == null) {
+            return "null";
+        }
+        String s = object.toString();
+        return s != null ? s : "null";        
     }
     
     @Override


[3/5] incubator-freemarker git commit: Simplified number/date-like formatting more: The TemplateValueFormat-s don't get the desired TemplateOutputFormat as parameter, instead, an TemplateValueFormat should just return the single type of output it was des

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/DateFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/DateFormatTest.java b/src/test/java/freemarker/core/DateFormatTest.java
index 490ba5b..b36a97f 100644
--- a/src/test/java/freemarker/core/DateFormatTest.java
+++ b/src/test/java/freemarker/core/DateFormatTest.java
@@ -266,57 +266,57 @@ public class DateFormatTest extends TemplateTest {
         String dateTimeFormatStr2 = dateTimeFormatStr + "'!'";
         
         assertEquals("2015.09.06. 13:00",
-                env.getTemplateDateFormat(TemplateDateModel.DATETIME, Date.class).formatToString(TM));
+                env.getTemplateDateFormat(TemplateDateModel.DATETIME, Date.class).formatToPlainText(TM));
         assertEquals("2015.09.06. 13:00!",
-                env.getTemplateDateFormat(dateTimeFormatStr2, TemplateDateModel.DATETIME, Date.class).formatToString(TM));
+                env.getTemplateDateFormat(dateTimeFormatStr2, TemplateDateModel.DATETIME, Date.class).formatToPlainText(TM));
         
         assertEquals("2015.09.06. (+0100)",
-                env.getTemplateDateFormat(TemplateDateModel.DATE, Date.class).formatToString(TM));
+                env.getTemplateDateFormat(TemplateDateModel.DATE, Date.class).formatToPlainText(TM));
         assertEquals("2015.09.06. (+0100)!",
-                env.getTemplateDateFormat(dateFormatStr2, TemplateDateModel.DATE, Date.class).formatToString(TM));
+                env.getTemplateDateFormat(dateFormatStr2, TemplateDateModel.DATE, Date.class).formatToPlainText(TM));
         
         assertEquals("13:00",
-                env.getTemplateDateFormat(TemplateDateModel.TIME, Date.class).formatToString(TM));
+                env.getTemplateDateFormat(TemplateDateModel.TIME, Date.class).formatToPlainText(TM));
         assertEquals("13:00!",
-                env.getTemplateDateFormat(timeFormatStr2, TemplateDateModel.TIME, Date.class).formatToString(TM));
+                env.getTemplateDateFormat(timeFormatStr2, TemplateDateModel.TIME, Date.class).formatToPlainText(TM));
         
         assertEquals("2015.09.06. 13:00",
-                env.getTemplateDateFormat(TemplateDateModel.DATETIME, Timestamp.class).formatToString(TM));
+                env.getTemplateDateFormat(TemplateDateModel.DATETIME, Timestamp.class).formatToPlainText(TM));
         assertEquals("2015.09.06. 13:00!",
-                env.getTemplateDateFormat(dateTimeFormatStr2, TemplateDateModel.DATETIME, Timestamp.class).formatToString(TM));
+                env.getTemplateDateFormat(dateTimeFormatStr2, TemplateDateModel.DATETIME, Timestamp.class).formatToPlainText(TM));
 
         assertEquals("2015.09.06. (+0000)",
-                env.getTemplateDateFormat(TemplateDateModel.DATE, java.sql.Date.class).formatToString(TM));
+                env.getTemplateDateFormat(TemplateDateModel.DATE, java.sql.Date.class).formatToPlainText(TM));
         assertEquals("2015.09.06. (+0000)!",
-                env.getTemplateDateFormat(dateFormatStr2, TemplateDateModel.DATE, java.sql.Date.class).formatToString(TM));
+                env.getTemplateDateFormat(dateFormatStr2, TemplateDateModel.DATE, java.sql.Date.class).formatToPlainText(TM));
 
         assertEquals("12:00",
-                env.getTemplateDateFormat(TemplateDateModel.TIME, Time.class).formatToString(TM));
+                env.getTemplateDateFormat(TemplateDateModel.TIME, Time.class).formatToPlainText(TM));
         assertEquals("12:00!",
-                env.getTemplateDateFormat(timeFormatStr2, TemplateDateModel.TIME, Time.class).formatToString(TM));
+                env.getTemplateDateFormat(timeFormatStr2, TemplateDateModel.TIME, Time.class).formatToPlainText(TM));
 
         {
             String dateTimeFormatStrLoc = dateTimeFormatStr + " EEEE";
             // Gets into cache:
             TemplateDateFormat format1
                     = env.getTemplateDateFormat(dateTimeFormatStrLoc, TemplateDateModel.DATETIME, Date.class);
-            assertEquals("2015.09.06. 13:00 Sunday", format1.formatToString(TM));
+            assertEquals("2015.09.06. 13:00 Sunday", format1.formatToPlainText(TM));
             // Different locale (not cached):
             assertEquals("2015.09.06. 13:00 Sonntag",
                     env.getTemplateDateFormat(dateTimeFormatStrLoc, TemplateDateModel.DATETIME, Date.class,
-                            Locale.GERMANY).formatToString(TM));
+                            Locale.GERMANY).formatToPlainText(TM));
             // Different locale and zone (not cached):
             assertEquals("2015.09.06. 14:00 Sonntag",
                     env.getTemplateDateFormat(dateTimeFormatStrLoc, TemplateDateModel.DATETIME, Date.class,
-                            Locale.GERMANY, TimeZone.getTimeZone("GMT+02"), TimeZone.getTimeZone("GMT+03")).formatToString(TM));
+                            Locale.GERMANY, TimeZone.getTimeZone("GMT+02"), TimeZone.getTimeZone("GMT+03")).formatToPlainText(TM));
             // Different locale and zone (not cached):
             assertEquals("2015.09.06. 15:00 Sonntag",
                     env.getTemplateDateFormat(dateTimeFormatStrLoc, TemplateDateModel.DATETIME, java.sql.Date.class,
-                            Locale.GERMANY, TimeZone.getTimeZone("GMT+02"), TimeZone.getTimeZone("GMT+03")).formatToString(TM));
+                            Locale.GERMANY, TimeZone.getTimeZone("GMT+02"), TimeZone.getTimeZone("GMT+03")).formatToPlainText(TM));
             // Check for corrupted cache:
             TemplateDateFormat format2
                     = env.getTemplateDateFormat(dateTimeFormatStrLoc, TemplateDateModel.DATETIME, Date.class);
-            assertEquals("2015.09.06. 13:00 Sunday", format2.formatToString(TM));
+            assertEquals("2015.09.06. 13:00 Sunday", format2.formatToPlainText(TM));
             assertSame(format1, format2);
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/EpochMillisDivTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/EpochMillisDivTemplateDateFormatFactory.java b/src/test/java/freemarker/core/EpochMillisDivTemplateDateFormatFactory.java
index 1a7bf3d..ef6e7dd 100644
--- a/src/test/java/freemarker/core/EpochMillisDivTemplateDateFormatFactory.java
+++ b/src/test/java/freemarker/core/EpochMillisDivTemplateDateFormatFactory.java
@@ -60,7 +60,7 @@ public class EpochMillisDivTemplateDateFormatFactory extends TemplateDateFormatF
         }
         
         @Override
-        public String formatToString(TemplateDateModel dateModel)
+        public String formatToPlainText(TemplateDateModel dateModel)
                 throws UnformattableValueException, TemplateModelException {
             return String.valueOf(TemplateFormatUtil.getNonNullDate(dateModel).getTime() / divisor);
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/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 216f8c0..bbf5cd5 100644
--- a/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java
+++ b/src/test/java/freemarker/core/EpochMillisTemplateDateFormatFactory.java
@@ -47,7 +47,7 @@ public class EpochMillisTemplateDateFormatFactory extends TemplateDateFormatFact
         private EpochMillisTemplateDateFormat() { }
         
         @Override
-        public String formatToString(TemplateDateModel dateModel)
+        public String formatToPlainText(TemplateDateModel dateModel)
                 throws UnformattableValueException, TemplateModelException {
             return String.valueOf(TemplateFormatUtil.getNonNullDate(dateModel).getTime());
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/HTMLISOTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/HTMLISOTemplateDateFormatFactory.java b/src/test/java/freemarker/core/HTMLISOTemplateDateFormatFactory.java
new file mode 100644
index 0000000..8e28ad8
--- /dev/null
+++ b/src/test/java/freemarker/core/HTMLISOTemplateDateFormatFactory.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.core;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import freemarker.template.TemplateDateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.utility.DateUtil;
+import freemarker.template.utility.DateUtil.CalendarFieldsToDateConverter;
+import freemarker.template.utility.DateUtil.DateParseException;
+
+public class HTMLISOTemplateDateFormatFactory extends TemplateDateFormatFactory {
+
+    public static final HTMLISOTemplateDateFormatFactory INSTANCE = new HTMLISOTemplateDateFormatFactory();
+    
+    private HTMLISOTemplateDateFormatFactory() {
+        // Defined to decrease visibility
+    }
+    
+    @Override
+    public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput,
+            Environment env) throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException {
+        TemplateFormatUtil.checkHasNoParameters(params);
+        return HTMLISOTemplateDateFormat.INSTANCE;
+    }
+
+    private static class HTMLISOTemplateDateFormat extends TemplateDateFormat {
+
+        private static final HTMLISOTemplateDateFormat INSTANCE = new HTMLISOTemplateDateFormat();
+
+        private DateUtil.TrivialDateToISO8601CalendarFactory calendarFactory;
+
+        private CalendarFieldsToDateConverter calToDateConverter;
+        
+        private HTMLISOTemplateDateFormat() { }
+        
+        @Override
+        public String formatToPlainText(TemplateDateModel dateModel)
+                throws UnformattableValueException, TemplateModelException {
+            if (calendarFactory == null) {
+                calendarFactory = new DateUtil.TrivialDateToISO8601CalendarFactory();
+            }
+            return DateUtil.dateToISO8601String(
+                    TemplateFormatUtil.getNonNullDate(dateModel),
+                    true, true, true, DateUtil.ACCURACY_SECONDS, DateUtil.UTC,
+                    calendarFactory);
+        }
+
+        @Override
+        public boolean isLocaleBound() {
+            return false;
+        }
+
+        @Override
+        public boolean isTimeZoneBound() {
+            return false;
+        }
+
+        @Override
+        public Date parse(String s, int dateType) throws UnparsableValueException {
+            try {
+                if (calToDateConverter == null) {
+                    calToDateConverter = new DateUtil.TrivialCalendarFieldsToDateConverter(); 
+                }
+                return DateUtil.parseISO8601DateTime(s, DateUtil.UTC, calToDateConverter);
+            } catch (DateParseException e) {
+                throw new UnparsableValueException("Malformed ISO date-time", e);
+            }
+        }
+
+        @Override
+        public Object format(TemplateDateModel dateModel) throws TemplateValueFormatException, TemplateModelException {
+            return HTMLOutputFormat.INSTANCE.fromMarkup(
+                    formatToPlainText(dateModel).replace("T", "<span class='T'>T</span>"));
+        }
+
+        @Override
+        public String getDescription() {
+            return "ISO UTC HTML";
+        }
+        
+    }
+
+}
+ 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/HexTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/HexTemplateNumberFormatFactory.java b/src/test/java/freemarker/core/HexTemplateNumberFormatFactory.java
index 4b5bf0b..54fc0dc 100644
--- a/src/test/java/freemarker/core/HexTemplateNumberFormatFactory.java
+++ b/src/test/java/freemarker/core/HexTemplateNumberFormatFactory.java
@@ -46,7 +46,7 @@ public class HexTemplateNumberFormatFactory extends TemplateNumberFormatFactory
         private HexTemplateNumberFormat() { }
         
         @Override
-        public String formatToString(TemplateNumberModel numberModel)
+        public String formatToPlainText(TemplateNumberModel numberModel)
                 throws UnformattableValueException, TemplateModelException {
             Number n = TemplateFormatUtil.getNonNullNumber(numberModel);
             try {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/LocAndTZSensitiveTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/LocAndTZSensitiveTemplateDateFormatFactory.java b/src/test/java/freemarker/core/LocAndTZSensitiveTemplateDateFormatFactory.java
index b307f2a..0b32bd7 100644
--- a/src/test/java/freemarker/core/LocAndTZSensitiveTemplateDateFormatFactory.java
+++ b/src/test/java/freemarker/core/LocAndTZSensitiveTemplateDateFormatFactory.java
@@ -51,7 +51,7 @@ public class LocAndTZSensitiveTemplateDateFormatFactory extends TemplateDateForm
         }
 
         @Override
-        public String formatToString(TemplateDateModel dateModel)
+        public String formatToPlainText(TemplateDateModel dateModel)
                 throws UnformattableValueException, TemplateModelException {
             return String.valueOf(TemplateFormatUtil.getNonNullDate(dateModel).getTime() + "@" + locale + ":" + timeZone.getID());
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/LocaleSensitiveTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/LocaleSensitiveTemplateNumberFormatFactory.java b/src/test/java/freemarker/core/LocaleSensitiveTemplateNumberFormatFactory.java
index 0b2baef..0c349cb 100644
--- a/src/test/java/freemarker/core/LocaleSensitiveTemplateNumberFormatFactory.java
+++ b/src/test/java/freemarker/core/LocaleSensitiveTemplateNumberFormatFactory.java
@@ -47,7 +47,7 @@ public class LocaleSensitiveTemplateNumberFormatFactory extends TemplateNumberFo
         }
         
         @Override
-        public String formatToString(TemplateNumberModel numberModel)
+        public String formatToPlainText(TemplateNumberModel numberModel)
                 throws UnformattableValueException, TemplateModelException {
             Number n = numberModel.getAsNumber();
             try {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/NumberFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/NumberFormatTest.java b/src/test/java/freemarker/core/NumberFormatTest.java
index 2c2fd40..ddfbf28 100644
--- a/src/test/java/freemarker/core/NumberFormatTest.java
+++ b/src/test/java/freemarker/core/NumberFormatTest.java
@@ -151,17 +151,17 @@ public class NumberFormatTest extends TemplateTest {
         TemplateNumberFormat defF = env.getTemplateNumberFormat();
         //
         TemplateNumberFormat explF = env.getTemplateNumberFormat("0.00");
-        assertEquals("1.25", explF.formatToString(new SimpleNumber(1.25)));
+        assertEquals("1.25", explF.formatToPlainText(new SimpleNumber(1.25)));
         //
         TemplateNumberFormat expl2F = env.getTemplateNumberFormat("@loc");
-        assertEquals("1.25_en_US", expl2F.formatToString(new SimpleNumber(1.25)));
+        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.formatToString(new SimpleNumber(1.25)));
+        assertEquals("1,25", explFFr.formatToPlainText(new SimpleNumber(1.25)));
         //
         TemplateNumberFormat expl2FFr = env.getTemplateNumberFormat("@loc", Locale.FRANCE);
-        assertEquals("1.25_fr_FR", expl2FFr.formatToString(new SimpleNumber(1.25)));
+        assertEquals("1.25_fr_FR", expl2FFr.formatToPlainText(new SimpleNumber(1.25)));
         
         assertSame(env.getTemplateNumberFormat(), defF);
         //
@@ -291,18 +291,16 @@ public class NumberFormatTest extends TemplateTest {
         getConfiguration().setNumberFormat("@printfG_3");
 
         String commonFTL = "${1234567} ${'cat:' + 1234567} ${0.0000123}";
-        assertOutput(commonFTL,
-                "1.23E+06 cat:1.23E+06 1.23E-05");
-        assertOutput("<#ftl outputFormat='HTML'>" + commonFTL,
-                "1.23*10<sup>6</sup> cat:1.23*10<sup>6</sup> 1.23*10<sup>-5</sup>");
-        assertOutput("<#ftl outputFormat='HTML'>${\"" + commonFTL + "\"}",
-                "1.23E+06 cat:1.23E+06 1.23E-05");
-        assertOutput("<#escape x as x?html>" + commonFTL + "</#escape>",
-                "1.23*10<sup>6</sup> cat:1.23E+06 1.23*10<sup>-5</sup>");
-        assertOutput("<#escape x as x?xhtml>" + commonFTL + "</#escape>",
-                "1.23*10<sup>6</sup> cat:1.23E+06 1.23*10<sup>-5</sup>");
-        assertOutput("<#escape x as x?xml>" + commonFTL + "</#escape>",
-                "1.23E+06 cat:1.23E+06 1.23E-05");
+        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);
+        // TODO: Should give markup, but currently does interpolation in plain text:
+        // 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

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/PrintfGTemplateNumberFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/PrintfGTemplateNumberFormatFactory.java b/src/test/java/freemarker/core/PrintfGTemplateNumberFormatFactory.java
index 47d46b1..ccaca18 100644
--- a/src/test/java/freemarker/core/PrintfGTemplateNumberFormatFactory.java
+++ b/src/test/java/freemarker/core/PrintfGTemplateNumberFormatFactory.java
@@ -68,7 +68,7 @@ public class PrintfGTemplateNumberFormatFactory extends TemplateNumberFormatFact
         }
         
         @Override
-        public String formatToString(TemplateNumberModel numberModel)
+        public String formatToPlainText(TemplateNumberModel numberModel)
                 throws UnformattableValueException, TemplateModelException {
             final Number n = TemplateFormatUtil.getNonNullNumber(numberModel);
             
@@ -90,12 +90,9 @@ public class PrintfGTemplateNumberFormatFactory extends TemplateNumberFormatFact
         }
 
         @Override
-        public Object formatToMarkupOrString(TemplateNumberModel numberModel, MarkupOutputFormat<?> outputFormat)
+        public Object format(TemplateNumberModel numberModel)
                 throws UnformattableValueException, TemplateModelException {
-            String strResult = formatToString(numberModel);
-            if (!(outputFormat instanceof HTMLOutputFormat || outputFormat instanceof XHTMLOutputFormat)) {
-                return strResult;
-            }
+            String strResult = formatToPlainText(numberModel);
             
             int expIdx = strResult.indexOf('E');
             if (expIdx == -1) {
@@ -108,7 +105,7 @@ public class PrintfGTemplateNumberFormatFactory extends TemplateNumberFormatFact
                 expSignifNumBegin++;
             }
             
-            return outputFormat.fromMarkup(
+            return HTMLOutputFormat.INSTANCE.fromMarkup(
                     strResult.substring(0, expIdx)
                     + "*10<sup>"
                     + (expStr.charAt(0) == '-' ? "-" : "") + expStr.substring(expSignifNumBegin)


[4/5] incubator-freemarker git commit: Simplified number/date-like formatting more: The TemplateValueFormat-s don't get the desired TemplateOutputFormat as parameter, instead, an TemplateValueFormat should just return the single type of output it was des

Posted by dd...@apache.org.
Simplified number/date-like formatting more: The TemplateValueFormat-s don't get the desired TemplateOutputFormat as parameter, instead, an TemplateValueFormat should just return the single type of output it was designed for when format(...) is called, or plain text if formatToPlainText(...) is called. This resulting behavior is less sophisticated, but easy to understand for the end users. Also this way no output format has to be around when formatting, which simplifies the core and the implementation of user methods that would otherwise need that information from the lexical context somehow. Also string built-ins are now call format(...) and then fail if the result is markup, rather than formatting the LHO to plain text. Some internal class name changes.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/c798862a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/c798862a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/c798862a

Branch: refs/heads/2.3
Commit: c798862ac437ded4e1ccf6566374e40febd3bc73
Parents: b680e9c
Author: ddekany <dd...@apache.org>
Authored: Sat Sep 26 19:17:57 2015 +0200
Committer: ddekany <dd...@apache.org>
Committed: Tue Sep 29 02:02:03 2015 +0200

----------------------------------------------------------------------
 .../freemarker/core/AddConcatExpression.java    |  20 ++--
 src/main/java/freemarker/core/Assignment.java   |   9 +-
 .../core/BuiltInForLegacyEscaping.java          |   4 +-
 .../BuiltInForMarkupOutputFormatRelated.java    |  46 -------
 .../core/BuiltInForOutputFormatRelated.java     |  46 -------
 .../java/freemarker/core/BuiltInForString.java  |   2 +-
 .../core/BuiltInsForMultipleTypes.java          |  14 +--
 .../core/BuiltInsForOutputFormatRelated.java    |   7 +-
 .../freemarker/core/BuiltInsForSequences.java   |   2 +-
 .../core/BuiltInsForStringsBasic.java           |   6 +-
 .../core/BuiltInsForStringsEncoding.java        |  26 +---
 .../freemarker/core/BuiltInsForStringsMisc.java |   2 +-
 .../java/freemarker/core/DollarVariable.java    |   4 +-
 .../java/freemarker/core/DynamicKeyName.java    |   4 +-
 src/main/java/freemarker/core/Environment.java  |  18 +--
 src/main/java/freemarker/core/EvalUtil.java     | 102 ++++++++++++----
 src/main/java/freemarker/core/Expression.java   |  30 ++++-
 src/main/java/freemarker/core/HashLiteral.java  |   4 +-
 .../core/ISOLikeTemplateDateFormat.java         |   2 +-
 src/main/java/freemarker/core/Include.java      |   6 +-
 src/main/java/freemarker/core/Interpret.java    |   6 +-
 .../freemarker/core/JavaTemplateDateFormat.java |   2 +-
 .../core/JavaTemplateNumberFormat.java          |   2 +-
 src/main/java/freemarker/core/LibraryLoad.java  |   2 +-
 src/main/java/freemarker/core/ListLiteral.java  |   4 +-
 .../core/MarkupOutputFormatBoundBuiltIn.java    |  46 +++++++
 src/main/java/freemarker/core/NewBI.java        |   2 +-
 .../java/freemarker/core/NumberLiteral.java     |   4 +-
 .../core/OutputFormatBoundBuiltIn.java          |  46 +++++++
 .../java/freemarker/core/PropertySetting.java   |   2 +-
 .../java/freemarker/core/StopInstruction.java   |   2 +-
 .../java/freemarker/core/StringLiteral.java     |   4 +-
 .../freemarker/core/TemplateDateFormat.java     |  23 ++--
 .../freemarker/core/TemplateNumberFormat.java   |  21 ++--
 src/main/javacc/FTL.jj                          |  14 +--
 src/manual/book.xml                             |  32 ++---
 .../core/AppMetaTemplateDateFormatFactory.java  |   2 +-
 .../core/BaseNTemplateNumberFormatFactory.java  |   4 +-
 .../freemarker/core/CorectionToTextualTest.java | 119 +++++++++++++++++++
 .../java/freemarker/core/DateFormatTest.java    |  34 +++---
 ...EpochMillisDivTemplateDateFormatFactory.java |   2 +-
 .../EpochMillisTemplateDateFormatFactory.java   |   2 +-
 .../core/HTMLISOTemplateDateFormatFactory.java  | 104 ++++++++++++++++
 .../core/HexTemplateNumberFormatFactory.java    |   2 +-
 ...AndTZSensitiveTemplateDateFormatFactory.java |   2 +-
 ...aleSensitiveTemplateNumberFormatFactory.java |   2 +-
 .../java/freemarker/core/NumberFormatTest.java  |  30 +++--
 .../PrintfGTemplateNumberFormatFactory.java     |  11 +-
 48 files changed, 560 insertions(+), 320 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/AddConcatExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/AddConcatExpression.java b/src/main/java/freemarker/core/AddConcatExpression.java
index f92b933..7c1eb75 100644
--- a/src/main/java/freemarker/core/AddConcatExpression.java
+++ b/src/main/java/freemarker/core/AddConcatExpression.java
@@ -45,17 +45,15 @@ final class AddConcatExpression extends Expression {
 
     private final Expression left;
     private final Expression right;
-    private final MarkupOutputFormat markupOutputFormat;
 
-    AddConcatExpression(Expression left, Expression right, MarkupOutputFormat markupOutputFormat) {
+    AddConcatExpression(Expression left, Expression right) {
         this.left = left;
         this.right = right;
-        this.markupOutputFormat = markupOutputFormat;
     }
 
     @Override
     TemplateModel _eval(Environment env) throws TemplateException {
-        return _eval(env, this, left, left.eval(env), right, right.eval(env), markupOutputFormat);
+        return _eval(env, this, left, left.eval(env), right, right.eval(env));
     }
 
     /**
@@ -67,8 +65,7 @@ final class AddConcatExpression extends Expression {
     static TemplateModel _eval(Environment env,
             TemplateObject parent,
             Expression leftExp, TemplateModel leftModel,
-            Expression rightExp, TemplateModel rightModel,
-            MarkupOutputFormat markupOutputFormat)
+            Expression rightExp, TemplateModel rightModel)
             throws TemplateModelException, TemplateException, NonStringException {
         if (leftModel instanceof TemplateNumberModel && rightModel instanceof TemplateNumberModel) {
             Number first = EvalUtil.modelToNumber((TemplateNumberModel) leftModel, leftExp);
@@ -78,10 +75,10 @@ final class AddConcatExpression extends Expression {
             return new ConcatenatedSequence((TemplateSequenceModel) leftModel, (TemplateSequenceModel) rightModel);
         } else {
             try {
-                Object leftOMOrStr = EvalUtil.coerceModelToMarkupOutputOrString(
-                        leftModel, leftExp, (String) null, markupOutputFormat, env);
-                Object rightOMOrStr = EvalUtil.coerceModelToMarkupOutputOrString(
-                        rightModel, rightExp, (String) null, markupOutputFormat, env);
+                Object leftOMOrStr = EvalUtil.coerceModelToStringOrMarkup(
+                        leftModel, leftExp, (String) null, env);
+                Object rightOMOrStr = EvalUtil.coerceModelToStringOrMarkup(
+                        rightModel, rightExp, (String) null, env);
 
                 if (leftOMOrStr instanceof String) {
                     if (rightOMOrStr instanceof String) {
@@ -169,8 +166,7 @@ final class AddConcatExpression extends Expression {
             String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) {
     	return new AddConcatExpression(
     	left.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState),
-    	right.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState),
-    	markupOutputFormat);
+    	right.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/Assignment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Assignment.java b/src/main/java/freemarker/core/Assignment.java
index 8f5206b..0521250 100644
--- a/src/main/java/freemarker/core/Assignment.java
+++ b/src/main/java/freemarker/core/Assignment.java
@@ -41,7 +41,6 @@ final class Assignment extends TemplateElement {
     private final String variableName;
     private final int operatorType;
     private final Expression valueExp;
-    private final MarkupOutputFormat markupOutputFormat;
     private Expression namespaceExp;
 
     static final int NAMESPACE = 1;
@@ -58,8 +57,7 @@ final class Assignment extends TemplateElement {
     Assignment(String variableName,
             int operator,
             Expression valueExp,
-            int scope,
-            MarkupOutputFormat markupOutputFormat) {
+            int scope) {
         this.scope = scope;
         
         this.variableName = variableName;
@@ -95,8 +93,6 @@ final class Assignment extends TemplateElement {
         }
         
         this.valueExp = valueExp;
-        
-        this.markupOutputFormat = markupOutputFormat;
     }
     
     void setNamespaceExp(Expression namespaceExp) {
@@ -169,8 +165,7 @@ final class Assignment extends TemplateElement {
                         throw InvalidReferenceException.getInstance(valueExp, env);
                     }
                 }
-                value = AddConcatExpression._eval(env,
-                        namespaceExp, null, lhoValue, valueExp, value, markupOutputFormat);
+                value = AddConcatExpression._eval(env, namespaceExp, null, lhoValue, valueExp, value);
             } else {  // Numerical operation
                 Number lhoNumber;
                 if (lhoValue instanceof TemplateNumberModel) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInForLegacyEscaping.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInForLegacyEscaping.java b/src/main/java/freemarker/core/BuiltInForLegacyEscaping.java
index 2ccd491..81fbe30 100644
--- a/src/main/java/freemarker/core/BuiltInForLegacyEscaping.java
+++ b/src/main/java/freemarker/core/BuiltInForLegacyEscaping.java
@@ -31,7 +31,7 @@ abstract class BuiltInForLegacyEscaping extends BuiltInBannedWhenAutoEscaping {
     TemplateModel _eval(Environment env)
     throws TemplateException {
         TemplateModel tm = target.eval(env);
-        Object moOrStr = EvalUtil.coerceModelToMarkupOutputOrString(tm, target, null, getMarkupOutputFormat(), env);
+        Object moOrStr = EvalUtil.coerceModelToStringOrMarkup(tm, target, null, env);
         if (moOrStr instanceof String) {
             return calculateResult((String) moOrStr, env);
         } else {
@@ -45,6 +45,4 @@ abstract class BuiltInForLegacyEscaping extends BuiltInBannedWhenAutoEscaping {
     
     abstract TemplateModel calculateResult(String s, Environment env) throws TemplateException;
     
-    abstract MarkupOutputFormat getMarkupOutputFormat();
-    
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInForMarkupOutputFormatRelated.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInForMarkupOutputFormatRelated.java b/src/main/java/freemarker/core/BuiltInForMarkupOutputFormatRelated.java
deleted file mode 100644
index 17eef81..0000000
--- a/src/main/java/freemarker/core/BuiltInForMarkupOutputFormatRelated.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package freemarker.core;
-
-import freemarker.template.TemplateException;
-import freemarker.template.TemplateModel;
-import freemarker.template.utility.NullArgumentException;
-
-abstract class BuiltInForMarkupOutputFormatRelated extends SpecialBuiltIn {
-    
-    protected MarkupOutputFormat outputFormat;
-    
-    void bindToMarkupOutputFormat(MarkupOutputFormat outputFormat) {
-        NullArgumentException.check(outputFormat);
-        this.outputFormat = outputFormat;
-    }
-    
-    @Override
-    TemplateModel _eval(Environment env) throws TemplateException {
-        if (outputFormat == null) {
-            // The parser should prevent this situation
-            throw new NullPointerException("outputFormat was null");
-        }
-        return calculateResult(env);
-    }
-
-    protected abstract TemplateModel calculateResult(Environment env)
-            throws TemplateException;
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInForOutputFormatRelated.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInForOutputFormatRelated.java b/src/main/java/freemarker/core/BuiltInForOutputFormatRelated.java
deleted file mode 100644
index d8c3df5..0000000
--- a/src/main/java/freemarker/core/BuiltInForOutputFormatRelated.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package freemarker.core;
-
-import freemarker.template.TemplateException;
-import freemarker.template.TemplateModel;
-import freemarker.template.utility.NullArgumentException;
-
-abstract class BuiltInForOutputFormatRelated extends SpecialBuiltIn {
-    
-    protected OutputFormat outputFormat;
-    
-    void bindToOutputFormat(OutputFormat outputFormat) {
-        NullArgumentException.check(outputFormat);
-        this.outputFormat = outputFormat;
-    }
-    
-    @Override
-    TemplateModel _eval(Environment env) throws TemplateException {
-        if (outputFormat == null) {
-            // The parser should prevent this situation
-            throw new NullPointerException("outputFormat was null");
-        }
-        return calculateResult(env);
-    }
-
-    protected abstract TemplateModel calculateResult(Environment env)
-            throws TemplateException;
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInForString.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInForString.java b/src/main/java/freemarker/core/BuiltInForString.java
index b9a3bd1..d38dfd7 100644
--- a/src/main/java/freemarker/core/BuiltInForString.java
+++ b/src/main/java/freemarker/core/BuiltInForString.java
@@ -31,7 +31,7 @@ abstract class BuiltInForString extends BuiltIn {
     abstract TemplateModel calculateResult(String s, Environment env) throws TemplateException;
     
     static String getTargetString(Expression target, Environment env) throws TemplateException {
-        return target.evalAndCoerceToString(env);
+        return target.evalAndCoerceToStringOrUnsupportedMarkup(env);
     }
     
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java b/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
index deb88b2..4b5d548 100644
--- a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
@@ -241,7 +241,7 @@ class BuiltInsForMultipleTypes {
             }
             // Otherwise, interpret as a string and attempt 
             // to parse it into a date.
-            String s = target.evalAndCoerceToString(env);
+            String s = target.evalAndCoerceToPlainText(env);
             return new DateParser(s, env);
         }
 
@@ -560,7 +560,7 @@ class BuiltInsForMultipleTypes {
             private TemplateModel formatWith(String key)
             throws TemplateModelException {
                 try {
-                    return new SimpleScalar(env.formatDateToString(dateModel, key, target, stringBI.this, true));
+                    return new SimpleScalar(env.formatDateToPlainText(dateModel, key, target, stringBI.this, true));
                 } catch (TemplateException e) {
                     // `e` should always be a TemplateModelException here, but to be sure: 
                     throw _CoreAPI.ensureIsTemplateModelException("Failed to format value", e); 
@@ -578,7 +578,7 @@ class BuiltInsForMultipleTypes {
                                 throw new BugException();
                             }
                         }
-                        cachedValue = EvalUtil.formatResultNotNull(defaultFormat.formatToString(dateModel));
+                        cachedValue = EvalUtil.assertFormatResultNotNull(defaultFormat.formatToPlainText(dateModel));
                     } catch (TemplateValueFormatException e) {
                         try {
                             throw MessageUtil.newCantFormatDateException(defaultFormat, target, e, true);
@@ -638,9 +638,9 @@ class BuiltInsForMultipleTypes {
                 String result;
                 try {
                     if (format instanceof BackwardCompatibleTemplateNumberFormat) {
-                        result = env.formatNumberToString(number, (BackwardCompatibleTemplateNumberFormat) format, target);
+                        result = env.formatNumberToPlainText(number, (BackwardCompatibleTemplateNumberFormat) format, target);
                     } else {
-                        result = env.formatNumberToString(numberModel, format, target, true);
+                        result = env.formatNumberToPlainText(numberModel, format, target, true);
                     }
                 } catch (TemplateException e) {
                     // `e` should always be a TemplateModelException here, but to be sure: 
@@ -654,10 +654,10 @@ class BuiltInsForMultipleTypes {
                 if (cachedValue == null) {
                     try {
                         if (defaultFormat instanceof BackwardCompatibleTemplateNumberFormat) {
-                            cachedValue = env.formatNumberToString(
+                            cachedValue = env.formatNumberToPlainText(
                                     number, (BackwardCompatibleTemplateNumberFormat) defaultFormat, target);
                         } else {
-                            cachedValue = env.formatNumberToString(numberModel, defaultFormat, target, true);
+                            cachedValue = env.formatNumberToPlainText(numberModel, defaultFormat, target, true);
                         }
                     } catch (TemplateException e) {
                         // `e` should always be a TemplateModelException here, but to be sure: 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java b/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
index 556d12b..8c8da51 100644
--- a/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
+++ b/src/main/java/freemarker/core/BuiltInsForOutputFormatRelated.java
@@ -43,19 +43,16 @@ class BuiltInsForOutputFormatRelated {
         
     }
     
-    static abstract class AbstractConverterBI extends BuiltInForMarkupOutputFormatRelated {
+    static abstract class AbstractConverterBI extends MarkupOutputFormatBoundBuiltIn {
 
         @Override
         protected TemplateModel calculateResult(Environment env) throws TemplateException {
             TemplateModel lhoTM = target.eval(env);
+            Object lhoMOOrStr = EvalUtil.coerceModelToStringOrMarkup(lhoTM, target, null, env);
             MarkupOutputFormat contextOF = outputFormat;
-            Object lhoMOOrStr = EvalUtil.coerceModelToMarkupOutputOrString(lhoTM, target, null, contextOF, env);
             if (lhoMOOrStr instanceof String) { // TemplateMarkupOutputModel
                 return calculateResult((String) lhoMOOrStr, contextOF, env);
             } else {
-                if (lhoMOOrStr == null) {
-                    throw EvalUtil.newModelHasStoredNullException(null, lhoTM, target);
-                }
                 TemplateMarkupOutputModel lhoMO = (TemplateMarkupOutputModel) lhoMOOrStr;
                 MarkupOutputFormat lhoOF = lhoMO.getOutputFormat();
                 // ATTENTION: Keep this logic in sync. with ${...}'s logic!

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForSequences.java b/src/main/java/freemarker/core/BuiltInsForSequences.java
index 323a85f..a12d274 100644
--- a/src/main/java/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/freemarker/core/BuiltInsForSequences.java
@@ -183,7 +183,7 @@ class BuiltInsForSequences {
                             hadItem = true;
                         }
                         try {
-                            sb.append(EvalUtil.coerceModelToString(item, null, null, env));
+                            sb.append(EvalUtil.coerceModelToStringOrUnsupportedMarkup(item, null, null, env));
                         } catch (TemplateException e) {
                             throw new _TemplateModelException(e,
                                     "\"?", key, "\" failed at index ", Integer.valueOf(idx), " with this error:\n\n",

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForStringsBasic.java b/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
index 28aa858..cc0cba8 100644
--- a/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
+++ b/src/main/java/freemarker/core/BuiltInsForStringsBasic.java
@@ -89,7 +89,7 @@ class BuiltInsForStringsBasic {
     
         @Override
         TemplateModel _eval(Environment env) throws TemplateException {
-            return new BIMethod(target.evalAndCoerceToString(env,
+            return new BIMethod(target.evalAndCoerceToStringOrUnsupportedMarkup(env,
                     "For sequences/collections (lists and such) use \"?seq_contains\" instead."));
         }
     }
@@ -217,9 +217,9 @@ class BuiltInsForStringsBasic {
         
         @Override
         TemplateModel _eval(Environment env) throws TemplateException {
-            return new BIMethod(target.evalAndCoerceToString(env,
+            return new BIMethod(target.evalAndCoerceToStringOrUnsupportedMarkup(env,
                     "For sequences/collections (lists and such) use \"?seq_index_of\" instead."));
-        } 
+        }
     }
     
     static class keep_afterBI extends BuiltInForString {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInsForStringsEncoding.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForStringsEncoding.java b/src/main/java/freemarker/core/BuiltInsForStringsEncoding.java
index 4f02805..3cedd62 100644
--- a/src/main/java/freemarker/core/BuiltInsForStringsEncoding.java
+++ b/src/main/java/freemarker/core/BuiltInsForStringsEncoding.java
@@ -39,11 +39,6 @@ class BuiltInsForStringsEncoding {
             TemplateModel calculateResult(String s, Environment env) {
                 return new SimpleScalar(StringUtil.HTMLEnc(s));
             }
-
-            @Override
-            MarkupOutputFormat getMarkupOutputFormat() {
-                return HTMLOutputFormat.INSTANCE;
-            }
         }
         
         private final BIBeforeICI2d3d20 prevICIObj = new BIBeforeICI2d3d20();
@@ -60,11 +55,7 @@ class BuiltInsForStringsEncoding {
         public Object getPreviousICIChainMember() {
             return prevICIObj;
         }
-
-        @Override
-        MarkupOutputFormat getMarkupOutputFormat() {
-            return HTMLOutputFormat.INSTANCE;
-        }
+        
     }
 
     static class j_stringBI extends BuiltInForString {
@@ -93,11 +84,6 @@ class BuiltInsForStringsEncoding {
         TemplateModel calculateResult(String s, Environment env) {
             return new SimpleScalar(StringUtil.RTFEnc(s));
         }
-
-        @Override
-        MarkupOutputFormat getMarkupOutputFormat() {
-            return RTFOutputFormat.INSTANCE;
-        }
     }
 
     static class urlBI extends BuiltInForString {
@@ -149,11 +135,6 @@ class BuiltInsForStringsEncoding {
         TemplateModel calculateResult(String s, Environment env) {
             return new SimpleScalar(StringUtil.XHTMLEnc(s));
         }
-
-        @Override
-        MarkupOutputFormat getMarkupOutputFormat() {
-            return XHTMLOutputFormat.INSTANCE;
-        }
     }
 
     static class xmlBI extends BuiltInForLegacyEscaping {
@@ -161,11 +142,6 @@ class BuiltInsForStringsEncoding {
         TemplateModel calculateResult(String s, Environment env) {
             return new SimpleScalar(StringUtil.XMLEnc(s));
         }
-
-        @Override
-        MarkupOutputFormat getMarkupOutputFormat() {
-            return XMLOutputFormat.INSTANCE;
-        }
     }
 
     // Can't be instantiated

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForStringsMisc.java b/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
index 246b483..8b83c2c 100644
--- a/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
+++ b/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
@@ -49,7 +49,7 @@ class BuiltInsForStringsMisc {
         }
     }
 
-    static class evalBI extends BuiltInForOutputFormatRelated {
+    static class evalBI extends OutputFormatBoundBuiltIn {
         
         @Override
         protected TemplateModel calculateResult(Environment env) throws TemplateException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/DollarVariable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/DollarVariable.java b/src/main/java/freemarker/core/DollarVariable.java
index 23a995b..a3abc31 100644
--- a/src/main/java/freemarker/core/DollarVariable.java
+++ b/src/main/java/freemarker/core/DollarVariable.java
@@ -59,8 +59,8 @@ final class DollarVariable extends Interpolation {
     void accept(Environment env) throws TemplateException, IOException {
         final TemplateModel tm = escapedExpression.eval(env);
         final Writer out = env.getOut();
-        final Object moOrStr = EvalUtil.coerceModelToMarkupOutputOrString(
-                tm, escapedExpression, null, markupOutputFormat, env);
+        final Object moOrStr = EvalUtil.coerceModelToStringOrMarkup(
+                tm, escapedExpression, null, env);
         if (moOrStr instanceof String) {
             final String s = (String) moOrStr;
             if (autoEscape) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/DynamicKeyName.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/DynamicKeyName.java b/src/main/java/freemarker/core/DynamicKeyName.java
index df9955d..b0addad 100644
--- a/src/main/java/freemarker/core/DynamicKeyName.java
+++ b/src/main/java/freemarker/core/DynamicKeyName.java
@@ -106,7 +106,7 @@ final class DynamicKeyName extends Expression {
         } 
         
         try {
-            String s = target.evalAndCoerceToString(env);
+            String s = target.evalAndCoerceToPlainText(env);
             try {
                 return new SimpleScalar(s.substring(index, index + 1));
             } catch (IndexOutOfBoundsException e) {
@@ -152,7 +152,7 @@ final class DynamicKeyName extends Expression {
         } else {
             targetSeq = null;
             try {
-                targetStr = target.evalAndCoerceToString(env);
+                targetStr = target.evalAndCoerceToPlainText(env);
             } catch (NonStringException e) {
                 throw new UnexpectedTypeException(
                         target, target.eval(env),

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/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 ca6d7b1..358c371 100644
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@ -1033,9 +1033,9 @@ public final class Environment extends Configurable {
      * @param exp
      *            The blamed expression if an error occurs; it's only needed for better error messages
      */
-    String formatNumberToString(TemplateNumberModel number, Expression exp, boolean useTempModelExc)
+    String formatNumberToPlainText(TemplateNumberModel number, Expression exp, boolean useTempModelExc)
             throws TemplateException {
-        return formatNumberToString(number, getTemplateNumberFormat(exp, useTempModelExc), exp, useTempModelExc);
+        return formatNumberToPlainText(number, getTemplateNumberFormat(exp, useTempModelExc), exp, useTempModelExc);
     }
 
     /**
@@ -1044,12 +1044,12 @@ public final class Environment extends Configurable {
      * @param exp
      *            The blamed expression if an error occurs; it's only needed for better error messages
      */
-    String formatNumberToString(
+    String formatNumberToPlainText(
             TemplateNumberModel number, TemplateNumberFormat format, Expression exp,
             boolean useTempModelExc)
             throws TemplateException {
         try {
-            return EvalUtil.formatResultNotNull(format.formatToString(number));
+            return EvalUtil.assertFormatResultNotNull(format.formatToPlainText(number));
         } catch (TemplateValueFormatException e) {
             throw MessageUtil.newCantFormatNumberException(format, exp, e, useTempModelExc);
         }
@@ -1061,7 +1061,7 @@ public final class Environment extends Configurable {
      * @param exp
      *            The blamed expression if an error occurs; it's only needed for better error messages
      */
-    String formatNumberToString(Number number, BackwardCompatibleTemplateNumberFormat format, Expression exp)
+    String formatNumberToPlainText(Number number, BackwardCompatibleTemplateNumberFormat format, Expression exp)
             throws TemplateModelException, _MiscTemplateException {
         try {
             return format.format(number);
@@ -1319,12 +1319,12 @@ public final class Environment extends Configurable {
      * @param tdmSourceExpr
      *            The blamed expression if an error occurs; only used for error messages.
      */
-    String formatDateToString(TemplateDateModel tdm, Expression tdmSourceExpr,
+    String formatDateToPlainText(TemplateDateModel tdm, Expression tdmSourceExpr,
             boolean useTempModelExc) throws TemplateException {
         TemplateDateFormat format = getTemplateDateFormat(tdm, tdmSourceExpr, useTempModelExc);
         
         try {
-            return EvalUtil.formatResultNotNull(format.formatToString(tdm));
+            return EvalUtil.assertFormatResultNotNull(format.formatToPlainText(tdm));
         } catch (TemplateValueFormatException e) {
             throw MessageUtil.newCantFormatDateException(format, tdmSourceExpr, e, useTempModelExc);
         }
@@ -1336,7 +1336,7 @@ public final class Environment extends Configurable {
      * @param blamedFormatterExp
      *            The blamed expression if an error occurs; only used for error messages.
      */
-    String formatDateToString(TemplateDateModel tdm, String formatString,
+    String formatDateToPlainText(TemplateDateModel tdm, String formatString,
             Expression blamedDateSourceExp, Expression blamedFormatterExp,
             boolean useTempModelExc) throws TemplateException {
         Date date = EvalUtil.modelToDate(tdm, blamedDateSourceExp);
@@ -1347,7 +1347,7 @@ public final class Environment extends Configurable {
                 useTempModelExc);
         
         try {
-            return EvalUtil.formatResultNotNull(format.formatToString(tdm));
+            return EvalUtil.assertFormatResultNotNull(format.formatToPlainText(tdm));
         } catch (TemplateValueFormatException e) {
             throw MessageUtil.newCantFormatDateException(format, blamedDateSourceExp, e, useTempModelExc);
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/EvalUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/EvalUtil.java b/src/main/java/freemarker/core/EvalUtil.java
index 6dfc68d..ca21ac5 100644
--- a/src/main/java/freemarker/core/EvalUtil.java
+++ b/src/main/java/freemarker/core/EvalUtil.java
@@ -286,8 +286,8 @@ class EvalUtil {
             boolean rightBool = ((TemplateBooleanModel) rightValue).getAsBoolean();
             cmpResult = (leftBool ? 1 : 0) - (rightBool ? 1 : 0);
         } else if (env.isClassicCompatible()) {
-            String leftSting = leftExp.evalAndCoerceToString(env);
-            String rightString = rightExp.evalAndCoerceToString(env);
+            String leftSting = leftExp.evalAndCoerceToPlainText(env);
+            String rightString = rightExp.evalAndCoerceToPlainText(env);
             cmpResult = env.getCollator().compare(leftSting, rightString);
         } else {
             if (typeMismatchMeansNotEqual) {
@@ -342,31 +342,56 @@ class EvalUtil {
     }
 
     /**
+     * Converts a value to plain text {@link String}, or a {@link TemplateMarkupOutputModel} if that's what the
+     * {@link TemplateValueFormat} involved produces.
+     * 
+     * @param seqTip
+     *            Tip to display if the value type is not coercable, but it's sequence or collection.
+     * 
      * @return Never {@code null}
      */
-    static String coerceModelToString(TemplateModel tm, Expression exp, String seqHint,
-            Environment env) throws TemplateException {
+    static Object coerceModelToStringOrMarkup(TemplateModel tm, Expression exp, String seqTip, Environment env)
+            throws TemplateException {
         if (tm instanceof TemplateNumberModel) {
-            return formatResultNotNull(env.formatNumberToString((TemplateNumberModel) tm, exp, false));
+            TemplateNumberModel tnm = (TemplateNumberModel) tm; 
+            TemplateNumberFormat format = env.getTemplateNumberFormat(exp, false);
+            try {
+                return assertFormatResultNotNull(format.format(tnm));
+            } catch (TemplateValueFormatException e) {
+                throw MessageUtil.newCantFormatNumberException(format, exp, e, false);
+            }
         } else if (tm instanceof TemplateDateModel) {
-            return formatResultNotNull(env.formatDateToString((TemplateDateModel) tm, exp, false));
-        } else {
-            return coerceModelToStringCommon(tm, exp, seqHint, false, env);
+            TemplateDateModel tdm = (TemplateDateModel) tm;
+            TemplateDateFormat format = env.getTemplateDateFormat(tdm, exp, false);
+            try {
+                return assertFormatResultNotNull(format.format(tdm));
+            } catch (TemplateValueFormatException e) {
+                throw MessageUtil.newCantFormatDateException(format, exp, e, false);
+            }
+        } else if (tm instanceof TemplateMarkupOutputModel) {
+            return tm;
+        } else { 
+            return coerceModelToTextualCommon(tm, exp, seqTip, true, env);
         }
     }
 
     /**
+     * Like {@link #coerceModelToStringOrMarkup(TemplateModel, Expression, String, Environment)}, but gives error
+     * if the result is markup. This is what you normally use where markup results can't be used.
+     *
+     * @param seqTip
+     *            Tip to display if the value type is not coercable, but it's sequence or collection.
+     * 
      * @return Never {@code null}
      */
-    static Object coerceModelToMarkupOutputOrString(TemplateModel tm, Expression exp, String seqHint,
-            MarkupOutputFormat markupOutputFormat, Environment env) throws TemplateException {
+    static String coerceModelToStringOrUnsupportedMarkup(
+            TemplateModel tm, Expression exp, String seqTip, Environment env)
+            throws TemplateException {
         if (tm instanceof TemplateNumberModel) {
             TemplateNumberModel tnm = (TemplateNumberModel) tm; 
             TemplateNumberFormat format = env.getTemplateNumberFormat(exp, false);
             try {
-                return formatResultNotNull(markupOutputFormat != null 
-                        ? format.formatToMarkupOrString(tnm, markupOutputFormat)
-                        : format.formatToString(tnm));
+                return ensureFormatResultString(format.format(tnm), exp, env);
             } catch (TemplateValueFormatException e) {
                 throw MessageUtil.newCantFormatNumberException(format, exp, e, false);
             }
@@ -374,16 +399,32 @@ class EvalUtil {
             TemplateDateModel tdm = (TemplateDateModel) tm;
             TemplateDateFormat format = env.getTemplateDateFormat(tdm, exp, false);
             try {
-                return formatResultNotNull(markupOutputFormat != null
-                        ? format.formatToMarkupOrString(tdm, markupOutputFormat)
-                        : format.formatToString(tdm));
+                return ensureFormatResultString(format.format(tdm), exp, env);
             } catch (TemplateValueFormatException e) {
                 throw MessageUtil.newCantFormatDateException(format, exp, e, false);
             }
-        } else if (tm instanceof TemplateMarkupOutputModel) {
-            return tm;
         } else { 
-            return coerceModelToStringCommon(tm, exp, seqHint, true, env);
+            return coerceModelToTextualCommon(tm, exp, seqTip, false, env);
+        }
+    }
+
+    /**
+     * Converts a value to plain text {@link String}, even if the {@link TemplateValueFormat} involved normally produces
+     * markup. This should be used rarely, where the user clearly intend to use the plain text variant of the format.
+     * 
+     * @param seqTip
+     *            Tip to display if the value type is not coercable, but it's sequence or collection.
+     * 
+     * @return Never {@code null}
+     */
+    static String coerceModelToPlainText(TemplateModel tm, Expression exp, String seqTip,
+            Environment env) throws TemplateException {
+        if (tm instanceof TemplateNumberModel) {
+            return assertFormatResultNotNull(env.formatNumberToPlainText((TemplateNumberModel) tm, exp, false));
+        } else if (tm instanceof TemplateDateModel) {
+            return assertFormatResultNotNull(env.formatDateToPlainText((TemplateDateModel) tm, exp, false));
+        } else {
+            return coerceModelToTextualCommon(tm, exp, seqTip, false, env);
         }
     }
 
@@ -396,7 +437,7 @@ class EvalUtil {
      *            
      * @return Never {@code null}
      */
-    private static String coerceModelToStringCommon(
+    private static String coerceModelToTextualCommon(
             TemplateModel tm, Expression exp, String seqHint, boolean supportsTOM, Environment env)
             throws TemplateModelException, InvalidReferenceException, TemplateException,
                     NonStringOrTemplateOutputException, NonStringException {
@@ -456,14 +497,31 @@ class EvalUtil {
         }
     }
 
-    static String formatResultNotNull(String r) {
+    private static String ensureFormatResultString(Object formatResult, Expression exp, Environment env)
+            throws NonStringException {
+        if (formatResult instanceof String) { 
+            return (String) formatResult;
+        }
+        
+        assertFormatResultNotNull(formatResult);
+        
+        TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) formatResult;
+        _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
+                "Value was formatted to convert it to string, but the result was markup of ouput format ",
+                new _DelayedJQuote(mo.getOutputFormat()), ".")
+                .tip("Use value?string to force formatting to plain text.")
+                .blame(exp);
+        throw new NonStringException(null, desc);
+    }
+
+    static String assertFormatResultNotNull(String r) {
         if (r != null) {
             return r;
         }
         throw new NullPointerException("TemplateValueFormatter result can't be null");
     }
 
-    static Object formatResultNotNull(Object r) {
+    static Object assertFormatResultNotNull(Object r) {
         if (r != null) {
             return r;
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/Expression.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Expression.java b/src/main/java/freemarker/core/Expression.java
index c1d5355..3129e19 100644
--- a/src/main/java/freemarker/core/Expression.java
+++ b/src/main/java/freemarker/core/Expression.java
@@ -82,15 +82,37 @@ abstract public class Expression extends TemplateObject {
         return constantValue != null ? constantValue : _eval(env);
     }
     
-    String evalAndCoerceToString(Environment env) throws TemplateException {
-        return EvalUtil.coerceModelToString(eval(env), this, null, env);
+    String evalAndCoerceToPlainText(Environment env) throws TemplateException {
+        return EvalUtil.coerceModelToPlainText(eval(env), this, null, env);
     }
 
     /**
      * @param seqTip Tip to display if the value type is not coercable, but it's sequence or collection.
      */
-    String evalAndCoerceToString(Environment env, String seqTip) throws TemplateException {
-        return EvalUtil.coerceModelToString(eval(env), this, seqTip, env);
+    String evalAndCoerceToPlainText(Environment env, String seqTip) throws TemplateException {
+        return EvalUtil.coerceModelToPlainText(eval(env), this, seqTip, env);
+    }
+
+    Object evalAndCoerceToStringOrMarkup(Environment env) throws TemplateException {
+        return EvalUtil.coerceModelToStringOrMarkup(eval(env), this, null, env);
+    }
+
+    /**
+     * @param seqTip Tip to display if the value type is not coercable, but it's sequence or collection.
+     */
+    Object evalAndCoerceToStringOrMarkup(Environment env, String seqTip) throws TemplateException {
+        return EvalUtil.coerceModelToStringOrMarkup(eval(env), this, seqTip, env);
+    }
+    
+    String evalAndCoerceToStringOrUnsupportedMarkup(Environment env) throws TemplateException {
+        return EvalUtil.coerceModelToStringOrUnsupportedMarkup(eval(env), this, null, env);
+    }
+
+    /**
+     * @param seqTip Tip to display if the value type is not coercable, but it's sequence or collection.
+     */
+    String evalAndCoerceToStringOrUnsupportedMarkup(Environment env, String seqTip) throws TemplateException {
+        return EvalUtil.coerceModelToStringOrUnsupportedMarkup(eval(env), this, seqTip, env);
     }
     
     Number evalToNumber(Environment env) throws TemplateException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/HashLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/HashLiteral.java b/src/main/java/freemarker/core/HashLiteral.java
index bec56db..dcf5f4a 100644
--- a/src/main/java/freemarker/core/HashLiteral.java
+++ b/src/main/java/freemarker/core/HashLiteral.java
@@ -114,7 +114,7 @@ final class HashLiteral extends Expression {
                 for (int i = 0; i < size; i++) {
                     Expression keyExp = (Expression) keys.get(i);
                     Expression valExp = (Expression) values.get(i);
-                    String key = keyExp.evalAndCoerceToString(env);
+                    String key = keyExp.evalAndCoerceToPlainText(env);
                     TemplateModel value = valExp.eval(env);
                     if (env == null || !env.isClassicCompatible()) {
                         valExp.assertNonNull(value, env);
@@ -130,7 +130,7 @@ final class HashLiteral extends Expression {
                 for (int i = 0; i < size; i++) {
                     Expression keyExp = (Expression) keys.get(i);
                     Expression valExp = (Expression) values.get(i);
-                    String key = keyExp.evalAndCoerceToString(env);
+                    String key = keyExp.evalAndCoerceToPlainText(env);
                     TemplateModel value = valExp.eval(env);
                     if (env == null || !env.isClassicCompatible()) {
                         valExp.assertNonNull(value, env);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/ISOLikeTemplateDateFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ISOLikeTemplateDateFormat.java b/src/main/java/freemarker/core/ISOLikeTemplateDateFormat.java
index b379413..690fd0f 100644
--- a/src/main/java/freemarker/core/ISOLikeTemplateDateFormat.java
+++ b/src/main/java/freemarker/core/ISOLikeTemplateDateFormat.java
@@ -177,7 +177,7 @@ abstract class ISOLikeTemplateDateFormat  extends TemplateDateFormat {
     }
     
     @Override
-    public final String formatToString(TemplateDateModel dateModel) throws TemplateModelException {
+    public final String formatToPlainText(TemplateDateModel dateModel) throws TemplateModelException {
         final Date date = TemplateFormatUtil.getNonNullDate(dateModel);
         return format(
                 date,

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/Include.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Include.java b/src/main/java/freemarker/core/Include.java
index 2455b0c..f18c314 100644
--- a/src/main/java/freemarker/core/Include.java
+++ b/src/main/java/freemarker/core/Include.java
@@ -80,7 +80,7 @@ final class Include extends TemplateElement {
                 try {
                     if (parseExp instanceof StringLiteral) {
                         // Legacy
-                        parse = Boolean.valueOf(StringUtil.getYesNo(parseExp.evalAndCoerceToString(null)));
+                        parse = Boolean.valueOf(StringUtil.getYesNo(parseExp.evalAndCoerceToPlainText(null)));
                     } else {
                         try {
                             parse = Boolean.valueOf(parseExp.evalToBoolean(template.getConfiguration()));
@@ -119,7 +119,7 @@ final class Include extends TemplateElement {
     
     @Override
     void accept(Environment env) throws TemplateException, IOException {
-        final String includedTemplateName = includedTemplateNameExp.evalAndCoerceToString(env);
+        final String includedTemplateName = includedTemplateNameExp.evalAndCoerceToPlainText(env);
         final String fullIncludedTemplateName;
         try {
             fullIncludedTemplateName = env.toFullTemplateName(getTemplate().getName(), includedTemplateName);
@@ -132,7 +132,7 @@ final class Include extends TemplateElement {
         final String encoding = this.encoding != null
                 ? this.encoding
                 : (encodingExp != null
-                        ? encodingExp.evalAndCoerceToString(env)
+                        ? encodingExp.evalAndCoerceToPlainText(env)
                         : null);
         
         final boolean parse;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/Interpret.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Interpret.java b/src/main/java/freemarker/core/Interpret.java
index 47c6f79..0b947ef 100644
--- a/src/main/java/freemarker/core/Interpret.java
+++ b/src/main/java/freemarker/core/Interpret.java
@@ -43,7 +43,7 @@ import freemarker.template.TemplateTransformModel;
  * specify another parameter to the method call in which case the
  * template name suffix is the specified id instead of "anonymous_interpreted".
  */
-class Interpret extends BuiltInForOutputFormatRelated {
+class Interpret extends OutputFormatBoundBuiltIn {
     
     /**
      * Constructs a template on-the-fly and returns it embedded in a
@@ -68,7 +68,7 @@ class Interpret extends BuiltInForOutputFormatRelated {
         if (model instanceof TemplateSequenceModel) {
             sourceExpr = ((Expression) new DynamicKeyName(target, new NumberLiteral(Integer.valueOf(0))).copyLocationFrom(target));
             if (((TemplateSequenceModel) model).size() > 1) {
-                id = ((Expression) new DynamicKeyName(target, new NumberLiteral(Integer.valueOf(1))).copyLocationFrom(target)).evalAndCoerceToString(env);
+                id = ((Expression) new DynamicKeyName(target, new NumberLiteral(Integer.valueOf(1))).copyLocationFrom(target)).evalAndCoerceToPlainText(env);
             }
         } else if (model instanceof TemplateScalarModel) {
             sourceExpr = target;
@@ -78,7 +78,7 @@ class Interpret extends BuiltInForOutputFormatRelated {
                     "sequence or string", new Class[] { TemplateSequenceModel.class, TemplateScalarModel.class },
                     env);
         }
-        String templateSource = sourceExpr.evalAndCoerceToString(env);
+        String templateSource = sourceExpr.evalAndCoerceToPlainText(env);
         Template parentTemplate = env.getTemplate();
         
         final Template interpretedTemplate;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/JavaTemplateDateFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/JavaTemplateDateFormat.java b/src/main/java/freemarker/core/JavaTemplateDateFormat.java
index 02a1460..1b630ae 100644
--- a/src/main/java/freemarker/core/JavaTemplateDateFormat.java
+++ b/src/main/java/freemarker/core/JavaTemplateDateFormat.java
@@ -39,7 +39,7 @@ class JavaTemplateDateFormat extends TemplateDateFormat {
     }
     
     @Override
-    public String formatToString(TemplateDateModel dateModel) throws TemplateModelException {
+    public String formatToPlainText(TemplateDateModel dateModel) throws TemplateModelException {
         return javaDateFormat.format(TemplateFormatUtil.getNonNullDate(dateModel));
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/JavaTemplateNumberFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/JavaTemplateNumberFormat.java b/src/main/java/freemarker/core/JavaTemplateNumberFormat.java
index f862c2b..27b85e2 100644
--- a/src/main/java/freemarker/core/JavaTemplateNumberFormat.java
+++ b/src/main/java/freemarker/core/JavaTemplateNumberFormat.java
@@ -34,7 +34,7 @@ final class JavaTemplateNumberFormat extends BackwardCompatibleTemplateNumberFor
     }
 
     @Override
-    public String formatToString(TemplateNumberModel numberModel) throws UnformattableValueException, TemplateModelException {
+    public String formatToPlainText(TemplateNumberModel numberModel) throws UnformattableValueException, TemplateModelException {
         Number number = TemplateFormatUtil.getNonNullNumber(numberModel);
         return format(number);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/LibraryLoad.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/LibraryLoad.java b/src/main/java/freemarker/core/LibraryLoad.java
index 0f883db..9a9c887 100644
--- a/src/main/java/freemarker/core/LibraryLoad.java
+++ b/src/main/java/freemarker/core/LibraryLoad.java
@@ -51,7 +51,7 @@ public final class LibraryLoad extends TemplateElement {
 
     @Override
     void accept(Environment env) throws TemplateException, IOException {
-        final String importedTemplateName = importedTemplateNameExp.evalAndCoerceToString(env);
+        final String importedTemplateName = importedTemplateNameExp.evalAndCoerceToPlainText(env);
         final String fullImportedTemplateName;
         try {
             fullImportedTemplateName = env.toFullTemplateName(getTemplate().getName(), importedTemplateName);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/ListLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ListLiteral.java b/src/main/java/freemarker/core/ListLiteral.java
index b4e43ae..db7b4c5 100644
--- a/src/main/java/freemarker/core/ListLiteral.java
+++ b/src/main/java/freemarker/core/ListLiteral.java
@@ -67,13 +67,13 @@ final class ListLiteral extends Expression {
                 return Collections.EMPTY_LIST;
             }
             case 1: {
-                return Collections.singletonList(((Expression) items.get(0)).evalAndCoerceToString(env));
+                return Collections.singletonList(((Expression) items.get(0)).evalAndCoerceToPlainText(env));
             }
             default: {
                 List result = new ArrayList(items.size());
                 for (ListIterator iterator = items.listIterator(); iterator.hasNext(); ) {
                     Expression exp = (Expression) iterator.next();
-                    result.add(exp.evalAndCoerceToString(env));
+                    result.add(exp.evalAndCoerceToPlainText(env));
                 }
                 return result;
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/MarkupOutputFormatBoundBuiltIn.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/MarkupOutputFormatBoundBuiltIn.java b/src/main/java/freemarker/core/MarkupOutputFormatBoundBuiltIn.java
new file mode 100644
index 0000000..5780124
--- /dev/null
+++ b/src/main/java/freemarker/core/MarkupOutputFormatBoundBuiltIn.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.core;
+
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.utility.NullArgumentException;
+
+abstract class MarkupOutputFormatBoundBuiltIn extends SpecialBuiltIn {
+    
+    protected MarkupOutputFormat outputFormat;
+    
+    void bindToMarkupOutputFormat(MarkupOutputFormat outputFormat) {
+        NullArgumentException.check(outputFormat);
+        this.outputFormat = outputFormat;
+    }
+    
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        if (outputFormat == null) {
+            // The parser should prevent this situation
+            throw new NullPointerException("outputFormat was null");
+        }
+        return calculateResult(env);
+    }
+
+    protected abstract TemplateModel calculateResult(Environment env)
+            throws TemplateException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/NewBI.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NewBI.java b/src/main/java/freemarker/core/NewBI.java
index d749b6f..c7842de 100644
--- a/src/main/java/freemarker/core/NewBI.java
+++ b/src/main/java/freemarker/core/NewBI.java
@@ -48,7 +48,7 @@ class NewBI extends BuiltIn {
     @Override
     TemplateModel _eval(Environment env)
             throws TemplateException {
-        return new ConstructorFunction(target.evalAndCoerceToString(env), env, target.getTemplate());
+        return new ConstructorFunction(target.evalAndCoerceToPlainText(env), env, target.getTemplate());
     }
 
     class ConstructorFunction implements TemplateMethodModelEx {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/NumberLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NumberLiteral.java b/src/main/java/freemarker/core/NumberLiteral.java
index 91ded3c..40b63d5 100644
--- a/src/main/java/freemarker/core/NumberLiteral.java
+++ b/src/main/java/freemarker/core/NumberLiteral.java
@@ -42,8 +42,8 @@ final class NumberLiteral extends Expression implements TemplateNumberModel {
     }
 
     @Override
-    public String evalAndCoerceToString(Environment env) throws TemplateException {
-        return env.formatNumberToString(this, this, false);
+    public String evalAndCoerceToPlainText(Environment env) throws TemplateException {
+        return env.formatNumberToPlainText(this, this, false);
     }
 
     public Number getAsNumber() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/OutputFormatBoundBuiltIn.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/OutputFormatBoundBuiltIn.java b/src/main/java/freemarker/core/OutputFormatBoundBuiltIn.java
new file mode 100644
index 0000000..b32617b
--- /dev/null
+++ b/src/main/java/freemarker/core/OutputFormatBoundBuiltIn.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.core;
+
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.utility.NullArgumentException;
+
+abstract class OutputFormatBoundBuiltIn extends SpecialBuiltIn {
+    
+    protected OutputFormat outputFormat;
+    
+    void bindToOutputFormat(OutputFormat outputFormat) {
+        NullArgumentException.check(outputFormat);
+        this.outputFormat = outputFormat;
+    }
+    
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        if (outputFormat == null) {
+            // The parser should prevent this situation
+            throw new NullPointerException("outputFormat was null");
+        }
+        return calculateResult(env);
+    }
+
+    protected abstract TemplateModel calculateResult(Environment env)
+            throws TemplateException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/PropertySetting.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/PropertySetting.java b/src/main/java/freemarker/core/PropertySetting.java
index ab8fec1..7c9da68 100644
--- a/src/main/java/freemarker/core/PropertySetting.java
+++ b/src/main/java/freemarker/core/PropertySetting.java
@@ -120,7 +120,7 @@ final class PropertySetting extends TemplateElement {
         } else if (mval instanceof TemplateNumberModel) {
             strval = ((TemplateNumberModel) mval).getAsNumber().toString();
         } else {
-            strval = value.evalAndCoerceToString(env);
+            strval = value.evalAndCoerceToStringOrUnsupportedMarkup(env);
         }
         env.setSetting(key, strval);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/StopInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/StopInstruction.java b/src/main/java/freemarker/core/StopInstruction.java
index 1d17326..c6a7526 100644
--- a/src/main/java/freemarker/core/StopInstruction.java
+++ b/src/main/java/freemarker/core/StopInstruction.java
@@ -37,7 +37,7 @@ final class StopInstruction extends TemplateElement {
         if (exp == null) {
             throw new StopException(env);
         }
-        throw new StopException(env, exp.evalAndCoerceToString(env));
+        throw new StopException(env, exp.evalAndCoerceToPlainText(env));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/StringLiteral.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/StringLiteral.java b/src/main/java/freemarker/core/StringLiteral.java
index 339da28..051c0aa 100644
--- a/src/main/java/freemarker/core/StringLiteral.java
+++ b/src/main/java/freemarker/core/StringLiteral.java
@@ -77,7 +77,7 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
     
     @Override
     TemplateModel _eval(Environment env) throws TemplateException {
-        return new SimpleScalar(evalAndCoerceToString(env));
+        return new SimpleScalar(evalAndCoerceToPlainText(env));
     }
 
     public String getAsString() {
@@ -93,7 +93,7 @@ final class StringLiteral extends Expression implements TemplateScalarModel {
     }
     
     @Override
-    String evalAndCoerceToString(Environment env) throws TemplateException {
+    String evalAndCoerceToPlainText(Environment env) throws TemplateException {
         if (dynamicValue == null) {
             return value;
         } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/TemplateDateFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateDateFormat.java b/src/main/java/freemarker/core/TemplateDateFormat.java
index 2f49d2d..daec487 100644
--- a/src/main/java/freemarker/core/TemplateDateFormat.java
+++ b/src/main/java/freemarker/core/TemplateDateFormat.java
@@ -53,28 +53,21 @@ public abstract class TemplateDateFormat extends TemplateValueFormat {
      * @throws TemplateModelException
      *             Exception thrown by the {@code dateModel} object when calling its methods.
      */
-    public abstract String formatToString(TemplateDateModel dateModel)
+    public abstract String formatToPlainText(TemplateDateModel dateModel)
             throws TemplateValueFormatException, TemplateModelException;
 
     /**
      * Formats the model to markup instead of to plain text if the result markup will be more than just plain text
      * escaped, otherwise falls back to formatting to plain text. If the markup result would be just the result of
-     * {@link #formatToString(TemplateDateModel)} escaped, it must return the {@link String} that
-     * {@link #formatToString(TemplateDateModel)} does.
+     * {@link #formatToPlainText(TemplateDateModel)} escaped, it must return the {@link String} that
+     * {@link #formatToPlainText(TemplateDateModel)} does.
      * 
-     * <p>The implementation in {@link TemplateDateFormat} simply calls {@link #formatToString(TemplateDateModel)}.
+     * <p>The implementation in {@link TemplateDateFormat} simply calls {@link #formatToPlainText(TemplateDateModel)}.
      * 
-     * @param outputFormat
-     *            When the result is a {@link TemplateMarkupOutputModel} result, it must be exactly of this output
-     *            format.
-     * 
-     * @return A {@link String} or a {@link TemplateMarkupOutputModel}; not {@code null}. If it's a
-     *         {@link TemplateMarkupOutputModel}, then it must have the output format specified in the
-     *         {@code outputFormat} parameter.
+     * @return A {@link String} or a {@link TemplateMarkupOutputModel}; not {@code null}.
      */
-    public Object formatToMarkupOrString(TemplateDateModel dateModel, MarkupOutputFormat<?> outputFormat)
-            throws TemplateValueFormatException, TemplateModelException {
-        return formatToString(dateModel);
+    public Object format(TemplateDateModel dateModel) throws TemplateValueFormatException, TemplateModelException {
+        return formatToPlainText(dateModel);
     }
 
     /**
@@ -94,7 +87,7 @@ public abstract class TemplateDateFormat extends TemplateValueFormat {
      * 
      * @return The interpretation of the text either as a {@link Date} or {@link TemplateDateModel}. Typically, a
      *         {@link Date}. {@link TemplateDateModel} is used if you have to attach some application-specific
-     *         meta-information thats also extracted during {@link #formatToString(TemplateDateModel)} (so if you format
+     *         meta-information thats also extracted during {@link #formatToPlainText(TemplateDateModel)} (so if you format
      *         something and then parse it, you get back an equivalent result). It can't be {@code null}. Known issue
      *         (at least in FTL 2): {@code ?date}/{@code ?time}/{@code ?datetime}, when not invoked as a method, can't
      *         return the {@link TemplateDateModel}, only the {@link Date} from inside it, hence the additional

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/java/freemarker/core/TemplateNumberFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateNumberFormat.java b/src/main/java/freemarker/core/TemplateNumberFormat.java
index 69a5da0..759d969 100644
--- a/src/main/java/freemarker/core/TemplateNumberFormat.java
+++ b/src/main/java/freemarker/core/TemplateNumberFormat.java
@@ -52,28 +52,23 @@ public abstract class TemplateNumberFormat extends TemplateValueFormat {
      * @throws TemplateModelException
      *             Exception thrown by the {@code dateModel} object when calling its methods.
      */
-    public abstract String formatToString(TemplateNumberModel numberModel)
+    public abstract String formatToPlainText(TemplateNumberModel numberModel)
             throws TemplateValueFormatException, TemplateModelException;
 
     /**
      * Formats the model to markup instead of to plain text if the result markup will be more than just plain text
      * escaped, otherwise falls back to formatting to plain text. If the markup result would be just the result of
-     * {@link #formatToString(TemplateNumberModel)} escaped, it must return the {@link String} that
-     * {@link #formatToString(TemplateNumberModel)} does.
+     * {@link #formatToPlainText(TemplateNumberModel)} escaped, it must return the {@link String} that
+     * {@link #formatToPlainText(TemplateNumberModel)} does.
      * 
-     * <p>The implementation in {@link TemplateNumberFormat} simply calls {@link #formatToString(TemplateNumberModel)}.
+     * <p>
+     * The implementation in {@link TemplateNumberFormat} simply calls {@link #formatToPlainText(TemplateNumberModel)}.
      * 
-     * @param outputFormat
-     *            When the result is a {@link TemplateMarkupOutputModel} result, it must be exactly of this output
-     *            format. Not {@code null}.
-     * 
-     * @return A {@link String} or a {@link TemplateMarkupOutputModel}; not {@code null}. If it's a
-     *         {@link TemplateMarkupOutputModel}, then it must have the output format specified in the
-     *         {@code outputFormat} parameter.
+     * @return A {@link String} or a {@link TemplateMarkupOutputModel}; not {@code null}.
      */
-    public Object formatToMarkupOrString(TemplateNumberModel numberModel, MarkupOutputFormat<?> outputFormat)
+    public Object format(TemplateNumberModel numberModel)
             throws TemplateValueFormatException, TemplateModelException {
-        return formatToString(numberModel);
+        return formatToPlainText(numberModel);
     }
     
     /**

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 33c8ecb..e234e90 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -1665,7 +1665,7 @@ Expression AdditiveExpression() :
             if (plus) {
 	            // plus is treated separately, since it is also
 	            // used for concatenation.
-                result = new AddConcatExpression(lhs, rhs, getMarkupOutputFormat());
+                result = new AddConcatExpression(lhs, rhs);
             } else {
                 numberLiteralOnly(lhs);
                 numberLiteralOnly(rhs);
@@ -2089,19 +2089,19 @@ Expression BuiltIn(Expression lhoExp) :
             return result;
         }
 
-        if (result instanceof BuiltInForMarkupOutputFormatRelated) {
+        if (result instanceof MarkupOutputFormatBoundBuiltIn) {
             if (!(outputFormat instanceof MarkupOutputFormat)) {
                 throw new ParseException(
                         "?" + t.image + " can't be used here, as the current output format isn't a markup (escaping) "
                         + "format: " + outputFormat, template, t);
             }
-            ((BuiltInForMarkupOutputFormatRelated) result).bindToMarkupOutputFormat((MarkupOutputFormat) outputFormat);
+            ((MarkupOutputFormatBoundBuiltIn) result).bindToMarkupOutputFormat((MarkupOutputFormat) outputFormat);
             
             return result;
         }
 
-        if (result instanceof BuiltInForOutputFormatRelated) {
-            ((BuiltInForOutputFormatRelated) result).bindToOutputFormat(outputFormat);
+        if (result instanceof OutputFormatBoundBuiltIn) {
+            ((OutputFormatBoundBuiltIn) result).bindToOutputFormat(outputFormat);
             
             return result;
         }
@@ -2933,7 +2933,7 @@ TemplateElement Assign() :
 		        )
 	        )
 	        {
-	            ass = new Assignment(varName, equalsOp.kind, exp, scope, getMarkupOutputFormat());
+	            ass = new Assignment(varName, equalsOp.kind, exp, scope);
                 if (exp != null) {
                    ass.setLocation(template, nameExp, exp);
                 } else {
@@ -2973,7 +2973,7 @@ TemplateElement Assign() :
 	                )
 	            )
 	            {
-	                ass = new Assignment(varName, equalsOp.kind, exp, scope, getMarkupOutputFormat());
+	                ass = new Assignment(varName, equalsOp.kind, exp, scope);
 	                if (exp != null) {
 	                   ass.setLocation(template, nameExp, exp);
 	                } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/manual/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/book.xml b/src/manual/book.xml
index 53c2958..b8bc656 100644
--- a/src/manual/book.xml
+++ b/src/manual/book.xml
@@ -14563,7 +14563,7 @@ German people write: ${12345678}</programlisting>
 German people write: 12.345.678,00</programlisting>
 
           <simplesect xml:id="topic.extendedJavaDecimalFormat">
-            <title>Extended Java decimal format </title>
+            <title>Extended Java decimal format</title>
 
             <indexterm>
               <primary>extended Java decimal format</primary>
@@ -25992,14 +25992,9 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
                   number and date/time/datetime formatting results, like
                   <literal>1.23*10&lt;sup&gt;6&lt;/sup&gt;</literal>, which
                   won't be accidentally auto-escaped, as FreeMarker knows that
-                  it's already HTML. (See [TODO] as an example.) Note that
-                  formatting to markup only happens in a context that has a
-                  markup <link
-                  linkend="dgui_misc_autoescaping_outputformat">output
-                  format</link>, otherwise FreeMarker will format to plain
-                  text, just as before. Also note that no out-of-the-box
-                  (non-custom) formatter formats to markup at the
-                  moment.</para>
+                  it's already HTML. (See [TODO] as an example.) Note that no
+                  out-of-the-box format formats to markup (at the moment), but
+                  you could write such custom format.</para>
                 </listitem>
 
                 <listitem>
@@ -26250,20 +26245,17 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
 
             <listitem>
               <para><literal>TemplateNumberFormat.format</literal>/<literal>TemplateDateFormat.format</literal>
-              overloads were renamed to <literal>formatToString</literal> and
-              <literal>formatToMarkupOrString</literal>. The last has
-              different semantics then its predecessor (which was marked as
-              draft), and it's actually used by FreeMarker. Thus, it's now
-              possible to have HTML or other markup in number and
+              overloads were changed to <literal>formatToPlainText</literal>
+              and <literal>format</literal>, the last returning either String
+              or a <literal>TemplateMarkupOutputModel</literal>. Also,
+              formatting to markup is now actually used by FreeMarker. Thus,
+              it's now possible to have HTML or other markup in number and
               date/time/datetime formatting results, like
               <literal>1.23*10&lt;sup&gt;6&lt;/sup&gt;</literal>, which won't
               be accidentally auto-escaped, as FreeMarker knows that it's
-              already HTML. (See [TODO] as an example.) Note that formatting
-              to markup only happens in a context that has a markup <link
-              linkend="dgui_misc_autoescaping_outputformat">output
-              format</link>, otherwise FreeMarker will format to plain text,
-              just like before. Also note that no out-of-the-box (non-custom)
-              formatter formats to markup at the moment.</para>
+              already HTML. (See [TODO] as an example.) Note that no
+              out-of-the-box format formats to markup (at the moment), but you
+              could write such custom format.</para>
             </listitem>
 
             <listitem>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/AppMetaTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/AppMetaTemplateDateFormatFactory.java b/src/test/java/freemarker/core/AppMetaTemplateDateFormatFactory.java
index e66e6f4..cc518ee 100644
--- a/src/test/java/freemarker/core/AppMetaTemplateDateFormatFactory.java
+++ b/src/test/java/freemarker/core/AppMetaTemplateDateFormatFactory.java
@@ -47,7 +47,7 @@ public class AppMetaTemplateDateFormatFactory extends TemplateDateFormatFactory
         private AppMetaTemplateDateFormat() { }
         
         @Override
-        public String formatToString(TemplateDateModel dateModel)
+        public String formatToPlainText(TemplateDateModel dateModel)
                 throws UnformattableValueException, TemplateModelException {
             String result = String.valueOf(TemplateFormatUtil.getNonNullDate(dateModel).getTime());
             if (dateModel instanceof AppMetaTemplateDateModel) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/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 f66559a..3639a38 100644
--- a/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java
+++ b/src/test/java/freemarker/core/BaseNTemplateNumberFormatFactory.java
@@ -80,7 +80,7 @@ public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactor
         }
         
         @Override
-        public String formatToString(TemplateNumberModel numberModel)
+        public String formatToPlainText(TemplateNumberModel numberModel)
                 throws TemplateModelException, TemplateValueFormatException {
             Number n = TemplateFormatUtil.getNonNullNumber(numberModel);
             try {
@@ -90,7 +90,7 @@ public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactor
                     throw new UnformattableValueException(
                             n + " doesn't fit into an int, and there was no fallback format specified.");
                 } else {
-                    return fallbackFormat.formatToString(numberModel);
+                    return fallbackFormat.formatToPlainText(numberModel);
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c798862a/src/test/java/freemarker/core/CorectionToTextualTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/CorectionToTextualTest.java b/src/test/java/freemarker/core/CorectionToTextualTest.java
new file mode 100644
index 0000000..968981a
--- /dev/null
+++ b/src/test/java/freemarker/core/CorectionToTextualTest.java
@@ -0,0 +1,119 @@
+package freemarker.core;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Date;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import freemarker.template.Configuration;
+import freemarker.template.SimpleDate;
+import freemarker.template.TemplateDateModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModelException;
+import freemarker.test.TemplateTest;
+
+@SuppressWarnings("boxing")
+public class CorectionToTextualTest extends TemplateTest {
+    
+    /** 2015-09-06T12:00:00Z */
+    private static long T = 1441540800000L;
+    private static TemplateDateModel TM = new SimpleDate(new Date(T), TemplateDateModel.DATETIME);
+    
+    @Test
+    public void testBasicStringBuiltins() throws IOException, TemplateException {
+        assertOutput("${s?upperCase}", "ABC");
+        assertOutput("${n?string?lowerCase}", "1.50e+03");
+        assertErrorContains("${n?lowerCase}", "convert", "string", "markup", "text/html");
+        assertOutput("${dt?string?lowerCase}", "2015-09-06t12:00:00z");
+        assertErrorContains("${dt?lowerCase}", "convert", "string", "markup", "text/html");
+        assertOutput("${b?upperCase}", "Y");
+        assertErrorContains("${m?upperCase}", "convertible to string", "HTMLOutputModel");
+    }
+
+    @Test
+    public void testEscBuiltin() throws IOException, TemplateException {
+        Configuration cfg = getConfiguration();
+        cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);
+        cfg.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
+        cfg.setBooleanFormat("<y>,<n>");
+        
+        assertOutput("${'a<b'?esc}", "a&lt;b");
+        assertOutput("${n?string?esc}", "1.50E+03");
+        assertOutput("${n?esc}", "1.50*10<sup>3</sup>");
+        assertOutput("${dt?string?esc}", "2015-09-06T12:00:00Z");
+        assertOutput("${dt?esc}", "2015-09-06<span class='T'>T</span>12:00:00Z");
+        assertOutput("${b?esc}", "&lt;y&gt;");
+        assertOutput("${m?esc}", "<p>M</p>");
+    }
+    
+    @Test
+    public void testStringOverloadedBuiltIns() throws IOException, TemplateException {
+        assertOutput("${s?contains('b')}", "y");
+        assertOutput("${n?string?contains('E')}", "y");
+        assertErrorContains("${n?contains('E')}", "convert", "string", "markup", "text/html");
+        assertErrorContains("${n?indexOf('E')}", "convert", "string", "markup", "text/html");
+        assertOutput("${dt?string?contains('0')}", "y");
+        assertErrorContains("${dt?contains('0')}", "convert", "string", "markup", "text/html");
+        assertErrorContains("${m?contains('0')}", "convertible to string", "HTMLOutputModel");
+        assertErrorContains("${m?indexOf('0')}", "convertible to string", "HTMLOutputModel");
+    }
+    
+    @Test
+    public void testMarkupStringBuiltIns() throws IOException, TemplateException {
+        assertErrorContains("${n?string?markupString}", "Expected", "markup", "string");
+        assertErrorContains("${n?markupString}", "Expected", "markup", "number");
+        assertErrorContains("${dt?markupString}", "Expected", "markup", "date");
+    }
+    
+    @Test
+    public void testSimpleInterpolation() throws IOException, TemplateException {
+        assertOutput("${s}", "abc");
+        assertOutput("${n?string}", "1.50E+03");
+        assertOutput("${n}", "1.50*10<sup>3</sup>");
+        assertOutput("${dt?string}", "2015-09-06T12:00:00Z");
+        assertOutput("${dt}", "2015-09-06<span class='T'>T</span>12:00:00Z");
+        assertOutput("${b}", "y");
+        assertOutput("${m}", "<p>M</p>");
+    }
+    
+    @Test
+    public void testConcatenation() throws IOException, TemplateException {
+        assertOutput("${s + '&'}", "abc&");
+        assertOutput("${n?string + '&'}", "1.50E+03&");
+        assertOutput("${n + '&'}", "1.50*10<sup>3</sup>&amp;");
+        assertOutput("${dt?string + '&'}", "2015-09-06T12:00:00Z&");
+        assertOutput("${dt + '&'}", "2015-09-06<span class='T'>T</span>12:00:00Z&amp;");
+        assertOutput("${b + '&'}", "y&");
+        assertOutput("${m + '&'}", "<p>M</p>&amp;");
+    }
+
+    @Test
+    public void testConcatenation2() throws IOException, TemplateException {
+        assertOutput("${'&' + s}", "&abc");
+        assertOutput("${'&' + n?string}", "&1.50E+03");
+        assertOutput("${'&' + n}", "&amp;1.50*10<sup>3</sup>");
+        assertOutput("${'&' + dt?string}", "&2015-09-06T12:00:00Z");
+        assertOutput("${'&' + dt}", "&amp;2015-09-06<span class='T'>T</span>12:00:00Z");
+        assertOutput("${'&' + b}", "&y");
+        assertOutput("${'&' + m}", "&amp;<p>M</p>");
+    }
+    
+    @Before
+    public void setup() throws TemplateModelException {
+        Configuration cfg = getConfiguration();
+        cfg.setCustomNumberFormats(Collections.singletonMap("G", PrintfGTemplateNumberFormatFactory.INSTANCE));
+        cfg.setCustomDateFormats(Collections.singletonMap("HI", HTMLISOTemplateDateFormatFactory.INSTANCE));
+        cfg.setNumberFormat("@G 3");
+        cfg.setDateTimeFormat("@HI");
+        cfg.setBooleanFormat("y,n");
+        
+        addToDataModel("s", "abc");
+        addToDataModel("n", 1500);
+        addToDataModel("dt", TM);
+        addToDataModel("b", Boolean.TRUE);
+        addToDataModel("m", HTMLOutputFormat.INSTANCE.fromMarkup("<p>M</p>"));
+    }
+
+}


[2/5] incubator-freemarker git commit: (Manual: Missing programlisting role)

Posted by dd...@apache.org.
(Manual: Missing programlisting role)


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/b680e9cd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/b680e9cd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/b680e9cd

Branch: refs/heads/2.3
Commit: b680e9cd7feca8f5d832e6ba68ea2e6e7caedfad
Parents: 9ce036f
Author: ddekany <dd...@apache.org>
Authored: Sat Sep 26 16:13:34 2015 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Sep 26 16:13:34 2015 +0200

----------------------------------------------------------------------
 src/manual/book.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b680e9cd/src/manual/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/book.xml b/src/manual/book.xml
index 57cdd37..53c2958 100644
--- a/src/manual/book.xml
+++ b/src/manual/book.xml
@@ -14462,7 +14462,7 @@ $42.00
           linkend="pgui_config_custom_formats">see more here</link>),
           like:</para>
 
-          <programlisting>${x?strong.@price}
+          <programlisting role="template">${x?strong.@price}
 ${x?strong.@weight}</programlisting>
 
           <para>where the custom format names were <quote>price</quote> and


[5/5] incubator-freemarker git commit: Merge remote-tracking branch 'origin/2.3-gae' into 2.3

Posted by dd...@apache.org.
Merge remote-tracking branch 'origin/2.3-gae' into 2.3


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/4b1550e7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/4b1550e7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/4b1550e7

Branch: refs/heads/2.3
Commit: 4b1550e786f5ec51f1b95cc4ca4e7836c09351c2
Parents: 84927d9 c798862
Author: ddekany <dd...@apache.org>
Authored: Tue Sep 29 02:02:48 2015 +0200
Committer: ddekany <dd...@apache.org>
Committed: Tue Sep 29 02:02:48 2015 +0200

----------------------------------------------------------------------
 .../freemarker/core/AddConcatExpression.java    |  21 ++--
 src/main/java/freemarker/core/Assignment.java   |   9 +-
 .../core/BuiltInForLegacyEscaping.java          |   4 +-
 .../BuiltInForMarkupOutputFormatRelated.java    |  46 -------
 .../core/BuiltInForOutputFormatRelated.java     |  46 -------
 .../java/freemarker/core/BuiltInForString.java  |   2 +-
 .../core/BuiltInsForMultipleTypes.java          |  14 +--
 .../core/BuiltInsForOutputFormatRelated.java    |   7 +-
 .../freemarker/core/BuiltInsForSequences.java   |   2 +-
 .../core/BuiltInsForStringsBasic.java           |   6 +-
 .../core/BuiltInsForStringsEncoding.java        |  26 +---
 .../freemarker/core/BuiltInsForStringsMisc.java |   2 +-
 .../java/freemarker/core/DollarVariable.java    |   6 +-
 .../java/freemarker/core/DynamicKeyName.java    |   4 +-
 src/main/java/freemarker/core/Environment.java  |  18 +--
 src/main/java/freemarker/core/EvalUtil.java     | 126 +++++++++++++++----
 src/main/java/freemarker/core/Expression.java   |  30 ++++-
 src/main/java/freemarker/core/HashLiteral.java  |   4 +-
 .../core/ISOLikeTemplateDateFormat.java         |   2 +-
 src/main/java/freemarker/core/Include.java      |   6 +-
 src/main/java/freemarker/core/Interpret.java    |   6 +-
 .../freemarker/core/JavaTemplateDateFormat.java |   2 +-
 .../core/JavaTemplateNumberFormat.java          |   2 +-
 src/main/java/freemarker/core/LibraryLoad.java  |   2 +-
 src/main/java/freemarker/core/ListLiteral.java  |   4 +-
 .../core/MarkupOutputFormatBoundBuiltIn.java    |  46 +++++++
 src/main/java/freemarker/core/NewBI.java        |   2 +-
 .../java/freemarker/core/NumberLiteral.java     |   4 +-
 .../core/OutputFormatBoundBuiltIn.java          |  46 +++++++
 .../java/freemarker/core/PropertySetting.java   |   2 +-
 .../java/freemarker/core/StopInstruction.java   |   2 +-
 .../java/freemarker/core/StringLiteral.java     |   4 +-
 .../freemarker/core/TemplateDateFormat.java     |  21 ++--
 .../freemarker/core/TemplateNumberFormat.java   |  19 ++-
 .../java/freemarker/ext/beans/BeanModel.java    |   8 +-
 src/main/javacc/FTL.jj                          |  14 +--
 src/manual/book.xml                             |  34 ++---
 .../core/AppMetaTemplateDateFormatFactory.java  |   2 +-
 .../core/BaseNTemplateNumberFormatFactory.java  |   4 +-
 .../freemarker/core/CorectionToTextualTest.java | 119 ++++++++++++++++++
 .../java/freemarker/core/DateFormatTest.java    |  34 ++---
 ...EpochMillisDivTemplateDateFormatFactory.java |   2 +-
 .../EpochMillisTemplateDateFormatFactory.java   |   2 +-
 .../core/HTMLISOTemplateDateFormatFactory.java  | 104 +++++++++++++++
 .../core/HexTemplateNumberFormatFactory.java    |   2 +-
 ...AndTZSensitiveTemplateDateFormatFactory.java |   2 +-
 ...aleSensitiveTemplateNumberFormatFactory.java |   2 +-
 .../java/freemarker/core/NumberFormatTest.java  |  30 +++--
 .../PrintfGTemplateNumberFormatFactory.java     |  11 +-
 49 files changed, 594 insertions(+), 319 deletions(-)
----------------------------------------------------------------------