You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sl...@apache.org on 2023/01/26 18:55:30 UTC

[daffodil] branch main updated: Only add positionally required separators for arrays with occursCountKind="implicit"

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 fc660df1a Only add positionally required separators for arrays with occursCountKind="implicit"
fc660df1a is described below

commit fc660df1aecfce81f276b79b9c9f9eb9ce893153
Author: Steve Lawrence <sl...@apache.org>
AuthorDate: Thu Jan 26 10:15:35 2023 -0500

    Only add positionally required separators for arrays with occursCountKind="implicit"
    
    If a schema has a separated array/optional with occursCountKind as fixed
    or expression, then we currently always unparse maxOccurs separators
    even if there were less than maxOccurs instances in the infoset. This is
    incorrect--we should only output a separator for each instance in the
    infoset and no more, regardless of the value of min/maxOccurs. Only with
    occursCountKind set to implicit should min/maxOccurs be taken into
    account when unparsing positionally required separators.
    
    The unparsePositionallyRequiredSeps function handles the separator logic
    for implicit arrays, but should be skipped otherwise. This modifies the
    condition so it is only considered for the implicit case.
    
    Also makes the "ock" variable in the MinMaxRepatsMixin public since it
    is useful in a number of places, including this new check.
    
    DAFFODIL-2231
---
 .../unparsers/SeparatedSequenceUnparsers.scala     |  21 +++--
 .../unparsers/UnseparatedSequenceUnparsers.scala   |   2 -
 .../processors/parsers/SequenceChildBases.scala    |   4 +-
 .../section23/dfdl_expressions/expressions.tdml    | 103 +++++++++++++++++++++
 .../dfdl_expressions/TestDFDLExpressions.scala     |   5 +
 5 files changed, 122 insertions(+), 13 deletions(-)

diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala
index 89ccc7310..7a0fb9095 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala
@@ -17,15 +17,16 @@
 package org.apache.daffodil.processors.unparsers
 
 import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.processors.ModelGroupRuntimeData
 import org.apache.daffodil.processors.{ ElementRuntimeData, SequenceRuntimeData, TermRuntimeData }
 import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
