From 363ac3131c24465bd2ea9aceac813c47835b7c1e Mon Sep 17 00:00:00 2001 From: Leonid Dubinsky Date: Fri, 7 Jan 2022 00:02:12 -0500 Subject: [PATCH] Handle XML namespace declarations even when the underlying parser is namespace-aware. fixes #506 --- jvm/src/test/scala/scala/xml/XMLTest.scala | 29 +++++++++++++++++++ .../scala/xml/parsing/FactoryAdapter.scala | 15 ++++++++++ 2 files changed, 44 insertions(+) diff --git a/jvm/src/test/scala/scala/xml/XMLTest.scala b/jvm/src/test/scala/scala/xml/XMLTest.scala index e7f3624f..3f6eb62e 100644 --- a/jvm/src/test/scala/scala/xml/XMLTest.scala +++ b/jvm/src/test/scala/scala/xml/XMLTest.scala @@ -629,6 +629,35 @@ class XMLTestJVM { // does not work: roundtripNodes(" ") } + // using non-namespace-aware parser, this always worked; + // using namespace-aware parser, this works with FactoryAdapter enhanced to handle startPrefixMapping() events; + // see https://github.com/scala/scala-xml/issues/506 + def roundtrip(namespaceAware: Boolean, xml: String): Unit = { + val parserFactory: javax.xml.parsers.SAXParserFactory = javax.xml.parsers.SAXParserFactory.newInstance() + parserFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true) + parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + parserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) + parserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/resolve-dtd-uris", false) + parserFactory.setNamespaceAware(namespaceAware) + parserFactory.setXIncludeAware(namespaceAware) + + assertEquals(xml, XML.withSAXParser(parserFactory.newSAXParser()).loadString(xml).toString()) + } + + @UnitTest + def namespaceUnaware: Unit = + roundtrip(namespaceAware = false, """""") + + @UnitTest + def namespaceAware: Unit = + roundtrip(namespaceAware = true, """""") + + @UnitTest + def namespaceAware2: Unit = + roundtrip(namespaceAware = true, """""") + @UnitTest def nodeSeqNs: Unit = { val x = { diff --git a/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala b/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala index 18d32633..eaa43afd 100644 --- a/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala +++ b/shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala @@ -211,10 +211,25 @@ abstract class FactoryAdapter extends DefaultHandler2 with factory.XMLLoader[Nod m = Attribute(Option(pre), key, Text(value), m) } + // Add namespace bindings for the prefix mappings declared by this element + // (if there are any, the parser is namespace-aware, and no namespace bindings were delivered as attributes). + // All `startPrefixMapping()` events will occur immediately before the corresponding `startElement()` event. + for ((prefix: String, uri: String) <- prefixMappings) + scpe = NamespaceBinding(if (prefix.isEmpty) null else prefix, uri, scpe) + + // Once the `prefixMappings` are processed into `scpe`, the list is emptied out + // so that already-declared namespaces are not re-declared on the nested elements. + prefixMappings = List.empty + scopeStack = scpe :: scopeStack attribStack = m :: attribStack } + private var prefixMappings: List[(String, String)] = List.empty + + override def startPrefixMapping(prefix: String, uri: String): Unit = + prefixMappings = (prefix, uri) :: prefixMappings + /** * Captures text or cdata. */