8000685: (props) Properties.storeToXML/loadFromXML should only require UTF-8 and UTF-16 to be supported
authoralanb
Wed, 17 Oct 2012 11:43:56 +0100
changeset 14187 9e193f8bab66
parent 14186 bfb891f72a79
child 14188 82c029bd5f70
8000685: (props) Properties.storeToXML/loadFromXML should only require UTF-8 and UTF-16 to be supported Reviewed-by: mchung, chegar
jdk/src/share/classes/java/util/Properties.java
jdk/src/share/classes/sun/util/spi/XmlPropertiesProvider.java
jdk/src/share/classes/sun/util/xml/PlatformXmlPropertiesProvider.java
jdk/test/java/util/Properties/LoadAndStoreXML.java
--- a/jdk/src/share/classes/java/util/Properties.java	Tue Oct 16 15:23:17 2012 -0700
+++ b/jdk/src/share/classes/java/util/Properties.java	Wed Oct 17 11:43:56 2012 +0100
@@ -81,8 +81,9 @@
  * <p> The {@link #loadFromXML(InputStream)} and {@link
  * #storeToXML(OutputStream, String, String)} methods load and store properties
  * in a simple XML format.  By default the UTF-8 character encoding is used,
- * however a specific encoding may be specified if required.  An XML properties
- * document has the following DOCTYPE declaration:
+ * however a specific encoding may be specified if required. Implementations
+ * are required to support UTF-8 and UTF-16 and may support other encodings.
+ * An XML properties document has the following DOCTYPE declaration:
  *
  * <pre>
  * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
@@ -853,23 +854,30 @@
      * Furthermore, the document must satisfy the properties DTD described
      * above.
      *
+     * <p> An implementation is required to read XML documents that use the
+     * "{@code UTF-8}" or "{@code UTF-16}" encoding. An implementation may
+     * support additional encodings.
+     *
      * <p>The specified stream is closed after this method returns.
      *
      * @param in the input stream from which to read the XML document.
      * @throws IOException if reading from the specified input stream
      *         results in an <tt>IOException</tt>.
+     * @throws java.io.UnsupportedEncodingException if the document's encoding
+     *         declaration can be read and it specifies an encoding that is not
+     *         supported
      * @throws InvalidPropertiesFormatException Data on input stream does not
      *         constitute a valid XML document with the mandated document type.
      * @throws NullPointerException if {@code in} is null.
      * @see    #storeToXML(OutputStream, String, String)
+     * @see    <a href="http://www.w3.org/TR/REC-xml/#charencoding">Character
+     *         Encoding in Entities</a>
      * @since 1.5
      */
     public synchronized void loadFromXML(InputStream in)
         throws IOException, InvalidPropertiesFormatException
     {
-        if (in == null)
-            throw new NullPointerException();
-        XmlSupport.load(this, in);
+        XmlSupport.load(this, Objects.requireNonNull(in));
         in.close();
     }
 
@@ -896,8 +904,6 @@
     public void storeToXML(OutputStream os, String comment)
         throws IOException
     {
-        if (os == null)
-            throw new NullPointerException();
         storeToXML(os, comment, "UTF-8");
     }
 
@@ -910,9 +916,13 @@
      * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
      * </pre>
      *
-     *<p>If the specified comment is {@code null} then no comment
+     * <p>If the specified comment is {@code null} then no comment
      * will be stored in the document.
      *
+     * <p> An implementation is required to support writing of XML documents
+     * that use the "{@code UTF-8}" or "{@code UTF-16}" encoding. An
+     * implementation may support additional encodings.
+     *
      * <p>The specified stream remains open after this method returns.
      *
      * @param os        the output stream on which to emit the XML document.
@@ -924,20 +934,23 @@
      *
      * @throws IOException if writing to the specified output stream
      *         results in an <tt>IOException</tt>.
+     * @throws java.io.UnsupportedEncodingException if the encoding is not
+     *         supported by the implementation.
      * @throws NullPointerException if {@code os} is {@code null},
      *         or if {@code encoding} is {@code null}.
      * @throws ClassCastException  if this {@code Properties} object
      *         contains any keys or values that are not
      *         {@code Strings}.
      * @see    #loadFromXML(InputStream)
+     * @see    <a href="http://www.w3.org/TR/REC-xml/#charencoding">Character
+     *         Encoding in Entities</a>
      * @since 1.5
      */
     public void storeToXML(OutputStream os, String comment, String encoding)
         throws IOException
     {
-        if (os == null)
-            throw new NullPointerException();
-        XmlSupport.save(this, os, comment, encoding);
+        XmlSupport.save(this, Objects.requireNonNull(os), comment,
+                        Objects.requireNonNull(encoding));
     }
 
     /**
--- a/jdk/src/share/classes/sun/util/spi/XmlPropertiesProvider.java	Tue Oct 16 15:23:17 2012 -0700
+++ b/jdk/src/share/classes/sun/util/spi/XmlPropertiesProvider.java	Wed Oct 17 11:43:56 2012 +0100
@@ -55,6 +55,9 @@
      * @param props the properties table to populate
      * @param in the input stream from which to read the XML document
      * @throws IOException if reading from the specified input stream fails
+     * @throws java.io.UnsupportedEncodingException if the document's encoding
+     *         declaration can be read and it specifies an encoding that is not
+     *         supported
      * @throws InvalidPropertiesFormatException Data on input stream does not
      *         constitute a valid XML document with the mandated document type.
      *
@@ -73,6 +76,8 @@
      * @param encoding the name of a supported character encoding
      *
      * @throws IOException if writing to the specified output stream fails
+     * @throws java.io.UnsupportedEncodingException if the encoding is not
+     *         supported by the implementation
      * @throws NullPointerException if {@code out} is null.
      * @throws ClassCastException  if this {@code Properties} object
      *         contains any keys or values that are not
--- a/jdk/src/share/classes/sun/util/xml/PlatformXmlPropertiesProvider.java	Tue Oct 16 15:23:17 2012 -0700
+++ b/jdk/src/share/classes/sun/util/xml/PlatformXmlPropertiesProvider.java	Wed Oct 17 11:43:56 2012 +0100
@@ -27,6 +27,7 @@
 
 import java.io.*;
 import java.util.*;
+import java.nio.charset.*;
 import org.xml.sax.*;
 import org.w3c.dom.*;
 import javax.xml.parsers.*;
@@ -127,6 +128,13 @@
                       String encoding)
         throws IOException
     {
+        // fast-fail for unsupported charsets as UnsupportedEncodingException may
+        // not be thrown later (see JDK-8000621)
+        try {
+            Charset.forName(encoding);
+        } catch (IllegalCharsetNameException | UnsupportedCharsetException x) {
+            throw new UnsupportedEncodingException(encoding);
+        }
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         DocumentBuilder db = null;
         try {
--- a/jdk/test/java/util/Properties/LoadAndStoreXML.java	Tue Oct 16 15:23:17 2012 -0700
+++ b/jdk/test/java/util/Properties/LoadAndStoreXML.java	Wed Oct 17 11:43:56 2012 +0100
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8000354
+ * @bug 8000354 8000685
  * @summary Basic test of storeToXML and loadToXML
  */
 
@@ -66,13 +66,13 @@
      * Sanity test that properties saved with Properties#storeToXML can be
      * read with Properties#loadFromXML.
      */
-    static void test() throws IOException {
+    static void testLoadAndStore(String encoding) throws IOException {
         Properties props = new Properties();
         props.put("k1", "foo");
         props.put("k2", "bar");
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        props.storeToXML(out, "no comment");
+        props.storeToXML(out, null, encoding);
 
         Properties p = new Properties();
         ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
@@ -85,19 +85,74 @@
         }
     }
 
+    /**
+     * Test loadFromXML with a document that does not have an encoding declaration
+     */
+    static void testLoadWithoutEncoding() throws IOException {
+        Properties expected = new Properties();
+        expected.put("foo", "bar");
+
+        String s = "<?xml version=\"1.0\"?>" +
+                   "<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">" +
+                   "<properties>" +
+                   "<entry key=\"foo\">bar</entry>" +
+                   "</properties>";
+        ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes("UTF-8"));
+        Properties props = new Properties();
+        props.loadFromXML(in);
+
+        if (!props.equals(expected)) {
+            System.err.println("loaded: " + props + ", expected: " + expected);
+            throw new RuntimeException("Test failed");
+        }
+    }
+
+     /**
+      * Test loadFromXML with unsupported encoding
+      */
+     static void testLoadWithBadEncoding() throws IOException {
+        String s = "<?xml version=\"1.0\" encoding=\"BAD\"?>" +
+                   "<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">" +
+                   "<properties>" +
+                   "<entry key=\"foo\">bar</entry>" +
+                   "</properties>";
+        ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes("UTF-8"));
+        Properties props = new Properties();
+        try {
+            props.loadFromXML(in);
+            throw new RuntimeException("UnsupportedEncodingException expected");
+        } catch (UnsupportedEncodingException expected) { }
+    }
+
+    /**
+     * Test storeToXML with unsupported encoding
+     */
+    static void testStoreWithBadEncoding() throws IOException {
+        Properties props = new Properties();
+        props.put("foo", "bar");
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            props.storeToXML(out, null, "BAD");
+            throw new RuntimeException("UnsupportedEncodingException expected");
+        } catch (UnsupportedEncodingException expected) { }
+    }
+
     public static void main(String[] args) throws IOException {
 
-        // run test without security manager
-        test();
+        testLoadAndStore("UTF-8");
+        testLoadAndStore("UTF-16");
+        testLoadWithoutEncoding();
+        testLoadWithBadEncoding();
+        testStoreWithBadEncoding();
 
-        // re-run test with security manager
+        // re-run sanity test with security manager
         Policy orig = Policy.getPolicy();
         Policy p = new SimplePolicy(new RuntimePermission("setSecurityManager"),
                                     new PropertyPermission("line.separator", "read"));
         Policy.setPolicy(p);
         System.setSecurityManager(new SecurityManager());
         try {
-            test();
+            testLoadAndStore("UTF-8");
         } finally {
             // turn off security manager and restore policy
             System.setSecurityManager(null);