-import org.apache.daffodil.schema.annotation.props.gen.{ SeparatorPosition }
-import org.apache.daffodil.processors.ModelGroupRuntimeData
-import scala.collection.mutable.Buffer
-import SeparatorSuppressionPolicy._
-import SeparatorPosition._
+import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy._
+import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
+import org.apache.daffodil.schema.annotation.props.gen.SeparatorPosition
+import org.apache.daffodil.schema.annotation.props.gen.SeparatorPosition._
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.MaybeInt
+import scala.collection.mutable.Buffer
 
 trait Separated { self: SequenceChildUnparser =>
 
@@ -458,8 +459,13 @@ class OrderedSeparatedSequenceUnparser(
     var numOccurrences = numOccurs
     unparserArg match {
       case unparser: RepOrderedSeparatedSequenceChildUnparser => {
-        val isBounded = unparser.isBoundedMax
-        if (unparser.isPositional && isBounded &&
+        // note that if we are unparsing an array/optional, we only add positionally
+        // required separators for occursCountKind="implicit". All other
+        // occursCountKind's use the number of elements in the infoset to determine
+        // the number of separators, which would have already been unparsed prior to
+        // this function being called.
+        if ((unparser.ock eq OccursCountKind.Implicit) &&
+          unparser.isPositional && unparser.isBoundedMax &&
           (!unparser.isDeclaredLast || !unparser.isPotentiallyTrailing)) {
           val maxReps = unparser.maxRepeats(state)
           while (numOccurrences < maxReps) {
@@ -469,7 +475,6 @@ class OrderedSeparatedSequenceUnparser(
             state.moveOverOneGroupIndexOnly()
             numOccurrences += 1
           }
-          //
         }
       }
       case _ => Assert.invariantFailed("Not a repeating element")
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala
index 9d4adb2ff..e4c657837 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala
@@ -43,8 +43,6 @@ class RepOrderedUnseparatedSequenceChildUnparser(
   extends RepeatingChildUnparser(childUnparser, srd, erd)
   with Unseparated {
 
-  private val ock = erd.maybeOccursCountKind.get
-
   override def checkArrayPosAgainstMaxOccurs(state: UState): Boolean = {
     if (ock eq OccursCountKind.Implicit)
       state.arrayPos <= maxRepeats(state)
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceChildBases.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceChildBases.scala
index 90d6f5eb6..5c9552d34 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceChildBases.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceChildBases.scala
@@ -460,7 +460,7 @@ trait MinMaxRepeatsMixin {
 
   def erd: ElementRuntimeData
 
-  private val ock = erd.maybeOccursCountKind.get
+  final val ock = erd.maybeOccursCountKind.get
 
   private val minRepeats_ = {
     val mr =
@@ -552,8 +552,6 @@ abstract class OccursCountMinMaxParser(
 
   Assert.invariant(erd.maybeOccursCountKind.isDefined)
 
-  private val ock = erd.maybeOccursCountKind.get
-
   Assert.invariant(ock == OccursCountKind.Implicit ||
     ock == OccursCountKind.Parsed)
 
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
index 9b99d3573..00f6858b1 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
@@ -1375,6 +1375,43 @@
       </xs:complexType>
     </xs:element>
 
+    <!-- same idea as ocke10, but uses occursCountKind="expression" -->
+    <xs:element name="ocke9">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="exists" type="xs:boolean" dfdl:terminator=":"
+            dfdl:textBooleanTrueRep="true" dfdl:textBooleanFalseRep="false" />
+          <xs:sequence dfdl:separator="|" dfdl:separatorPosition="infix">
+            <xs:element name="field1" type="xs:string" minOccurs="0"
+              dfdl:occursCountKind="expression"
+              dfdl:occursCount="{ if (../ex:exists eq fn:true()) then 1 else 0 }" />
+            <xs:element name="field2" type="xs:string" dfdl:occursCountKind="implicit" maxOccurs="unbounded" />
+          </xs:sequence>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <!-- same idea as ocke9, but uses occursCountKind="implicit" plus a discriminator -->
+    <xs:element name="ocke10">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="exists" type="xs:boolean" dfdl:terminator=":"
+            dfdl:textBooleanTrueRep="true" dfdl:textBooleanFalseRep="false" />
+          <xs:sequence dfdl:separator="|" dfdl:separatorPosition="infix">
+            <xs:element name="field1" type="xs:string" minOccurs="0"
+              dfdl:occursCountKind="implicit">
+              <xs:annotation>
+                <xs:appinfo source="http://www.ogf.org/dfdl/">
+                  <dfdl:discriminator>{ ../ex:exists eq fn:true() }</dfdl:discriminator>
+                </xs:appinfo>
+              </xs:annotation>
+            </xs:element>
+            <xs:element name="field2" type="xs:string" dfdl:occursCountKind="implicit" maxOccurs="unbounded" />
+          </xs:sequence>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
     <xs:element name="expr_space" dfdl:lengthKind="implicit">
       <xs:complexType>
         <xs:sequence dfdl:separator=",">
@@ -1764,6 +1801,72 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+  <tdml:unparserTestCase name="ocke_optional_separator_01" root="ocke9"
+    model="expressions-Embedded.dfdl.xsd" description="occursCountKind expression - DFDL-23-011R">
+
+    <tdml:document>true:field1|field2a|field2b|field2c</tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:ocke9 xmlns:ex="http://example.com">
+          <ex:exists>true</ex:exists>
+          <ex:field1>field1</ex:field1>
+          <ex:field2>field2a</ex:field2>
+          <ex:field2>field2b</ex:field2>
+          <ex:field2>field2c</ex:field2>
+        </ex:ocke9>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+  <tdml:unparserTestCase name="ocke_optional_separator_02" root="ocke9"
+    model="expressions-Embedded.dfdl.xsd" description="occursCountKind expression - DFDL-23-011R">
+
+    <tdml:document>false:field2a|field2b|field2c</tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:ocke9 xmlns:ex="http://example.com">
+          <ex:exists>false</ex:exists>
+          <ex:field2>field2a</ex:field2>
+          <ex:field2>field2b</ex:field2>
+          <ex:field2>field2c</ex:field2>
+        </ex:ocke9>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+  <tdml:unparserTestCase name="ocke_optional_separator_03" root="ocke10"
+    model="expressions-Embedded.dfdl.xsd" description="occursCountKind expression - DFDL-23-011R">
+
+    <tdml:document>true:field1|field2a|field2b|field2c</tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:ocke10 xmlns:ex="http://example.com">
+          <ex:exists>true</ex:exists>
+          <ex:field1>field1</ex:field1>
+          <ex:field2>field2a</ex:field2>
+          <ex:field2>field2b</ex:field2>
+          <ex:field2>field2c</ex:field2>
+        </ex:ocke10>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+  <tdml:unparserTestCase name="ocke_optional_separator_04" root="ocke10"
+    model="expressions-Embedded.dfdl.xsd" description="occursCountKind expression - DFDL-23-011R">
+
+    <tdml:document>false:field2a|field2b|field2c</tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:ocke10 xmlns:ex="http://example.com">
+          <ex:exists>false</ex:exists>
+          <ex:field2>field2a</ex:field2>
+          <ex:field2>field2b</ex:field2>
+          <ex:field2>field2c</ex:field2>
+        </ex:ocke10>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
 <!--
      Test Name: internal_space_preserved
         Schema: expressions-Embedded.dfdl.xsd
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
index fa0b2da0a..3af67b2b2 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
@@ -237,6 +237,11 @@ class TestDFDLExpressions {
   @Test def test_ocke_array_index_step_dne(): Unit = { runner.runOneTest("ocke_array_index_step_dne") }
   @Test def test_ocke_non_upward(): Unit = { runner.runOneTest("ocke_non_upward") }
   @Test def test_ocke_single_upward(): Unit = { runner.runOneTest("ocke_single_upward") }
+  @Test def test_ocke_optional_separator_01(): Unit = { runner.runOneTest("ocke_optional_separator_01") }
+  @Test def test_ocke_optional_separator_02(): Unit = { runner.runOneTest("ocke_optional_separator_02") }
+  @Test def test_ocke_optional_separator_03(): Unit = { runner.runOneTest("ocke_optional_separator_03") }
+  @Test def test_ocke_optional_separator_04(): Unit = { runner.runOneTest("ocke_optional_separator_04") }
+
   @Test def test_internal_space_preserved(): Unit = { runner.runOneTest("internal_space_preserved") }
   @Test def test_internal_space_preserved2(): Unit = { runner.runOneTest("internal_space_preserved2") }
   @Test def test_internal_space_preserved3a(): Unit = { runner.runOneTest("internal_space_preserved3a") }