8219692: DOM and SAX parsers ignore namespace
authorjoehw
Tue, 21 May 2019 14:55:30 -0700
changeset 54969 6bd29804ace0
parent 54968 c13b1382aa30
child 54970 76d3d96a8bc2
8219692: DOM and SAX parsers ignore namespace Reviewed-by: lancea
src/java.xml/share/classes/javax/xml/parsers/DocumentBuilderFactory.java
src/java.xml/share/classes/javax/xml/parsers/SAXParserFactory.java
test/jaxp/javax/xml/jaxp/unittest/parsers/BaseParsingTest.java
--- a/src/java.xml/share/classes/javax/xml/parsers/DocumentBuilderFactory.java	Tue May 21 13:40:56 2019 -0700
+++ b/src/java.xml/share/classes/javax/xml/parsers/DocumentBuilderFactory.java	Tue May 21 14:55:30 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,8 @@
  */
 
 public abstract class DocumentBuilderFactory {
-
+    private static final String DEFAULT_IMPL =
+            "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
     private boolean validating = false;
     private boolean namespaceAware = false;
     private boolean whitespace = false;
@@ -55,6 +56,76 @@
     }
 
     /**
+     * Creates a new NamespaceAware instance of the {@code DocumentBuilderFactory}
+     * builtin system-default implementation. Parsers produced by the factory
+     * instance provides support for XML namespaces by default.
+     *
+     * @implSpec
+     * In addition to creating a factory instance using the same process as
+     * {@link #newDefaultInstance()}, this method must set NamespaceAware to true.
+     *
+     * @return a new instance of the {@code DocumentBuilderFactory} builtin
+     *         system-default implementation.
+     *
+     * @since 13
+     */
+    public static DocumentBuilderFactory newDefaultNSInstance() {
+        return makeNSAware(new DocumentBuilderFactoryImpl());
+    }
+
+    /**
+     * Creates a new NamespaceAware instance of a {@code DocumentBuilderFactory}.
+     * Parsers produced by the factory instance provides support for XML namespaces
+     * by default.
+     *
+     * @implSpec
+     * In addition to creating a factory instance using the same process as
+     * {@link #newInstance()}, this method must set NamespaceAware to true.
+     *
+     * @return a new instance of a {@code DocumentBuilderFactory}
+     *
+     * @throws FactoryConfigurationError in case of {@linkplain
+     *         java.util.ServiceConfigurationError service configuration error}
+     *         or if the implementation is not available or cannot be instantiated.
+     *
+     * @since 13
+     */
+    public static DocumentBuilderFactory newNSInstance() {
+        return makeNSAware(FactoryFinder.find(DocumentBuilderFactory.class, DEFAULT_IMPL));
+    }
+
+    /**
+     * Creates a new NamespaceAware instance of a {@code DocumentBuilderFactory}
+     * from the class name. Parsers produced by the factory instance provides
+     * support for XML namespaces by default.
+     *
+     * @implSpec
+     * In addition to creating a factory instance using the same process as
+     * {@link #newInstance(java.lang.String, java.lang.ClassLoader)}, this method
+     * must set NamespaceAware to true.
+     *
+     * @param factoryClassName a fully qualified factory class name that provides
+     *                         implementation of
+     *                         {@code javax.xml.parsers.DocumentBuilderFactory}.
+     *
+     * @param classLoader the {@code ClassLoader} used to load the factory class.
+     *                    If it is {@code null}, the current {@code Thread}'s
+     *                    context classLoader is used to load the factory class.
+     *
+     * @return a new instance of a {@code DocumentBuilderFactory}
+     *
+     * @throws FactoryConfigurationError if {@code factoryClassName} is {@code null}, or
+     *                                   the factory class cannot be loaded, instantiated.
+     *
+     * @since 13
+     */
+    public static DocumentBuilderFactory newNSInstance(String factoryClassName,
+            ClassLoader classLoader) {
+            return makeNSAware(FactoryFinder.newInstance(
+                    DocumentBuilderFactory.class, factoryClassName, classLoader, false));
+    }
+
+    /**
      * Creates a new instance of the {@code DocumentBuilderFactory} builtin
      * system-default implementation.
      *
@@ -141,7 +212,7 @@
                 /* The default property name according to the JAXP spec */
                 DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory"
                 /* The fallback implementation class name */
