This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/main by this push:
     new 1c582cfc7 Fix separatorSuppressionPolicy="never"
1c582cfc7 is described below

commit 1c582cfc73a8333788a6501243eff1f944ba757f
Author: Steve Lawrence <[email protected]>
AuthorDate: Thu May 25 13:26:52 2023 -0400

    Fix separatorSuppressionPolicy="never"
    
    Currently, parsing with occursCountKind "implicit" and separator
    suppression policy "never" behaves similar to occursCountKind "fixed",
    in that it requires maxOccurs instances, but does not allow for absent
    representations. This is incorrect--SSP "never" only requires non-absent
    instance up to minOccurs, and afterwards allows absent occurrences as
    long as maxOccurs separators are found.
    
    To fix this, this modifies parsing to use the same logic as
    trailingEmpty and trailingEmptyStrict, but adds additional logic at the
    end of parsing a repetition to ensure that there were no errors or
    missing separators. This is done by using the same Rep parser, but
    adding a new PositionalNever flag to differentiate the logic. A new
    arrayCompleteChecks function is used to check if this flag is set and
    create a parse error if errors or missing separators occurred. This also
    renames finalChecks to sequenceCompleteChecks to differentiate it from
    arrayCompleteChecks.
    
    Currently, the unparsing logic is also broken for occursCountKind
    "implicit" and separator suppression policy "never" if there are no
    instances of an array/optional element. This is because the logic always
    looks for a "start" event, which may not exist if minOccurs is zero and
    all instances have an absent representation.
    
    To fix this, this modifies unparseWithNoSuppression to not require
    "start" events and relies on the existing shouldDoUnparse function to
    determine if the current event is the right one to unparse. The existing
    logic will then output any missing separators as needed, with a slight
    tweak to handle an off-by-one error for infix separators.
    
    DAFFODIL-2802, DAFFODIL-2499
---
 .../core/grammar/SequenceGrammarMixin.scala        |   2 +-
 .../core/grammar/primitives/SequenceChild.scala    |   8 +-
 .../runtime1/SeparatedSequenceUnparsers.scala      | 179 +++++++++-----------
 .../SeparatedSequenceChildParseResultHelper.scala  |  26 ++-
 .../parsers/SeparatedSequenceParsers.scala         |  11 +-
 .../processors/parsers/SequenceChildBases.scala    |  11 +-
 .../parsers/SequenceChildParseResultHelper.scala   |  11 --
 .../processors/parsers/SequenceParserBases.scala   |   3 +-
 .../section07/escapeScheme/escapeScenarios.tdml    |   3 -
 .../sequence_groups/SequenceGroupDelimiters.tdml   |  98 ++++++++++-
 .../org/apache/daffodil/usertests/SepTests.tdml    | 188 ++++++++++++++++++++-
 .../sequence_groups/TestSequenceGroups.scala       |  21 +++
 .../apache/daffodil/usertests/TestSepTests.scala   |  16 +-
 13 files changed, 427 insertions(+), 150 deletions(-)

diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
index 1ad6817e6..5d8e25f70 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
@@ -176,7 +176,7 @@ trait SequenceGrammarMixin extends GrammarMixin with 
SequenceTermRuntime1Mixin {
           "separatorSuppressionPolicy='never' with occursCountKind='implicit' 
requires bounded maxOccurs.",
         )
       case (e: EB, Ordered__, Never______, Implicit__, ___, max) =>
-        new RepOrderedExactlyNSequenceChild(this, e, groupIndex, max)
+        new RepOrderedWithMinMaxSequenceChild(this, e, groupIndex)
       case (
             e: EB,
             Ordered__,
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
index 8ab3d71a3..2dadde201 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
@@ -153,7 +153,7 @@ abstract class SequenceChild(protected val sq: 
SequenceTermBase, child: Term, gr
               case (true, false, TrailingEmptyStrict, UNB)
                   if !eb.isLastDeclaredRepresentedInSequence =>
                 Assert.invariantFailed("Should be SDE found elsewhere.")
-              case (true, _, Never, _) => Positional
+              case (true, _, Never, _) => PositionalNever
               case (true, _, AnyEmpty, _) => NonPositional
               case (true, _, TrailingEmpty, _) => PositionalTrailingLax
               case (true, _, TrailingEmptyStrict, _) => 
PositionalTrailingStrict
@@ -465,7 +465,7 @@ class ScalarOrderedSequenceChild(sq: SequenceTermBase, 
term: Term, groupIndex: I
           isModelGroupRepPossiblyZeroLength,
           isModelGroupRepNonZeroLength,
         )
-      case Positional =>
+      case Positional | PositionalNever =>
         new PositionalGroupSeparatedSequenceChildParseResultHelper(
           mgrd,
           sscb,
@@ -509,7 +509,7 @@ class ScalarOrderedSequenceChild(sq: SequenceTermBase, 
term: Term, groupIndex: I
           isEmptyRepZeroLength,
           isEmptyRepNonZeroLength,
         )
-      case Positional =>
+      case Positional | PositionalNever =>
         new PositionalScalarElementSeparatedSequenceChildParseResultHelper(
           sscb,
           erd,
@@ -601,7 +601,7 @@ sealed abstract class RepElementSequenceChild(
           isEmptyRepZeroLength,
           isEmptyRepNonZeroLength,
         )
-      case Positional =>
+      case Positional | PositionalNever =>
         new PositionalRepElementSeparatedSequenceChildParseResultHelper(
           sscb,
           erd,
diff --git 
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
 
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
index d98a9d008..d824a5726 100644
--- 
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
+++ 
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
@@ -610,122 +610,95 @@ class OrderedSeparatedSequenceUnparser(
           state.arrayIterationIndexStack.push(1L)
           state.occursIndexStack.push(1L)
           val erd = unparser.erd
+          Assert.invariant(erd.isArray || erd.isOptional)
+          Assert.invariant(erd.isRepresented) // arrays/optionals cannot have 
inputValueCalc
+
           var numOccurrences = 0
           val maxReps = unparser.maxRepeats(state)
-          // val isBounded = unparser.isBoundedMax // not needed for the 
no-suppression case
 
-          //
-          // 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. If we don't 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) {
-                //
-                // 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(state.inspect)
+          val ev = state.inspectAccessor
+          val isArr = erd.isArray
 
-                  Assert.invariant(
-                    erd.isRepresented,
-                  ) // since this is an array, can't have inputValueCalc
+          // If the event is for this Rep unparser, we need to consume the 
StartArray event
+          if (ev.erd eq erd) {
+            unparser.startArrayOrOptional(state)
+          }
 
-                  if (isArr)
-                    if (state.dataProc.isDefined)
-                      state.dataProc.get.beforeRepetition(state, this)
+          // Unparse each occurrence of this array in the infoset. Note that 
there could be zero
+          // occurrences
+          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
+
+            if (isArr && state.dataProc.isDefined)
+              state.dataProc.get.beforeRepetition(state, this)
+
+            unparseOne(unparser, erd, state)
+            numOccurrences += 1
+            Assert.invariant(
+              state.arrayIterationIndexStack.length == 
arrayIterationIndexStackDepthBefore,
+            )
+            state.moveOverOneArrayIterationIndexOnly()
+            Assert.invariant(state.arrayIterationPos == 
arrayIterationIndexBefore + 1)
 
-                  unparseOne(unparser, erd, state)
-                  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.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)
 
-                  Assert.invariant(state.groupIndexStack.length == 
groupIndexStackDepthBefore)
-                  state.moveOverOneGroupIndexOnly() // array elements are 
always represented.
-                  Assert.invariant(state.groupPos == groupIndexBefore + 1)
+            if (isArr && state.dataProc.isDefined)
+              state.dataProc.get.afterRepetition(state, this)
+          }
 
-                  if (isArr)
-                    if (state.dataProc.isDefined)
-                      state.dataProc.get.afterRepetition(state, this)
-                }
-                //
-                // If not enough occurences in array, we output extra 
separators
-                //
-                if (maxReps > numOccurrences) {
-                  var numExtraSeps = erd.maxOccurs - numOccurrences
-                  while (numExtraSeps > 0) {
-                    unparseJustSeparator(state)
-                    numExtraSeps -= 1
-                  }
-                }
-                unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
-                  state,
-                  unparser,
-                  numOccurrences,
-                  maxReps,
-                  state.arrayIterationPos - 1,
-                )
-                unparser.endArrayOrOptional(erd, state)
+          // If not enough occurrences are in the infoset, we output extra 
separators because
+          // we are unparsing with no suppression
+          if (maxReps > numOccurrences) {
+            var numExtraSeps = {
+              val sepsNeeded = erd.maxOccurs - numOccurrences
+              if ((spos eq Infix) && state.groupPos == 1) {
+                // If separatorPosition is infix and we haven't output 
anything for this sequence
+                // yet, then we need one less extra separator, since the 
separator is skipped
+                // for the first instance of infix separators.
+                sepsNeeded - 1
               } else {
-                //
-                // start array for some other array. Not this one. So we
-                // don't unparse anything here, and we'll go on to the next
-                // sequence child, which hopefully will be a matching array.
-                //
-                Assert.invariant(erd.minOccurs == 0L)
+                sepsNeeded
               }
-            } else if (ev.isStart) {
-              Assert.invariant(!ev.erd.isArray && !erd.isOptional)
-              //
-              // 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)
-              //
-              val eventNQN = ev.erd.namedQName
-              Assert.invariant(eventNQN != erd.namedQName)
-            } else {
-              Assert.invariant(ev.isEnd && ev.erd.isComplexType)
-              unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
-                state,
-                unparser,
-                numOccurrences,
-                maxReps,
-                0,
-              )
             }
-          } else {
-            // no event (state.inspect returned false)
-            Assert.invariantFailed("No event for unparsing.")
+            while (numExtraSeps > 0) {
+              unparseJustSeparator(state)
+              numExtraSeps -= 1
+            }
           }
+
+          unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
+            state,
+            unparser,
+            numOccurrences,
+            maxReps,
+            state.arrayIterationPos - 1,
+          )
+
+          // If the event is for this Rep unparser, we need to consume the 
EndArray event
+          if (ev.erd eq erd) {
+            unparser.endArrayOrOptional(erd, state)
+          }
+
           state.arrayIterationIndexStack.pop()
           state.occursIndexStack.pop()
         }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
