private def unparseWithSuppression()

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()
  }