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

com.gitlab.mvysny.konsumexml.StaxReader.kt Maven / Gradle / Ivy

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

import com.gitlab.mvysny.konsumexml.stax.StaxEventType
import com.gitlab.mvysny.konsumexml.stax.StaxParser
import java.io.Closeable
import java.io.InputStream
import javax.xml.stream.XMLStreamException
import javax.xml.stream.XMLStreamReader
import javax.xml.stream.events.XMLEvent

/**
 * Wraps [stax] and provides a higher-level API for iterating XML events, with the following properties:
 *
 * These events are always ignored and are skipped over silently:
 * * [XMLEvent.ENTITY_DECLARATION]
 * * [XMLEvent.SPACE]
 * * [XMLEvent.COMMENT]
 * * [XMLEvent.DTD]
 * * [XMLEvent.START_DOCUMENT]
 * * [XMLEvent.PROCESSING_INSTRUCTION]
 *
 * These events cause konsumer to fail and throw [KonsumerException]:
 * * [XMLEvent.ENTITY_REFERENCE] since we expect all entities to be expanded.
 * * [XMLEvent.NAMESPACE] can't really happen in well-formed XML
 * * [XMLEvent.ATTRIBUTE] can't really happen in well-formed XML; attributes are reported as part of [XMLEvent.START_ELEMENT]
 * * [XMLEvent.NOTATION_DECLARATION]
 *
 * This reader will therefore only return events of type:
 * * [XMLEvent.CDATA], [XMLEvent.CHARACTERS]
 * * [XMLEvent.END_DOCUMENT]
 * * [XMLEvent.START_ELEMENT]
 * * [XMLEvent.END_ELEMENT]
 *
 * It is also possible to "push back" current event, so that [hasNext] returns true and [next] returns the current event, unless
 * the current event is [XMLEvent.END_DOCUMENT], in that case [hasNext] will return false.
 *
 * All methods throw [KonsumerException] on any unexpected contents (such as unexpected attribute nodes),
 * [javax.xml.stream.XMLStreamException] on any I/O errors and XML parsing errors.
 *
 * Not thread-safe.
 *
 * Closing this reader will close both [stax] parser and [inputStreamToClose].
 *
 * The client is expected to read additional information about the event from [stax], e.g. [StaxParser.text].
 * @property stax the lower-level API, wrapping the actual STAX Parser directly.
 */
public class StaxReader(
        public val stax: StaxParser,
        private val inputStreamToClose: InputStream? = null
) : Closeable, Iterator {

    private var pushBack = false

    private fun skipIgnoredEvents() {
        while(true) {
            when (stax.eventType) {
                StaxEventType.EntityDeclaration, StaxEventType.Space, StaxEventType.Comment, StaxEventType.DTD,
                StaxEventType.StartDocument, StaxEventType.ProcessingInstruction -> {
                    // ignore
                    stax.next()
                }
                StaxEventType.Attribute -> throw KonsumerException(stax.location, null, "unexpected ATTRIBUTE")
                StaxEventType.EntityReference -> throw KonsumerException(stax.location, null, "Expected entities to be expanded")
                StaxEventType.Namespace -> throw KonsumerException(stax.location, null, "unexpected NAMESPACE")
                StaxEventType.NotationDeclaration -> throw KonsumerException(stax.location, null, "unexpected NOTATION_DECLARATION")
                else -> return
            }
        }
    }

    /**
     * Pushes back current event, so that [hasNext] returns true and [next] returns the current event, unless
     * the current event is [XMLEvent.END_DOCUMENT], in that case this function does nothing.
     *
     * It's not possible to push back multiple events since it's not possible to push back [stax] itself and the client
     * reads current stuff from [stax].
     */
    public fun pushBack() {
        check(!pushBack) { "cannot push back more than 1 event" }
        if (stax.hasNext()) {
            pushBack = true
        }
    }

    /**
     * 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
     */
    override fun hasNext(): Boolean = pushBack || stax.hasNext()

    /**
     * See [XMLStreamReader.next] for details. Only returns the following events: [XMLEvent.CDATA], [XMLEvent.CHARACTERS],
     * [XMLEvent.END_DOCUMENT], [XMLEvent.START_ELEMENT], [XMLEvent.END_ELEMENT]
     */
    override fun next(): StaxEventType {
        if (!pushBack) {
            stax.next()
        } else {
            pushBack = false
        }
        skipIgnoredEvents()
        return stax.eventType
    }

    override fun close() {
        stax.close()
        inputStreamToClose?.close()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy