From fd67593a8bfbf890d8dfbdcc1167495a1d05f58b Mon Sep 17 00:00:00 2001 From: HarrisL2 Date: Tue, 2 Dec 2025 19:41:42 -0500 Subject: [PATCH] Mark relevant types as Nullable --- build.sbt | 10 +++++ jvm/src/test/scala/scala/xml/XMLTest.scala | 25 +++++++------ .../scala/xml/ScalaVersionSpecific.scala | 10 +++-- .../main/scala-2/scala/xml/Nullables.scala | 21 +++++++++++ .../main/scala-3/scala/xml/Nullables.scala | 16 ++++++++ .../src/main/scala/scala/xml/Attribute.scala | 27 +++++++------- .../src/main/scala/scala/xml/Document.scala | 9 +++-- shared/src/main/scala/scala/xml/Elem.scala | 13 ++++--- .../src/main/scala/scala/xml/MetaData.scala | 37 ++++++++++--------- .../scala/scala/xml/NamespaceBinding.scala | 21 ++++++----- shared/src/main/scala/scala/xml/Node.scala | 13 ++++--- shared/src/main/scala/scala/xml/Null.scala | 17 +++++---- .../scala/scala/xml/PrefixedAttribute.scala | 17 +++++---- shared/src/main/scala/scala/xml/QNode.scala | 3 +- .../src/main/scala/scala/xml/TopScope.scala | 5 ++- .../scala/scala/xml/UnprefixedAttribute.scala | 19 +++++----- shared/src/main/scala/scala/xml/Utility.scala | 29 ++++++++------- shared/src/main/scala/scala/xml/XML.scala | 5 ++- shared/src/main/scala/scala/xml/dtd/DTD.scala | 3 +- .../src/main/scala/scala/xml/dtd/Decl.scala | 5 ++- .../main/scala/scala/xml/dtd/ExternalID.scala | 15 ++++---- .../include/CircularIncludeException.scala | 4 +- .../UnavailableResourceException.scala | 4 +- .../scala/xml/include/XIncludeException.scala | 8 ++-- .../xml/include/sax/EncodingHeuristics.scala | 9 +++-- .../xml/include/sax/XIncludeFilter.scala | 7 ++-- .../scala/xml/include/sax/XIncluder.scala | 5 ++- .../xml/parsing/ConstructingHandler.scala | 4 +- .../xml/parsing/DefaultMarkupHandler.scala | 4 +- .../scala/xml/parsing/FactoryAdapter.scala | 21 ++++++----- .../scala/xml/parsing/MarkupHandler.scala | 11 +++--- .../scala/xml/parsing/MarkupParser.scala | 35 +++++++++--------- .../xml/parsing/NoBindingFactoryAdapter.scala | 3 +- .../test/scala/scala/xml/AttributeTest.scala | 3 +- .../test/scala/scala/xml/MetaDataTest.scala | 7 ++-- .../test/scala/scala/xml/NodeSeqTest.scala | 1 + shared/src/test/scala/scala/xml/XMLTest.scala | 19 +++++----- 37 files changed, 276 insertions(+), 189 deletions(-) create mode 100644 shared/src/main/scala-2/scala/xml/Nullables.scala create mode 100644 shared/src/main/scala-3/scala/xml/Nullables.scala diff --git a/build.sbt b/build.sbt index d1fd9a4f8..c061bf752 100644 --- a/build.sbt +++ b/build.sbt @@ -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.) diff --git a/jvm/src/test/scala/scala/xml/XMLTest.scala b/jvm/src/test/scala/scala/xml/XMLTest.scala index 4e5dba1a6..41b0df5b6 100644 --- a/jvm/src/test/scala/scala/xml/XMLTest.scala +++ b/jvm/src/test/scala/scala/xml/XMLTest.scala @@ -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 @@ -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("") @@ -385,9 +386,9 @@ class XMLTestJVM { @UnitTest def t5052(): Unit = { - assertTrue( xml_== ) + assertTrue( xml_== ) assertTrue( xml_== ) - assertTrue( xml_== ) + assertTrue( xml_== ) assertTrue( xml_== ) } @@ -400,9 +401,9 @@ class XMLTestJVM { assertHonorsIterableContract(.attributes) assertHonorsIterableContract(.attributes) assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) - assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) + assertHonorsIterableContract(.attributes) } @UnitTest @@ -459,9 +460,9 @@ class XMLTestJVM { @UnitTest def attributes(): Unit = { val noAttr: Elem = - val attrNull: Elem = + val attrNull: Elem = val attrNone: Elem = - val preAttrNull: Elem = + val preAttrNull: Elem = val preAttrNone: Elem = assertEquals(noAttr, attrNull) assertEquals(noAttr, attrNone) @@ -469,8 +470,8 @@ class XMLTestJVM { assertEquals(noAttr, preAttrNone) val xml1: Elem = - val xml2: Elem = - val xml3: Elem = + val xml2: Elem = + val xml3: Elem = assertEquals(xml1, xml2) assertEquals(xml1, xml3) @@ -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) } diff --git a/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala b/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala index 49da95b8b..979ffb407 100644 --- a/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala +++ b/shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala @@ -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 @@ -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 => diff --git a/shared/src/main/scala-2/scala/xml/Nullables.scala b/shared/src/main/scala-2/scala/xml/Nullables.scala new file mode 100644 index 000000000..707939c91 --- /dev/null +++ b/shared/src/main/scala-2/scala/xml/Nullables.scala @@ -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 + } + } +} diff --git a/shared/src/main/scala-3/scala/xml/Nullables.scala b/shared/src/main/scala-3/scala/xml/Nullables.scala new file mode 100644 index 000000000..41417080c --- /dev/null +++ b/shared/src/main/scala-3/scala/xml/Nullables.scala @@ -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 +} diff --git a/shared/src/main/scala/scala/xml/Attribute.scala b/shared/src/main/scala/scala/xml/Attribute.scala index 1c7c35750..50078d076 100644 --- a/shared/src/main/scala/scala/xml/Attribute.scala +++ b/shared/src/main/scala/scala/xml/Attribute.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ import scala.collection.Seq /** @@ -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) @@ -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) } diff --git a/shared/src/main/scala/scala/xml/Document.scala b/shared/src/main/scala/scala/xml/Document.scala index 8fe1c6ae4..650018aa2 100644 --- a/shared/src/main/scala/scala/xml/Document.scala +++ b/shared/src/main/scala/scala/xml/Document.scala @@ -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 @@ -42,7 +43,7 @@ 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 @@ -50,17 +51,17 @@ class Document extends NodeSeq with Serializable { * 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 diff --git a/shared/src/main/scala/scala/xml/Elem.scala b/shared/src/main/scala/scala/xml/Elem.scala index 6b77f1642..426052fee 100755 --- a/shared/src/main/scala/scala/xml/Elem.scala +++ b/shared/src/main/scala/scala/xml/Elem.scala @@ -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 @@ -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)) @@ -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, @@ -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, diff --git a/shared/src/main/scala/scala/xml/MetaData.scala b/shared/src/main/scala/scala/xml/MetaData.scala index 6bdd080f0..b9842478a 100644 --- a/shared/src/main/scala/scala/xml/MetaData.scala +++ b/shared/src/main/scala/scala/xml/MetaData.scala @@ -17,6 +17,7 @@ import Utility.sbToString import scala.annotation.tailrec import scala.collection.AbstractIterable import scala.collection.Seq +import xml.Nullables._ object MetaData { /** @@ -37,13 +38,13 @@ object MetaData { * namespace URIs via the given scope. */ def normalize(attribs: MetaData, scope: NamespaceBinding): MetaData = { - def iterate(md: MetaData, normalized_attribs: MetaData, set: Set[String]): MetaData = + def iterate(md: MetaData, normalized_attribs: MetaData, set: Set[Nullable[String]]): MetaData = if (md.isNull) { normalized_attribs } else if (md.value == null) iterate(md.next, normalized_attribs, set) else { - val key: String = getUniversalKey(md, scope) + val key: Nullable[String] = getUniversalKey(md, scope) if (set(key)) iterate(md.next, normalized_attribs, set) else @@ -56,8 +57,8 @@ object MetaData { /** * returns key if md is unprefixed, pre+key is md is prefixed */ - def getUniversalKey(attrib: MetaData, scope: NamespaceBinding): String = attrib match { - case prefixed: PrefixedAttribute => scope.getURI(prefixed.pre) + prefixed.key + def getUniversalKey(attrib: MetaData, scope: NamespaceBinding): Nullable[String] = attrib match { + case prefixed: PrefixedAttribute => scope.getURI(prefixed.pre).asInstanceOf[String] + prefixed.key case unprefixed: UnprefixedAttribute => unprefixed.key } @@ -107,7 +108,7 @@ abstract class MetaData * @param key * @return value as Seq[Node] if key is found, null otherwise */ - def apply(key: String): ScalaVersionSpecific.SeqOfNode + def apply(key: String): Nullable[ScalaVersionSpecific.SeqOfNode] /** * convenience method, same as `apply(namespace, owner.scope, key)`. @@ -116,7 +117,7 @@ abstract class MetaData * @param owner the element owning this attribute list * @param key the attribute key */ - final def apply(namespace_uri: String, owner: Node, key: String): ScalaVersionSpecific.SeqOfNode = + final def apply(namespace_uri: String, owner: Node, key: Nullable[String]): Nullable[ScalaVersionSpecific.SeqOfNode] = apply(namespace_uri, owner.scope, key) /** @@ -127,7 +128,7 @@ abstract class MetaData * @param k to be looked for * @return value as Seq[Node] if key is found, null otherwise */ - def apply(namespace_uri: String, scp: NamespaceBinding, k: String): ScalaVersionSpecific.SeqOfNode + def apply(namespace_uri: Nullable[String], scp: NamespaceBinding, k: Nullable[String]): Nullable[ScalaVersionSpecific.SeqOfNode] /** * returns a copy of this MetaData item with next field set to argument. @@ -135,7 +136,7 @@ abstract class MetaData def copy(next: MetaData): MetaData /** if owner is the element of this metadata item, returns namespace */ - def getNamespace(owner: Node): String + def getNamespace(owner: Node): Nullable[String] def hasNext: Boolean = Null != next @@ -166,16 +167,16 @@ abstract class MetaData } /** returns key of this MetaData item */ - def key: String + def key: Nullable[String] /** returns value of this MetaData item */ - def value: ScalaVersionSpecific.SeqOfNode + def value: Nullable[ScalaVersionSpecific.SeqOfNode] /** * Returns a String containing "prefix:key" if the first key is * prefixed, and "key" otherwise. */ - def prefixedKey: String = this match { + def prefixedKey: Nullable[String] = this match { case x: Attribute if x.isPrefixed => s"${x.pre}:$key" case _ => key } @@ -183,8 +184,8 @@ abstract class MetaData /** * Returns a Map containing the attributes stored as key/value pairs. */ - def asAttrMap: Map[String, String] = - iterator.map(x => (x.prefixedKey, NodeSeq.fromSeq(x.value).text)).toMap + def asAttrMap: Map[Nullable[String], String] = + iterator.map(x => (x.prefixedKey, NodeSeq.fromSeq(x.value.nn).text)).toMap /** returns Null or the next MetaData item */ def next: MetaData @@ -195,7 +196,7 @@ abstract class MetaData * @param key * @return value in Some(Seq[Node]) if key is found, None otherwise */ - final def get(key: String): Option[ScalaVersionSpecific.SeqOfNode] = Option(apply(key)) + final def get(key: String): Option[ScalaVersionSpecific.SeqOfNode] = Option(apply(key).asInstanceOf[ScalaVersionSpecific.SeqOfNode]) /** same as get(uri, owner.scope, key) */ final def get(uri: String, owner: Node, key: String): Option[ScalaVersionSpecific.SeqOfNode] = @@ -209,8 +210,8 @@ abstract class MetaData * @param key to be looked fore * @return value as `Some[Seq[Node]]` if key is found, None otherwise */ - final def get(uri: String, scope: NamespaceBinding, key: String): Option[ScalaVersionSpecific.SeqOfNode] = - Option(apply(uri, scope, key)) + final def get(uri: Nullable[String], scope: NamespaceBinding, key: String): Option[ScalaVersionSpecific.SeqOfNode] = + Option(apply(uri, scope, key).asInstanceOf[ScalaVersionSpecific.SeqOfNode]) protected def toString1: String = sbToString(toString1) @@ -229,9 +230,9 @@ abstract class MetaData */ def wellformed(scope: NamespaceBinding): Boolean - def remove(key: String): MetaData + def remove(key: Nullable[String]): MetaData - def remove(namespace: String, scope: NamespaceBinding, key: String): MetaData + def remove(namespace: Nullable[String], scope: NamespaceBinding, key: String): MetaData final def remove(namespace: String, owner: Node, key: String): MetaData = remove(namespace, owner.scope, key) diff --git a/shared/src/main/scala/scala/xml/NamespaceBinding.scala b/shared/src/main/scala/scala/xml/NamespaceBinding.scala index 8efe27d79..c495931a0 100644 --- a/shared/src/main/scala/scala/xml/NamespaceBinding.scala +++ b/shared/src/main/scala/scala/xml/NamespaceBinding.scala @@ -14,6 +14,7 @@ package scala package xml import scala.collection.Seq +import xml.Nullables._ /** * The class `NamespaceBinding` represents namespace bindings @@ -25,12 +26,12 @@ import scala.collection.Seq */ // Note: used by the Scala compiler. @SerialVersionUID(0 - 2518644165573446725L) -case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBinding) extends AnyRef with Equality { +case class NamespaceBinding(prefix: Nullable[String], uri: Nullable[String], parent: Nullable[NamespaceBinding]) extends AnyRef with Equality { if (prefix == "") throw new IllegalArgumentException("zero length prefix not allowed") - def getURI(prefix: String): String = - if (this.prefix == prefix) uri else parent.getURI(prefix) + def getURI(prefix: Nullable[String]): Nullable[String] = + if (this.prefix == prefix) uri else parent.nn.getURI(prefix) /** * Returns some prefix that is mapped to the URI. @@ -39,21 +40,21 @@ case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBindin * @return the prefix that is mapped to the input URI, or null * if no prefix is mapped to the URI. */ - def getPrefix(uri: String): String = - if (uri == this.uri) prefix else parent.getPrefix(uri) + def getPrefix(uri: String): Nullable[String] = + if (uri == this.uri) prefix else parent.nn.getPrefix(uri) override def toString: String = Utility.sbToString(buildString(_, TopScope)) private def shadowRedefined(stop: NamespaceBinding): NamespaceBinding = { - def prefixList(x: NamespaceBinding): List[String] = + def prefixList(x: Nullable[NamespaceBinding]): List[Nullable[String]] = if ((x == null) || x.eq(stop)) Nil else x.prefix :: prefixList(x.parent) - def fromPrefixList(l: List[String]): NamespaceBinding = l match { + def fromPrefixList(l: List[Nullable[String]]): NamespaceBinding = l match { case Nil => stop case x :: xs => NamespaceBinding(x, this.getURI(x), fromPrefixList(xs)) } - val ps0: List[String] = prefixList(this).reverse - val ps: List[String] = ps0.distinct + val ps0: List[Nullable[String]] = prefixList(this).reverse + val ps: List[Nullable[String]] = ps0.distinct if (ps.size == ps0.size) this else fromPrefixList(ps) } @@ -80,6 +81,6 @@ case class NamespaceBinding(prefix: String, uri: String, parent: NamespaceBindin val prefixStr: String = if (prefix != null) s":$prefix" else "" val uriStr: String = if (uri != null) uri else "" - parent.doBuildString(sb.append(s""" xmlns$prefixStr="$uriStr""""), stop) // copy(ignore) + parent.nn.doBuildString(sb.append(s""" xmlns$prefixStr="$uriStr""""), stop) // copy(ignore) } } diff --git a/shared/src/main/scala/scala/xml/Node.scala b/shared/src/main/scala/scala/xml/Node.scala index d9c41dd1e..c2a769524 100755 --- a/shared/src/main/scala/scala/xml/Node.scala +++ b/shared/src/main/scala/scala/xml/Node.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ import scala.collection.Seq /** @@ -28,7 +29,7 @@ object Node { /** the empty namespace */ val EmptyNamespace: String = "" - def unapplySeq(n: Node): Some[(String, MetaData, ScalaVersionSpecific.SeqOfNode)] = + def unapplySeq(n: Node): Some[(Nullable[String], MetaData, ScalaVersionSpecific.SeqOfNode)] = Some((n.label, n.attributes, n.child)) } @@ -48,10 +49,10 @@ object Node { abstract class Node extends NodeSeq with ScalaVersionSpecificNode { /** prefix of this node */ - def prefix: String = null + def prefix: Nullable[String] = null /** label of this node. I.e. "foo" for <foo/>) */ - def label: String + def label: Nullable[String] /** * used internally. Atom/Molecule = -1 PI = -2 Comment = -3 EntityRef = -5 @@ -72,7 +73,7 @@ abstract class Node extends NodeSeq with ScalaVersionSpecificNode { /** * convenience, same as `getNamespace(this.prefix)` */ - def namespace: String = getNamespace(this.prefix) + def namespace: Nullable[String] = getNamespace(this.prefix) /** * Convenience method, same as `scope.getURI(pre)` but additionally @@ -82,7 +83,7 @@ abstract class Node extends NodeSeq with ScalaVersionSpecificNode { * @return the namespace if `scope != null` and prefix was * found, else `null` */ - def getNamespace(pre: String): String = if (scope == null) null else scope.getURI(pre) + def getNamespace(pre: Nullable[String]): Nullable[String] = if (scope == null) null else scope.getURI(pre) /** * Convenience method, looks up an unprefixed attribute in attributes of this node. @@ -190,7 +191,7 @@ abstract class Node extends NodeSeq with ScalaVersionSpecificNode { /** * Returns a type symbol (e.g. DTD, XSD), default `'''null'''`. */ - def xmlType: TypeSymbol = null + def xmlType: Nullable[TypeSymbol] = null /** * Returns a text representation of this node. Note that this is not equivalent to diff --git a/shared/src/main/scala/scala/xml/Null.scala b/shared/src/main/scala/scala/xml/Null.scala index c8e35640c..6200e7f29 100644 --- a/shared/src/main/scala/scala/xml/Null.scala +++ b/shared/src/main/scala/scala/xml/Null.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ import scala.collection.Iterator import scala.collection.Seq @@ -31,12 +32,12 @@ case object Null extends MetaData { override def filter(f: MetaData => Boolean): ScalaVersionSpecificReturnTypes.NullFilter = this override def copy(next: MetaData): MetaData = next - override def getNamespace(owner: Node): ScalaVersionSpecificReturnTypes.NullGetNamespace = null + override def getNamespace(owner: Node): ScalaVersionSpecificReturnTypes.NullGetNamespace = null.asInstanceOf[ScalaVersionSpecificReturnTypes.NullGetNamespace] override def hasNext: Boolean = false - override def next: ScalaVersionSpecificReturnTypes.NullNext = null - override def key: ScalaVersionSpecificReturnTypes.NullKey = null - override def value: ScalaVersionSpecificReturnTypes.NullValue = null + override def next: ScalaVersionSpecificReturnTypes.NullNext = null.asInstanceOf[ScalaVersionSpecificReturnTypes.NullNext] + override def key: ScalaVersionSpecificReturnTypes.NullKey = null.asInstanceOf[ScalaVersionSpecificReturnTypes.NullKey] + override def value: ScalaVersionSpecificReturnTypes.NullValue = null.asInstanceOf[ScalaVersionSpecificReturnTypes.NullValue] override def isPrefixed: Boolean = false override def length: Int = 0 @@ -48,8 +49,8 @@ case object Null extends MetaData { } override protected def basisForHashCode: Seq[Any] = Nil - override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullApply3 = null - override def apply(key: String): ScalaVersionSpecific.SeqOfNode = + override def apply(namespace: Nullable[String], scope: NamespaceBinding, key: Nullable[String]): ScalaVersionSpecificReturnTypes.NullApply3 = null.asInstanceOf[ScalaVersionSpecificReturnTypes.NullApply3] + override def apply(key: String): Nullable[ScalaVersionSpecific.SeqOfNode] = if (Utility.isNameStart(key.head)) null else throw new IllegalArgumentException(s"not a valid attribute name '$key', so can never match !") @@ -62,6 +63,6 @@ case object Null extends MetaData { override def wellformed(scope: NamespaceBinding): Boolean = true - override def remove(key: String): ScalaVersionSpecificReturnTypes.NullRemove = this - override def remove(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullRemove = this + override def remove(key: Nullable[String]): ScalaVersionSpecificReturnTypes.NullRemove = this + override def remove(namespace: Nullable[String], scope: NamespaceBinding, key: String): ScalaVersionSpecificReturnTypes.NullRemove = this } diff --git a/shared/src/main/scala/scala/xml/PrefixedAttribute.scala b/shared/src/main/scala/scala/xml/PrefixedAttribute.scala index 1dc6ba124..c6109cf33 100644 --- a/shared/src/main/scala/scala/xml/PrefixedAttribute.scala +++ b/shared/src/main/scala/scala/xml/PrefixedAttribute.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ import scala.collection.Seq /** @@ -27,12 +28,12 @@ import scala.collection.Seq class PrefixedAttribute( override val pre: String, override val key: String, - _value: Seq[Node], + _value: Nullable[Seq[Node]], val next1: MetaData ) extends Attribute { - override val value: ScalaVersionSpecific.SeqOfNode = if (_value == null) null else _value match { + override val value: Nullable[ScalaVersionSpecific.SeqOfNode] = if (_value == null) null else _value match { case ns: ScalaVersionSpecific.SeqOfNode => ns case _ => _value.toVector } @@ -40,8 +41,8 @@ class PrefixedAttribute( override val next: MetaData = if (value != null) next1 else next1.remove(key) /** same as this(pre, key, Text(value), next), or no attribute if value is null */ - def this(pre: String, key: String, value: String, next: MetaData) = - this(pre, key, if (value != null) Text(value) else null: NodeSeq, next) + def this(pre: String, key: String, value: Nullable[String], next: MetaData) = + this(pre, key, if (value != null) Text(value) else null: Nullable[NodeSeq], next) /** same as this(pre, key, value.get, next), or no attribute if value is None */ def this(pre: String, key: String, value: Option[Seq[Node]], next: MetaData) = @@ -54,16 +55,16 @@ class PrefixedAttribute( override def copy(next: MetaData): PrefixedAttribute = new PrefixedAttribute(pre, key, value, next) - override def getNamespace(owner: Node): String = + override def getNamespace(owner: Node): Nullable[String] = owner.getNamespace(pre) /** forwards the call to next (because caller looks for unprefixed attribute */ - override def apply(key: String): ScalaVersionSpecific.SeqOfNode = next(key) + override def apply(key: String): Nullable[ScalaVersionSpecific.SeqOfNode] = next(key) /** * gets attribute value of qualified (prefixed) attribute with given key */ - override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode = + override def apply(namespace: Nullable[String], scope: NamespaceBinding, key: Nullable[String]): Nullable[ScalaVersionSpecific.SeqOfNode] = if (key == this.key && scope.getURI(pre) == namespace) value else @@ -71,5 +72,5 @@ class PrefixedAttribute( } object PrefixedAttribute { - def unapply(x: PrefixedAttribute): Some[(String, String, Seq[Node], MetaData)] = Some((x.pre, x.key, x.value, x.next)) + def unapply(x: PrefixedAttribute): Some[(String, String, Nullable[Seq[Node]], MetaData)] = Some((x.pre, x.key, x.value, x.next)) } diff --git a/shared/src/main/scala/scala/xml/QNode.scala b/shared/src/main/scala/scala/xml/QNode.scala index ddf7818c4..9d96eeecb 100644 --- a/shared/src/main/scala/scala/xml/QNode.scala +++ b/shared/src/main/scala/scala/xml/QNode.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ /** * This object provides an extractor method to match a qualified node with * its namespace URI @@ -20,6 +21,6 @@ package xml * @author Burak Emir */ object QNode { - def unapplySeq(n: Node): Some[(String, String, MetaData, ScalaVersionSpecific.SeqOfNode)] = + def unapplySeq(n: Node): Some[(Nullable[String], Nullable[String], MetaData, ScalaVersionSpecific.SeqOfNode)] = Some((n.scope.getURI(n.prefix), n.label, n.attributes, n.child)) } diff --git a/shared/src/main/scala/scala/xml/TopScope.scala b/shared/src/main/scala/scala/xml/TopScope.scala index 0b82e4b63..a9c030256 100644 --- a/shared/src/main/scala/scala/xml/TopScope.scala +++ b/shared/src/main/scala/scala/xml/TopScope.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ /** * top level namespace scope. only contains the predefined binding * for the "xml" prefix which is bound to @@ -20,10 +21,10 @@ package xml */ object TopScope extends NamespaceBinding(null, null, null) { - override def getURI(prefix1: String): String = + override def getURI(prefix1: Nullable[String]): Nullable[String] = if (prefix1 == XML.xml) XML.namespace else null - override def getPrefix(uri1: String): String = + override def getPrefix(uri1: String): Nullable[String] = if (uri1 == XML.namespace) XML.xml else null override def toString: String = "" diff --git a/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala b/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala index 877a59b25..cdde2a9dc 100644 --- a/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala +++ b/shared/src/main/scala/scala/xml/UnprefixedAttribute.scala @@ -13,6 +13,7 @@ package scala package xml +import xml.Nullables._ import scala.collection.Seq /** @@ -22,13 +23,13 @@ import scala.collection.Seq */ // Note: used by the Scala compiler. class UnprefixedAttribute( - override val key: String, - _value: Seq[Node], + override val key: Nullable[String], + _value: Nullable[Seq[Node]], next1: MetaData ) extends Attribute { - override val value: ScalaVersionSpecific.SeqOfNode = if (_value == null) null else _value match { + override val value: Nullable[ScalaVersionSpecific.SeqOfNode] = if (_value == null) null else _value match { case ns: ScalaVersionSpecific.SeqOfNode => ns case _ => _value.toVector } @@ -37,8 +38,8 @@ class UnprefixedAttribute( override val next: MetaData = if (value != null) next1 else next1.remove(key) /** same as this(key, Text(value), next), or no attribute if value is null */ - def this(key: String, value: String, next: MetaData) = - this(key, if (value != null) Text(value) else null: NodeSeq, next) + def this(key: String, value: Nullable[String], next: MetaData) = + this(key, if (value != null) Text(value) else null: Nullable[NodeSeq], next) /** same as this(key, value.get, next), or no attribute if value is None */ def this(key: String, value: Option[Seq[Node]], next: MetaData) = @@ -47,7 +48,7 @@ class UnprefixedAttribute( /** returns a copy of this unprefixed attribute with the given next field*/ override def copy(next: MetaData): UnprefixedAttribute = new UnprefixedAttribute(key, value, next) - final override def getNamespace(owner: Node): String = null + final override def getNamespace(owner: Node): Nullable[String] = null /** * Gets value of unqualified (unprefixed) attribute with given key, null if not found @@ -55,7 +56,7 @@ class UnprefixedAttribute( * @param key * @return value as Seq[Node] if key is found, null otherwise */ - override def apply(key: String): ScalaVersionSpecific.SeqOfNode = + override def apply(key: String): Nullable[ScalaVersionSpecific.SeqOfNode] = if (key == this.key) value else next(key) /** @@ -66,9 +67,9 @@ class UnprefixedAttribute( * @param key * @return .. */ - override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode = + override def apply(namespace: Nullable[String], scope: NamespaceBinding, key: Nullable[String]): Nullable[ScalaVersionSpecific.SeqOfNode] = next(namespace, scope, key) } object UnprefixedAttribute { - def unapply(x: UnprefixedAttribute): Some[(String, Seq[Node], MetaData)] = Some((x.key, x.value, x.next)) + def unapply(x: UnprefixedAttribute): Some[(Nullable[String], Nullable[Seq[Node]], MetaData)] = Some((x.key, x.value, x.next)) } diff --git a/shared/src/main/scala/scala/xml/Utility.scala b/shared/src/main/scala/scala/xml/Utility.scala index cf85be14f..68ed32cbe 100755 --- a/shared/src/main/scala/scala/xml/Utility.scala +++ b/shared/src/main/scala/scala/xml/Utility.scala @@ -18,6 +18,7 @@ import scala.collection.mutable import scala.language.implicitConversions import scala.collection.Seq import scala.collection.immutable.{Seq => ISeq} +import xml.Nullables._ /** * The `Utility` object provides utility functions for processing instances @@ -78,9 +79,9 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU /** returns a sorted attribute list */ def sort(md: MetaData): MetaData = if (md.isNull || md.next.isNull) md else { - val key: String = md.key - val smaller: MetaData = sort(md.filter { m => m.key < key }) - val greater: MetaData = sort(md.filter { m => m.key > key }) + val key: String = md.key.nn + val smaller: MetaData = sort(md.filter { m => m.key.nn < key }) + val greater: MetaData = sort(md.filter { m => m.key.nn > key }) smaller.foldRight(md.copy(greater)) ((x, xs) => x.copy(xs)) } @@ -137,20 +138,20 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU * * @return `'''null'''` if `ref` was not a predefined entity. */ - final def unescape(ref: String, s: StringBuilder): StringBuilder = + final def unescape(ref: String, s: StringBuilder): Nullable[StringBuilder] = unescMap.get(ref).map(s.append).orNull /** * Returns a set of all namespaces used in a sequence of nodes * and all their descendants, including the empty namespaces. */ - def collectNamespaces(nodes: Seq[Node]): mutable.Set[String] = - nodes.foldLeft(new mutable.HashSet[String]) { (set, x) => collectNamespaces(x, set); set } + def collectNamespaces(nodes: Seq[Node]): mutable.Set[Nullable[String]] = + nodes.foldLeft(new mutable.HashSet[Nullable[String]]) { (set, x) => collectNamespaces(x, set); set } /** * Adds all namespaces in node to set. */ - def collectNamespaces(n: Node, set: mutable.Set[String]): Unit = + def collectNamespaces(n: Node, set: mutable.Set[Nullable[String]]): Unit = if (n.doCollectNamespaces) { set += n.namespace for (a <- n.attributes) a match { @@ -222,12 +223,12 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU minimizeTags: MinimizeMode.Value, sb: StringBuilder ): Unit = { - @tailrec def ser(nss: List[List[Node]], pscopes: List[NamespaceBinding], spaced: List[Boolean], toClose: List[Node]): Unit = nss match { + @tailrec def ser(nss: List[List[Node]], pscopes: List[NamespaceBinding], spaced: List[Boolean], toClose: List[Nullable[Node]]): Unit = nss match { case List(Nil) => case Nil :: rests => if (toClose.head != null) { sb.append("') } ser(rests, pscopes.tail, spaced.tail, toClose.tail) @@ -296,7 +297,7 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU /** * Returns a hashcode for the given constituents of a node */ - def hashCode(pre: String, label: String, attribHashCode: Int, scpeHash: Int, children: Seq[Node]): Int = + def hashCode(pre: Nullable[String], label: Nullable[String], attribHashCode: Int, scpeHash: Int, children: Seq[Node]): Int = scala.util.hashing.MurmurHash3.orderedHash(label +: attribHashCode +: scpeHash +: children, pre.##) def appendQuoted(s: String): String = sbToString(appendQuoted(s, _)) @@ -323,7 +324,7 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU sb.append('"') } - def getName(s: String, index: Int): String = + def getName(s: String, index: Int): Nullable[String] = if (index >= s.length) null else { val xs: String = s.drop(index) if (xs.nonEmpty && isNameStart(xs.head)) xs.takeWhile(isNameChar) @@ -334,14 +335,14 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU * Returns `'''null'''` if the value is a correct attribute value, * error message if it isn't. */ - def checkAttributeValue(value: String): String = { + def checkAttributeValue(value: String): Nullable[String] = { var i: Int = 0 while (i < value.length) { value.charAt(i) match { case '<' => return "< not allowed in attribute value" case '&' => - val n: String = getName(value, i + 1) + val n: Nullable[String] = getName(value, i + 1) if (n == null) return s"malformed entity reference in attribute value [$value]" i = i + n.length + 1 @@ -357,7 +358,7 @@ object Utility extends AnyRef with parsing.TokenTests with ScalaVersionSpecificU // unused, untested def parseAttributeValue(value: String): ScalaVersionSpecific.SeqOfNode = { val sb: StringBuilder = new StringBuilder - var rfb: StringBuilder = null + var rfb: Nullable[StringBuilder] = null val nb: NodeBuffer = new NodeBuffer() val it: Iterator[Char] = value.iterator diff --git a/shared/src/main/scala/scala/xml/XML.scala b/shared/src/main/scala/scala/xml/XML.scala index 3dfe36b27..dff151454 100755 --- a/shared/src/main/scala/scala/xml/XML.scala +++ b/shared/src/main/scala/scala/xml/XML.scala @@ -17,6 +17,7 @@ import factory.XMLLoader import java.io.{File, FileDescriptor, FileInputStream, FileOutputStream, InputStream, Reader, StringReader, Writer} import java.nio.channels.Channels import scala.util.control.Exception +import xml.Nullables._ object Source { def fromFile(name: String): InputSource = fromFile(new File(name)) @@ -96,7 +97,7 @@ object XML extends XMLLoader[Elem] { node: Node, enc: String = "UTF-8", xmlDecl: Boolean = false, - doctype: dtd.DocType = null + doctype: Nullable[dtd.DocType] = null ): Unit = { val fos: FileOutputStream = new FileOutputStream(filename) val w: Writer = Channels.newWriter(fos.getChannel, enc) @@ -121,7 +122,7 @@ object XML extends XMLLoader[Elem] { node: Node, enc: String, xmlDecl: Boolean, - doctype: dtd.DocType, + doctype: Nullable[dtd.DocType], minimizeTags: MinimizeMode.Value = MinimizeMode.Default ): Unit = { /* TODO: optimize by giving writer parameter to toXML*/ diff --git a/shared/src/main/scala/scala/xml/dtd/DTD.scala b/shared/src/main/scala/scala/xml/dtd/DTD.scala index 9b334a8dc..fd9c22bde 100644 --- a/shared/src/main/scala/scala/xml/dtd/DTD.scala +++ b/shared/src/main/scala/scala/xml/dtd/DTD.scala @@ -14,6 +14,7 @@ package scala package xml package dtd +import xml.Nullables._ import scala.collection.mutable import scala.collection.Seq @@ -23,7 +24,7 @@ import scala.collection.Seq * @author Burak Emir */ abstract class DTD { - var externalID: ExternalID = _ + var externalID: Nullable[ExternalID] = _ var decls: List[Decl] = Nil def notations: Seq[NotationDecl] = Nil def unparsedEntities: Seq[EntityDecl] = Nil diff --git a/shared/src/main/scala/scala/xml/dtd/Decl.scala b/shared/src/main/scala/scala/xml/dtd/Decl.scala index c2cf53e18..1965c4e9c 100644 --- a/shared/src/main/scala/scala/xml/dtd/Decl.scala +++ b/shared/src/main/scala/scala/xml/dtd/Decl.scala @@ -15,6 +15,7 @@ package xml package dtd import Utility.sbToString +import xml.Nullables._ /** * XML declarations @@ -55,12 +56,12 @@ case class AttListDecl(name: String, attrs: List[AttrDecl]) extends MarkupDecl { * versions might provide a way to access the attribute types more * directly. */ -case class AttrDecl(name: String, tpe: String, default: DefaultDecl) { +case class AttrDecl(name: String, tpe: String, default: Nullable[DefaultDecl]) { override def toString: String = sbToString(buildString) def buildString(sb: StringBuilder): StringBuilder = { sb.append(s" $name $tpe ") - default.buildString(sb) + default.nn.buildString(sb) } } diff --git a/shared/src/main/scala/scala/xml/dtd/ExternalID.scala b/shared/src/main/scala/scala/xml/dtd/ExternalID.scala index 80b9da867..5913558f5 100644 --- a/shared/src/main/scala/scala/xml/dtd/ExternalID.scala +++ b/shared/src/main/scala/scala/xml/dtd/ExternalID.scala @@ -13,6 +13,7 @@ package scala package xml package dtd +import xml.Nullables._ /** * an ExternalIDs - either PublicID or SystemID @@ -20,8 +21,8 @@ package dtd * @author Burak Emir */ sealed abstract class ExternalID extends parsing.TokenTests { - def quoted(s: String): String = { - val c: Char = if (s.contains('"')) '\'' else '"' + def quoted(s: Nullable[String]): String = { + val c: Char = if (s.nn.contains('"')) '\'' else '"' s"$c$s$c" } @@ -35,8 +36,8 @@ sealed abstract class ExternalID extends parsing.TokenTests { def buildString(sb: StringBuilder): StringBuilder = sb.append(this.toString) - def systemId: String - def publicId: String + def systemId: Nullable[String] + def publicId: Nullable[String] } /** @@ -59,7 +60,7 @@ case class SystemID(override val systemId: String) extends ExternalID { * @param publicId the public identifier literal * @param systemId (can be null for notation pubIDs) the system identifier literal */ -case class PublicID(override val publicId: String, override val systemId: String) extends ExternalID { +case class PublicID(override val publicId: String, override val systemId: Nullable[String]) extends ExternalID { if (!checkPubID(publicId)) throw new IllegalArgumentException("publicId must consist of PubidChars") @@ -82,8 +83,8 @@ case class PublicID(override val publicId: String, override val systemId: String * @author Michael Bayne */ object NoExternalID extends ExternalID { - override val publicId: ScalaVersionSpecificReturnTypes.NoExternalIDId = null - override val systemId: ScalaVersionSpecificReturnTypes.NoExternalIDId = null + override val publicId: Nullable[ScalaVersionSpecificReturnTypes.NoExternalIDId] = null + override val systemId: Nullable[ScalaVersionSpecificReturnTypes.NoExternalIDId] = null override def toString: String = "" } diff --git a/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala b/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala index ecff559df..0039a4473 100644 --- a/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala +++ b/shared/src/main/scala/scala/xml/include/CircularIncludeException.scala @@ -14,11 +14,13 @@ package scala package xml package include +import xml.Nullables._ + /** * A `CircularIncludeException` is thrown when an included document attempts * to include itself or one of its ancestor documents. */ -class CircularIncludeException(message: String) extends XIncludeException { +class CircularIncludeException(message: Nullable[String]) extends XIncludeException { /** * Constructs a `CircularIncludeException` with `'''null'''`. diff --git a/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala b/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala index 7499a3f14..883ee8161 100644 --- a/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala +++ b/shared/src/main/scala/scala/xml/include/UnavailableResourceException.scala @@ -14,11 +14,13 @@ package scala package xml package include +import xml.Nullables._ + /** * An `UnavailableResourceException` is thrown when an included document * cannot be found or loaded. */ -class UnavailableResourceException(message: String) +class UnavailableResourceException(message: Nullable[String]) extends XIncludeException(message) { def this() = this(null) } diff --git a/shared/src/main/scala/scala/xml/include/XIncludeException.scala b/shared/src/main/scala/scala/xml/include/XIncludeException.scala index 4d2a809cf..a8d4fe21a 100644 --- a/shared/src/main/scala/scala/xml/include/XIncludeException.scala +++ b/shared/src/main/scala/scala/xml/include/XIncludeException.scala @@ -14,6 +14,8 @@ package scala package xml package include +import xml.Nullables._ + /** * `XIncludeException` is the generic superclass for all checked exceptions * that may be thrown as a result of a violation of XInclude's rules. @@ -25,14 +27,14 @@ package include * * @param message the detail message. */ -class XIncludeException(message: String) extends Exception(message) { +class XIncludeException(message: Nullable[String]) extends Exception(message) { /** * uses `'''null'''` as its error detail message. */ def this() = this(null) - private var rootCause: Throwable = _ + private var rootCause: Nullable[Throwable] = _ /** * When an `IOException`, `MalformedURLException` or other generic @@ -57,5 +59,5 @@ class XIncludeException(message: String) extends Exception(message) { * @return Throwable the underlying exception which caused the * `XIncludeException` to be thrown */ - def getRootCause: Throwable = this.rootCause + def getRootCause: Nullable[Throwable] = this.rootCause } diff --git a/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala b/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala index fe8b0e323..63f89b066 100644 --- a/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala +++ b/shared/src/main/scala/scala/xml/include/sax/EncodingHeuristics.scala @@ -16,6 +16,7 @@ package include.sax import java.io.InputStream import scala.util.matching.Regex +import xml.Nullables._ /** * `EncodingHeuristics` reads from a stream @@ -50,9 +51,9 @@ object EncodingHeuristics { * @return the name of the encoding. */ def readEncodingFromStream(in: InputStream): String = { - var ret: String = null + var ret: Nullable[String] = null val bytesToRead: Int = 1024 // enough to read most XML encoding declarations - def resetAndRet: String = { in.reset(); ret } + def resetAndRet: Nullable[String] = { in.reset(); ret } // This may fail if there are a lot of space characters before the end // of the encoding declaration @@ -71,7 +72,7 @@ object EncodingHeuristics { case _ => null } if (ret != null) - return resetAndRet + return resetAndRet.nn def readASCIIEncoding: String = { val data: Array[Byte] = new Array[Byte](bytesToRead - 4) @@ -98,6 +99,6 @@ object EncodingHeuristics { case (0x4C, 0x6F, 0xA7, 0x94) => utf8 // XXX EBCDIC case _ => utf8 // no XML or text declaration present } - resetAndRet + resetAndRet.nn } } diff --git a/shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala b/shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala index b82414d61..71c4df8bb 100644 --- a/shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala +++ b/shared/src/main/scala/scala/xml/include/sax/XIncludeFilter.scala @@ -15,6 +15,7 @@ package xml package include.sax import scala.xml.include._ +import xml.Nullables._ import org.xml.sax.{Attributes, Locator, XMLReader} import org.xml.sax.helpers.{AttributesImpl, NamespaceSupport, XMLFilterImpl, XMLReaderFactory} @@ -250,7 +251,7 @@ class XIncludeFilter extends XMLFilterImpl { private def includeTextDocument(url: String, encoding1: String): Unit = { var encoding: String = encoding1 if (encoding == null || encoding.trim.isEmpty) encoding = "UTF-8" - var source: URL = null + var source: Nullable[URL] = null try { val base: URL = bases.peek source = new URL(base, url) @@ -262,7 +263,7 @@ class XIncludeFilter extends XMLFilterImpl { } try { - val uc: URLConnection = source.openConnection + val uc: URLConnection = source.nn.openConnection val in: BufferedInputStream = new BufferedInputStream(uc.getInputStream) val encodingFromHeader: String = uc.getContentEncoding var contentType: String = uc.getContentType @@ -293,7 +294,7 @@ class XIncludeFilter extends XMLFilterImpl { case e: UnsupportedEncodingException => throw new SAXException(s"Unsupported encoding: $encoding$getLocation", e) case e: IOException => - throw new SAXException(s"Document not found: ${source.toExternalForm}$getLocation", e) + throw new SAXException(s"Document not found: ${source.nn.toExternalForm}$getLocation", e) } } diff --git a/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala b/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala index ec94be793..7b9809bde 100644 --- a/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala +++ b/shared/src/main/scala/scala/xml/include/sax/XIncluder.scala @@ -14,6 +14,7 @@ package scala package xml package include.sax +import xml.Nullables._ import org.xml.sax.{ ContentHandler, Locator, Attributes } import org.xml.sax.ext.LexicalHandler import java.io.{ OutputStream, OutputStreamWriter, IOException } @@ -159,14 +160,14 @@ class XIncluder(outs: OutputStream, encoding: String) extends ContentHandler wit // Just need this reference so we can ask if a comment is // inside an include element or not - private var filter: XIncludeFilter = _ + private var filter: Nullable[XIncludeFilter] = _ def setFilter(filter: XIncludeFilter): Unit = { this.filter = filter } override def comment(ch: Array[Char], start: Int, length: Int): Unit = { - if (!inDTD && !filter.insideIncludeElement) { + if (!inDTD && !filter.nn.insideIncludeElement) { try { out.write("