From 3d644e54eb8b9d06032b2453106b827d35fafb3e Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:41:05 -0400 Subject: [PATCH 1/7] Remove XMLEventReader --- .../scala/scala/xml/pull/XMLEventReader.scala | 167 ------------------ 1 file changed, 167 deletions(-) delete mode 100755 shared/src/main/scala/scala/xml/pull/XMLEventReader.scala diff --git a/shared/src/main/scala/scala/xml/pull/XMLEventReader.scala b/shared/src/main/scala/scala/xml/pull/XMLEventReader.scala deleted file mode 100755 index 338b7b28a..000000000 --- a/shared/src/main/scala/scala/xml/pull/XMLEventReader.scala +++ /dev/null @@ -1,167 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2019, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package pull - -import scala.io.Source -import java.lang.Thread -import java.util.concurrent.LinkedBlockingQueue -import java.nio.channels.ClosedChannelException -import scala.xml.parsing.{ ExternalSources, MarkupHandler, MarkupParser } - -/** - * Main entry point into creating an event-based XML parser. Treating this - * as a [[scala.collection.Iterator]] will provide access to the generated events. - * @param src A [[scala.io.Source]] for XML data to parse - * - * @author Burak Emir - * @author Paul Phillips - */ -@deprecated("Consider javax.xml.stream.XMLEventReader instead.", "1.1.1") -class XMLEventReader(src: Source) - extends scala.collection.AbstractIterator[XMLEvent] - with ProducerConsumerIterator[XMLEvent] { - - // We implement a pull parser as an iterator, but since we may be operating on - // a stream (e.g. XML over a network) there may be arbitrarily long periods when - // the queue is empty. Fortunately the ProducerConsumerIterator is ideally - // suited to this task, possibly because it was written for use by this class. - - // to override as necessary - val preserveWS = true - - override val MaxQueueSize = 1000 - protected case object POISON extends XMLEvent - val EndOfStream = POISON - - // thread machinery - private[this] val parser = new Parser(src) - private[this] val parserThread = new Thread(parser, "XMLEventReader") - parserThread.start - // enqueueing the poison object is the reliable way to cause the - // iterator to terminate; hasNext will return false once it sees it. - // Calling interrupt() on the parserThread is the only way we can get - // it to stop producing tokens since it's lost deep in document() - - // we cross our fingers the interrupt() gets to its target, but if it - // fails for whatever reason the iterator correctness is not impacted, - // only performance (because it will finish the entire XML document, - // or at least as much as it can fit in the queue.) - def stop() = { - produce(POISON) - parserThread.interrupt() - } - - private class Parser(val input: Source) extends MarkupHandler with MarkupParser with ExternalSources with Runnable { - val preserveWS = XMLEventReader.this.preserveWS - // track level for elem memory usage optimization - private var level = 0 - - // this is Parser's way to add to the queue - the odd return type - // is to conform to MarkupHandler's interface - def setEvent(es: XMLEvent*): NodeSeq = { - es foreach produce - NodeSeq.Empty - } - - override def elemStart(pos: Int, pre: String, label: String, attrs: MetaData, scope: NamespaceBinding): Unit = { - level += 1 - setEvent(EvElemStart(pre, label, attrs, scope)) - } - override def elemEnd(pos: Int, pre: String, label: String): Unit = { - setEvent(EvElemEnd(pre, label)) - level -= 1 - } - - // this is a dummy to satisfy MarkupHandler's API - // memory usage optimization return one for top level to satisfy - // MarkupParser.document() otherwise NodeSeq.Empty - private var ignoreWritten = false - final def elem(pos: Int, pre: String, label: String, attrs: MetaData, pscope: NamespaceBinding, empty: Boolean, nodes: NodeSeq): NodeSeq = - if (level == 1 && !ignoreWritten) { ignoreWritten = true; } else NodeSeq.Empty - - def procInstr(pos: Int, target: String, txt: String) = setEvent(EvProcInstr(target, txt)) - def comment(pos: Int, txt: String) = setEvent(EvComment(txt)) - def entityRef(pos: Int, n: String) = setEvent(EvEntityRef(n)) - def text(pos: Int, txt: String) = setEvent(EvText(txt)) - - override def run(): Unit = { - curInput = input - try { - interruptibly { this.initialize.document() } - } catch { - case e:Exception => setEvent(ExceptionEvent(e)) - } - setEvent(POISON) - } - } -} - -// An internal class used to propagate exception from helper threads to API end users. -private case class ExceptionEvent(exception:Exception) extends XMLEvent - -// An iterator designed for one or more producers to generate -// elements, and a single consumer to iterate. Iteration will continue -// until closeIterator() is called, after which point producers -// calling produce() will receive interruptions. -// -// Since hasNext may block indefinitely if nobody is producing, -// there is also an available() method which will return true if -// the next call hasNext is guaranteed not to block. -// -// This is not thread-safe for multiple consumers! -trait ProducerConsumerIterator[T >: Null] extends Iterator[T] { - // abstract - iterator-specific distinguished object for marking eos - val EndOfStream: T - - // defaults to unbounded - override to positive Int if desired - val MaxQueueSize = -1 - - def interruptibly[A](body: => A): Option[A] = try Some(body) catch { - case _: InterruptedException => - Thread.currentThread.interrupt(); None - case _: ClosedChannelException => None - } - - private[this] lazy val queue = - if (MaxQueueSize < 0) new LinkedBlockingQueue[T]() - else new LinkedBlockingQueue[T](MaxQueueSize) - private[this] var buffer: T = _ - private def fillBuffer() = { - buffer = interruptibly(queue.take) getOrElse EndOfStream - isElement(buffer) - } - private def isElement(x: T) = x != null && x != EndOfStream - private def eos() = buffer == EndOfStream - - // public producer interface - this is the only method producers call, so - // LinkedBlockingQueue's synchronization is all we need. - def produce(x: T): Unit = if (!eos) interruptibly(queue put x) - - // consumer/iterator interface - we need not synchronize access to buffer - // because we required there to be only one consumer. - def hasNext = !eos && (buffer != null || fillBuffer) - - def next() = { - if (eos()) throw new NoSuchElementException("ProducerConsumerIterator") - if (buffer == null) fillBuffer() - if (buffer.isInstanceOf[ExceptionEvent]) throw buffer.asInstanceOf[ExceptionEvent].exception - - drainBuffer() - } - - def available() = isElement(buffer) || isElement(queue.peek) - - private def drainBuffer() = { - assert(!eos) - val res = buffer - buffer = null - res - } -} From 64857d562e085c09f3258b9e85876b6c44c61e8a Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:42:25 -0400 Subject: [PATCH 2/7] Drop XMLEventReader from scala.xml.Properties.pickJarBasedOn --- shared/src/main/scala/scala/xml/XML.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/main/scala/scala/xml/XML.scala b/shared/src/main/scala/scala/xml/XML.scala index 90bebc93c..0e87530d9 100755 --- a/shared/src/main/scala/scala/xml/XML.scala +++ b/shared/src/main/scala/scala/xml/XML.scala @@ -117,5 +117,5 @@ object XML extends XMLLoader[Elem] { object Properties extends scala.util.PropertiesTrait { protected def propCategory = "scala-xml" - protected def pickJarBasedOn = classOf[scala.xml.pull.XMLEventReader] + protected def pickJarBasedOn = classOf[scala.xml.Node] } From 5b785db5094e03d4612f629653509b038de1646c Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:43:00 -0400 Subject: [PATCH 3/7] Remove tests for XMLEventReader --- .../scala/xml/pull/XMLEventReaderTest.scala | 195 ------------------ 1 file changed, 195 deletions(-) delete mode 100644 jvm/src/test/scala/scala/xml/pull/XMLEventReaderTest.scala diff --git a/jvm/src/test/scala/scala/xml/pull/XMLEventReaderTest.scala b/jvm/src/test/scala/scala/xml/pull/XMLEventReaderTest.scala deleted file mode 100644 index 0c869d4f5..000000000 --- a/jvm/src/test/scala/scala/xml/pull/XMLEventReaderTest.scala +++ /dev/null @@ -1,195 +0,0 @@ -package scala.xml -package pull - -import org.junit.Test -import org.junit.Assert.{assertEquals,assertFalse, assertTrue} - -import scala.io.Source -import scala.xml.parsing.FatalError - -class XMLEventReaderTest { - - val src = Source.fromString("!") - - private def toSource(s: String) = new Source { - val iter = s.iterator - override def reportError(pos: Int, msg: String, out: java.io.PrintStream = Console.err): Unit = {} - } - - @Test - def pull: Unit = { - val er = new XMLEventReader(src) - assertTrue(er.next match { - case EvElemStart(_, "hello", _, _) => true - case _ => false - }) - assertTrue(er.next match { - case EvElemStart(_, "world", _, _) => true - case _ => false - }) - assertTrue(er.next match { - case EvElemEnd(_, "world") => true - case _ => false - }) - assertTrue(er.next match { - case EvText("!") => true - case _ => false - }) - assertTrue(er.next match { - case EvElemEnd(_, "hello") => true - case _ => false - }) - er.stop // allow thread to be garbage-collected - } - - @Test - def issue35: Unit = { - val broken = " - | - | - | - | - | - | - | - | - | - | - |""".stripMargin - - val er = new XMLEventReader(toSource(data)) - while(er.hasNext) er.next() - er.stop() - } - - @Test - def entityRefTest: Unit = { // SI-7796 - val source = Source.fromString(""'<>&") - val er = new XMLEventReader(source) - - assertTrue(er.next match { - case EvElemStart(_, "text", _, _) => true - case _ => false - }) - - assertEquals(EvEntityRef("quot"), er.next) - assertEquals(EvEntityRef("apos"), er.next) - assertEquals(EvEntityRef("lt"), er.next) - assertEquals(EvEntityRef("gt"), er.next) - assertEquals(EvEntityRef("amp"), er.next) - - assertTrue(er.next match { - case EvElemEnd(_, "text") => true - case _ => false - }) - - assertTrue(er.isEmpty) - } -} From bf2ed577b4d92e32b32875dbddbf3674f4801556 Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:47:54 -0400 Subject: [PATCH 4/7] Drop pull.XMLEvent --- .../main/scala/scala/xml/pull/XMLEvent.scala | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 shared/src/main/scala/scala/xml/pull/XMLEvent.scala diff --git a/shared/src/main/scala/scala/xml/pull/XMLEvent.scala b/shared/src/main/scala/scala/xml/pull/XMLEvent.scala deleted file mode 100644 index 736d99e5a..000000000 --- a/shared/src/main/scala/scala/xml/pull/XMLEvent.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2019, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml -package pull - -/** - * An XML event for pull parsing. All events received during - * parsing will be one of the subclasses of this trait. - */ -@deprecated("Consider javax.xml.stream.events.XMLEvent instead.", "1.1.1") -trait XMLEvent - -/** - * An Element's start tag was encountered. - * @param pre prefix, if any, on the element. This is the `xs` in `foo`. - * @param label the name of the element, not including the prefix - * @param attrs any attributes on the element - */ -case class EvElemStart(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding) extends XMLEvent - -/** - * An Element's end tag was encountered. - * @param pre prefix, if any, on the element. This is the `xs` in `foo`. - * @param label the name of the element, not including the prefix - */ -case class EvElemEnd(pre: String, label: String) extends XMLEvent - -/** - * A text node was encountered. - * @param text the text that was found - */ -case class EvText(text: String) extends XMLEvent - -/** - * An entity reference was encountered. - * @param entity the name of the entity, e.g. `gt` when encountering the entity `>` - */ -case class EvEntityRef(entity: String) extends XMLEvent - -/** - * A processing instruction was encountered. - * @param target the "PITarget" of the processing instruction. For the instruction ``, the target would - * be `foo` - * @param text the remainder of the instruction. For the instruction ``, the text would - * be `bar="baz"` - * @see [[http://www.w3.org/TR/REC-xml/#sec-pi]] - */ -case class EvProcInstr(target: String, text: String) extends XMLEvent - -/** - * A comment was encountered - * @param text the text of the comment - */ -case class EvComment(text: String) extends XMLEvent From 196af4004a554b586a8ee36ead63a653d52728ab Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:49:20 -0400 Subject: [PATCH 5/7] Drop trait XMLEvent from Document and SpecialNode classes --- shared/src/main/scala/scala/xml/Document.scala | 2 +- shared/src/main/scala/scala/xml/SpecialNode.scala | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/shared/src/main/scala/scala/xml/Document.scala b/shared/src/main/scala/scala/xml/Document.scala index f36b28658..ccce9519b 100644 --- a/shared/src/main/scala/scala/xml/Document.scala +++ b/shared/src/main/scala/scala/xml/Document.scala @@ -20,7 +20,7 @@ import scala.collection.Seq * @author Burak Emir */ @SerialVersionUID(-2289320563321795109L) -class Document extends NodeSeq with pull.XMLEvent with Serializable { +class Document extends NodeSeq with Serializable { /** * An ordered list of child information items, in document diff --git a/shared/src/main/scala/scala/xml/SpecialNode.scala b/shared/src/main/scala/scala/xml/SpecialNode.scala index 0c2d90335..773aff9dc 100644 --- a/shared/src/main/scala/scala/xml/SpecialNode.scala +++ b/shared/src/main/scala/scala/xml/SpecialNode.scala @@ -13,12 +13,9 @@ package xml * `SpecialNode` is a special XML node which represents either text * `(PCDATA)`, a comment, a `PI`, or an entity ref. * - * `SpecialNode`s also play the role of [[scala.xml.pull.XMLEvent]]s for - * pull-parsing. - * * @author Burak Emir */ -abstract class SpecialNode extends Node with pull.XMLEvent { +abstract class SpecialNode extends Node { /** always empty */ final override def attributes = Null From 67ac459a6e31600ac9876eb7813554e9f6be5db9 Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:49:33 -0400 Subject: [PATCH 6/7] Drop pull namespace --- .../main/scala/scala/xml/pull/package.scala | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 shared/src/main/scala/scala/xml/pull/package.scala diff --git a/shared/src/main/scala/scala/xml/pull/package.scala b/shared/src/main/scala/scala/xml/pull/package.scala deleted file mode 100644 index 38f23ef96..000000000 --- a/shared/src/main/scala/scala/xml/pull/package.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2019, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package xml - -/** - * Classes needed to view an XML document as a series of events. The document - * is parsed by an [[scala.xml.pull.XMLEventReader]] instance. You can treat it as - * an [[scala.collection.Iterator]] to retrieve the events, which are all - * subclasses of [[scala.xml.pull.XMLEvent]]. - * - * {{{ - * scala> val source = Source.fromString(""" - * - * - * ]>Hello&bar;>""") - * - * source: scala.io.Source = non-empty iterator - * - * scala> val reader = new XMLEventReader(source) - * reader: scala.xml.pull.XMLEventReader = non-empty iterator - * - * scala> reader.foreach{ println(_) } - * EvProcInstr(instruction,custom value="customvalue") - * EvText( - * ) - * EvElemStart(null,foo,,) - * EvText(Hello) - * EvComment( this is a comment ) - * EvElemStart(null,bar,,) - * EvText(BAR) - * EvElemEnd(null,bar) - * EvElemStart(null,bar,,) - * EvEntityRef(gt) - * EvElemEnd(null,bar) - * EvElemEnd(null,foo) - * EvText( - * - * ) - * - * }}} - */ -package object pull From a121f473bef32d89a458da2e8c4c298f41281005 Mon Sep 17 00:00:00 2001 From: "Aaron S. Hawley" Date: Sun, 18 Mar 2018 18:50:21 -0400 Subject: [PATCH 7/7] Add mima exclude filters for XMLEventReader --- build.sbt | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a18bc22bc..adf7dc93b 100644 --- a/build.sbt +++ b/build.sbt @@ -28,8 +28,40 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform) import com.typesafe.tools.mima.core._ import com.typesafe.tools.mima.core.ProblemFilters._ Seq( + // scala-xml 1.1.1 deprecated XMLEventReader, so it broke + // binary compatibility for 2.0.0 in the following way: + exclude[MissingClassProblem]("scala.xml.pull.EvComment"), + exclude[MissingClassProblem]("scala.xml.pull.EvComment$"), + exclude[MissingClassProblem]("scala.xml.pull.EvElemEnd"), + exclude[MissingClassProblem]("scala.xml.pull.EvElemEnd$"), + exclude[MissingClassProblem]("scala.xml.pull.EvElemStart"), + exclude[MissingClassProblem]("scala.xml.pull.EvElemStart$"), + exclude[MissingClassProblem]("scala.xml.pull.EvEntityRef"), + exclude[MissingClassProblem]("scala.xml.pull.EvEntityRef$"), + exclude[MissingClassProblem]("scala.xml.pull.EvProcInstr"), + exclude[MissingClassProblem]("scala.xml.pull.EvProcInstr$"), + exclude[MissingClassProblem]("scala.xml.pull.EvText"), + exclude[MissingClassProblem]("scala.xml.pull.EvText$"), + exclude[MissingClassProblem]("scala.xml.pull.ExceptionEvent"), + exclude[MissingClassProblem]("scala.xml.pull.ExceptionEvent$"), + exclude[MissingClassProblem]("scala.xml.pull.ProducerConsumerIterator"), + exclude[MissingClassProblem]("scala.xml.pull.XMLEvent"), + exclude[MissingClassProblem]("scala.xml.pull.XMLEventReader"), + exclude[MissingClassProblem]("scala.xml.pull.XMLEventReader$POISON$"), + exclude[MissingClassProblem]("scala.xml.pull.XMLEventReader$Parser"), + exclude[MissingClassProblem]("scala.xml.pull.package"), + exclude[MissingClassProblem]("scala.xml.pull.package$"), + exclude[MissingTypesProblem]("scala.xml.Atom"), + exclude[MissingTypesProblem]("scala.xml.Comment"), + exclude[MissingTypesProblem]("scala.xml.Document"), + exclude[MissingTypesProblem]("scala.xml.EntityRef"), + exclude[MissingTypesProblem]("scala.xml.PCData"), + exclude[MissingTypesProblem]("scala.xml.ProcInstr"), + exclude[MissingTypesProblem]("scala.xml.SpecialNode"), + exclude[MissingTypesProblem]("scala.xml.Text"), + exclude[MissingTypesProblem]("scala.xml.Unparsed"), // Scala 2.12 deprecated mutable.Stack, so we broke - // binary compatibility for 1.1.0 in the following way: + // binary compatibility for 2.0.0 in the following way: exclude[IncompatibleMethTypeProblem]("scala.xml.parsing.FactoryAdapter.scopeStack_="), exclude[IncompatibleResultTypeProblem]("scala.xml.parsing.FactoryAdapter.hStack"), exclude[IncompatibleResultTypeProblem]("scala.xml.parsing.FactoryAdapter.scopeStack"),