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 5ca4319c8 Always create an unparse error when a required array is
missing
5ca4319c8 is described below
commit 5ca4319c83da3d5caf5d9e17ce6a05a021e621ab
Author: Steve Lawrence <[email protected]>
AuthorDate: Thu Apr 18 08:52:26 2024 -0400
Always create an unparse error when a required array is missing
Currently, if a required array in an unseparated sequence is missing
from an infoset then we create an unparse error. However, if that
required but missing array is followed by another array in the infoset
then we abort. This is incorrect--we should create an unparse error just
like other cases.
To fix this, this modifies the unseparated sequence unparser logic so
that we first check if this event is even for this array. If it is, then
it must be an start event and we perform the normal logic. If it isn't,
then it means we have zero occurrences of this array and the event must
have been the start event for a following element or the end event for a
parent element. Either way, we check if zero occurrences is valid for
this array and create an unparse error if not. With this refactored
logic, there is now no difference between a missing array followed by a
scalar, optional, or another array.
DAFFODIL-2891
---
.../runtime1/UnseparatedSequenceUnparsers.scala | 127 +++++++++------------
.../UnparseArrayExpressionConstant.tdml | 38 ++++++
.../TestUnparseArrayOptionalElem.scala | 3 +
3 files changed, 98 insertions(+), 70 deletions(-)
diff --git
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/UnseparatedSequenceUnparsers.scala
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/UnseparatedSequenceUnparsers.scala
index 903ae07a6..5fb0babc7 100644
---
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/UnseparatedSequenceUnparsers.scala
+++
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/UnseparatedSequenceUnparsers.scala
@@ -97,7 +97,6 @@ class OrderedUnseparatedSequenceUnparser(
val erd = unparser.erd
var numOccurrences = 0
val maxReps = unparser.maxRepeats(state)
- val minReps = unparser.minRepeats(state)
//
// The number of occurrances we unparse is always exactly driven
@@ -107,78 +106,66 @@ class OrderedUnseparatedSequenceUnparser(
// 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
+
+ // we must have an event
+ Assert.invariant(state.inspect, "No event for unparing.")
+
+ val ev = state.inspectAccessor
+ if (ev.erd eq erd) {
+ // must be a start event for this array/optional unparser
val isArr = ev.isArray
- if (ev.isStart && (isArr || ev.erd.isOptional)) {
- val eventNQN = ev.namedQName
- if (ev.erd eq erd) {
- //
- // StartArray for this unparser's array element
- //
- unparser.startArrayOrOptional(state)
- while ({
- doUnparser = unparser.shouldDoUnparser(unparser, state)
- doUnparser
- }) {
- if (isArr)
- if (state.dataProc.isDefined)
- state.dataProc.get.beforeRepetition(state, this)
-
- unparseOne(unparser, erd, state)
- numOccurrences += 1
- state.moveOverOneArrayIterationIndexOnly()
- state.moveOverOneOccursIndexOnly()
- state.moveOverOneGroupIndexOnly() // array elements are
always represented.
-
- if (isArr)
- if (state.dataProc.isDefined)
- state.dataProc.get.afterRepetition(state, this)
- }
-
- unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
- state,
- unparser,
- numOccurrences,
- maxReps,
- state.arrayIterationPos - 1,
- )
- unparser.endArrayOrOptional(erd, state)
- } else {
- //
- // start array but not for the expected array,
- // rather 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.
- //
- // minReps has to be 0, meaning it is allowed to have zero
- // occurrences (not necessarily valid, but allowed),
- // because we got zero instances of this array
- //
- Assert.invariant(minReps == 0L)
- }
- } else if (ev.isStart) {
- Assert.invariant(!isArr && !ev.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.namedQName
- Assert.invariant(eventNQN != erd.namedQName)
- } else {
- Assert.invariant(ev.isEnd && ev.erd.isComplexType)
- unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
- state,
- unparser,
- numOccurrences,
- maxReps,
- 0,
- )
+ Assert.invariant(ev.isStart && (isArr || ev.erd.isOptional))
+
+ //
+ // StartArray for this unparser's array element
+ //
+ unparser.startArrayOrOptional(state)
+ while ({
+ doUnparser = unparser.shouldDoUnparser(unparser, state)
+ doUnparser
+ }) {
+ if (isArr)
+ if (state.dataProc.isDefined)
+ state.dataProc.get.beforeRepetition(state, this)
+
+ unparseOne(unparser, erd, state)
+ numOccurrences += 1
+ state.moveOverOneArrayIterationIndexOnly()
+ state.moveOverOneOccursIndexOnly()
+ state.moveOverOneGroupIndexOnly() // array elements are always
represented.
+
+ if (isArr)
+ if (state.dataProc.isDefined)
+ state.dataProc.get.afterRepetition(state, this)
}
+
+ // DAFFODIL-115: if the number of occurrences is less than
minOccurs we are supposed
+ // to check if the element is defaultable and add elements until
we reach that
+ // number. Daffodil does not currently support defaulting during
unparsing.
+
+ unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
+ state,
+ unparser,
+ numOccurrences,
+ maxReps,
+ state.arrayIterationPos - 1,
+ )
+ unparser.endArrayOrOptional(erd, state)
} else {
- // no event (state.inspect returned false)
- Assert.invariantFailed("No event for unparing.")
+ // this is either a start event for a following element or an end
event for a
+ // parent element. Either way, we never saw a start event for this
array/optional,
+ // which means there were zero occurrenes. Make sure that is
allowed for this array
+ //
+ // DAFFODIL-115: if the number of occurrences is less than
minOccurs we are supposed
+ // to check if the element is defaultable and add elements until
we reach that
+ // number. Daffodil does not currently support defaulting during
unparsing.
+ unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
+ state,
+ unparser,
+ numOccurrences,
+ maxReps,
+ 0,
+ )
}
state.arrayIterationIndexStack.pop()
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/UnparseArrayExpressionConstant.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/UnparseArrayExpressionConstant.tdml
index b0e504e0e..258766b7e 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/UnparseArrayExpressionConstant.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/UnparseArrayExpressionConstant.tdml
@@ -102,6 +102,16 @@
</xs:complexType>
</xs:element>
+ <xs:element name="r7" dfdl:ref="ex:root">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="sca" type="xs:int" />
+ <xs:element name="req" type="xs:string" minOccurs="1" maxOccurs="5"
dfdl:occursCount="{../ex:sca}"/>
+ <xs:element name="req2" type="xs:string" minOccurs="1" maxOccurs="5"
dfdl:occursCountKind="parsed"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
</tdml:defineSchema>
<!--
@@ -338,4 +348,32 @@
</tdml:unparserTestCase>
+<!--
+ Test Name: exprReqAbsentArray_01
+ Schema: s1
+ Root: r7
+ Purpose: This test demonsrates unparsing an array with occursCount set to
an expression.
+-->
+
+ <tdml:unparserTestCase name="exprReqAbsentArray_01" root="r7"
+ model="s1" roundTrip="true">
+
+ <tdml:infoset xmlns="http://example.com">
+ <tdml:dfdlInfoset>
+ <r7>
+ <sca>0</sca>
+ <req2>2</req2>
+ <req2>2</req2>
+ <req2>2</req2>
+ </r7>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:errors>
+ <tdml:error>Unparse Error</tdml:error>
+ <tdml:error>Expected 1 additional</tdml:error>
+ <tdml:error>req</tdml:error>
+ <tdml:error>req2</tdml:error>
+ </tdml:errors>
+ </tdml:unparserTestCase>
+
</tdml:testSuite>
diff --git
a/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestUnparseArrayOptionalElem.scala
b/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestUnparseArrayOptionalElem.scala
index 9afdf3f4a..0a28c56f0 100644
---
a/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestUnparseArrayOptionalElem.scala
+++
b/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestUnparseArrayOptionalElem.scala
@@ -69,6 +69,9 @@ class TestUnparseArrayOptionalElem {
@Test def test_exprOptParsedData_04(): Unit = {
runner_expr.runOneTest("exprOptParsedData_04")
}
+ @Test def test_exprReqAbsentArray_01(): Unit = {
+ runner_expr.runOneTest("exprReqAbsentArray_01")
+ }
@Test def test_fixedUnparseArrayTooManyElements01(): Unit = {
runner_fixed.runOneTest("fixedUnparseArrayTooManyElements01")