in daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala [301:548]
private def unparseWithSuppression(state: UState): Unit = {
state.groupIndexStack.push(1L) // one-based indexing
var index = 0
var doUnparser = false
val limit = childUnparsers.length
lazy val trailingSuspendedOps = Buffer[SuppressableSeparatorUnparserSuspendableOperation]()
while (index < limit) {
val childUnparser = childUnparsers(index)
val trd = childUnparser.trd
state.pushTRD(
trd
) // because we inspect before we call the unparse1 for the child unparser.
val zlDetector = childUnparser.zeroLengthDetector
childUnparser match {
case unparser: RepOrderedSeparatedSequenceChildUnparser => {
state.arrayIterationIndexStack.push(1L)
state.occursIndexStack.push(1L)
val erd = unparser.erd
var numOccurrences = 0
val maxReps = unparser.maxRepeats(state)
//
// The number of occurrances we unparse is always exactly driven
// by the number of infoset events for the repeating/optional element.
//
// For RepUnparser - array/optional case - in all cases we should get a
// startArray event. That is, defaulting of required array elements
// (up to minOccurs) happens elsewhere, and we get events for all of those
// here.
//
// If we don't get any array element events, then the element must be
// entirely optional, so we get no events for it at all.
//
if (state.inspect) {
val ev = state.inspectAccessor
val isArr = erd.isArray
if (ev.isStart && (isArr || erd.isOptional)) {
if (ev.erd eq erd) {
//
// Note: leaving in some of these println, since debugger for unparsing is so inadequate currently.
// This is the only way to figure out what is going on.
//
// System.err.println("Starting unparse of array/opt %s. Array Index Stack is: %s".format(
// erd.namedQName, state.arrayIndexStack))
//
// StartArray for this unparser's array element
//
unparser.startArrayOrOptional(state)
while ({
doUnparser = unparser.shouldDoUnparser(unparser, state)
doUnparser
}) {
//
// These are so we can check invariants on these stacks being
// pushed and popped reliably, and incremented only once
//
val arrayIterationIndexBefore = state.arrayIterationPos
val arrayIterationIndexStackDepthBefore =
state.arrayIterationIndexStack.length
val occursIndexBefore = state.occursPos
val occursIndexStackDepthBefore = state.occursIndexStack.length
val groupIndexBefore = state.groupPos
val groupIndexStackDepthBefore = state.groupIndexStack.length
Assert.invariant(
erd.isRepresented
) // since this is an array, can't have inputValueCalc
if (isArr)
if (state.dataProc.isDefined)
state.dataProc.get.beforeRepetition(state, this)
if (
unparser.isKnownStaticallyNotToSuppressSeparator || {
val isKnownNonZeroLength =
zlDetector.isKnownNonZeroLength(state.inspectAccessor.info.element)
isKnownNonZeroLength
}
) {
unparseOne(unparser, erd, state)
} else {
unparseOneWithSuppression(
unparser,
erd,
state,
trailingSuspendedOps,
onlySeparatorFlag = false
)
}
numOccurrences += 1
Assert.invariant(
state.arrayIterationIndexStack.length == arrayIterationIndexStackDepthBefore
)
state.moveOverOneArrayIterationIndexOnly()
Assert.invariant(state.arrayIterationPos == arrayIterationIndexBefore + 1)
Assert.invariant(state.occursIndexStack.length == occursIndexStackDepthBefore)
state.moveOverOneOccursIndexOnly()
Assert.invariant(state.occursPos == occursIndexBefore + 1)
Assert.invariant(state.groupIndexStack.length == groupIndexStackDepthBefore)
state.moveOverOneGroupIndexOnly() // array elements are always represented.
Assert.invariant(state.groupPos == groupIndexBefore + 1)
if (isArr)
if (state.dataProc.isDefined)
state.dataProc.get.afterRepetition(state, this)
}
numOccurrences = unparsePositionallyRequiredSeps(
unparser,
erd,
state,
numOccurrences,
trailingSuspendedOps
)
unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
state,
unparser,
numOccurrences,
maxReps,
state.arrayIterationPos - 1
)
unparser.endArrayOrOptional(erd, state)
} else {
//
// start array for some other array. Not this one.
//
Assert.invariant(erd.minOccurs == 0L)
numOccurrences = unparsePositionallyRequiredSeps(
unparser,
erd,
state,
numOccurrences,
trailingSuspendedOps
)
}
} else if (ev.isStart) {
Assert.invariant(!ev.erd.isArray && !erd.isOptional)
val eventNQN = ev.erd.namedQName
Assert.invariant(eventNQN != erd.namedQName)
//
// start of scalar.
// That has to be for a different element later in the sequence
// since this one has a RepUnparser (i.e., is NOT scalar)
//
numOccurrences = unparsePositionallyRequiredSeps(
unparser,
erd,
state,
numOccurrences,
trailingSuspendedOps
)
} else {
Assert.invariant(ev.isEnd && ev.erd.isComplexType)
unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
state,
unparser,
numOccurrences,
maxReps,
0
)
numOccurrences = unparsePositionallyRequiredSeps(
unparser,
erd,
state,
numOccurrences,
trailingSuspendedOps
)
}
} else {
// no event (state.inspect returned false)
Assert.invariantFailed("No event for unparsing.")
}
state.arrayIterationIndexStack.pop()
state.occursIndexStack.pop()
}
case scalarUnparser =>
trd match {
case erd: ElementRuntimeData => {
// scalar element case. These always get associated separator (if represented)
unparseOne(scalarUnparser, trd, state) // handles case of non-represented.
if (erd.isRepresented)
state.moveOverOneGroupIndexOnly()
}
case mgrd: ModelGroupRuntimeData => {
//
// There are cases where we suppress the separator associated with a model group child
//
// The model group must have no required syntax (initiator/terminator nor alignment)
// and all optional children (none of which can be present if there are unsuppressed separators)
//
// The model group must be zero-length
// The SSP must be AnyEmpty or
// The SSP must be TrailingEmpty | TrailingEmptyStrict, AND the model group must be
// potentially trailing AND actually trailing.
//
// Most of the above is static information. Only whether the length is nonZero or zero, and
// whether it is actually trailing are run-time concepts.
//
if (scalarUnparser.isKnownStaticallyNotToSuppressSeparator) {
unparseOne(scalarUnparser, trd, state)
} else {
unparseOneWithSuppression(
scalarUnparser,
trd,
state,
trailingSuspendedOps,
onlySeparatorFlag = false
)
}
state.moveOverOneGroupIndexOnly()
}
}
}
state.popTRD(trd)
index += 1
}
ssp match {
case TrailingEmpty | TrailingEmptyStrict => {
//
// For trailing empty suppression, things have to be actually trailing in the sequence.
// By setting the after-state to the complete end of the sequence, we insure that we suppress
// separators only if there is nothing at all after them.
//
// Note: Quadratic behavior here.
// When determining if trailing, each suspended separator will examine a list of length N, where
// N is number of children in the list. It will examine them only to determine if the entries are
// zero-length or not. But these chains could in principle have shared structure so that there
// would, in principle, be only one chain length N, not Sum(for i from 1 to N)of(N - i), whichis O(n^2).
//
// In practice, these chains are short (separator suppression seldom applies to long arrays, usually to
// optional fields near the end of a record. So the above may simply not matter.
//
for (suspendedOp <- trailingSuspendedOps.toSeq) {
suspendedOp.captureStateAtEndOfPotentiallyZeroLengthRegionFollowingTheSeparator(state)
}
}
case _ => // do nothing
}
state.groupIndexStack.pop()
}