Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

SI-6939 Fix namespace binding (xmlns) not overriding outer binding

Given a nested XML literal to the compiler Elem instance is generated with
namespace binding of the inner element copying that of the outer element:

    val foo = <x:foo xmlns:x="http://foo.com/">
      <x:bar xmlns:x="http://bar.com/"><x:baz/></x:bar></x:foo>

With the above example, `foo.child.head.scope.toString` returns

    " xmlns:x="http://bar.com/" xmlns:x="http://foo.com/""

This is incorrect since the outer xmls:x should be overridden by the inner binding.

XML library also parses XML document in a similar manner:

    val foo2 = scala.xml.XML.loadString("""<x:foo xmlns:x="http://foo.com/"><x:bar xmlns:x="http://bar.com/"><x:baz/></x:bar></x:foo>""")

Despite this erroneous behavior, since the structure of NamespaceBinding class
is designed to be singly-linked list, the stacking of namespace bindings allows
constant-time creation with simple implementation.
Since the inner namespace binding comes before the outer one, query methods like `getURI` method behave correctly.

Because this bug is manifested when Elem is turned back into XML string, it could be fixed by shadowing the redefined namespace binding right when
buildString is called. With this change `foo.child.head.scope.toString` now returns:

    " xmlns:x="http://bar.com/""
  • Loading branch information...
commit b6f898f0811a72b423b6bef17cd2bf6791f1f5f0 1 parent 22dcec6
eugene yokota eed3si9n authored
24 src/library/scala/xml/NamespaceBinding.scala
View
@@ -38,6 +38,22 @@ case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBindin
override def toString(): String = sbToString(buildString(_, TopScope))
+ private def shadowRedefined: NamespaceBinding = shadowRedefined(TopScope)
+
+ private def shadowRedefined(stop: NamespaceBinding): NamespaceBinding = {
+ def prefixList(x: NamespaceBinding): List[String] =
+ if ((x == null) || (x eq stop)) Nil
+ else x.prefix :: prefixList(x.parent)
+ def fromPrefixList(l: List[String]): NamespaceBinding = l match {
+ case Nil => stop
+ case x :: xs => new NamespaceBinding(x, this.getURI(x), fromPrefixList(xs))
+ }
+ val ps0 = prefixList(this).reverse
+ val ps = ps0.distinct
+ if (ps.size == ps0.size) this
+ else fromPrefixList(ps)
+ }
+
override def canEqual(other: Any) = other match {
case _: NamespaceBinding => true
case _ => false
@@ -53,12 +69,16 @@ case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBindin
def buildString(stop: NamespaceBinding): String = sbToString(buildString(_, stop))
def buildString(sb: StringBuilder, stop: NamespaceBinding) {
- if (this eq stop) return // contains?
+ shadowRedefined(stop).doBuildString(sb, stop)
+ }
+
+ private def doBuildString(sb: StringBuilder, stop: NamespaceBinding) {
+ if ((this == null) || (this eq stop)) return // contains?
val s = " xmlns%s=\"%s\"".format(
(if (prefix != null) ":" + prefix else ""),
(if (uri != null) uri else "")
)
- parent.buildString(sb append s, stop) // copy(ignore)
+ parent.doBuildString(sb append s, stop) // copy(ignore)
}
}
13 test/files/run/t6939.scala
View
@@ -0,0 +1,13 @@
+object Test extends App {
+ val foo = <x:foo xmlns:x="http://foo.com/"><x:bar xmlns:x="http://bar.com/"><x:baz/></x:bar></x:foo>
+ assert(foo.child.head.scope.toString == """ xmlns:x="http://bar.com/"""")
+
+ val fooDefault = <foo xmlns="http://foo.com/"><bar xmlns="http://bar.com/"><baz/></bar></foo>
+ assert(fooDefault.child.head.scope.toString == """ xmlns="http://bar.com/"""")
+
+ val foo2 = scala.xml.XML.loadString("""<x:foo xmlns:x="http://foo.com/"><x:bar xmlns:x="http://bar.com/"><x:baz/></x:bar></x:foo>""")
+ assert(foo2.child.head.scope.toString == """ xmlns:x="http://bar.com/"""")
+
+ val foo2Default = scala.xml.XML.loadString("""<foo xmlns="http://foo.com/"><bar xmlns="http://bar.com/"><baz/></bar></foo>""")
+ assert(foo2Default.child.head.scope.toString == """ xmlns="http://bar.com/"""")
+}
Please sign in to comment.
Something went wrong with that request. Please try again.