freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [2/2] incubator-freemarker git commit: Settings that have contained a charset name (sourceEncoding, outputEncoding, URLEscapingCharset) are now of type Charset, not String. For string based configuration sources (such as .properties files) this means tha
Date Tue, 28 Mar 2017 23:10:32 GMT
Settings that have contained a charset name (sourceEncoding, outputEncoding, URLEscapingCharset) are now of type Charset, not String. For string based configuration sources (such as .properties files) this means that:

- Unrecognized charset names are now errors
- For recognized names the charset name will be normalized (like "latin1" becomes to "ISO-8859-1").
- In "object builder expressions" Charset values can now be constructed like `Charset("ISO-8859-5")`. Note that as the type of the settings have changed, now you can't just write something like `TemplateConfiguration(sourceEncoding = "UTF-8")`, but `TemplateConfiguration(sourceEncoding = Charset("UTF-8"))`.


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

Branch: refs/heads/3
Commit: 27a62ce0158e689d0bf23b239447d6f40721ccc4
Parents: 5062b4e
Author: ddekany <ddekany@apache.org>
Authored: Wed Mar 29 01:09:12 2017 +0200
Committer: ddekany <ddekany@apache.org>
Committed: Wed Mar 29 01:10:08 2017 +0200

----------------------------------------------------------------------
 .../freemarker/core/ASTExpBuiltInVariable.java  |  9 ++-
 .../core/BuiltInsForStringsEncoding.java        | 30 ++++---
 .../apache/freemarker/core/Configuration.java   | 51 ++++++------
 .../org/apache/freemarker/core/Environment.java |  9 ++-
 .../MutableProcessingAndParseConfiguration.java | 13 +--
 .../core/MutableProcessingConfiguration.java    | 41 +++++-----
 .../freemarker/core/ParserConfiguration.java    |  3 +-
 .../core/ProcessingConfiguration.java           |  9 ++-
 .../org/apache/freemarker/core/Template.java    | 37 +++++----
 .../freemarker/core/TemplateConfiguration.java  | 17 ++--
 .../freemarker/core/TemplateLanguage.java       | 15 ++--
 .../core/WrongTemplateCharsetException.java     | 12 +--
 .../apache/freemarker/core/_CharsetBuilder.java | 41 ++++++++++
 .../core/_ObjectBuilderSettingEvaluator.java    | 14 +++-
 ..._ParserConfigurationWithInheritedFormat.java |  4 +-
 .../freemarker/core/debug/DebuggerClient.java   |  3 +-
 .../freemarker/core/debug/DebuggerServer.java   |  9 ++-
 .../templateresolver/TemplateLoadingResult.java |  5 +-
 .../impl/DefaultTemplateResolver.java           |  5 +-
 .../freemarker/core/util/_StringUtil.java       |  9 ++-
 .../freemarker/servlet/FreemarkerServlet.java   | 30 ++++---
 .../freemarker/servlet/jsp/TaglibFactory.java   | 14 ++--
 src/main/javacc/FTL.jj                          | 21 ++++-
 src/manual/en_US/FM3-CHANGE-LOG.txt             |  7 ++
 .../org/apache/freemarker/core/ASTPrinter.java  |  5 +-
 .../apache/freemarker/core/CamelCaseTest.java   | 29 +++----
 .../freemarker/core/ConfigurationTest.java      | 64 ++++++++-------
 .../freemarker/core/EncodingOverrideTest.java   | 31 +++-----
 .../core/ObjectBuilderSettingsTest.java         | 40 +++++++++-
 .../core/TemplateConfigurationTest.java         | 51 ++++--------
 ...igurationWithDefaltTemplateResolverTest.java | 32 ++++----
 .../core/TemplateConstructorsTest.java          | 52 +++++++-----
 .../core/TemplateGetEncodingTest.java           | 10 ++-
 .../DefaultTemplateResolverTest.java            |  8 +-
 .../manualtest/GettingStartedExample.java       |  3 +-
 .../TemplateConfigurationExamples.java          | 19 ++---
 .../servlet/FreemarkerServletTest.java          | 84 ++++++++++----------
 .../freemarker/test/ResourcesExtractor.java     |  3 +-
 .../apache/freemarker/test/TemplateTest.java    |  6 +-
 .../test/servlet/Model2TesterServlet.java       |  3 +-
 .../freemarker/test/servlet/WebAppTestCase.java |  7 +-
 .../test/templatesuite/TemplateTestCase.java    | 10 ++-
 .../freemarker/test/util/FileTestCase.java      | 26 +++---
 .../TemplateConfigurationExamples1.properties   |  2 +-
 .../TemplateConfigurationExamples3.properties   |  2 +-
 .../freemarker/test/templatesuite/testcases.xml |  6 +-
 46 files changed, 528 insertions(+), 373 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java b/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
index 2b4f756..9110fd3 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java
@@ -19,6 +19,7 @@
 
 package org.apache.freemarker.core;
 
+import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.Date;
 