-                "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
+                DEFAULT_IMPL);
     }
 
     /**
@@ -185,6 +256,11 @@
                         factoryClassName, classLoader, false);
     }
 
+    private static DocumentBuilderFactory makeNSAware(DocumentBuilderFactory dbf) {
+        dbf.setNamespaceAware(true);
+        return dbf;
+    }
+
     /**
      * Creates a new instance of a {@link javax.xml.parsers.DocumentBuilder}
      * using the currently configured parameters.
--- a/src/java.xml/share/classes/javax/xml/parsers/SAXParserFactory.java	Tue May 21 13:40:56 2019 -0700
+++ b/src/java.xml/share/classes/javax/xml/parsers/SAXParserFactory.java	Tue May 21 14:55:30 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -41,6 +41,8 @@
  * @since 1.4
  */
 public abstract class SAXParserFactory {
+    private static final String DEFAULT_IMPL =
+            "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl";
 
     /**
      * Should Parsers be validating?
@@ -60,6 +62,76 @@
     }
 
     /**
+     * Creates a new NamespaceAware instance of the {@code SAXParserFactory}
+     * builtin system-default implementation. Parsers produced by the factory
+     * instance provides support for XML namespaces by default.
+     *
+     * @implSpec
+     * In addition to creating a factory instance using the same process as
+     * {@link #newDefaultInstance()}, this method must set NamespaceAware to true.
+     *
+     * @return a new instance of the {@code SAXParserFactory} builtin
+     *         system-default implementation.
+     *
+     * @since 13
+     */
+    public static SAXParserFactory newDefaultNSInstance() {
+        return makeNSAware(new SAXParserFactoryImpl());
+    }
+
+    /**
+     * Creates a new NamespaceAware instance of a {@code SAXParserFactory}.
+     * Parsers produced by the factory instance provides support for XML
+     * namespaces by default.
+     *
+     * @implSpec
+     * In addition to creating a factory instance using the same process as
+     * {@link #newInstance()}, this method must set NamespaceAware to true.
+     *
+     * @return a new instance of the {@code SAXParserFactory}
+     *
+     * @throws FactoryConfigurationError in case of {@linkplain
+     *         java.util.ServiceConfigurationError service configuration error}
+     *         or if the implementation is not available or cannot be instantiated.
+     *
+     * @since 13
+     */
+    public static SAXParserFactory newNSInstance() {
+        return makeNSAware(FactoryFinder.find(SAXParserFactory.class, DEFAULT_IMPL));
+    }
+
+    /**
+     * Creates a new NamespaceAware instance of a {@code SAXParserFactory} from
+     * the class name. Parsers produced by the factory instance provides
+     * support for XML namespaces by default.
+     *
+     * @implSpec
+     * In addition to creating a factory instance using the same process as
+     * {@link #newInstance(java.lang.String, java.lang.ClassLoader)}, this method
+     * must set NamespaceAware to true.
+     *
+     * @param factoryClassName a fully qualified factory class name that provides
+     *                         implementation of
+     *                         {@code javax.xml.parsers.SAXParserFactory}.
+     *
+     * @param classLoader the {@code ClassLoader} used to load the factory class.
+     *                    If it is {@code null}, the current {@code Thread}'s
+     *                    context classLoader is used to load the factory class.
+     *
+     * @return a new instance of the {@code SAXParserFactory}
+     *
+     * @throws FactoryConfigurationError if {@code factoryClassName} is {@code null}, or
+     *                                   the factory class cannot be loaded, instantiated.
+     *
+     * @since 13
+     */
+    public static SAXParserFactory newNSInstance(String factoryClassName,
+            ClassLoader classLoader) {
+            return makeNSAware(FactoryFinder.newInstance(
+                    SAXParserFactory.class, factoryClassName, classLoader, false));
+    }
+
+    /**
      * Creates a new instance of the {@code SAXParserFactory} builtin
      * system-default implementation.
      *
@@ -148,7 +220,7 @@
                 /* The default property name according to the JAXP spec */
                 SAXParserFactory.class,
                 /* The fallback implementation class name */
-                "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
+                DEFAULT_IMPL);
     }
 
     /**
@@ -192,6 +264,11 @@
                     factoryClassName, classLoader, false);
     }
 
+    private static SAXParserFactory makeNSAware(SAXParserFactory spf) {
+        spf.setNamespaceAware(true);
+        return spf;
+    }
+
     /**
      * Creates a new instance of a SAXParser using the currently
      * configured factory parameters.
--- a/test/jaxp/javax/xml/jaxp/unittest/parsers/BaseParsingTest.java	Tue May 21 13:40:56 2019 -0700
+++ b/test/jaxp/javax/xml/jaxp/unittest/parsers/BaseParsingTest.java	Tue May 21 14:55:30 2019 -0700
@@ -26,18 +26,27 @@
 import java.io.StringReader;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamReader;
+import org.testng.Assert;
 import static org.testng.Assert.assertEquals;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Listeners;
 import org.testng.annotations.Test;
 import org.w3c.dom.Document;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSSerializer;
+import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
 
 /**
  * @test
- * @bug 8169450 8222415
+ * @bug 8169450 8222415 8219692
  * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
  * @run testng/othervm -DrunSecMngr=true parsers.BaseParsingTest
  * @run testng/othervm parsers.BaseParsingTest
@@ -45,6 +54,84 @@
  */
 @Listeners({jaxp.library.BasePolicy.class})
 public class BaseParsingTest {
+    private static final String DOM_IMPL =
+            "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
+    private static final String SAX_IMPL =
+            "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl";
+
+    String xml_8219692 = "<a "
+            + "xmlns=\"http://openjdk_java_net/xml/defaultNS\" "
+            + "xmlns:p1=\"http://openjdk_java_net/xml/serializer/\">"
+            + "<b>in default namespace</b></a>";
+
+    /**
+     * Creates NamespaceAware parsers using old and new factory methods.
+     * @return NamespaceAware parsers
+     * @throws ParserConfigurationException
+     */
+    @DataProvider(name = "NSAwareDOMFactory")
+    public static Object[][] getNSDOMFactory() throws Exception {
+        boolean isNSAware = true;
+
+        return new Object[][]{
+            {getDOMParser(DocumentBuilderFactory.newDefaultInstance(), isNSAware)},
+            {getDOMParser(DocumentBuilderFactory.newInstance(), isNSAware)},
+            {getDOMParser(DocumentBuilderFactory.newInstance(DOM_IMPL, null), isNSAware)},
+            // using the new methods
+            {DocumentBuilderFactory.newDefaultNSInstance().newDocumentBuilder()},
+            {DocumentBuilderFactory.newNSInstance().newDocumentBuilder()},
+            {DocumentBuilderFactory.newNSInstance(DOM_IMPL, null).newDocumentBuilder()}
+        };
+    }
+
+    /**
+     * Creates parsers using the old instance methods. By default, they are
+     * not Namespace Aware.
+     * @return non-NamespaceAware parsers
+     * @throws ParserConfigurationException
+     */
+    @DataProvider(name = "DOMFactory")
+    public static Object[][] getDOMFactory() throws Exception {
+        boolean isNSAware = false;
+
+        return new Object[][]{
+            {getDOMParser(DocumentBuilderFactory.newDefaultInstance(), isNSAware)},
+            {getDOMParser(DocumentBuilderFactory.newInstance(), isNSAware)},
+            {getDOMParser(DocumentBuilderFactory.newInstance(DOM_IMPL, null), isNSAware)}
+        };
+    }
+
+
+    /**
+     * Creates NamespaceAware parsers using old and new factory methods.
+     * @return NamespaceAware parsers
+     * @throws ParserConfigurationException
+     */
+    @DataProvider(name = "NSAwareSAXFactory")
+    public static Object[][] getNSSAXFactory() throws Exception {
+        boolean isNSAware = true;
+
+        return new Object[][]{
+            {getSAXParser(SAXParserFactory.newDefaultInstance(), isNSAware)},
+            {getSAXParser(SAXParserFactory.newInstance(), isNSAware)},
+            {getSAXParser(SAXParserFactory.newInstance(SAX_IMPL, null), isNSAware)},
+            // using the new methods
+            {SAXParserFactory.newDefaultNSInstance().newSAXParser()},
+            {SAXParserFactory.newNSInstance().newSAXParser()},
+            {SAXParserFactory.newNSInstance(SAX_IMPL, null).newSAXParser()},
+        };
+    }
+
+    @DataProvider(name = "SAXFactory")
+    public static Object[][] getSAXFactory() throws Exception {
+        boolean isNSAware = false;
+
+        return new Object[][]{
+            {getSAXParser(SAXParserFactory.newDefaultInstance(), isNSAware)},
+            {getSAXParser(SAXParserFactory.newInstance(), isNSAware)},
+            {getSAXParser(SAXParserFactory.newInstance(SAX_IMPL, null), isNSAware)},
+        };
+    }
 
     @DataProvider(name = "xmlDeclarations")
     public static Object[][] xmlDeclarations() {
@@ -174,4 +261,96 @@
         Document doc = db.parse(is);
         assertEquals("UTF-16LE", doc.getInputEncoding());
     }
+
+    /**
+     * @bug 8219692
+     * Verifies that the default namespace declaration is preserved when
+     * NamespaceAware is set on the parser.
+     * @throws Exception
+     */
+    @Test(dataProvider = "NSAwareDOMFactory")
+    public void testNSAwareDOMFactory(DocumentBuilder db) throws Exception {
+        LSSerializer ls = getSerializer(db);
+        String out = ls.writeToString(getDoc(db, xml_8219692));
+        System.out.println(out);
+        Assert.assertTrue(out.contains("http://openjdk_java_net/xml/defaultNS"));
+    }
+
+    /**
+     * @bug 8219692
+     * Verifies that the default namespace declaration is missing when the
+     * old factory methods are used.
+     * @throws Exception
+     */
+    @Test(dataProvider = "DOMFactory")
+    public void testDOMFactory(DocumentBuilder db) throws Exception {
+        LSSerializer ls = getSerializer(db);
+        String out = ls.writeToString(getDoc(db, xml_8219692));
+        System.out.println(out);
+        Assert.assertFalse(out.contains("http://openjdk_java_net/xml/defaultNS"));
+    }
+
+    /**
+     * @bug 8219692
+     * Verifies that the default namespace declaration is preserved when
+     * NamespaceAware is set on the parser.
+     * @throws Exception
+     */
+    @Test(dataProvider = "NSAwareSAXFactory")
+    public void testNSAwareSAXFactory(SAXParser sp) throws Exception {
+        MyHandler h = new MyHandler();
+        sp.parse(new InputSource(new StringReader(xml_8219692)), h);
+
+        Assert.assertTrue(h.isNSAware);
+    }
+
+    /**
+     * @bug 8219692
+     * Verifies that the default namespace declaration is missing when the
+     * old factory methods are used.
+     * @throws Exception
+     */
+    @Test(dataProvider = "SAXFactory")
+    public void testSAXFactory(SAXParser sp) throws Exception {
+        MyHandler h = new MyHandler();
+        sp.parse(new InputSource(new StringReader(xml_8219692)), h);
+
+        Assert.assertFalse(h.isNSAware);
+    }
+
+    private static DocumentBuilder getDOMParser(DocumentBuilderFactory dbf, boolean isNSAware)
+            throws Exception {
+        dbf.setNamespaceAware(isNSAware);
+        return dbf.newDocumentBuilder();
+    }
+
+    private static SAXParser getSAXParser(SAXParserFactory spf, boolean isNSAware)
+            throws Exception {
+        spf.setNamespaceAware(isNSAware);
+        return spf.newSAXParser();
+    }
+
+    private LSSerializer getSerializer(DocumentBuilder db) throws Exception {
+        DOMImplementationLS di = (DOMImplementationLS) db.getDOMImplementation();
+        return di.createLSSerializer();
+    }
+
+    private Document getDoc(DocumentBuilder db, String xml) throws Exception {
+        InputSource is = new InputSource(new StringReader(xml));
+        return db.parse(is);
+    }
+
+    /**
+     * SAX Handler
+     */
+    class MyHandler extends DefaultHandler {
+        boolean isNSAware = false;
+
+        @Override
+        public void startElement(String uri, String localName, String qName,
+            Attributes attributes) throws SAXException {
+            isNSAware = "http://openjdk_java_net/xml/defaultNS".equals(uri)
+                    && ("a".equals(localName) || "b".equals(localName));
+        }
+    }
 }