diff --git a/jvm/src/test/scala/scala/xml/XMLTest.scala b/jvm/src/test/scala/scala/xml/XMLTest.scala index 90e4a65c..d5a606d4 100644 --- a/jvm/src/test/scala/scala/xml/XMLTest.scala +++ b/jvm/src/test/scala/scala/xml/XMLTest.scala @@ -24,10 +24,10 @@ class XMLTestJVM { def Elem(prefix: 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.load(new InputSource(new StringReader(""))) - lazy val parsedxml11: Elem = XML.load(new InputSource(new StringReader(""))) + lazy val parsedxml1: Elem = XML.loadString("") + lazy val parsedxml11: Elem = XML.loadString("") val xmlFile2: String = "Peter BunemanDan SuciuData on ze webJohn MitchellFoundations of Programming Languages" - lazy val parsedxml2: Elem = XML.load(new InputSource(new StringReader(xmlFile2))) + lazy val parsedxml2: Elem = XML.loadString(xmlFile2) @UnitTest def equality(): Unit = { @@ -46,9 +46,7 @@ class XMLTestJVM { assertTrue(Array(parsedxml1).toList sameElements List(parsedxml11)) val x2: String = "Peter BunemanDan SuciuData on ze web" - - val i: InputSource = new InputSource(new StringReader(x2)) - val x2p: Elem = XML.load(i) + val x2p: Elem = XML.loadString(x2) assertEquals(Elem(null, "book", e, sc, Elem(null, "author", e, sc, Text("Peter Buneman")), @@ -146,8 +144,7 @@ class XMLTestJVM { val xmlAttrValueNorm: String = "" { - val isrcA: InputSource = new InputSource(new StringReader(xmlAttrValueNorm)) - val parsedxmlA: Elem = XML.load(isrcA) + val parsedxmlA: Elem = XML.loadString(xmlAttrValueNorm) val c: Char = (parsedxmlA \ "@nom").text.charAt(0) assertTrue(c == '\u015e') } @@ -689,6 +686,17 @@ class XMLTestJVM { assertTrue(gotAnError) } + // Here we see that opening InputStream prematurely, as was done previously, breaks XInclude. + @UnitTest(expected = classOf[org.xml.sax.SAXParseException]) def xIncludeNeedsSystemId(): Unit = { + val parserFactory = xercesInternal + parserFactory.setNamespaceAware(true) + parserFactory.setXIncludeAware(true) + XML + .withSAXParser(parserFactory.newSAXParser) + .load(getClass.getResource("site.xml").openStream()) + .toString + } + // Now that we can use XML parser configured to be namespace-aware, // we can also configure it to be XInclude-aware and process XML Includes: def check( @@ -700,7 +708,7 @@ class XMLTestJVM { parserFactory.setXIncludeAware(true) val actual: String = XML .withSAXParser(parserFactory.newSAXParser) - .load(getClass.getResource(resourceName).toString) + .load(getClass.getResource(resourceName)) .toString assertEquals(expected, actual) diff --git a/shared/src/main/scala/scala/xml/XML.scala b/shared/src/main/scala/scala/xml/XML.scala index b5ea9133..16a23aa0 100755 --- a/shared/src/main/scala/scala/xml/XML.scala +++ b/shared/src/main/scala/scala/xml/XML.scala @@ -19,13 +19,13 @@ import java.nio.channels.Channels import scala.util.control.Exception.ultimately object Source { - def fromFile(file: File): InputSource = new InputSource(new FileInputStream(file)) - def fromFile(fd: FileDescriptor): InputSource = new InputSource(new FileInputStream(fd)) - def fromFile(name: String): InputSource = new InputSource(new FileInputStream(name)) - + def fromFile(name: String): InputSource = fromFile(new File(name)) + def fromFile(file: File): InputSource = fromUrl(file.toURI.toURL) + def fromUrl(url: java.net.URL): InputSource = fromSysId(url.toString) + def fromSysId(sysID: String): InputSource = new InputSource(sysID) + def fromFile(fd: FileDescriptor): InputSource = fromInputStream(new FileInputStream(fd)) def fromInputStream(is: InputStream): InputSource = new InputSource(is) def fromReader(reader: Reader): InputSource = new InputSource(reader) - def fromSysId(sysID: String): InputSource = new InputSource(sysID) def fromString(string: String): InputSource = fromReader(new StringReader(string)) } diff --git a/shared/src/main/scala/scala/xml/factory/XMLLoader.scala b/shared/src/main/scala/scala/xml/factory/XMLLoader.scala index ff722d6e..acaf6353 100644 --- a/shared/src/main/scala/scala/xml/factory/XMLLoader.scala +++ b/shared/src/main/scala/scala/xml/factory/XMLLoader.scala @@ -14,7 +14,7 @@ package scala package xml package factory -import org.xml.sax.{SAXNotRecognizedException, XMLReader} +import org.xml.sax.{SAXNotRecognizedException, SAXNotSupportedException, XMLReader} import javax.xml.parsers.SAXParserFactory import parsing.{FactoryAdapter, NoBindingFactoryAdapter} import java.io.{File, FileDescriptor, InputStream, Reader} @@ -22,24 +22,28 @@ import java.net.URL /** * Presents collection of XML loading methods which use the parser - * created by "def parser". + * created by "def parser" or the reader created by "def reader". */ trait XMLLoader[T <: Node] { import scala.xml.Source._ def adapter: FactoryAdapter = new NoBindingFactoryAdapter() + private def setSafeDefaults(parserFactory: SAXParserFactory): Unit = { + parserFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true) + parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + parserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) + parserFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false) + parserFactory.setFeature("http://xml.org/sax/features/resolve-dtd-uris", false) + parserFactory.setXIncludeAware(false) + parserFactory.setNamespaceAware(false) + } + private lazy val parserInstance: ThreadLocal[SAXParser] = new ThreadLocal[SAXParser] { override def initialValue: SAXParser = { - val parser: SAXParserFactory = SAXParserFactory.newInstance - parser.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true) - parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) - parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) - parser.setFeature("http://xml.org/sax/features/external-parameter-entities", false) - parser.setFeature("http://xml.org/sax/features/external-general-entities", false) - parser.setFeature("http://xml.org/sax/features/resolve-dtd-uris", false) - parser.setXIncludeAware(false) - parser.setNamespaceAware(false) - parser.newSAXParser + val parserFactory: SAXParserFactory = SAXParserFactory.newInstance + setSafeDefaults(parserFactory) + parserFactory.newSAXParser } } @@ -51,72 +55,68 @@ trait XMLLoader[T <: Node] { /** * Loads XML from the given InputSource, using the supplied parser. - * The methods available in scala.xml.XML use the XML parser in the JDK. + * The methods available in scala.xml.XML use the XML parser in the JDK + * (unless another parser is present on the classpath). */ - def loadXML(source: InputSource, parser: SAXParser): T = loadXML(source, parser.getXMLReader) + def loadXML(inputSource: InputSource, parser: SAXParser): T = loadXML(inputSource, parser.getXMLReader) - def loadXMLNodes(source: InputSource, parser: SAXParser): Seq[Node] = loadXMLNodes(source, parser.getXMLReader) + def loadXMLNodes(inputSource: InputSource, parser: SAXParser): Seq[Node] = loadXMLNodes(inputSource, parser.getXMLReader) - private def loadXML(source: InputSource, reader: XMLReader): T = { - val result: FactoryAdapter = parse(source, reader) + private def loadXML(inputSource: InputSource, reader: XMLReader): T = { + val result: FactoryAdapter = parse(inputSource, reader) result.rootElem.asInstanceOf[T] } - - private def loadXMLNodes(source: InputSource, reader: XMLReader): Seq[Node] = { - val result: FactoryAdapter = parse(source, reader) + + private def loadXMLNodes(inputSource: InputSource, reader: XMLReader): Seq[Node] = { + val result: FactoryAdapter = parse(inputSource, reader) result.prolog ++ (result.rootElem :: result.epilogue) } - private def parse(source: InputSource, reader: XMLReader): FactoryAdapter = { - if (source == null) throw new IllegalArgumentException("InputSource cannot be null") + private def parse(inputSource: InputSource, xmlReader: XMLReader): FactoryAdapter = { + if (inputSource == null) throw new IllegalArgumentException("InputSource cannot be null") val result: FactoryAdapter = adapter - reader.setContentHandler(result) - reader.setDTDHandler(result) + xmlReader.setContentHandler(result) + xmlReader.setDTDHandler(result) /* Do not overwrite pre-configured EntityResolver. */ - if (reader.getEntityResolver == null) reader.setEntityResolver(result) + if (xmlReader.getEntityResolver == null) xmlReader.setEntityResolver(result) /* Do not overwrite pre-configured ErrorHandler. */ - if (reader.getErrorHandler == null) reader.setErrorHandler(result) + if (xmlReader.getErrorHandler == null) xmlReader.setErrorHandler(result) try { - reader.setProperty("http://xml.org/sax/properties/lexical-handler", result) + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", result) } catch { case _: SAXNotRecognizedException => + case _: SAXNotSupportedException => } result.scopeStack = TopScope :: result.scopeStack - reader.parse(source) + xmlReader.parse(inputSource) result.scopeStack = result.scopeStack.tail result } - /** loads XML from given InputSource. */ - def load(source: InputSource): T = loadXML(source, reader) - - /** Loads XML from the given file, file descriptor, or filename. */ + /** Loads XML. */ + def load(inputSource: InputSource): T = loadXML(inputSource, reader) + def loadFile(fileName: String): T = load(fromFile(fileName)) def loadFile(file: File): T = load(fromFile(file)) - def loadFile(fd: FileDescriptor): T = load(fromFile(fd)) - def loadFile(name: String): T = load(fromFile(name)) - - /** loads XML from given InputStream, Reader, sysID, or URL. */ - def load(is: InputStream): T = load(fromInputStream(is)) + def load(url: URL): T = load(fromUrl(url)) + def load(sysId: String): T = load(fromSysId(sysId)) + def loadFile(fileDescriptor: FileDescriptor): T = load(fromFile(fileDescriptor)) + def load(inputStream: InputStream): T = load(fromInputStream(inputStream)) def load(reader: Reader): T = load(fromReader(reader)) - def load(sysID: String): T = load(fromSysId(sysID)) - def load(url: URL): T = load(fromInputStream(url.openStream())) - - /** Loads XML from the given String. */ def loadString(string: String): T = load(fromString(string)) /** Load XML nodes, including comments and processing instructions that precede and follow the root element. */ - def loadNodes(source: InputSource): Seq[Node] = loadXMLNodes(source, reader) + def loadNodes(inputSource: InputSource): Seq[Node] = loadXMLNodes(inputSource, reader) + def loadFileNodes(fileName: String): Seq[Node] = loadNodes(fromFile(fileName)) def loadFileNodes(file: File): Seq[Node] = loadNodes(fromFile(file)) - def loadFileNodes(fd: FileDescriptor): Seq[Node] = loadNodes(fromFile(fd)) - def loadFileNodes(name: String): Seq[Node] = loadNodes(fromFile(name)) - def loadNodes(is: InputStream): Seq[Node] = loadNodes(fromInputStream(is)) + def loadNodes(url: URL): Seq[Node] = loadNodes(fromUrl(url)) + def loadNodes(sysId: String): Seq[Node] = loadNodes(fromSysId(sysId)) + def loadFileNodes(fileDescriptor: FileDescriptor): Seq[Node] = loadNodes(fromFile(fileDescriptor)) + def loadNodes(inputStream: InputStream): Seq[Node] = loadNodes(fromInputStream(inputStream)) def loadNodes(reader: Reader): Seq[Node] = loadNodes(fromReader(reader)) - def loadNodes(sysID: String): Seq[Node] = loadNodes(fromSysId(sysID)) - def loadNodes(url: URL): Seq[Node] = loadNodes(fromInputStream(url.openStream())) def loadStringNodes(string: String): Seq[Node] = loadNodes(fromString(string)) }