package scales.xml.parser.pull
import javax.xml.stream._
import scales.utils._
import scales.xml.parser._
import strategies.{MemoryOptimisationStrategy, OptimisationToken}
import scales.xml.impl.{FromParser, IsFromParser}
import scales.xml.{
PullType,
CData,
Comment,
Elem,
PI,
Text,
EndElem,
Misc,
Attributes,
ScalesXml,
Prolog,
EndMisc,
DTD,
Declaration,
AttributeQName,
DocLike,
Xml11, Xml10,
emptyAttributes
}
trait XmlPull extends Iterator[PullType] with DocLike {
type Token <: OptimisationToken
implicit val weAreInAParser : FromParser = IsFromParser
import ScalesXml.defaultVersion
protected[xml] val parser: XMLStreamReader
protected[xml] val resourceCloser: () => Unit
protected[xml] def internalClose {}
protected[xml] val strategy : MemoryOptimisationStrategy[Token]
protected[xml] val token : Token
private[xml] var current: PullType = null
private[xml] var vprolog: Prolog = Prolog()
private[xml] var emisc: EndMisc = EndMisc()
def prolog = vprolog
def end = emisc
protected[xml] var depth = -1
protected[xml] var haveRoot = false
protected[xml] def start = {
while (depth == -1) {
current = pumpEvent
if (current.isLeft && (current.left.get eq PullUtils.dtdDummy)) {
vprolog = vprolog.copy(dtd = Some(
DTD("", "", "")
))
}
if (depth == -1) {
vprolog = vprolog.copy(misc = vprolog.misc :+ PullUtils.getMisc(current, "prolog"))
}
}
}
final val it = this: Iterator[PullType]
def hasNext = current ne null
def next: PullType = {
val c = current
if (current eq null) throw new NoSuchElementException("The end of the document has been reached")
current = pumpEvent
if ((current ne null) && current.isRight && depth == -1) {
var ends = pumpEvent
while (ends ne null) {
emisc = emisc.copy(misc = emisc.misc :+ PullUtils.getMisc(ends, "document end Misc"))
ends = pumpEvent
}
}
c
}
protected[xml] def pumpEvent: PullType = {
if (!parser.hasNext) return null
var nextEvent = XMLStreamConstants.END_DOCUMENT
try {
val (event, num, odepth, oprolog) = PullUtils.pumpEvent(parser, strategy, token, vprolog, depth){_ => pumpEvent}
nextEvent = num
depth = odepth
vprolog = oprolog
event
} finally {
if (nextEvent == XMLStreamConstants.END_DOCUMENT) {
internalClose
}
}
}
}
object PullUtils {
private[xml] val dtdDummy = PI("onlyforme", "init")
implicit val weAreInAParser : FromParser = IsFromParser
def getMisc(c: PullType, in: String): Misc =
c.fold[Misc](e => e match {
case ev: Comment => Left(ev)
case ev: PI => Right(ev)
case _ => error("Got an event (" + e + ") that should not be in the " + in)
}, f => error("End element found in " + in + " " + c))
def getAttributes[Token <: OptimisationToken]( parser: XMLStreamReader, strategy : MemoryOptimisationStrategy[Token], token : Token ): Attributes = {
import ScalesXml.toQName
val count = parser.getAttributeCount()
val ar = strategy.attributeArray(count, token)
var i = 0
while (i < count) {
val jqname = parser.getAttributeName(i)
val pre = jqname.getPrefix
val local = jqname.getLocalPart
val aqname: AttributeQName =
if ((pre eq null) || (pre.length == 0))
strategy.noNamespaceQName(local, token)
else
strategy.prefixedQName(local, jqname.getNamespaceURI, pre, token)
ar.update(i,
strategy.attribute(aqname,
parser.getAttributeValue(i), token)
)
i += 1
}
scales.xml.impl.AttributeSet.unsafe(ar, count)
}
def getNamespaces[Token <: OptimisationToken]( parser: XMLStreamReader, strategy : MemoryOptimisationStrategy[Token], token : Token ): Map[String, String] = {
val count = parser.getNamespaceCount()
var i = 0
var map = Map[String, String]()
while (i < count) {
val pre = parser.getNamespacePrefix(i)
if (pre ne null) {
map += (pre -> parser.getNamespaceURI(i))
}
i += 1
}
map
}
def getElemQName[Token <: OptimisationToken]( parser: XMLStreamReader, strategy : MemoryOptimisationStrategy[Token], token : Token ) = {
val ns = parser.getNamespaceURI
val pre = parser.getPrefix
val local = parser.getLocalName
if ((pre eq null) || (pre.length == 0)) {
if ((ns eq null) || (ns.length == 0))
strategy.noNamespaceQName(local, token)
else
strategy.unprefixedQName(local, ns, token)
} else
strategy.prefixedQName(local, ns, pre, token)
}
def pumpEvent[Token <: OptimisationToken]( parser: XMLStreamReader, strategy : MemoryOptimisationStrategy[Token], token : Token, prolog : Prolog, idepth : Int )(otherEventHandler : Int => PullType) : (PullType, Int, Int, Prolog) = {
var depth = idepth
var vprolog = prolog
var nextEvent = XMLStreamConstants.END_DOCUMENT
nextEvent = parser.next
val event: PullType = nextEvent match {
case XMLStreamConstants.START_ELEMENT =>
depth += 1
strategy.elem(getElemQName(parser, strategy, token), getAttributes(parser, strategy, token), getNamespaces(parser, strategy, token), token)
case XMLStreamConstants.END_ELEMENT => depth -= 1; EndElem(getElemQName(parser, strategy, token), getNamespaces(parser, strategy, token))
case XMLStreamConstants.CHARACTERS => Text(parser.getText)
case XMLStreamConstants.CDATA => CData(parser.getText)
case XMLStreamConstants.COMMENT => Comment(parser.getText)
case XMLStreamConstants.PROCESSING_INSTRUCTION => PI(parser.getPITarget(), parser.getPIData())
case XMLStreamConstants.SPACE => Text(parser.getText)
case XMLStreamConstants.START_DOCUMENT => {
val ec = parser.getCharacterEncodingScheme()
vprolog = vprolog.copy(decl = Declaration(
version = if (parser.getVersion() == "1.1")
Xml11 else Xml10,
encoding = if (ec eq null) defaultCharset else java.nio.charset.Charset.forName(ec),
standalone = parser.isStandalone()))
val (nev, nex, nde, vvp) = pumpEvent(parser, strategy, token, vprolog, depth)(otherEventHandler)
nextEvent = nex
depth = nde
vprolog = vvp
nev
}
case XMLStreamConstants.DTD => dtdDummy
case _ => otherEventHandler(nextEvent)
}
(event, nextEvent, depth, vprolog)
}
}