All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gitlab.mvysny.konsumexml.stax.StaxParser.kt Maven / Gradle / Ivy

The newest version!
package com.gitlab.mvysny.konsumexml.stax

import com.gitlab.mvysny.konsumexml.existsClass
import java.io.Closeable
import java.io.InputStream
import java.lang.RuntimeException
import javax.xml.namespace.QName
import javax.xml.stream.XMLStreamException

public enum class StaxEventType {
    StartElement,
    EndElement,
    ProcessingInstruction,
    Characters,
    Comment,
    Space,
    StartDocument,
    EndDocument,
    EntityReference,
    Attribute,
    DTD,
    CData,
    Namespace,
    NotationDeclaration,
    EntityDeclaration
}

/**
 * A low-level STAX Parser API, wrapping the actual STAX XML parser. Currently there are two implementors:
 *
 * * [JavaxXmlStreamStaxParser] using javax.xml.stream API which is present on
 *  regular Java but not on Android
 * * [OrgXmlpullStaxParser] using the org.xmlpull API which is present on Android.
 *
 * [close] closes the underlying parser and releases its resources, but does not
 * need to close the underlying input stream.
 */
public interface StaxParser : Closeable {
    /**
     * Returns the current event type. Only valid after [next] has been
     * called.
     */
    public val eventType: StaxEventType

    /**
     * Return the current location of the processor.
     * If the Location is unknown the processor should return
     * an implementation of Location that returns -1 for the
     * location and null for the publicId and systemId.
     * The location information is only valid after [next] has been
     * called.
     */
    public val location: Location

    /**
     * Returns a QName for the current START_ELEMENT or END_ELEMENT event
     * @return the QName for the current START_ELEMENT or END_ELEMENT event
     * @throws IllegalStateException if this is not a START_ELEMENT or
     * END_ELEMENT
     */
    public val elementName: QName

    /**
     * Returns the qname of the attribute at the provided index
     * @param index the position of the attribute, zero-based, must be 0..[attributeCount]-1
     * @return the QName of the attribute
     * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
     * @throws IllegalArgumentException if index is out-of-bounds.
     */
    public fun getAttributeName(index: Int): QName

    /**
     * Returns the count of attributes on this START_ELEMENT,
     * this method is only valid on a START_ELEMENT or ATTRIBUTE.  This
     * count excludes namespace definitions.  Attribute indices are
     * zero-based.
     * @return returns the number of attributes
     * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
     */
    public val attributeCount: Int

    /**
     * Returns the normalized attribute value of the
     * attribute with the namespace and localName
     * @param namespaceURI the namespace of the attribute
     * @param localName the local name of the attribute, cannot be null
     * @return returns the value of the attribute , returns null if not found
     * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
     */
    public fun getAttributeValue(namespaceURI: String, localName: String): String?

    /**
     * Returns the current value of the parse event as a string,
     * this returns the string value of a CHARACTERS event,
     * returns the value of a COMMENT, the replacement value
     * for an ENTITY_REFERENCE, the string value of a CDATA section,
     * the string value for a SPACE event,
     * or the String value of the internal subset of the DTD.
     * If an ENTITY_REFERENCE has been resolved, any character data
     * will be reported as CHARACTERS events.
     * @return the current text or null
     * @throws java.lang.IllegalStateException if this state is not
     * a valid text state.
     */
    public val text: String?

