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

Reply via email to