Skip to content

Allow using FactoryAdapter as ContentHandler #765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
15 changes: 13 additions & 2 deletions jvm/src/test/scala/scala/xml/XMLTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package scala.xml

import org.junit.{Test => UnitTest}
import org.junit.Assert.{assertEquals, assertFalse, assertNull, assertThrows, assertTrue}
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStreamReader, IOException, ObjectInputStream,
ObjectOutputStream, OutputStreamWriter, PrintStream, StringWriter}

import java.io.{ByteArrayInputStream, ByteArrayOutputStream, IOException, InputStream, InputStreamReader, ObjectInputStream, ObjectOutputStream, OutputStreamWriter, PrintStream, StringWriter}
import java.net.URL
import scala.xml.dtd.{DocType, PublicID}
import scala.xml.parsing.ConstructingParser
Expand Down Expand Up @@ -1081,4 +1081,15 @@ class XMLTestJVM {

assertEquals("", x.xEntityValue())
}

@UnitTest
def factoryAdapterAsContentHandler(): Unit = {
// scala-xml#764
val p = defaultParserFactory.newSAXParser()
val handler = new parsing.NoBindingFactoryAdapter
val input = new ByteArrayInputStream("<hello>world</hello>".getBytes("UTF-8"))
p.parse(input, handler)
val n: Node = handler.document.docElem
assert(n.text == "world")
}
}
45 changes: 34 additions & 11 deletions shared/src/main/scala/scala/xml/parsing/FactoryAdapter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,47 @@ abstract class FactoryAdapter extends DefaultHandler2 with factory.XMLLoader[Nod
// reference to the XMLReader that parses the document; this is used to query
// features (e.g., 'is-standalone') and properties (e.g., document-xml-version) -
// see http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html
private var xmlReader: Option[XMLReader] = None
private[this] var xmlReader: Option[XMLReader] = None

private var dtdBuilder: Option[DtdBuilder] = None
private[this] var dtdBuilder: Option[DtdBuilder] = None
private def inDtd: Boolean = dtdBuilder.isDefined && !dtdBuilder.get.isDone

private var document: Option[Document] = None
private var baseURI: Option[String] = None
private var xmlEncoding: Option[String] = None

private var prefixMappings: List[(String, String)] = List.empty
/**
* The [[Document]] built during [[endDocument]]. This allows using [[FactoryAdapter]] (or [[NoBindingFactoryAdapter]])
* for converting other XML representations accepting an [[org.xml.sax.ContentHandler]] to scala.xml.
*
* {{{
* // using org.jdom:jdom2:2.0.6.1
*
* import scala.xml._
* import org.jdom2._
* import org.jdom2.output._
*
* def jd2s(d: Document): Node = {
* val out = new SAXOutputter
* val h = new parsing.NoBindingFactoryAdapter
* out.setContentHandler(h)
* out.output(d)
* h.document.docElem
* }
* }}}
*/
def document: Document = _document

private[this] var _document: Document = null
private[this] var baseURI: Option[String] = None
private[this] var xmlEncoding: Option[String] = None

private[this] var prefixMappings: List[(String, String)] = List.empty

// TODO all the variables should be private, but - binary compatibility...
var prolog: List[Node] = List.empty
var rootElem: Node = _
var epilogue: List[Node] = List.empty

val buffer: StringBuilder = new StringBuilder()
private var inCDATA: Boolean = false
private[this] var inCDATA: Boolean = false

/** List of attributes
*
Expand Down Expand Up @@ -140,7 +163,7 @@ abstract class FactoryAdapter extends DefaultHandler2 with factory.XMLLoader[Nod
this.xmlReader = Some(xmlReader)
xmlReader.parse(inputSource)

document.get
_document
}

// abstract methods
Expand Down Expand Up @@ -213,7 +236,7 @@ abstract class FactoryAdapter extends DefaultHandler2 with factory.XMLLoader[Nod
epilogue = hStack.init.reverse

val document = new Document
this.document = Some(document)
this._document = document
document.children = prolog ++ rootElem ++ epilogue
document.docElem = rootElem
document.dtd = dtdBuilder.map(_.dtd).orNull
Expand All @@ -222,15 +245,15 @@ abstract class FactoryAdapter extends DefaultHandler2 with factory.XMLLoader[Nod

document.version =
try {
Option(xmlReader.get.getProperty("http://xml.org/sax/properties/document-xml-version").asInstanceOf[String])
xmlReader.map(_.getProperty("http://xml.org/sax/properties/document-xml-version").asInstanceOf[String])
} catch {
case _: SAXNotRecognizedException => None
case _: SAXNotSupportedException => None
}

document.standAlone =
try {
Some(xmlReader.get.getFeature("http://xml.org/sax/features/is-standalone"))
xmlReader.map(_.getFeature("http://xml.org/sax/features/is-standalone"))
} catch {
case _: SAXNotRecognizedException => None
case _: SAXNotSupportedException => None
Expand Down