    /**
     * Get next parsing event - a processor may return all contiguous
     * character data in a single chunk, or it may split it into several chunks.
     * If the property javax.xml.stream.isCoalescing is set to true
     * element content must be coalesced and only one CHARACTERS event
     * must be returned for contiguous element content or
     * CDATA Sections.
     *
     * By default entity references must be
     * expanded and reported transparently to the application.
     * An exception will be thrown if an entity reference cannot be expanded.
     * If element content is empty (i.e. content is "") then no CHARACTERS event will be reported.
     *
     * Given the following XML:
     * ```xml
     * content textHello]]>other content

* ``` * The behavior of calling next() when being on foo will be: * 1. the comment (COMMENT) * 2. then the characters section (CHARACTERS) * 3. then the CDATA section (another CHARACTERS) * 4. then the next characters section (another CHARACTERS) * 5. then the END_ELEMENT * * * **NOTE:** empty element (such as ``) will be reported * with two separate events: START_ELEMENT, END_ELEMENT - This preserves * parsing equivalency of empty element to ``. * * This method will throw an IllegalStateException if it is called after [hasNext] returns false. * * @throws NoSuchElementException if this is called when hasNext() returns false * @throws XMLStreamException if there is an error processing the underlying XML source */ public fun next() /** * Returns true if there are more parsing events and false * if there are no more events. * * This method will return false if the current state of the XMLStreamReader is * END_DOCUMENT. * @return true if there are more events, false otherwise * @throws XMLStreamException if there is a fatal error detecting the next state */ public fun hasNext(): Boolean } /** * A location in the XML file being parsed. * @property lineNumber Return the line number where the current event ends, * returns -1 if none is available. 1-based (first row has the number of 1). * @property columnNumber Return the column number where the current event ends, * returns -1 if none is available. 1-based (first column has the number of 1). * @property characterOffset Return the byte or character offset into the input source this location * is pointing to. If the input source is a file or a byte stream then * this is the byte offset into that stream, but if the input source is * a character media then the offset is the character offset. * Returns -1 if there is no offset available. * @property publicId Returns the public ID of the XML or null if not available. * @property systemId the system ID, or null if not available */ public data class Location(val lineNumber: Int, val columnNumber: Int, val characterOffset: Int, val publicId: String?, val systemId: String?) { override fun toString(): String = "line $lineNumber column $columnNumber at $publicId $systemId" } /** * Produces XML parser for given XML `stream`. The `systemId` optionally refers to the XML - * it's either a URL or an absolute path in a local filesystem. */ public typealias XmlParserFactory = (stream: InputStream, systemId: String?) -> StaxParser /** * Produces [StaxParser]s. Automatically chooses the proper implementation depending on the platform we're running on. * See [isJavaxXmlStreamAvailable] and [isOrgXmlpullAvailable] for more details. */ public object StaxParserFactory { /** * Checks whether the `javax.xml.stream` API is available. It is not available on Androids. */ public val isJavaxXmlStreamAvailable: Boolean = existsClass("javax.xml.stream.XMLStreamReader") && // also test for javax.xml.stream.FactoryFinder; should fix https://gitlab.com/mvysny/konsume-xml/-/issues/17 existsClass("javax.xml.stream.FactoryFinder") /** * Checks whether the `org.xmlpull` API is available. It is typically available on Androids 8+ and also on JDK * when you include either `org.ogce:xpp3:1.1.6`, `kxml2` or similar libraries. */ public val isOrgXmlpullAvailable: Boolean = existsClass("org.xmlpull.v1.XmlPullParser") /** * The default [StaxParser] factory. It tries to use `javax.xml.stream` since that one is also able to expand * entity references; if it isn't available then `org.xmlpull` is used. */ public val defaultFactory: XmlParserFactory = { stream, systemId -> when { isJavaxXmlStreamAvailable -> JavaxXmlStreamStaxParser.create(stream, systemId) isOrgXmlpullAvailable -> OrgXmlpullStaxParser.create(stream, systemId) else -> throw RuntimeException("No StaX library is available") } } /** * The factory producing [StaxParser]. By default set to [defaultFactory]. * * Change this to use any [StaxParser] implementation you need, or to provide different parser configuration etc. */ public var factory: XmlParserFactory = defaultFactory /** * Calls [factory] to create new [StaxParser]. The parser will read given [stream]; if you know the origin (absolute * name of the file, http source url), pass it as [systemId]. * * Closing the parser will not close the stream, you need to close the stream separately. */ public fun create(stream: InputStream, systemId: String? = null): StaxParser = factory(stream, systemId) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy