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")