in daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala [74:408]
override protected def parse(pstate: PState): Unit = {
pstate.mpstate.groupIndexStack.push(1L)
try {
val children = childParsers
var scpIndex = 0
val limit = children.length
var resultOfTry: ParseAttemptStatus = ParseAttemptStatus.Uninitialized
val infosetIndexStart = pstate.infoset.asInstanceOf[DIComplex].numChildren
if (!isOrdered) {
// If this is an unordered sequence, upon completion of parsing all the
// elements we will reorder the elements into schema definition order.
// This means that we cannot let the infoset walker walk any of the
// elements while we are parsing because their event order might change.
// To ensure we don't walk, add a block on the parent of the infoset
// elements. The infoset walker will inspect this to see if it should
// walk any children. We'll remove this block once the unordered sequence
// is complete.
pstate.infoset.infosetWalkerBlockCount += 1
}
/**
* On exit from the sequence loop, if the last thing was Missing, we
* want to look back one prior to see if that followed a EmptyRep or AbsentRep,
* so that we can implement the check for trailingEmptyStrict
*/
var priorResultOfTry: ParseAttemptStatus = ParseAttemptStatus.Uninitialized
var child: SequenceChildParser = null
var isDone = false
//
// This loop iterates over the children terms of the sequence
//
while (!isDone && (scpIndex < limit) && (pstate.processorStatus eq Success)) {
// keep track of the current last child node. If the last child changes
// while parsing, we know a new child was added in this loop
child = children(scpIndex).asInstanceOf[SequenceChildParser]
child match {
case parser: RepeatingChildParser if isOrdered => {
//
// The sequence child is an array/repeating element (or optional
// element as the runtime doesn't distinguish them) of an ordered
// sequence. Unordred sequences are treated as scalars below.
//
val min = parser.minRepeats(pstate)
val max = parser.maxRepeats(pstate)
val isBounded = parser.isBoundedMax
val erd = parser.trd.asInstanceOf[ElementRuntimeData]
parser.startArray(pstate)
//
// This case for array/optionals where the number of occurences is
// determined by speculative parsing. OCK=implicit with min/maxOccurs
// different, or OCK=parsed.
//
priorResultOfTry = resultOfTry
resultOfTry = ParseAttemptStatus.Uninitialized
var ais: ArrayIndexStatus = ArrayIndexStatus.Uninitialized
while (
(ais ne Done) && { // check ais for Done in case it was assigned
ais = parser.arrayIndexStatus(min, max, pstate)
(pstate.isSuccess) && (ais ne Done) // check ais for done from min/max computation
}
) {
val roStatus = ais.asInstanceOf[RequiredOptionalStatus]
val priorPos = pstate.bitPos0b
{
//
// Note: Performance - counting on Scala compiler to optimize away
// this 2-tuple to avoid allocation in the inner loop here.
//
val (nextAIS, nextResultOfTry) = parseOneInstance(parser, pstate, roStatus)
ais = nextAIS
priorResultOfTry = resultOfTry
resultOfTry = nextResultOfTry
}
val currentPos = pstate.bitPos0b
if (
pstate.isSuccess && !isBounded && (resultOfTry match {
case ParseAttemptStatus.AbsentRep => true
case _: ParseAttemptStatus.SuccessParseAttemptStatus => true
case _ => false
})
) {
//
// result of try could be missing if we just ended an array
// by speculation.
//
// result of try could also be absent if we just ended a group
// by not finding a separator
//
ais = checkForwardProgress(pstate, parser, currentPos, priorPos, ais)
}
//
// advance array position.
// Done unconditionally, as some failures get converted into successes
//
// If ultimately this is a real failure, then nothing cares about this, it is
// about to get popped/cleared anyway.
//
if (ais ne Done) {
pstate.mpstate.moveOverOneArrayIterationIndexOnly()
// If the emptyElementParsePolicy is set to treatAsAbsent, don't
// increment the occursIndex if the element is absent
if (resultOfTry != AbsentRep) {
pstate.mpstate.moveOverOneOccursIndexOnly()
}
}
if (
currentPos > priorPos ||
((resultOfTry eq AbsentRep) && pstate.isSuccess) ||
resultOfTry.isInstanceOf[SuccessParseAttemptStatus]
) {
// If we consumed some bits, then we moved past something, and so
// we're definitely not first in the group any more.
//
// Or if AbsentRep, that means we sucessfully parsed a
// zero-length separated element. Even though this element may
// not end up in the infoset due to separator suppression, we
// must still increment the group index since that is used to
// determine when to consume infix separators
//
// Otherwise, the parse failed or nothing was consumed and we do
// should not increment the group index.
pstate.mpstate.moveOverOneGroupIndexOnly()
}
val newLastChildNode = pstate.infoset.maybeLastChild
if (newLastChildNode.isDefined) {
// We have potentially added a child to to this complex during
// this array loop.
//
// If the new child is a DIArray, we know this DIArray has at
// least one element, but we don't know if we actually added a
// new one in this loop or not. So just get the last array
// element and set it as final anyways.
//
// If it's not a DIArray, that means it's just an optional
// simple/complex and that will get set final below where all
// other non-array elements get set as final.
val lastChild = newLastChildNode.get
if (lastChild.isArray) {
// not simple or complex, must be an array
val lastArrayElem = lastChild.maybeLastChild
if (lastArrayElem.isDefined) {
lastArrayElem.get.isFinal = true
pstate.walker.walk()
}
}
}
} // end while for each repeat
parser.endArray(pstate)
parser.arrayCompleteChecks(pstate, resultOfTry, priorResultOfTry)
} // end match case RepeatingChildParser
case nonRepresentedParser: NonRepresentedSequenceChildParser => {
// should never have non-represented children in unordered sequences
Assert.invariant(isOrdered)
nonRepresentedParser.parseOne(pstate, null)
// don't need to digest result from this. All
// information about success/failure is in the pstate.
//
// We do NOT move over the group index state for non-represented things.
}
// This case for scalar parsers. This includes both scalar elements,
// and model group terms (choices, or sequences that are children of a
// sequence). A model group term is considered scalar in that they
// cannot be repeating at all in DFDL v1.0.
//
// This case is also used for all children of unordered sequences. In
// that case, we repeatedly attempt to parse all the children (starting
// over on success), until all children fail or if discriminated
// content causes us to bail early.
case scalarParser => {
val diagnosticsBeforeAttempt = pstate.diagnostics
val roStatus =
if (isOrdered)
scalarParser.maybeStaticRequiredOptionalStatus.get
else {
// Treat unordered sequence children as if they are required.
// This way if they fail, we will simply backtrack and try the
// next child
RequiredOptionalStatus.Required
}
val (_, nextResultOfTry) = parseOneInstance(scalarParser, pstate, roStatus)
priorResultOfTry = resultOfTry
resultOfTry = nextResultOfTry
resultOfTry match {
case AbsentRep => {
// a scalar element, or a model group is absent. That means no separator
// was found for it.
//
// That means were at the end of the representation of this sequence,
// This is only returned as resultOfTry if it is
// OK for us to act on it. I.e., we know that the situation is
// Positional trailing, with a group that can have zero-length representation.
// and no separator was found for it.
//
// So we mask the failure, and exit the sequence successfully
pstate.setSuccess()
isDone = true
// If we're masking the failure, we don't want the error dianostics
// to flow up. Restore the diagnostics from before the parse
// attempt
pstate.diagnostics = diagnosticsBeforeAttempt
}
// This child alternative of an unordered sequence failed, and that
// failure occurred after a discriminator within that child
// evaluated to true. This means that this unordered sequence has
// failed. We set isDone to true so we do not attempt anymore
// children of this unordered sequence. Additionally, that failure
// is still in the PState, which will cause us to backtrack to the
// nearest unresolved point of uncertainty.
case UnorderedSeqDiscriminatedFailure => isDone = true
// We failed to parse a single instance of an unordered sequence
// element, and we did not hit a discriminator. parseOneInstance
// will have backtracked to before this instance was attempted, so
// we can just try to parse the next child. We do not need to do
// anything special, the end of this loop will increment to the
// next child.
case _: FailedParseAttemptStatus if (!isOrdered) => // no-op
case _ => {
if (isOrdered) {
// Successfully parsed a scalar ordered sequence element,
// nothing to do. We'll increment scpIndex before looping
// back around and try parsing the next sequence child
} else {
// Successfully parsed an unordered sequence child. We want to
// try parsing the unordered sequence children again from the
// beginning, so we set the index to -1 so it is incremented
// back to zero at the end of the while loop
scpIndex = -1
}
// We successfully parsed something, so we must increment the group
// index
pstate.mpstate.moveOverOneGroupIndexOnly()
}
}
} // end case scalarParser
} // end match case parser
// now that we have finished parsing a single instance of this sequence,
// we need to potentially set things as final, get the last child to
// determine if it changed from the saved last child, which lets us know
// if a new child was actually added.
val newLastChildNode = pstate.infoset.maybeLastChild
if (!isOrdered) {
// In the special case of unordered sequences with arrays, we do not
// use the RepatingChildParser. Instead we parse on instance at a time
// in this loop. So array elements aren't set final above like normal
// arrays are.
//
// So if the last child node is a DIArray, we must set new array
// elements as final here. We can't know if we actually added a new
// DIArray element or not, so just set the last one as final
// regardless.
//
// Note that we do not need to do a null check because in an unordered
// sequence we are blocking, so we can't possibly walk/free any of
// these newly added elements.
if (newLastChildNode.isDefined && newLastChildNode.get.isArray) {
// we have a new last child, and it's not simple or complex, so must
// be an array. Set its last child final
newLastChildNode.get.maybeLastChild.get.isFinal = true
}
}
// We finished parsing one part of a sequence, which could either be an
// array, simple, or complex. We aren't sure if we actually added a new
// element or not, but in case we did, mark the last node as final.
//
// Additionally, if this is an ordered sequence, try to walk the infoset
// to output events for this potentially new element. If this is an
// unordered sequence, walking is unnecessary. This is because we may
// need to reorder the infoset once this unordered sequence is complete
// (via flattenAndValidateChildNodes below) and cannot walk until that
// happens. To ensure we don't walk even if a child parser tries to call
// walk() we incremented infosetWalkerBlockCount at the beginning of this
// function, so the walker is effectively blocked from making any
// progress. So we don't even bother calling walk() in this case.
if (newLastChildNode.isDefined) {
newLastChildNode.get.isFinal = true
if (isOrdered) pstate.walker.walk()
}
scpIndex += 1
} // end while for each sequence child parser
if (!isOrdered) {
// we are unordered, so we need to reorder the new children into schema
// definition order, flatten arrays, and validate
val infoset = pstate.infoset.asInstanceOf[DIComplex]
infoset.flattenAndValidateChildNodes(pstate, infosetIndexStart)
// now that we have flattened children, we can decrement the block count
// that we incremented above. This will allow the infoset walker to walk
// into the new children that are now in the correct order.
pstate.infoset.infosetWalkerBlockCount -= 1
// we've unblocked the unordered sequence, try walking to output
// everything we've created
pstate.walker.walk()
}
if (child ne null) child.sequenceCompleteChecks(pstate, resultOfTry, priorResultOfTry)
()
} finally {
pstate.mpstate.groupIndexStack.pop()
}
}