@@ -210,12 +211,12 @@ final class ASTExpBuiltInVariable extends ASTExpression {
             return ASTDirMacro.DO_NOTHING_MACRO;
         }
         if (name == OUTPUT_ENCODING || name == OUTPUT_ENCODING_CC) {
-            String s = env.getOutputEncoding();
-            return SimpleScalar.newInstanceOrNull(s);
+            Charset encoding = env.getOutputEncoding();
+            return encoding != null ? new SimpleScalar(encoding.name()) : null;
         }
         if (name == URL_ESCAPING_CHARSET || name == URL_ESCAPING_CHARSET_CC) {
-            String s = env.getURLEscapingCharset();
-            return SimpleScalar.newInstanceOrNull(s);
+            Charset charset = env.getURLEscapingCharset();
+            return charset != null ? new SimpleScalar(charset.name()) : null;
         }
         if (name == ERROR) {
             return new SimpleScalar(env.getCurrentRecoveredErrorMessage());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
index e3b00fb..80eb9d3 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
@@ -20,6 +20,8 @@
 package org.apache.freemarker.core;
 
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
 import java.util.List;
 
 import org.apache.freemarker.core.model.TemplateMethodModel;
@@ -77,8 +79,8 @@ class BuiltInsForStringsEncoding {
             }
     
             @Override
-            protected String encodeWithCharset(String cs) throws UnsupportedEncodingException {
-                return _StringUtil.URLEnc(targetAsString, cs);
+            protected String encodeWithCharset(Charset charset) throws UnsupportedEncodingException {
+                return _StringUtil.URLEnc(targetAsString, charset);
             }
             
         }
@@ -99,8 +101,8 @@ class BuiltInsForStringsEncoding {
             }
     
             @Override
-            protected String encodeWithCharset(String cs) throws UnsupportedEncodingException {
-                return _StringUtil.URLPathEnc(targetAsString, cs);
+            protected String encodeWithCharset(Charset charset) throws UnsupportedEncodingException {
+                return _StringUtil.URLPathEnc(targetAsString, charset);
             }
             
         }
@@ -143,14 +145,22 @@ class BuiltInsForStringsEncoding {
             this.env = env;
         }
         
-        protected abstract String encodeWithCharset(String cs) throws UnsupportedEncodingException;
+        protected abstract String encodeWithCharset(Charset charset) throws UnsupportedEncodingException;
     
         @Override
         public Object exec(List args) throws TemplateModelException {
             parent.checkMethodArgCount(args.size(), 1);
             try {
-                return new SimpleScalar(encodeWithCharset((String) args.get(0)));
-            } catch (UnsupportedEncodingException e) {
+                String charsetName = (String) args.get(0);
+                Charset charset = null;
+                try {
+                    charset = Charset.forName(charsetName);
+                } catch (UnsupportedCharsetException e) {
+                    throw new _TemplateModelException(e, "Wrong charset name, or charset is unsupported by the runtime "
+                            + "environment: " + _StringUtil.jQuote(charsetName));
+                }
+                return new SimpleScalar(encodeWithCharset(charset));
+            } catch (Exception e) {
                 throw new _TemplateModelException(e, "Failed to execute URL encoding.");
             }
         }
@@ -158,8 +168,8 @@ class BuiltInsForStringsEncoding {
         @Override
         public String getAsString() throws TemplateModelException {
             if (cachedResult == null) {
-                String cs = env.getEffectiveURLEscapingCharset();
-                if (cs == null) {
+                Charset charset = env.getEffectiveURLEscapingCharset();
+                if (charset == null) {
                     throw new _TemplateModelException(
                             "To do URL encoding, the framework that encloses "
                             + "FreeMarker must specify the output encoding "
@@ -172,7 +182,7 @@ class BuiltInsForStringsEncoding {
                             + "foo?url('ISO-8859-1').");
                 }
                 try {
-                    cachedResult = encodeWithCharset(cs);
+                    cachedResult = encodeWithCharset(charset);
                 } catch (UnsupportedEncodingException e) {
                     throw new _TemplateModelException(e, "Failed to execute URL encoding.");
                 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java b/src/main/java/org/apache/freemarker/core/Configuration.java
index 826e2d0..52bfd19 100644
--- a/src/main/java/org/apache/freemarker/core/Configuration.java
+++ b/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -23,6 +23,8 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Collections;
@@ -88,7 +90,6 @@ import org.apache.freemarker.core.util.StandardCompress;
 import org.apache.freemarker.core.util.XmlEscape;
 import org.apache.freemarker.core.util._ClassUtil;
 import org.apache.freemarker.core.util._NullArgumentException;
-import org.apache.freemarker.core.util._SecurityUtil;
 import org.apache.freemarker.core.util._SortedArraySet;
 import org.apache.freemarker.core.util._StringUtil;
 import org.apache.freemarker.core.util._UnmodifiableCompositeSet;
@@ -126,7 +127,7 @@ import org.apache.freemarker.core.util._UnmodifiableCompositeSet;
  *       useless. (For the most common cases you can use the convenience methods,
  *       {@link #setDirectoryForTemplateLoading(File)} and {@link #setClassForTemplateLoading(Class, String)} and
  *       {@link #setClassLoaderForTemplateLoading(ClassLoader, String)} too.)
- *   <li>{@link #setEncoding(String) encoding}: The default value is system dependent, which makes it
+ *   <li>{@link #setSourceEncoding(Charset) sourceEncoding}: The default value is system dependent, which makes it
  *       fragile on servers, so it should be set explicitly, like to "UTF-8" nowadays. 
  *   <li>{@link #setTemplateExceptionHandler(TemplateExceptionHandler) template_exception_handler}: For developing
  *       HTML pages, the most convenient value is {@link TemplateExceptionHandler#HTML_DEBUG_HANDLER}. For production,
@@ -427,7 +428,7 @@ public final class Configuration extends MutableProcessingConfiguration<Configur
     private boolean templateExceptionHandlerExplicitlySet;
     private boolean logTemplateExceptionsExplicitlySet;
     private boolean localeExplicitlySet;
-    private boolean defaultEncodingExplicitlySet;
+    private boolean sourceEncodingExplicitlySet;
     private boolean timeZoneExplicitlySet;
 
     private HashMap/*<String, TemplateModel>*/ sharedVariables = new HashMap();
@@ -442,7 +443,7 @@ public final class Configuration extends MutableProcessingConfiguration<Configur
      */
     private HashMap<String, Object> rewrappableSharedVariables = null;
     
-    private String encoding = getDefaultSourceEncoding();
+    private Charset sourceEncoding = getDefaultSourceEncoding();
 
     /**
      * @deprecated Use {@link #Configuration(Version)} instead. Note that the version can be still modified later with
@@ -2032,23 +2033,23 @@ public final class Configuration extends MutableProcessingConfiguration<Configur
      * the charset of the template.
      *
      * <p>Individual templates may specify their own charset by starting with
-     * <tt>&lt;#ftl encoding="..."&gt;</tt>. However, before that's detected, at least part of template must be
+     * <tt>&lt;#ftl sourceEncoding="..."&gt;</tt>. However, before that's detected, at least part of template must be
      * decoded with some charset first, so this setting and {@linkplain #getTemplateConfigurations() template
      * configuration} still has a role.
      *
-     * <p>Defaults to the default system encoding, which can change from one server to
+     * <p>Defaults to the default system sourceEncoding, which can change from one server to
      * another, so <b>you should always set this setting</b>. If you don't know what charset your should chose,
      * {@code "UTF-8"} is usually a good choice.
      *
-     * @param encoding The name of the charset, such as {@code "UTF-8"} or {@code "ISO-8859-1"}
+     * @param sourceEncoding The charset, for example {@link StandardCharsets#UTF_8}.
      */
-    public void setEncoding(String encoding) {
-        this.encoding = encoding;
-        defaultEncodingExplicitlySet = true;
+    public void setSourceEncoding(Charset sourceEncoding) {
+        this.sourceEncoding = sourceEncoding;
+        sourceEncodingExplicitlySet = true;
     }
 
-    public String getSourceEncoding() {
-        return encoding;
+    public Charset getSourceEncoding() {
+        return sourceEncoding;
     }
 
     /**
@@ -2056,29 +2057,25 @@ public final class Configuration extends MutableProcessingConfiguration<Configur
      *
      * @since 2.3.26
      */
-    public void unsetDefaultEncoding() {
-        if (defaultEncodingExplicitlySet) {
-            setEncoding(getDefaultSourceEncoding());
-            defaultEncodingExplicitlySet = false;
+    public void unsetSourceEncoding() {
+        if (sourceEncodingExplicitlySet) {
+            setSourceEncoding(getDefaultSourceEncoding());
+            sourceEncodingExplicitlySet = false;
         }
     }
 
     /**
-     * Tells if {@link #setEncoding(String)} (or equivalent) was already called on this instance, or it just holds the
+     * Tells if {@link #setSourceEncoding(Charset)} (or equivalent) was already called on this instance, or it just holds the
      * default value.
      *
      * @since 2.3.26
      */
-    public boolean isDefaultEncodingExplicitlySet() {
-        return defaultEncodingExplicitlySet;
+    public boolean isSourceEncodingExplicitlySet() {
+        return sourceEncodingExplicitlySet;
     }
 
-    static private String getDefaultSourceEncoding() {
-        return getJVMDefaultEncoding();
-    }
-
-    static private String getJVMDefaultEncoding() {
-        return _SecurityUtil.getSystemProperty("file.encoding", "utf-8");
+    static private Charset getDefaultSourceEncoding() {
+        return Charset.defaultCharset();
     }
 
     /**
@@ -2305,9 +2302,9 @@ public final class Configuration extends MutableProcessingConfiguration<Configur
             
             if (SOURCE_ENCODING_KEY_SNAKE_CASE.equals(name) || SOURCE_ENCODING_KEY_CAMEL_CASE.equals(name)) {
                 if (JVM_DEFAULT_VALUE.equalsIgnoreCase(value)) {
-                    setEncoding(getJVMDefaultEncoding());
+                    setSourceEncoding(Charset.defaultCharset());
                 } else {
-                    setEncoding(value);
+                    setSourceEncoding(Charset.forName(value));
                 }
             } else if (LOCALIZED_LOOKUP_KEY_SNAKE_CASE.equals(name) || LOCALIZED_LOOKUP_KEY_CAMEL_CASE.equals(name)) {
                 setLocalizedLookup(_StringUtil.getYesNo(value));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Environment.java b/src/main/java/org/apache/freemarker/core/Environment.java
index ce95b15..6733045 100644
--- a/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/src/main/java/org/apache/freemarker/core/Environment.java
@@ -24,6 +24,7 @@ import java.io.PrintWriter;
 import java.io.Serializable;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.nio.charset.Charset;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.text.Collator;
@@ -185,7 +186,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     private int nodeNamespaceIndex;
     private String currentNodeName, currentNodeNS;
 
-    private String cachedURLEscapingCharset;
+    private Charset cachedURLEscapingCharset;
     private boolean cachedURLEscapingCharsetSet;
 
     private boolean fastInvalidReferenceExceptions;
@@ -941,7 +942,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    public void setURLEscapingCharset(String urlEscapingCharset) {
+    public void setURLEscapingCharset(Charset urlEscapingCharset) {
         cachedURLEscapingCharsetSet = false;
         super.setURLEscapingCharset(urlEscapingCharset);
     }
@@ -952,7 +953,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
      * change the output encoding on-the-fly.
      */
     @Override
-    public void setOutputEncoding(String outputEncoding) {
+    public void setOutputEncoding(Charset outputEncoding) {
         cachedURLEscapingCharsetSet = false;
         super.setOutputEncoding(outputEncoding);
     }
@@ -961,7 +962,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
      * Returns the name of the charset that should be used for URL encoding. This will be <code>null</code> if the
      * information is not available. The function caches the return value, so it's quick to call it repeatedly.
      */
-    String getEffectiveURLEscapingCharset() {
+    Charset getEffectiveURLEscapingCharset() {
         if (!cachedURLEscapingCharsetSet) {
             cachedURLEscapingCharset = getURLEscapingCharset();
             if (cachedURLEscapingCharset == null) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/MutableProcessingAndParseConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/MutableProcessingAndParseConfiguration.java b/src/main/java/org/apache/freemarker/core/MutableProcessingAndParseConfiguration.java
index 4d7a1e1..03cfb95 100644
--- a/src/main/java/org/apache/freemarker/core/MutableProcessingAndParseConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/MutableProcessingAndParseConfiguration.java
@@ -20,6 +20,7 @@
 package org.apache.freemarker.core;
 
 import java.io.InputStream;
+import java.nio.charset.Charset;
 
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -38,7 +39,7 @@ public abstract class MutableProcessingAndParseConfiguration<
     private Integer autoEscapingPolicy;
     private Boolean recognizeStandardFileExtensions;
     private OutputFormat outputFormat;
-    private String sourceEncoding;
+    private Charset sourceEncoding;
     private Integer tabSize;
 
     protected MutableProcessingAndParseConfiguration(Version incompatibleImprovements) {
@@ -227,17 +228,17 @@ public abstract class MutableProcessingAndParseConfiguration<
         return recognizeStandardFileExtensions != null;
     }
 
-    public String getSourceEncoding() {
+    public Charset getSourceEncoding() {
         return sourceEncoding != null ? sourceEncoding : getDefaultSourceEncoding();
     }
 
-    protected abstract String getDefaultSourceEncoding();
+    protected abstract Charset getDefaultSourceEncoding();
 
     /**
      * The charset to be used when reading the template "file" that the {@link TemplateLoader} returns as binary
      * ({@link InputStream}). If the {@code #ftl} header specifies an charset, that will override this.
      */
-    public void setSourceEncoding(String sourceEncoding) {
+    public void setSourceEncoding(Charset sourceEncoding) {
         _NullArgumentException.check("sourceEncoding", sourceEncoding);
         this.sourceEncoding = sourceEncoding;
     }
@@ -263,10 +264,10 @@ public abstract class MutableProcessingAndParseConfiguration<
     @Override
     public int getTabSize() {
         return tabSize != null ? tabSize.intValue()
-                : getDefailtTabSize();
+                : getDefaultTabSize();
     }
 
-    protected abstract int getDefailtTabSize();
+    protected abstract int getDefaultTabSize();
 
     /**
      * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java b/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
index eef4188..d9200b0 100644
--- a/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
@@ -21,6 +21,7 @@ package org.apache.freemarker.core;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.nio.charset.Charset;
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -337,9 +338,9 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     private TemplateExceptionHandler templateExceptionHandler;
     private ArithmeticEngine arithmeticEngine;
     private ObjectWrapper objectWrapper;
-    private String outputEncoding;
+    private Charset outputEncoding;
     private boolean outputEncodingSet;
-    private String urlEscapingCharset;
+    private Charset urlEscapingCharset;
     private boolean urlEscapingCharsetSet;
     private Boolean autoFlush;
     private TemplateClassResolver newBuiltinClassResolver;
@@ -1306,21 +1307,21 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      * 
      * <p>Defaults to {@code null} (unknown).
      */
-    public void setOutputEncoding(String outputEncoding) {
+    public void setOutputEncoding(Charset outputEncoding) {
         this.outputEncoding = outputEncoding;
         outputEncodingSet = true;
     }
 
     /**
-     * Fluent API equivalent of {@link #setOutputEncoding(String)}
+     * Fluent API equivalent of {@link #setOutputEncoding(Charset)}
      */
-    public SelfT outputEncoding(String value) {
+    public SelfT outputEncoding(Charset value) {
         setOutputEncoding(value);
         return self();
     }
 
     @Override
-    public String getOutputEncoding() {
+    public Charset getOutputEncoding() {
         return outputEncodingSet
                 ? outputEncoding
                 : (parent != null ? parent.getOutputEncoding() : null);
@@ -1338,25 +1339,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     
     /**
      * Sets the URL escaping charset. If not set ({@code null}), the output encoding
-     * ({@link #setOutputEncoding(String)}) will be used for URL escaping.
+     * ({@link #setOutputEncoding(Charset)}) will be used for URL escaping.
      * 
      * Defaults to {@code null}.
      */
-    public void setURLEscapingCharset(String urlEscapingCharset) {
+    public void setURLEscapingCharset(Charset urlEscapingCharset) {
         this.urlEscapingCharset = urlEscapingCharset;
         urlEscapingCharsetSet = true;
     }
 
     /**
-     * Fluent API equivalent of {@link #setURLEscapingCharset(String)}
+     * Fluent API equivalent of {@link #setURLEscapingCharset(Charset)}
      */
-    public SelfT urlEscapingCharset(String value) {
+    public SelfT urlEscapingCharset(Charset value) {
         setURLEscapingCharset(value);
         return self();
     }
 
     @Override
-    public String getURLEscapingCharset() {
+    public Charset getURLEscapingCharset() {
         return urlEscapingCharsetSet
                 ? urlEscapingCharset
                 : (parent != null ? parent.getURLEscapingCharset() : null);
@@ -1982,10 +1983,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      *       as {@link #setSQLDateAndTimeTimeZone(TimeZone) setSQLDateAndTimeTimeZone(null)}.
      *       
      *   <li><p>{@code "output_encoding"}:
-     *       See {@link #setOutputEncoding(String)}.
+     *       See {@link #setOutputEncoding(Charset)}.
      *       
      *   <li><p>{@code "url_escaping_charset"}:
-     *       See {@link #setURLEscapingCharset(String)}.
+     *       See {@link #setURLEscapingCharset(Charset)}.
      *       
      *   <li><p>{@code "auto_flush"}:
      *       See {@link #setAutoFlush(boolean)}.
@@ -2088,8 +2089,8 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      *       {@link Configuration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}
      *       {@code "disable"} for {@link Configuration#DISABLE_AUTO_ESCAPING_POLICY}.
      *       
-     *   <li><p>{@code "encoding"}:
-     *       See {@link Configuration#setEncoding(String)}; since 2.3.26 also accepts value "JVM default"
+     *   <li><p>{@code "sourceEncoding"}:
+     *       See {@link Configuration#setSourceEncoding(Charset)}; since 2.3.26 also accepts value "JVM default"
      *       (not case sensitive) to set the Java environment default value.
      *       <br>As the default value is the system default, which can change
      *       from one server to another, <b>you should always set this!</b>
@@ -2266,7 +2267,11 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      *   </li>
      *   <li>
      *     <p>{@link TimeZone} objects can be created like {@code TimeZone("UTC")}, despite that there's no a such
-     *     constructor (since 2.3.24).
+     *     constructor.
+     *   </li>
+     *   <li>
+     *     <p>{@link Charset} objects can be created like {@code Charset("ISO-8859-5")}, despite that there's no a such
+     *     constructor.
      *   </li>
      *   <li>
      *     <p>The classes and methods that the expression meant to access must be all public.
@@ -2368,10 +2373,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
             } else if (BOOLEAN_FORMAT_KEY_SNAKE_CASE.equals(name) || BOOLEAN_FORMAT_KEY_CAMEL_CASE.equals(name)) {
                 setBooleanFormat(value);
             } else if (OUTPUT_ENCODING_KEY_SNAKE_CASE.equals(name) || OUTPUT_ENCODING_KEY_CAMEL_CASE.equals(name)) {
-                setOutputEncoding(value);
+                setOutputEncoding(Charset.forName(value));
             } else if (URL_ESCAPING_CHARSET_KEY_SNAKE_CASE.equals(name)
                     || URL_ESCAPING_CHARSET_KEY_CAMEL_CASE.equals(name)) {
-                setURLEscapingCharset(value);
+                setURLEscapingCharset(Charset.forName(value));
             } else if (AUTO_FLUSH_KEY_SNAKE_CASE.equals(name) || AUTO_FLUSH_KEY_CAMEL_CASE.equals(name)) {
                 setAutoFlush(_StringUtil.getYesNo(value));
             } else if (SHOW_ERROR_TIPS_KEY_SNAKE_CASE.equals(name) || SHOW_ERROR_TIPS_KEY_CAMEL_CASE.equals(name)) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/ParserConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ParserConfiguration.java b/src/main/java/org/apache/freemarker/core/ParserConfiguration.java
index a620506..e515826 100644
--- a/src/main/java/org/apache/freemarker/core/ParserConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/ParserConfiguration.java
@@ -19,6 +19,7 @@
 package org.apache.freemarker.core;
 
 import java.io.Writer;
+import java.nio.charset.Charset;
 
 import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
 import org.apache.freemarker.core.outputformat.OutputFormat;
@@ -144,6 +145,6 @@ public interface ParserConfiguration {
      * reading template files in a locale for which no explicit encoding
      * was specified. Defaults to the default system encoding.
      */
-    String getSourceEncoding();
+    Charset getSourceEncoding();
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java b/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
index 16cbdd5..33b028e 100644
--- a/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
@@ -20,6 +20,7 @@
 package org.apache.freemarker.core;
 
 import java.io.Writer;
+import java.nio.charset.Charset;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -202,9 +203,9 @@ public interface ProcessingConfiguration {
     boolean isObjectWrapperSet();
 
     /**
-     * Getter pair of {@link MutableProcessingConfiguration#setOutputEncoding(String)}.
+     * Getter pair of {@link MutableProcessingConfiguration#setOutputEncoding(Charset)}.
      */
-    String getOutputEncoding();
+    Charset getOutputEncoding();
 
     /**
      * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
@@ -214,9 +215,9 @@ public interface ProcessingConfiguration {
     boolean isOutputEncodingSet();
 
     /**
-     * Getter pair of {@link MutableProcessingConfiguration#setURLEscapingCharset(String)}.
+     * Getter pair of {@link MutableProcessingConfiguration#setURLEscapingCharset(Charset)}.
      */
-    String getURLEscapingCharset();
+    Charset getURLEscapingCharset();
 
     /**
      * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/Template.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Template.java b/src/main/java/org/apache/freemarker/core/Template.java
index e4c3669..a82c131 100644
--- a/src/main/java/org/apache/freemarker/core/Template.java
+++ b/src/main/java/org/apache/freemarker/core/Template.java
@@ -31,6 +31,7 @@ import java.io.StringReader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.lang.reflect.UndeclaredThrowableException;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -82,7 +83,8 @@ public class Template extends MutableProcessingConfiguration<Template> implement
     private Map macros = new HashMap();
     private List imports = new Vector();
     private ASTElement rootElement;
-    private String sourceEncoding, defaultNS;
+    private Charset sourceEncoding;
+    String defaultNS;
     private Serializable customLookupCondition;
     private int actualTagSyntax;
     private int actualNamingConvention;
@@ -130,11 +132,11 @@ public class Template extends MutableProcessingConfiguration<Template> implement
     }
 
     /**
-     * Convenience constructor for {@link #Template(String, String, Reader, Configuration, String) Template(name, null,
+     * Convenience constructor for {@link #Template(String, String, Reader, Configuration, Charset) Template(name, null,
      * reader, cfg, sourceEncoding)}.
      */
-    public Template(String name, Reader reader, Configuration cfg, String encoding) throws IOException {
-        this(name, null, reader, cfg, encoding);
+    public Template(String name, Reader reader, Configuration cfg, Charset sourceEncoding) throws IOException {
+        this(name, null, reader, cfg, sourceEncoding);
     }
 
     /**
@@ -144,7 +146,7 @@ public class Template extends MutableProcessingConfiguration<Template> implement
      * 
      * @param name
      *            The path of the template file relatively to the (virtual) directory that you use to store the
-     *            templates (except if {@link #Template(String, String, Reader, Configuration, String) sourceName}
+     *            templates (except if {@link #Template(String, String, Reader, Configuration, Charset) sourceName}
      *            differs from it). Shouldn't start with {@code '/'}. Should use {@code '/'}, not {@code '\'}. Check
      *            {@link #getName()} to see how the name will be used. The name should be independent of the actual
      *            storage mechanism and physical location as far as possible. Even when the templates are stored
@@ -187,12 +189,13 @@ public class Template extends MutableProcessingConfiguration<Template> implement
      * @since 2.3.22
      */
    public Template(
-           String name, String sourceName, Reader reader, Configuration cfg, String sourceEncoding) throws IOException {
+           String name, String sourceName, Reader reader, Configuration cfg, Charset sourceEncoding) throws
+           IOException {
        this(name, sourceName, reader, cfg, null, sourceEncoding);
    }
    
     /**
-     * Same as {@link #Template(String, String, Reader, Configuration, String)}, but also specifies a
+     * Same as {@link #Template(String, String, Reader, Configuration, Charset)}, but also specifies a
      * {@link TemplateConfiguration}. This is mostly meant to be used by FreeMarker internally, but advanced users might
      * still find this useful.
      * 
@@ -208,20 +211,20 @@ public class Template extends MutableProcessingConfiguration<Template> implement
      *            call {@link TemplateConfiguration#apply(Template)} on the resulting {@link Template} so that
      *            {@link MutableProcessingConfiguration} settings will be set too, because this constructor only uses it as a
      *            {@link ParserConfiguration}.
-     * @param encoding
-     *            Same as in {@link #Template(String, String, Reader, Configuration, String)}.
+     * @param sourceEncoding
+     *            Same as in {@link #Template(String, String, Reader, Configuration, Charset)}.
      * 
      * @since 2.3.24
      */
    public Template(
            String name, String sourceName, Reader reader,
            Configuration cfg, ParserConfiguration customParserConfiguration,
-           String encoding) throws IOException {
-       this(name, sourceName, reader, cfg, customParserConfiguration, encoding, null);
+           Charset sourceEncoding) throws IOException {
+       this(name, sourceName, reader, cfg, customParserConfiguration, sourceEncoding, null);
     }
 
     /**
-     * Same as {@link #Template(String, String, Reader, Configuration, ParserConfiguration, String)}, but allows
+     * Same as {@link #Template(String, String, Reader, Configuration, ParserConfiguration, Charset)}, but allows
      * specifying the {@code streamToUnmarkWhenEncEstabd} {@link InputStream}.
      *
      * @param streamToUnmarkWhenEncEstabd
@@ -235,7 +238,7 @@ public class Template extends MutableProcessingConfiguration<Template> implement
    public Template(
            String name, String sourceName, Reader reader,
            Configuration cfg, ParserConfiguration customParserConfiguration,
-           String sourceEncoding, InputStream streamToUnmarkWhenEncEstabd) throws IOException, ParseException {
+           Charset sourceEncoding, InputStream streamToUnmarkWhenEncEstabd) throws IOException, ParseException {
         this(name, sourceName, cfg, customParserConfiguration);
 
        setSourceEncoding(sourceEncoding);
@@ -287,7 +290,7 @@ public class Template extends MutableProcessingConfiguration<Template> implement
     }
 
     /**
-     * Same as {@link #createPlainTextTemplate(String, String, String, Configuration, String)} with {@code null}
+     * Same as {@link #createPlainTextTemplate(String, String, String, Configuration, Charset)} with {@code null}
      * {@code sourceName} argument.
      */
     static public Template createPlainTextTemplate(String name, String content, Configuration config) {
@@ -310,7 +313,7 @@ public class Template extends MutableProcessingConfiguration<Template> implement
      * @since 2.3.22
      */
     static public Template createPlainTextTemplate(String name, String sourceName, String content, Configuration config,
-               String sourceEncoding) {
+               Charset sourceEncoding) {
         Template template;
         try {
             template = new Template(name, sourceName, new StringReader("X"), config);
@@ -572,7 +575,7 @@ public class Template extends MutableProcessingConfiguration<Template> implement
      *            The sourceEncoding that was used to read this template, or {@code null} if the source of the template
      *            already gives back text (as opposed to binary data), so no decoding with a charset was needed.
      */
-    void setSourceEncoding(String sourceEncoding) {
+    void setSourceEncoding(Charset sourceEncoding) {
         this.sourceEncoding = sourceEncoding;
     }
 
@@ -580,7 +583,7 @@ public class Template extends MutableProcessingConfiguration<Template> implement
      * The sourceEncoding that was used to read this template, or {@code null} if the source of the template
      * already gives back text (as opposed to binary data), so no decoding with a charset was needed.
      */
-    public String getSourceEncoding() {
+    public Charset getSourceEncoding() {
         return sourceEncoding;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java b/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
index 8b4300f..54b524e 100644
--- a/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
@@ -19,6 +19,7 @@
 package org.apache.freemarker.core;
 
 import java.io.Reader;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -47,7 +48,7 @@ import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
  * already found.
  * 
  * <p>
- * Note on the sourceEncoding setting {@code sourceEncoding}: See {@link #setSourceEncoding(String)}.
+ * Note on the sourceEncoding setting {@code sourceEncoding}: See {@link #setSourceEncoding(Charset)}.
  * 
  * <p>
  * Note that the result value of the reader methods (getter and "is" methods) is usually not useful unless the value of
@@ -73,7 +74,7 @@ import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
  * precedence.
  * </ul>
  * 
- * @see Template#Template(String, String, Reader, Configuration, ParserConfiguration, String)
+ * @see Template#Template(String, String, Reader, Configuration, ParserConfiguration, Charset)
  * 
  * @since 2.3.24
  */
@@ -87,7 +88,7 @@ public final class TemplateConfiguration extends MutableProcessingConfiguration<
     private Integer autoEscapingPolicy;
     private Boolean recognizeStandardFileExtensions;
     private OutputFormat outputFormat;
-    private String sourceEncoding;
+    private Charset sourceEncoding;
     private Integer tabSize;
 
     /**
@@ -285,7 +286,7 @@ public final class TemplateConfiguration extends MutableProcessingConfiguration<
      * 
      * <p>
      * Note that the {@code sourceEncoding} setting of the {@link Template} counts as unset if it's {@code null},
-     * even if {@code null} was set via {@link Template#setSourceEncoding(String)}.
+     * even if {@code null} was set via {@link Template#setSourceEncoding(Charset)}.
      *
      * @throws IllegalStateException
      *             If the parent configuration wasn't yet set.
@@ -558,7 +559,7 @@ public final class TemplateConfiguration extends MutableProcessingConfiguration<
         return recognizeStandardFileExtensions != null;
     }
 
-    public String getSourceEncoding() {
+    public Charset getSourceEncoding() {
         return sourceEncoding != null ? sourceEncoding : getNonNullParentConfiguration().getSourceEncoding();
     }
 
@@ -571,7 +572,7 @@ public final class TemplateConfiguration extends MutableProcessingConfiguration<
      * above behavior is not guaranteed by this class alone; you have to ensure it. Also, read the note on
      * {@code sourceEncoding} in the documentation of {@link #apply(Template)}.
      */
-    public void setSourceEncoding(String sourceEncoding) {
+    public void setSourceEncoding(Charset sourceEncoding) {
         _NullArgumentException.check("sourceEncoding", sourceEncoding);
         this.sourceEncoding = sourceEncoding;
     }
@@ -783,7 +784,7 @@ public final class TemplateConfiguration extends MutableProcessingConfiguration<
     }
 
     @Override
-    public String getOutputEncoding() {
+    public Charset getOutputEncoding() {
         try {
             return super.getOutputEncoding();
         } catch (NullPointerException e) {
@@ -793,7 +794,7 @@ public final class TemplateConfiguration extends MutableProcessingConfiguration<
     }
 
     @Override
-    public String getURLEscapingCharset() {
+    public Charset getURLEscapingCharset() {
         try {
             return super.getURLEscapingCharset();
         } catch (NullPointerException e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TemplateLanguage.java b/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
index 37abf56..260b35f 100644
--- a/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
+++ b/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.nio.charset.Charset;
 
 import org.apache.freemarker.core.util._StringUtil;
 
@@ -42,7 +43,9 @@ public abstract class TemplateLanguage {
         }
 
         @Override
-        public Template parse(String name, String sourceName, Reader reader, Configuration cfg, ParserConfiguration customParserConfiguration, String encoding, InputStream streamToUnmarkWhenEncEstabd) throws IOException, ParseException {
+        public Template parse(String name, String sourceName, Reader reader, Configuration cfg, ParserConfiguration
+                customParserConfiguration, Charset encoding, InputStream streamToUnmarkWhenEncEstabd) throws
+                IOException, ParseException {
             return new Template(name, sourceName, reader, cfg, customParserConfiguration,
                     encoding, streamToUnmarkWhenEncEstabd);
         }
@@ -55,7 +58,9 @@ public abstract class TemplateLanguage {
         }
 
         @Override
-        public Template parse(String name, String sourceName, Reader reader, Configuration cfg, ParserConfiguration customParserConfiguration, String encoding, InputStream streamToUnmarkWhenEncEstabd) throws IOException, ParseException {
+        public Template parse(String name, String sourceName, Reader reader, Configuration cfg, ParserConfiguration
+                customParserConfiguration, Charset encoding, InputStream streamToUnmarkWhenEncEstabd)
+                throws IOException, ParseException {
             // Read the contents into a StringWriter, then construct a single-text-block template from it.
             final StringBuilder sb = new StringBuilder();
             final char[] buf = new char[4096];
@@ -77,18 +82,18 @@ public abstract class TemplateLanguage {
 
     /**
      * Returns if the template can specify its own charset inside the template. If so, {@link #parse(String, String,
-     * Reader, Configuration, ParserConfiguration, String, InputStream)} can throw
+     * Reader, Configuration, ParserConfiguration, Charset, InputStream)} can throw
      * {@link WrongTemplateCharsetException}, and it might gets a non-{@code null} for the {@link InputStream}
      * parameter.
      */
     public abstract boolean getCanSpecifyCharsetInContent();
 
     /**
-     * See {@link Template#Template(String, String, Reader, Configuration, ParserConfiguration, String, InputStream)}.
+     * See {@link Template#Template(String, String, Reader, Configuration, ParserConfiguration, Charset, InputStream)}.
      */
     public abstract Template parse(String name, String sourceName, Reader reader,
                                    Configuration cfg, ParserConfiguration customParserConfiguration,
-                                   String encoding, InputStream streamToUnmarkWhenEncEstabd)
+                                   Charset encoding, InputStream streamToUnmarkWhenEncEstabd)
             throws IOException, ParseException;
 
     public String getName() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java b/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java
index 18d3ed4..799efb4 100644
--- a/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java
+++ b/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java
@@ -19,6 +19,8 @@
 
 package org.apache.freemarker.core;
 
+import java.nio.charset.Charset;
+
 /**
  * Thrown by the {@link Template} constructors that specify a non-{@code null} encoding whoch doesn't match the
  * encoding specified in the {@code #ftl} header of the template.
@@ -26,13 +28,13 @@ package org.apache.freemarker.core;
 public class WrongTemplateCharsetException extends ParseException {
     private static final long serialVersionUID = 1L;
 
-    private final String templateSpecifiedEncoding;
-    private final String constructorSpecifiedEncoding;
+    private final Charset templateSpecifiedEncoding;
+    private final Charset constructorSpecifiedEncoding;
 
     /**
      * @since 2.3.22
      */
-    public WrongTemplateCharsetException(String templateSpecifiedEncoding, String constructorSpecifiedEncoding) {
+    public WrongTemplateCharsetException(Charset templateSpecifiedEncoding, Charset constructorSpecifiedEncoding) {
         this.templateSpecifiedEncoding = templateSpecifiedEncoding;
         this.constructorSpecifiedEncoding = constructorSpecifiedEncoding;
     }
@@ -47,14 +49,14 @@ public class WrongTemplateCharsetException extends ParseException {
     /**
      * @since 2.3.22
      */
-    public String getTemplateSpecifiedEncoding() {
+    public Charset getTemplateSpecifiedEncoding() {
         return templateSpecifiedEncoding;
     }
 
     /**
      * @since 2.3.22
      */
-    public String getConstructorSpecifiedEncoding() {
+    public Charset getConstructorSpecifiedEncoding() {
         return constructorSpecifiedEncoding;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java b/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java
new file mode 100644
index 0000000..3234de8
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+import java.nio.charset.Charset;
+
+import org.apache.freemarker.core.util._NullArgumentException;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ */
+public class _CharsetBuilder {
+
+    private final String name;
+
+    public _CharsetBuilder(String name) {
+        _NullArgumentException.check(name);
+        this.name = name;
+    }
+
+    public Charset build() {
+        return Charset.forName(name);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
index 2769e49..c817517 100644
--- a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -681,8 +681,18 @@ public class _ObjectBuilderSettingEvaluator {
             addWithSimpleName(SHORTHANDS, UndefinedOutputFormat.class);
             
             addWithSimpleName(SHORTHANDS, Locale.class);
-            String tzbClassName = _TimeZoneBuilder.class.getName();
-            SHORTHANDS.put("TimeZone", tzbClassName.substring(0, tzbClassName.length() - 7));
+
+            {
+                String tzbClassName = _TimeZoneBuilder.class.getName();
+                SHORTHANDS.put("TimeZone",
+                        tzbClassName.substring(0, tzbClassName.length() - BUILDER_CLASS_POSTFIX_2.length()));
+            }
+
+            {
+                String csClassName = _CharsetBuilder.class.getName();
+                SHORTHANDS.put("Charset",
+                        csClassName.substring(0, csClassName.length() - BUILDER_CLASS_POSTFIX_2.length()));
+            }
 
             // For accessing static fields:
             addWithSimpleName(SHORTHANDS, Configuration.class);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/_ParserConfigurationWithInheritedFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ParserConfigurationWithInheritedFormat.java b/src/main/java/org/apache/freemarker/core/_ParserConfigurationWithInheritedFormat.java
index 78acde4..bfaf93f 100644
--- a/src/main/java/org/apache/freemarker/core/_ParserConfigurationWithInheritedFormat.java
+++ b/src/main/java/org/apache/freemarker/core/_ParserConfigurationWithInheritedFormat.java
@@ -18,6 +18,8 @@
  */
 package org.apache.freemarker.core;
 
+import java.nio.charset.Charset;
+
 import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 
@@ -127,7 +129,7 @@ public final class _ParserConfigurationWithInheritedFormat implements ParserConf
         return wrappedPCfg.isTabSizeSet();
     }
 
-    public String getSourceEncoding() {
+    public Charset getSourceEncoding() {
         return wrappedPCfg.getSourceEncoding();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java b/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java
index a4612e2..2af3136 100644
--- a/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java
+++ b/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java
@@ -24,6 +24,7 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
+import java.nio.charset.StandardCharsets;
 import java.rmi.RemoteException;
 import java.rmi.server.RemoteObject;
 import java.security.MessageDigest;
@@ -71,7 +72,7 @@ public class DebuggerClient {
                 }
                 byte[] challenge = (byte[]) in.readObject();
                 MessageDigest md = MessageDigest.getInstance("SHA");
-                md.update(password.getBytes("UTF-8"));
+                md.update(password.getBytes(StandardCharsets.UTF_8));
                 md.update(challenge);
                 out.writeObject(md.digest());
                 return new LocalDebuggerProxy((Debugger) in.readObject());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java b/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java
index d142d16..29fa199 100644
--- a/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java
+++ b/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java
@@ -23,16 +23,16 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
 import java.security.MessageDigest;
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.Random;
 
 import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core.debug.Debugger;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
 import org.apache.freemarker.core.util._SecurityUtil;
 import org.slf4j.Logger;
@@ -56,8 +56,9 @@ class DebuggerServer {
     public DebuggerServer(Serializable debuggerStub) {
         port = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.port", Debugger.DEFAULT_PORT).intValue();
         try {
-            password = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.password", "").getBytes("UTF-8");
-        } catch (UnsupportedEncodingException e) {
+            password = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.password", "").getBytes(
+                    StandardCharsets.UTF_8);
+        } catch (UnsupportedCharsetException e) {
             throw new UndeclaredThrowableException(e);
         }
         this.debuggerStub = debuggerStub;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
index 6bed700..e1a9490 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
@@ -23,6 +23,7 @@ import java.io.BufferedReader;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.Serializable;
+import java.nio.charset.Charset;
 import java.util.Date;
 
 import org.apache.freemarker.core.Configuration;
@@ -94,8 +95,8 @@ public final class TemplateLoadingResult {
      * @param templateConfiguration
      *            Usually {@code null}, as usually the backing storage mechanism doesn't store such information; see
      *            {@link #getTemplateConfiguration()}. The most probable application is supplying the charset (encoding)
-     *            used by the {@link InputStream} (via {@link TemplateConfiguration#setSourceEncoding(String)}), but only
-     *            do that if the storage mechanism really knows what the charset is.
+     *            used by the {@link InputStream} (via {@link TemplateConfiguration#setSourceEncoding(Charset)}), but
+     *            only do that if the storage mechanism really knows what the charset is.
      */
     public TemplateLoadingResult(TemplateLoadingSource source, Serializable version, InputStream inputStream,
             TemplateConfiguration templateConfiguration) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java b/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
index 666ef58..537b4eb 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
@@ -26,6 +26,7 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.Serializable;
 import java.lang.reflect.Method;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -551,7 +552,7 @@ public class DefaultTemplateResolver extends TemplateResolver {
         if (tc != null && tc.isLocaleSet()) {
             locale = tc.getLocale();
         }
-        String initialEncoding = tc != null ? tc.getSourceEncoding() : config.getSourceEncoding();
+        Charset initialEncoding = tc != null ? tc.getSourceEncoding() : config.getSourceEncoding();
         TemplateLanguage templateLanguage = tc != null ? tc.getTemplateLanguage() : config .getTemplateLanguage();
 
         Template template;
@@ -592,7 +593,7 @@ public class DefaultTemplateResolver extends TemplateResolver {
                     template = templateLanguage.parse(name, sourceName, reader, config, tc,
                             initialEncoding, markedInputStream);
                 } catch (WrongTemplateCharsetException charsetException) {
-                    final String templateSpecifiedEncoding = charsetException.getTemplateSpecifiedEncoding();
+                    final Charset templateSpecifiedEncoding = charsetException.getTemplateSpecifiedEncoding();
 
                     if (inputStream != null) {
                         // We restart InputStream to re-decode it with the new charset.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/util/_StringUtil.java b/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
index f842aff..2827c84 100644
--- a/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
+++ b/src/main/java/org/apache/freemarker/core/util/_StringUtil.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core.util;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
+import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
@@ -314,23 +315,23 @@ public class _StringUtil {
      * URL encoding (like%20this) for query parameter values, path <em>segments</em>, fragments; this encodes all
      * characters that are reserved anywhere.
      */
-    public static String URLEnc(String s, String charset) throws UnsupportedEncodingException {
+    public static String URLEnc(String s, Charset charset) throws UnsupportedEncodingException {
         return URLEnc(s, charset, false);
     }
     
     /**
-     * Like {@link #URLEnc(String, String)} but doesn't escape the slash character ({@code /}).
+     * Like {@link #URLEnc(String, Charset)} but doesn't escape the slash character ({@code /}).
      * This can be used to encode a path only if you know that no folder or file name will contain {@code /}
      * character (not in the path, but in the name itself), which usually stands, as the commonly used OS-es don't
      * allow that.
      * 
      * @since 2.3.21
      */
-    public static String URLPathEnc(String s, String charset) throws UnsupportedEncodingException {
+    public static String URLPathEnc(String s, Charset charset) throws UnsupportedEncodingException {
         return URLEnc(s, charset, true);
     }
     
-    private static String URLEnc(String s, String charset, boolean keepSlash)
+    private static String URLEnc(String s, Charset charset, boolean keepSlash)
             throws UnsupportedEncodingException {
         int ln = s.length();
         int i;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
index 5260b86..3b67622 100644
--- a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
+++ b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java
@@ -212,16 +212,16 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  * template usually just inherits that from the {@link Configuration}), and if that's not set, then reads the source
  * charset of the template, just like {@value #INIT_PARAM_VALUE_LEGACY}, and if that's {@code null} (which happens if
  * the template was loaded from a non-binary source) then it will be UTF-8. Then it passes the charset acquired this way
- * to {@link HttpServletResponse#setCharacterEncoding(String)} and {@link Environment#setOutputEncoding(String)}. (It
+ * to {@link HttpServletResponse#setCharacterEncoding(String)} and {@link Environment#setOutputEncoding(Charset)}. (It
  * doesn't call the legacy {@link HttpServletResponse#setContentType(String)} API to set the charset.) (Note that if the
  * template has a {@code content_type} template attribute (which is deprecated) that specifies a charset, it will be
  * used as the output charset of that template.)
  * <li>{@value #INIT_PARAM_VALUE_DO_NOT_SET}: {@link FreemarkerServlet} will not set the {@link HttpServletResponse}
- * "character encoding". It will still call {@link Environment#setOutputEncoding(String)}, so that the running template
+ * "character encoding". It will still call {@link Environment#setOutputEncoding(Charset)}, so that the running template
  * will be aware of the charset used for the output.
  * <li>{@value #INIT_PARAM_VALUE_FORCE_PREFIX} + charset name, for example {@code force UTF-8}: The output charset will
  * be the one specified after "force" + space, regardless of everything. The charset specified this way is passed to
- * {@link HttpServletResponse#setCharacterEncoding(String)} and {@link Environment#setOutputEncoding(String)}. If the
+ * {@link HttpServletResponse#setCharacterEncoding(String)} and {@link Environment#setOutputEncoding(Charset)}. If the
  * charset name is not recognized by Java, the servlet initialization will fail.
  * </ul>
  *
@@ -631,7 +631,7 @@ public class FreemarkerServlet extends HttpServlet {
                         throw new ConflictingInitParamsException(
                                 Configuration.SOURCE_ENCODING_KEY, DEPR_INITPARAM_ENCODING);
                     }
-                    config.setEncoding(value);
+                    config.setSourceEncoding(Charset.forName(value));
                 } else if (name.equals(DEPR_INITPARAM_TEMPLATE_DELAY)) { // BC
                     if (getInitParameter(Configuration.TEMPLATE_UPDATE_DELAY_KEY) != null) {
                         throw new ConflictingInitParamsException(
@@ -857,7 +857,7 @@ public class FreemarkerServlet extends HttpServlet {
             // Using the Servlet 2.4 way of setting character encoding.
             if (responseCharacterEncoding != ResponseCharacterEncoding.FORCE_CHARSET) {
                 if (!tempSpecContentTypeContainsCharset) {
-                    response.setCharacterEncoding(getOutputEncodingForTemplate(template));
+                    response.setCharacterEncoding(getOutputEncodingForTemplate(template).name());
                 }
             } else {
                 response.setCharacterEncoding(forcedResponseCharacterEncoding.name());
@@ -878,8 +878,18 @@ public class FreemarkerServlet extends HttpServlet {
                     // Process the template
                     Environment env = template.createProcessingEnvironment(model, response.getWriter());
                     if (responseCharacterEncoding != ResponseCharacterEncoding.LEGACY) {
-                        String actualOutputCharset = response.getCharacterEncoding();
-                        if (actualOutputCharset != null) {
+                        String actualOutputCharsetName = response.getCharacterEncoding();
+                        if (actualOutputCharsetName != null) {
+                            Charset actualOutputCharset = null;
+                            try {
+                                actualOutputCharset = Charset.forName(actualOutputCharsetName);
+                            } catch (Exception e) {
+                                throw new IllegalStateException(
+                                        "Failed to resolve charset name returned by "
+                                        + " HttpServletResponse.getCharacterEncoding(): "
+                                        + _StringUtil.jQuote(actualOutputCharsetName),
+                                        e);
+                            }
                             env.setOutputEncoding(actualOutputCharset);
                         }
                     }
@@ -917,13 +927,13 @@ public class FreemarkerServlet extends HttpServlet {
         env.process();
     }
 
-    private String getOutputEncodingForTemplate(Template template) {
-        String outputEncoding = responseCharacterEncoding == ResponseCharacterEncoding.LEGACY ? null
+    private Charset getOutputEncodingForTemplate(Template template) {
+        Charset outputEncoding = responseCharacterEncoding == ResponseCharacterEncoding.LEGACY ? null
                 : template.getOutputEncoding();
         // [FM3] Don't use template.getSourceEncoding() here; it might can't encode the dynamic values inserted.
         return outputEncoding != null ? outputEncoding
                 : template.getSourceEncoding() != null ? template.getSourceEncoding()
-                : StandardCharsets.UTF_8.name();
+                : StandardCharsets.UTF_8;
     }
 
     private ContentType getTemplateSpecificContentType(final Template template) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java b/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
index ec4a5d0..4452fa1 100644
--- a/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
+++ b/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java
@@ -37,6 +37,7 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -71,7 +72,6 @@ import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util._ClassUtil;
 import org.apache.freemarker.core.util._NullArgumentException;
-import org.apache.freemarker.core.util._SecurityUtil;
 import org.apache.freemarker.core.util._StringUtil;
 import org.apache.freemarker.servlet.FreemarkerServlet;
 import org.apache.freemarker.servlet.HttpRequestHashModel;
@@ -122,8 +122,6 @@ public class TaglibFactory implements TemplateHashModel {
     private static final String DEFAULT_TLD_RESOURCE_PATH = META_INF_ABS_PATH + "taglib.tld";
     private static final String JAR_URL_ENTRY_PATH_START = "!/";
 
-    private static final String PLATFORM_FILE_ENCODING = _SecurityUtil.getSystemProperty("file.encoding", "utf-8");
-
     private final ServletContext servletContext;
 
     private ObjectWrapper objectWrapper;
@@ -971,7 +969,7 @@ public class TaglibFactory implements TemplateHashModel {
             relativeEntryPath = relativeEntryPath.substring(1);
         }
         try {
-            return new URL(jarBaseEntryUrl, _StringUtil.URLPathEnc(relativeEntryPath, PLATFORM_FILE_ENCODING));
+            return new URL(jarBaseEntryUrl, _StringUtil.URLPathEnc(relativeEntryPath, Charset.defaultCharset()));
         } catch (UnsupportedEncodingException e) {
             throw new BugException();
         }
@@ -999,7 +997,7 @@ public class TaglibFactory implements TemplateHashModel {
     }
 
     /**
-     * Converts an URL to a {@code File} object, if the URL format (scheme) makes is possible.
+     * Converts an URL to a {@code File} object, if the URL format (scheme) makes it possible.
      */
     private File urlToFileOrNull(URL url) {
         if (test_emulateNoUrlToFileConversions) {
@@ -1018,7 +1016,7 @@ public class TaglibFactory implements TemplateHashModel {
             // URL.getFile() doesn't decode %XX-s (used for spaces and non-US-ASCII letters usually), so we do.
             // As it was originally created for a file somewhere, we hope that it uses the platform default encoding.
             try {
-                filePath = URLDecoder.decode(url.getFile(), PLATFORM_FILE_ENCODING);
+                filePath = URLDecoder.decode(url.getFile(), Charset.defaultCharset().name());
             } catch (UnsupportedEncodingException e2) {
                 throw new BugException(e2);
             }
@@ -1066,7 +1064,7 @@ public class TaglibFactory implements TemplateHashModel {
                     + JAR_URL_ENTRY_PATH_START
                     + URLEncoder.encode(
                             entryPath.startsWith("/") ? entryPath.substring(1) : entryPath,
-                            PLATFORM_FILE_ENCODING));
+                            Charset.defaultCharset().name()));
         } catch (Exception e) {
             LOG.error("Couldn't get URL for serlvetContext resource "
                         + _StringUtil.jQuoteNoXSS(servletContextJarFilePath)
@@ -1350,7 +1348,7 @@ public class TaglibFactory implements TemplateHashModel {
                 entryPath = normalizeJarEntryPath(
                         URLDecoder.decode(
                                 urlEF.substring(sepIdx + JAR_URL_ENTRY_PATH_START.length()),
-                                PLATFORM_FILE_ENCODING),
+                                Charset.defaultCharset().name()),
                         false);
             }
             

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 8af611e..0e79431 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -37,6 +37,8 @@ import org.apache.freemarker.core.model.impl.*;
 import org.apache.freemarker.core.util.*;
 import java.io.*;
 import java.util.*;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
 
 /**
  * This class is generated by JavaCC from a grammar file.
@@ -3887,13 +3889,24 @@ void HeaderElement() :
                         } catch (TemplateModelException tme) {}
                     }
                     if (template != null) {
-                        if (ks.equalsIgnoreCase("sourceEncoding")) {
+                        if (ks.equalsIgnoreCase("encoding")) {
                             if (vs == null) {
                                 throw new ParseException("Expected a string constant for \"" + ks + "\".", exp);
                             }
-                            if (template.getSourceEncoding() != null && vs != null
-                                    && !template.getSourceEncoding().equalsIgnoreCase(vs)) {
-                                throw new WrongTemplateCharsetException(vs, template.getSourceEncoding());
+                            Charset encoding;
+                            try {
+                                encoding = Charset.forName(vs);
+                            } catch (UnsupportedCharsetException e) {
+                                throw new ParseException("Unknown charset (check name, or ensure that the"
+                                        + "charset is known by the runtime environment): " + vs,
+                                        exp);
+                            } catch (Exception e) {
+                                throw new ParseException("Failed resolve charset name: " + vs, exp, e
+                                        .getCause());
+                            }
+                            if (template.getSourceEncoding() != null
+                                    && !template.getSourceEncoding().equals(encoding)) {
+                                throw new WrongTemplateCharsetException(encoding, template.getSourceEncoding());
                             }
                             // There will be no WrongTemplateCharsetException exception, release mark buffer:
                             if (streamToUnmarkWhenEncEstabd != null) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/manual/en_US/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/src/manual/en_US/FM3-CHANGE-LOG.txt b/src/manual/en_US/FM3-CHANGE-LOG.txt
index bcab8e5..2e10077 100644
--- a/src/manual/en_US/FM3-CHANGE-LOG.txt
+++ b/src/manual/en_US/FM3-CHANGE-LOG.txt
@@ -187,3 +187,10 @@ the FreeMarer 3 changelog here:
   - Renamed Configuration.defaultEncoding to sourceEncoding, also added sourceEncoding to ParserConfiguration, and renamed
     TemplateConfiguration.encoding and Template.encoding to sourceEncoding. (Before this, defaultEncoding was exclusive
     to Configuration, but now it's like any other ParserConfiguration setting that can be overidden on the 3 levels.)
+- Settings that have contained a charset name (sourceEncoding, outputEncoding, URLEscapingCharset) are now of type Charset,
+  not String. For string based configuration sources (such as .properties files) this means that:
+  - Unrecognized charset names are now errors
+  - For recognized names the charset name will be normalized (like "latin1" becomes to "ISO-8859-1").
+  - In "object builder expressions" Charset values can now be constructed like `Charset("ISO-8859-5")`.
+    Note that as the type of the settings have changed, now you can't just write something like
+    `TemplateConfiguration(sourceEncoding = "UTF-8")`, but `TemplateConfiguration(sourceEncoding = Charset("UTF-8"))`.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/test/java/org/apache/freemarker/core/ASTPrinter.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ASTPrinter.java b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
index 10701cf..ddd8e9b 100644
--- a/src/test/java/org/apache/freemarker/core/ASTPrinter.java
+++ b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
@@ -34,6 +34,7 @@ import java.nio.ByteBuffer;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
 import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
 import java.util.Enumeration;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -180,9 +181,9 @@ public class ASTPrinter {
         }
         
         try {
-            return decode(buffer, Charset.forName("UTF-8"));
+            return decode(buffer, StandardCharsets.UTF_8);
         } catch (CharacterCodingException e) {
-            return decode(buffer, Charset.forName("ISO-8859-1"));
+            return decode(buffer, StandardCharsets.ISO_8859_1);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/27a62ce0/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/CamelCaseTest.java b/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
index a3da802..7c07127 100644
--- a/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
+++ b/src/test/java/org/apache/freemarker/core/CamelCaseTest.java
@@ -21,6 +21,7 @@ package org.apache.freemarker.core;
 import static org.junit.Assert.*;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Locale;
@@ -36,8 +37,8 @@ public class CamelCaseTest extends TemplateTest {
 
     @Test
     public void camelCaseSpecialVars() throws IOException, TemplateException {
-        getConfiguration().setOutputEncoding("utf-8");
-        getConfiguration().setURLEscapingCharset("iso-8859-1");
+        getConfiguration().setOutputEncoding(StandardCharsets.UTF_8);
+        getConfiguration().setURLEscapingCharset(StandardCharsets.ISO_8859_1);
         getConfiguration().setLocale(Locale.GERMANY);
         assertOutput("${.dataModel?isHash?c}", "true");
         assertOutput("${.data_model?is_hash?c}", "true");
@@ -49,12 +50,12 @@ public class CamelCaseTest extends TemplateTest {
         assertOutput("${.current_template_name!'null'}", "null");
         assertOutput("${.mainTemplateName!'null'}", "null");
         assertOutput("${.main_template_name!'null'}", "null");
-        assertOutput("${.outputEncoding}", "utf-8");
-        assertOutput("${.output_encoding}", "utf-8");
+        assertOutput("${.outputEncoding}", StandardCharsets.UTF_8.name());
+        assertOutput("${.output_encoding}", StandardCharsets.UTF_8.name());
         assertOutput("${.outputFormat}", UndefinedOutputFormat.INSTANCE.getName());
         assertOutput("${.output_format}", UndefinedOutputFormat.INSTANCE.getName());
-        assertOutput("${.urlEscapingCharset}", "iso-8859-1");
-        assertOutput("${.url_escaping_charset}", "iso-8859-1");
+        assertOutput("${.urlEscapingCharset}", StandardCharsets.ISO_8859_1.name());
+        assertOutput("${.url_escaping_charset}", StandardCharsets.ISO_8859_1.name());
         assertOutput("${.currentNode!'-'}", "-");
         assertOutput("${.current_node!'-'}", "-");
     }
@@ -86,7 +87,7 @@ public class CamelCaseTest extends TemplateTest {
     
     @Test
     public void camelCaseFtlHeaderParameters() throws IOException, TemplateException {
-        getConfiguration().setOutputEncoding("utf-8");
+        getConfiguration().setOutputEncoding(StandardCharsets.UTF_8);
         
         assertOutput(
                 "<#ftl "
@@ -118,18 +119,18 @@ public class CamelCaseTest extends TemplateTest {
         
         getConfiguration().setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
         assertErrorContains("<#ftl strip_whitespace=true>", "naming convention");
-        assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", "utf-8");
+        assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
         
         getConfiguration().setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
         assertErrorContains("<#ftl stripWhitespace=true>", "naming convention");
-        assertOutput("<#ftl strip_whitespace=true>${.output_encoding}", "utf-8");
+        assertOutput("<#ftl strip_whitespace=true>${.output_encoding}", StandardCharsets.UTF_8.name());
         
         getConfiguration().setNamingConvention(Configuration.AUTO_DETECT_NAMING_CONVENTION);
-        assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", "utf-8");
-        assertOutput("<#ftl encoding='iso-8859-1' stripWhitespace=true>${.outputEncoding}", "utf-8");
-        assertOutput("<#ftl stripWhitespace=true encoding='iso-8859-1'>${.outputEncoding}", "utf-8");
-        assertOutput("<#ftl encoding='iso-8859-1' strip_whitespace=true>${.output_encoding}", "utf-8");
-        assertOutput("<#ftl strip_whitespace=true encoding='iso-8859-1'>${.output_encoding}", "utf-8");
+        assertOutput("<#ftl stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
+        assertOutput("<#ftl encoding='iso-8859-1' stripWhitespace=true>${.outputEncoding}", StandardCharsets.UTF_8.name());
+        assertOutput("<#ftl stripWhitespace=true encoding='iso-8859-1'>${.outputEncoding}", StandardCharsets.UTF_8.name());
+        assertOutput("<#ftl encoding='iso-8859-1' strip_whitespace=true>${.output_encoding}", StandardCharsets.UTF_8.name());
+        assertOutput("<#ftl strip_whitespace=true encoding='iso-8859-1'>${.output_encoding}", StandardCharsets.UTF_8.name());
     }
     
     @Test


Mime
View raw message