com.gitlab.mvysny.konsumexml.stax.JavaxXmlStreamStaxParser.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of konsume-xml Show documentation
Show all versions of konsume-xml Show documentation
Konsume-XML: A simple functional XML parser with no annotations
The newest version!
package com.gitlab.mvysny.konsumexml.stax
import java.io.InputStream
import javax.xml.XMLConstants
import javax.xml.namespace.QName
import javax.xml.stream.XMLInputFactory
import javax.xml.stream.XMLStreamReader
/**
* This parser uses the [XMLStreamReader] which is present in every JDK however it is not present in Android by default.
* Call [create] to create the parser instance.
*/
public open class JavaxXmlStreamStaxParser protected constructor(public val r: XMLStreamReader) : StaxParser {
override fun next() {
r.next()
}
override val location: Location
get() = r.location.let { Location(it.lineNumber, it.columnNumber, it.characterOffset, it.publicId, it.systemId) }
override val eventType: StaxEventType
get() = types[r.eventType - 1]
override val elementName: QName
get() = r.name
public companion object {
private val types = listOf(StaxEventType.StartElement, StaxEventType.EndElement, StaxEventType.ProcessingInstruction,
StaxEventType.Characters, StaxEventType.Comment, StaxEventType.Space, StaxEventType.StartDocument,
StaxEventType.EndDocument, StaxEventType.EntityReference, StaxEventType.Attribute, StaxEventType.DTD,
StaxEventType.CData, StaxEventType.Namespace, StaxEventType.NotationDeclaration, StaxEventType.EntityDeclaration)
public fun create(`in`: InputStream, systemId: String? = null,
xmlInputFactory: XMLInputFactory = XMLInputFactory.newInstance()): JavaxXmlStreamStaxParser {
val xmlStreamReader: XMLStreamReader = xmlInputFactory.createXMLStreamReader(systemId, `in`)
if (xmlStreamReader.javaClass.name.startsWith("com.bea")) {
return JavaxXmlStreamComBeaStaxParser(xmlStreamReader)
}
return JavaxXmlStreamStaxParser(xmlStreamReader)
}
}
private fun QName.hotfixRetardedAndroid(): QName {
if (localPart.startsWith("xml:")) {
// yes, Android Studio JVM will happily return attribute with localname "xml:lang" and empty namespace even though
// it should process namespaces BY DEFAULT
return QName(XMLConstants.XML_NS_URI, localPart.removePrefix("xml:"))
}
return this
}
override fun getAttributeName(index: Int): QName {
val name: QName = requireNotNull(r.getAttributeName(index)) {
"Got null attribute name, possibly index-out-of-bounds: $index not in 0..${attributeCount - 1}"
}
return name.hotfixRetardedAndroid()
}
override val attributeCount: Int
get() = r.attributeCount
override fun getAttributeValue(namespaceURI: String, localName: String): String? = r.getAttributeValue(namespaceURI, localName)
override val text: String
get() = r.text
override fun close() {
r.close()
}
override fun hasNext(): Boolean = r.hasNext()
}
/**
* Contains workaround for https://github.com/codehaus/stax/issues/1 . See
* https://gitlab.com/mvysny/konsume-xml/-/issues/12 for more details.
*/
internal class JavaxXmlStreamComBeaStaxParser(r: XMLStreamReader): JavaxXmlStreamStaxParser(r) {
override val location: Location
get() = r.location.let { Location(it.lineNumber, it.columnNumber + 1, it.characterOffset, it.publicId, it.systemId) }
override fun getAttributeValue(namespaceURI: String, localName: String): String? {
// use null instead of an empty string, because of https://github.com/codehaus/stax/issues/1
return r.getAttributeValue(namespaceURI.ifBlank { null }, localName)
}
}