Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform, NativePlatform)
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.get"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.attribute"),

// Nullable types result in a raw Seq, is fixed in 3.8
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Attribute.unapply"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Attribute.apply"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Attribute.value"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.MetaData.value"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.unapply"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.this"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.unapply"),
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.this"),

// trait Attribute now extends trait ScalaVersionSpecificMetaData to ensure the previous signatures
// with return type `collection.Seq` remain valid.
// (trait Attribute extends MetaData, but that parent is not present in bytecode because it's a class.)
Expand Down
25 changes: 13 additions & 12 deletions jvm/src/test/scala/scala/xml/XMLTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import java.net.URL
import scala.xml.dtd.{DocType, PublicID}
import scala.xml.parsing.ConstructingParser
import scala.xml.Utility.sort
import xml.Nullables._

object XMLTestJVM {
val e: MetaData = Null //Node.NoAttributes
Expand All @@ -17,7 +18,7 @@ object XMLTestJVM {
class XMLTestJVM {
import XMLTestJVM.{e, sc}

def Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem =
def Elem(prefix: Nullable[String], label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem =
scala.xml.Elem.apply(prefix, label, attributes, scope, minimizeEmpty = true, child: _*)

lazy val parsedxml1: Elem = XML.loadString("<hello><world/></hello>")
Expand Down Expand Up @@ -385,9 +386,9 @@ class XMLTestJVM {

@UnitTest
def t5052(): Unit = {
assertTrue(<elem attr={ null: String }/> xml_== <elem/>)
assertTrue(<elem attr={ null: Nullable[String] }/> xml_== <elem/>)
assertTrue(<elem attr={ None }/> xml_== <elem/>)
assertTrue(<elem/> xml_== <elem attr={ null: String }/>)
assertTrue(<elem/> xml_== <elem attr={ null: Nullable[String] }/>)
assertTrue(<elem/> xml_== <elem attr={ None }/>)
}

Expand All @@ -400,9 +401,9 @@ class XMLTestJVM {
assertHonorsIterableContract(<a y={ None }/>.attributes)
assertHonorsIterableContract(<a y={ None } x=""/>.attributes)
assertHonorsIterableContract(<a a="" y={ None }/>.attributes)
assertHonorsIterableContract(<a y={ null: String }/>.attributes)
assertHonorsIterableContract(<a y={ null: String } x=""/>.attributes)
assertHonorsIterableContract(<a a="" y={ null: String }/>.attributes)
assertHonorsIterableContract(<a y={ null: Nullable[String] }/>.attributes)
assertHonorsIterableContract(<a y={ null: Nullable[String] } x=""/>.attributes)
assertHonorsIterableContract(<a a="" y={ null: Nullable[String] }/>.attributes)
}

@UnitTest
Expand Down Expand Up @@ -459,18 +460,18 @@ class XMLTestJVM {
@UnitTest
def attributes(): Unit = {
val noAttr: Elem = <t/>
val attrNull: Elem = <t a={ null: String }/>
val attrNull: Elem = <t a={ null: Nullable[String] }/>
val attrNone: Elem = <t a={ None: Option[Seq[Node]] }/>
val preAttrNull: Elem = <t p:a={ null: String }/>
val preAttrNull: Elem = <t p:a={ null: Nullable[String] }/>
val preAttrNone: Elem = <t p:a={ None: Option[Seq[Node]] }/>
assertEquals(noAttr, attrNull)
assertEquals(noAttr, attrNone)
assertEquals(noAttr, preAttrNull)
assertEquals(noAttr, preAttrNone)

val xml1: Elem = <t b="1" d="2"/>
val xml2: Elem = <t a={ null: String } p:a={ null: String } b="1" c={ null: String } d="2"/>
val xml3: Elem = <t b="1" c={ null: String } d="2" a={ null: String } p:a={ null: String }/>
val xml2: Elem = <t a={ null: Nullable[String] } p:a={ null: Nullable[String] } b="1" c={ null: Nullable[String] } d="2"/>
val xml3: Elem = <t b="1" c={ null: Nullable[String] } d="2" a={ null: Nullable[String] } p:a={ null: Nullable[String] }/>
assertEquals(xml1, xml2)
assertEquals(xml1, xml3)

Expand Down Expand Up @@ -759,8 +760,8 @@ class XMLTestJVM {
def documentBaseURI(): Unit = {
val url: URL = resourceUrl("site")
// XMLLoader returns the document's baseURI:
assert(XML.withSAXParser(xercesInternal.newSAXParser).loadDocument(url).baseURI.endsWith("/test-classes/scala/xml/site.xml"))
assert(XML.withSAXParser(xercesExternal.newSAXParser).loadDocument(url).baseURI.endsWith("/test-classes/scala/xml/site.xml"))
assert(XML.withSAXParser(xercesInternal.newSAXParser).loadDocument(url).baseURI.nn.endsWith("/test-classes/scala/xml/site.xml"))
assert(XML.withSAXParser(xercesExternal.newSAXParser).loadDocument(url).baseURI.nn.endsWith("/test-classes/scala/xml/site.xml"))
// ConstructingParser does not return it of course: since it uses scala.io.Source it has no idea where is the XML coming from:
assertNull(ConstructingParser.fromSource(scala.io.Source.fromURI(url.toURI), preserveWS = false).document().baseURI)
}
Expand Down
10 changes: 6 additions & 4 deletions shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

package scala.xml


import xml.Nullables._
import scala.collection.immutable.StrictOptimizedSeqOps
import scala.collection.{View, SeqOps, IterableOnce, immutable, mutable}
import scala.collection.BuildFrom
Expand Down Expand Up @@ -65,11 +67,11 @@ private[xml] trait ScalaVersionSpecificNode { self: Node =>
}

private[xml] trait ScalaVersionSpecificMetaData { self: MetaData =>
def apply(key: String): scala.collection.Seq[Node]
def apply(namespace_uri: String, owner: Node, key: String): scala.collection.Seq[Node]
def apply(namespace_uri: String, scp: NamespaceBinding, k: String): scala.collection.Seq[Node]
def apply(key: String): Nullable[scala.collection.Seq[Node]]
def apply(namespace_uri: String, owner: Node, key: Nullable[String]): Nullable[scala.collection.Seq[Node]]
def apply(namespace_uri: Nullable[String], scp: NamespaceBinding, k: Nullable[String]): Nullable[scala.collection.Seq[Node]]

def value: scala.collection.Seq[Node]
def value: Nullable[scala.collection.Seq[Node]]
}

private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer =>
Expand Down
21 changes: 21 additions & 0 deletions shared/src/main/scala-2/scala/xml/Nullables.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.xml
object Nullables {
type Nullable[T] = T
implicit class NonNullOps[T](private val x: T) extends AnyVal {
def nn: T = {
x
}
}
}
16 changes: 16 additions & 0 deletions shared/src/main/scala-3/scala/xml/Nullables.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.xml
object Nullables {
type Nullable[T] = T | Null
}
27 changes: 14 additions & 13 deletions shared/src/main/scala/scala/xml/Attribute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package scala
package xml

import xml.Nullables._
import scala.collection.Seq

/**
Expand All @@ -22,21 +23,21 @@ import scala.collection.Seq
* @author Burak Emir
*/
object Attribute {
def unapply(x: Attribute): Option[(String, Seq[Node], MetaData)] = x match {
def unapply(x: Attribute): Option[(Nullable[String], Nullable[Seq[Node]], MetaData)] = x match {
case PrefixedAttribute(_, key, value, next) => Some((key, value, next))
case UnprefixedAttribute(key, value, next) => Some((key, value, next))
case _ => None
}

/** Convenience functions which choose Un/Prefixedness appropriately */
def apply(key: String, value: Seq[Node], next: MetaData): Attribute =
def apply(key: Nullable[String], value: Seq[Node], next: MetaData): Attribute =
new UnprefixedAttribute(key, value, next)

def apply(pre: String, key: String, value: String, next: MetaData): Attribute =
def apply(pre: Nullable[String], key: String, value: String, next: MetaData): Attribute =
if (pre == null || pre == "") new UnprefixedAttribute(key, value, next)
else new PrefixedAttribute(pre, key, value, next)

def apply(pre: String, key: String, value: Seq[Node], next: MetaData): Attribute =
def apply(pre: Nullable[String], key: String, value: Seq[Node], next: MetaData): Attribute =
if (pre == null || pre == "") new UnprefixedAttribute(key, value, next)
else new PrefixedAttribute(pre, key, value, next)

Expand All @@ -54,29 +55,29 @@ object Attribute {
* @author Burak Emir
*/
trait Attribute extends MetaData with ScalaVersionSpecificMetaData {
def pre: String // will be null if unprefixed
override val key: String
override val value: ScalaVersionSpecific.SeqOfNode
def pre: Nullable[String] // will be null if unprefixed
override val key: Nullable[String]
override val value: Nullable[ScalaVersionSpecific.SeqOfNode]
override val next: MetaData

override def apply(key: String): ScalaVersionSpecific.SeqOfNode
override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode
override def apply(key: String): Nullable[ScalaVersionSpecific.SeqOfNode]
override def apply(namespace: Nullable[String], scope: NamespaceBinding, key: Nullable[String]): Nullable[ScalaVersionSpecific.SeqOfNode]
override def copy(next: MetaData): Attribute

override def remove(key: String): MetaData =
override def remove(key: Nullable[String]): MetaData =
if (!isPrefixed && this.key == key) next
else copy(next.remove(key))

override def remove(namespace: String, scope: NamespaceBinding, key: String): MetaData =
override def remove(namespace: Nullable[String], scope: NamespaceBinding, key: String): MetaData =
if (this.key == key && scope.getURI(pre) == namespace) next
else copy(next.remove(namespace, scope, key))

override def isPrefixed: Boolean = pre != null

override def getNamespace(owner: Node): String
override def getNamespace(owner: Node): Nullable[String]

override def wellformed(scope: NamespaceBinding): Boolean = {
val arg: String = if (isPrefixed) scope.getURI(pre) else null
val arg: Nullable[String] = if (isPrefixed) scope.getURI(pre) else null
(next(arg, scope, key) == null) && next.wellformed(scope)
}

Expand Down
9 changes: 5 additions & 4 deletions shared/src/main/scala/scala/xml/Document.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package scala
package xml

import scala.collection.Seq
import xml.Nullables._

/**
* A document information item (according to InfoSet spec). The comments
Expand Down Expand Up @@ -42,25 +43,25 @@ class Document extends NodeSeq with Serializable {
var docElem: Node = _

/** The dtd that comes with the document, if any */
var dtd: scala.xml.dtd.DTD = _
var dtd: Nullable[scala.xml.dtd.DTD] = _

/**
* An unordered set of notation information items, one for each notation
* declared in the DTD. If any notation is multiply declared, this property
* has no value.
*/
def notations: Seq[scala.xml.dtd.NotationDecl] =
dtd.notations
dtd.nn.notations

/**
* An unordered set of unparsed entity information items, one for each
* unparsed entity declared in the DTD.
*/
def unparsedEntities: Seq[scala.xml.dtd.EntityDecl] =
dtd.unparsedEntities
dtd.nn.unparsedEntities

/** The base URI of the document entity. */
var baseURI: String = _
var baseURI: Nullable[String] = _

/**
* The name of the character encoding scheme in which the document entity
Expand Down
13 changes: 7 additions & 6 deletions shared/src/main/scala/scala/xml/Elem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package scala
package xml

import scala.collection.Seq
import xml.Nullables._

/**
* This singleton object contains the `apply` and `unapplySeq` methods for
Expand All @@ -24,10 +25,10 @@ import scala.collection.Seq
// Note: used by the Scala compiler.
object Elem {

def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem =
def apply(prefix: Nullable[String], label: Nullable[String], attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem =
new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*)

def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] =
def unapplySeq(n: Node): Option[(Nullable[String], Nullable[String], MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] =
n match {
case _: SpecialNode | _: Group => None
case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child))
Expand Down Expand Up @@ -55,8 +56,8 @@ object Elem {
*/
// Note: used by the Scala compiler.
class Elem(
override val prefix: String,
override val label: String,
override val prefix: Nullable[String],
override val label: Nullable[String],
attributes1: MetaData,
override val scope: NamespaceBinding,
val minimizeEmpty: Boolean,
Expand Down Expand Up @@ -98,8 +99,8 @@ class Elem(
* @return a new symbol with updated attributes
*/
def copy(
prefix: String = this.prefix,
label: String = this.label,
prefix: Nullable[String] = this.prefix,
label: Nullable[String] = this.label,
attributes: MetaData = this.attributes,
scope: NamespaceBinding = this.scope,
minimizeEmpty: Boolean = this.minimizeEmpty,
Expand Down
Loading