index 43494475e..e70103511 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
@@ -62,6 +62,7 @@ object SeparatedSequenceChildBehavior {
   sealed abstract class PositionalLike extends Type
   sealed abstract class PositionalTrailing extends PositionalLike
   case object Positional extends PositionalLike
+  case object PositionalNever extends PositionalLike
   case object PositionalTrailingLax extends PositionalTrailing
   case object PositionalTrailingStrict extends PositionalTrailing
   case object NonPositional extends Type
@@ -94,7 +95,30 @@ trait SeparatedSequenceChildParseResultHelper extends 
SequenceChildParseResultHe
    * Define this as final here so we aren't creating proliferation of
    * traits/classes just for this one little issue.
    */
-  final override def finalChecks(
+  final def arrayCompleteChecks(
+    parser: SequenceChildParser,
+    pstate: PState,
+    resultOfTry: ParseAttemptStatus,
+    priorResultOfTry: ParseAttemptStatus,
+  ): Unit = {
+    if ((sscb eq PositionalNever)) {
+      val resultToTest = resultOfTry
+      resultToTest match {
+        case ParseAttemptStatus.FailureUnspecified | 
ParseAttemptStatus.MissingSeparator =>
+          parser.PE(
+            pstate,
+            "maxOccurs instances and their separators are required when 
dfdl:separatorSuppressionPolicy='never'",
+          )
+        case _ => // ok
+      }
+    }
+  }
+
+  /**
+   * Define this as final here so we aren't creating proliferation of
+   * traits/classes just for this one little issue.
+   */
+  final def sequenceCompleteChecks(
     parser: SequenceChildParser,
     pstate: PState,
     resultOfTry: ParseAttemptStatus,
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
index 62bf01c4e..8f542cf62 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
@@ -48,12 +48,19 @@ trait Separated { self: SequenceChildParser =>
     separatorHelper.parseOneWithSeparator(pstate, requiredOptional)
   }
 
-  final override def finalChecks(
+  final override def arrayCompleteChecks(
     pstate: PState,
     resultOfTry: ParseAttemptStatus,
     priorResultOfTry: ParseAttemptStatus,
   ): Unit =
-    parseResultHelper.finalChecks(self, pstate, resultOfTry, priorResultOfTry)
+    parseResultHelper.arrayCompleteChecks(self, pstate, resultOfTry, 
priorResultOfTry)
+
+  final override def sequenceCompleteChecks(
+    pstate: PState,
+    resultOfTry: ParseAttemptStatus,
+    priorResultOfTry: ParseAttemptStatus,
+  ): Unit =
+    parseResultHelper.sequenceCompleteChecks(self, pstate, resultOfTry, 
priorResultOfTry)
 
 }
 
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
index 0fb555991..acede97ad 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
@@ -215,7 +215,16 @@ abstract class SequenceChildParser(
 
   def pouStatus: PoUStatus
 
-  def finalChecks(
+  def arrayCompleteChecks(
+    pstate: PState,
+    resultOfTry: ParseAttemptStatus,
+    priorResultOfTry: ParseAttemptStatus,
+  ): Unit = {
+    // does nothing by default.
+    // overridden in separated sequence child parsers in some cases
+  }
+
+  def sequenceCompleteChecks(
     pstate: PState,
     resultOfTry: ParseAttemptStatus,
     priorResultOfTry: ParseAttemptStatus,
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
index 3059f0db5..de54c80cd 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
@@ -87,17 +87,6 @@ trait SequenceChildParseResultHelper extends Serializable {
     requiredOptional: RequiredOptionalStatus,
   ): ParseAttemptStatus
 
-  /**
-   * Overridden for PositionalTrailingStrict case.
-   */
-  def finalChecks(
-    parser: SequenceChildParser,
-    pstate: PState,
-    resultOfTry: ParseAttemptStatus,
-    priorResultOfTry: ParseAttemptStatus,
-  ): Unit = {
-    // do nothing by default
-  }
 }
 
 trait ElementSequenceChildParseResultHelper extends 
SequenceChildParseResultHelper {
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
index 59c741c5e..b1275f114 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
@@ -245,6 +245,7 @@ abstract class SequenceParserBase(
 
             } // end while for each repeat
             parser.endArray(pstate)
+            parser.arrayCompleteChecks(pstate, resultOfTry, priorResultOfTry)
           } // end match case RepeatingChildParser
 
           case nonRepresentedParser: NonRepresentedSequenceChildParser => {
@@ -402,7 +403,7 @@ abstract class SequenceParserBase(
         pstate.walker.walk()
       }
 
-      if (child ne null) child.finalChecks(pstate, resultOfTry, 
priorResultOfTry)
+      if (child ne null) child.sequenceCompleteChecks(pstate, resultOfTry, 
priorResultOfTry)
       ()
     } finally {
       pstate.mpstate.groupIndexStack.pop()
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
index 96143b854..811b859b1 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
@@ -169,7 +169,6 @@
       <dfdlInfoset>
         <tns:e_infix_never>
           <x>foo</x>
-          <y/>
       </tns:e_infix_never>
       </dfdlInfoset>
     </infoset>
@@ -215,7 +214,6 @@
       <dfdlInfoset>
         <tns:e_infix_never>
           <x>foo;</x>
-          <y></y>
         </tns:e_infix_never>
       </dfdlInfoset>
     </infoset>
@@ -241,7 +239,6 @@
       <dfdlInfoset>
         <tns:e_infix_never>
           <x>foo/</x>
-          <y></y>
         </tns:e_infix_never>
       </dfdlInfoset>
     </infoset>
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
index 574253888..95a362126 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
@@ -629,6 +629,22 @@
       </xs:complexType>
     </xs:element>
 
+    <xs:element name="policyNeverOptionalStringArray">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="," dfdl:sequenceKind="ordered" 
dfdl:separatorSuppressionPolicy="never">
+          <xs:element name="A" type="xs:string" minOccurs="0" maxOccurs="5" 
dfdl:occursCountKind="implicit" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="policyNeverOptionalIntArray">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="," dfdl:sequenceKind="ordered" 
dfdl:separatorSuppressionPolicy="never">
+          <xs:element name="A" type="xs:int" minOccurs="0" maxOccurs="5" 
dfdl:occursCountKind="implicit" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
   </tdml:defineSchema>
 
   <tdml:parserTestCase name="separatorSuppressionPolicy_never" 
model="separatorSuppressionPolicy">
@@ -641,10 +657,90 @@
         <tns:policyNever>
           <tns:A>Hello</tns:A>
           <tns:B>world</tns:B>
-          <tns:C></tns:C>
         </tns:policyNever>
       </tdml:dfdlInfoset>
     </tdml:infoset>
   </tdml:parserTestCase>
 
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalStringArray_1" 
model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[1,2,3,4,5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalStringArray>
+          <A>1</A>
+          <A>2</A>
+          <A>3</A>
+          <A>4</A>
+          <A>5</A>
+        </tns:policyNeverOptionalStringArray>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalStringArray_2" 
model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[,,,,]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalStringArray />
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalStringArray_3" 
model="separatorSuppressionPolicy" root="policyNeverOptionalStringArray">
+    <tdml:document><![CDATA[,,,]]></tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>maxOccurs</tdml:error>
+      <tdml:error>required</tdml:error>
+      <tdml:error>separatorSuppressionPolicy</tdml:error>
+      <tdml:error>never</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalIntArray_1" 
model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[1,2,3,4,5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalIntArray>
+          <A>1</A>
+          <A>2</A>
+          <A>3</A>
+          <A>4</A>
+          <A>5</A>
+        </tns:policyNeverOptionalIntArray>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalIntArray_2" 
model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[,,,,]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalIntArray />
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalIntArray_3" 
model="separatorSuppressionPolicy" root="policyNeverOptionalIntArray">
+    <tdml:document><![CDATA[1,a,3,4,5]]></tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>maxOccurs</tdml:error>
+      <tdml:error>required</tdml:error>
+      <tdml:error>separatorSuppressionPolicy</tdml:error>
+      <tdml:error>never</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase 
name="separatorSuppressionPolicy_never_optionalIntArray_4" 
model="separatorSuppressionPolicy" root="policyNeverOptionalIntArray">
+    <tdml:document><![CDATA[1,2,3,4]]></tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>maxOccurs</tdml:error>
+      <tdml:error>required</tdml:error>
+      <tdml:error>separatorSuppressionPolicy</tdml:error>
+      <tdml:error>never</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml 
b/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml
index 8fe36b725..eb2d97dd5 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml
@@ -223,14 +223,7 @@
 
   <!-- Test for DAFFODIL-2499. empty strings should not be creating empty 
elements here. -->
   <tdml:parserTestCase name="test_sep_ssp_never_1" root="file1" model="s3"
-                       implementations="ibm">
-    <!--
-      This test doesn't work on Daffodil due  to DAFFODIL-2499.
-
-      However, we enable it in the scala test driver (not commented out) but 
just
-      specify only the ibm implementation here, so that if cross testing this 
file
-      you will see that it works there.
-    -->
+                       implementations="daffodil ibm">
     <tdml:document>madonna,,,,,,,,,</tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
@@ -268,7 +261,7 @@
 
   <!-- Test for DAFFODIL-2499. empty strings should not be creating empty 
elements here. -->
   <tdml:parserTestCase name="test_sep_ssp_never_3" root="file1" model="s3"
-                       implementations="ibm">
+                       implementations="daffodil ibm">
     <tdml:document>madonna,,,,,,,,,</tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
@@ -440,4 +433,181 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+
+  <tdml:defineSchema name="s6" elementFormDefault="unqualified">
+
+    <xs:include 
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format
+      ref="ex:GeneralFormat"
+      emptyValueDelimiterPolicy="none"
+      lengthKind="delimited"
+      separatorPosition="infix"
+      separatorSuppressionPolicy="trailingEmpty"
+    />
+
+    <xs:group name="emptyElementDetectionAssertion">
+      <xs:sequence>
+        <xs:element name="emptyElementDetected" type="xs:string">
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:discriminator test="{ fn:string-length(.) eq 0 }"/>
+            </xs:appinfo>
+          </xs:annotation>
+        </xs:element>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:assert test="{ fn:false() }"/>
+            </xs:appinfo>
+          </xs:annotation>
+        </xs:sequence>
+      </xs:sequence>
+    </xs:group>
+
+    <xs:element name="root" dfdl:initiator="RECORD">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="/" dfdl:separatorPosition="prefix" 
dfdl:terminator="%NL;">
+          <xs:element name="field1" type="xs:string" />
+          <xs:element name="field2" type="xs:string" minOccurs="0" />
+          <xs:element name="field3" type="xs:string" minOccurs="0" />
+          <xs:element name="groupOfFields" minOccurs="1" maxOccurs="3" 
dfdl:occursCountKind="parsed">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:sequence dfdl:separator="/" dfdl:separatorPosition="infix" 
dfdl:separatorSuppressionPolicy="never">
+                  <xs:element name="field4" minOccurs="0">
+                    <xs:complexType>
+                      <xs:choice>
+                        <xs:group ref="ex:emptyElementDetectionAssertion" />
+                        <xs:sequence dfdl:separator="-">
+                          <xs:element name="field4a" type="xs:string" />
+                          <xs:element name="field4b" type="xs:string" />
+                        </xs:sequence>
+                      </xs:choice>
+                    </xs:complexType>
+                  </xs:element>
+                  <xs:element name="field5" minOccurs="0">
+                    <xs:complexType>
+                      <xs:choice>
+                        <xs:group ref="ex:emptyElementDetectionAssertion" />
+                        <xs:sequence dfdl:separator="-">
+                          <xs:element name="field5a" type="xs:string" />
+                          <xs:element name="field5b" type="xs:string" />
+                        </xs:sequence>
+                      </xs:choice>
+                    </xs:complexType>
+                  </xs:element>
+                  <xs:element name="field6" minOccurs="0">
+                    <xs:complexType>
+                      <xs:choice>
+                        <xs:group ref="ex:emptyElementDetectionAssertion" />
+                        <xs:sequence dfdl:separator="-">
+                          <xs:element name="field6a" type="xs:string" />
+                          <xs:element name="field6b" type="xs:string" />
+                        </xs:sequence>
+                      </xs:choice>
+                    </xs:complexType>
+                  </xs:element>
+                </xs:sequence>
+                <xs:sequence>
+                  <xs:annotation>
+                    <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                      <dfdl:assert test="{ fn:exists(field4) or 
fn:exists(field5) or fn:exists(field6) }" />
+                    </xs:appinfo>
+                  </xs:annotation>
+                </xs:sequence>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+          <xs:element name="field7" type="xs:string" minOccurs="0" />
+          <xs:element name="field8" type="xs:string" minOccurs="0" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="test_sep_ssp_never_6" root="root" model="s6"
+                       implementations="daffodil">
+    <tdml:document>
+      <tdml:documentPart type="text" 
replaceDFDLEntities="true">RECORD/1/2/3/4-1/5-1/6-1/4-2/5-2/6-2/7/8%LF;</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:root xmlns:ex="http://example.com";>
+          <field1>1</field1>
+          <field2>2</field2>
+          <field3>3</field3>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>1</field4b>
+            </field4>
+            <field5>
+              <field5a>5</field5a>
+              <field5b>1</field5b>
+            </field5>
+            <field6>
+              <field6a>6</field6a>
+              <field6b>1</field6b>
+            </field6>
+          </groupOfFields>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>2</field4b>
+            </field4>
+            <field5>
+              <field5a>5</field5a>
+              <field5b>2</field5b>
+            </field5>
+            <field6>
+              <field6a>6</field6a>
+              <field6b>2</field6b>
+            </field6>
+          </groupOfFields>
+          <field7>7</field7>
+          <field8>8</field8>
+        </ex:root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="test_sep_ssp_never_7" root="root" model="s6"
+                       implementations="daffodil">
+    <tdml:document>
+      <tdml:documentPart type="text" 
replaceDFDLEntities="true">RECORD/1/2/3/4-1//6-1/4-2/5-2//7/8%LF;</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:root xmlns:ex="http://example.com";>
+          <field1>1</field1>
+          <field2>2</field2>
+          <field3>3</field3>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>1</field4b>
+            </field4>
+            <field6>
+              <field6a>6</field6a>
+              <field6b>1</field6b>
+            </field6>
+          </groupOfFields>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>2</field4b>
+            </field4>
+            <field5>
+              <field5a>5</field5a>
+              <field5b>2</field5b>
+            </field5>
+          </groupOfFields>
+          <field7>7</field7>
+          <field8>8</field8>
+        </ex:root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git 
a/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
 
b/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
index 435cb9981..9bdeb771f 100644
--- 
a/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
+++ 
b/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
@@ -57,6 +57,27 @@ class TestSequenceGroups {
   @Test def test_separatorSuppressionPolicy_never(): Unit = {
     runner_01.runOneTest("separatorSuppressionPolicy_never")
   }
+  @Test def test_separatorSuppressionPolicy_never_optionalStringArray_1(): 
Unit = {
+    
runner_01.runOneTest("separatorSuppressionPolicy_never_optionalStringArray_1")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalStringArray_2(): 
Unit = {
+    
runner_01.runOneTest("separatorSuppressionPolicy_never_optionalStringArray_2")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalStringArray_3(): 
Unit = {
+    
runner_01.runOneTest("separatorSuppressionPolicy_never_optionalStringArray_3")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_1(): Unit = 
{
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_1")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_2(): Unit = 
{
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_2")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_3(): Unit = 
{
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_3")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_4(): Unit = 
{
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_4")
+  }
 
   // DAFFODIL-669
   //  @Test def test_emptySequenceSDE() { 
runner_02.runOneTest("emptySequenceSDE") }
diff --git 
a/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala 
b/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala
index fbc82c851..2453e6799 100644
--- 
a/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala
+++ 
b/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala
@@ -47,28 +47,18 @@ class TestSepTests {
     runner.runOneTest("test_sep_trailingEmptyStrict_2")
   }
 
-  // DAFFODIL-2499 - separatorSuppressionPolicy 'never'
-  // Note: this test isn't commented out, because it works for IBM DFDL in 
cross testing
-  // The TDML for this test just has it disabled for the daffodil 
implementation.
-  // Add daffodil to implementations to see the erroneous daffodil behavior.
   @Test def test_sep_ssp_never_1(): Unit = { 
runner.runOneTest("test_sep_ssp_never_1") }
-
   @Test def test_sep_ssp_never_2(): Unit = { 
runner.runOneTest("test_sep_ssp_never_2") }
-
-  // DAFFODIL-2499 - separatorSuppressionPolicy 'never'
-  // Note: this test isn't commented out, because it works for IBM DFDL in 
cross testing
-  // The TDML for this test just has it disabled for the daffodil 
implementation.
-  // Add daffodil to implementations to see the erroneous daffodil behavior.
   @Test def test_sep_ssp_never_3(): Unit = { 
runner.runOneTest("test_sep_ssp_never_3") }
-
   @Test def test_sep_ssp_never_4_ibm(): Unit = { 
runner.runOneTest("test_sep_ssp_never_4_ibm") }
-
   @Test def test_sep_ssp_never_4_daffodil(): Unit = {
     runner.runOneTest("test_sep_ssp_never_4_daffodil")
   }
-
   @Test def test_sep_ssp_never_5(): Unit = { 
runner.runOneTest("test_sep_ssp_never_5") }
 
+  @Test def test_sep_ssp_never_6(): Unit = { 
runner.runOneTest("test_sep_ssp_never_6") }
+  @Test def test_sep_ssp_never_7(): Unit = { 
runner.runOneTest("test_sep_ssp_never_7") }
+
   // DAFFODIL-2791
   @Test def test_treatAsMissing_occursIndex(): Unit = {
     runner.runOneTest("test_treatAsMissing_occursIndex")


Reply via email to