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/02/23 16:36:12 UTC

[daffodil] branch main updated: Better error message when contentLength/valueLength argument is an array

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 0987b93cc Better error message when contentLength/valueLength argument is an array
0987b93cc is described below

commit 0987b93ccba910eeb1538f4cd221857f2e3fb7cf
Author: Mike McGann <mm...@owlcyberdefense.com>
AuthorDate: Wed Feb 15 16:13:07 2023 -0500

    Better error message when contentLength/valueLength argument is an array
    
    The dfdl:contentLength and dfdl:valueLength functions accept a
    NodeType.Exists type as the first argument to require the value to be a
    node and to prevent the use of a literal. When passed an array, which is
    prohibited, the error message is misleading because it refers to the
    parent of the array and not the array itself. This change introduces an
    explicit check for an array and raises an SDE with a better error
    message.
    
    DAFFODIL-1651
---
 .../apache/daffodil/core/dpath/Expression.scala    |  21 ++-
 .../apache/daffodil/runtime1/dpath/NodeInfo.scala  |   2 +-
 .../section23/dfdl_functions/Functions.tdml        | 208 ++++++++++++++++++++-
 .../dfdl_expressions/TestDFDLExpressions.scala     |   8 +
 4 files changed, 232 insertions(+), 7 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala b/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
index 6e86b3115..b02338771 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
@@ -745,6 +745,8 @@ abstract class PathExpression() extends Expression {
    */
   lazy val isPathToOneWholeArray: Boolean = {
     if (steps == Nil) false // root is never an array
+    if (steps == Nil) false // root is never an array
+    else if (isSelf) false // self path is never an array
     else
       steps.last.isArray &&
       !steps.last.pred.isDefined && // last cannot have a [N] pred
@@ -758,6 +760,11 @@ abstract class PathExpression() extends Expression {
           else true
       }
   }
+
+  def isSelf = steps match {
+    case List(Self(_)) => true
+    case _ => false
+  }
 }
 
 case class RootPathExpression(relPath: Option[RelativePathExpression])
@@ -2829,13 +2836,21 @@ sealed abstract class LengthExprBase(
   ) {
 
   protected final def leafReferencedElements = {
-    val arg = args(0).asInstanceOf[PathExpression]
-    val steps = arg.steps
+    val path = args(0) match {
+      case arg: PathExpression => arg
+      case _ => {
+        // $COVERAGE-OFF$
+        Assert.invariantFailed("NodeInfo.Exists, but arg was not a PathExpression.")
+        // $COVERAGE-ON$
+      }
+    }
+    if (path.isPathToOneWholeArray)
+      SDE(s"First argument to ${fnQName} cannot be a path to an array")
+    val steps = path.steps
     val lst = steps.last
     val res = lst.stepElements.toSet
     res
   }
-
 }
 
 case class ContentLengthExpr(
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
index 2a1601ead..8e8fa944a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/dpath/NodeInfo.scala
@@ -387,7 +387,7 @@ object NodeInfo extends Enum {
   }
 
   /**
-   * For things like fn:exists, fn:empty, dfdl:contenLength
+   * For things like fn:exists, fn:empty, dfdl:contentLength
    */
   protected sealed trait ExistsKind extends AnyType.Kind
   case object Exists extends TypeNode('Exists, AnyType) with AnyTypeKind {
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
index 75adbccab..d3b9ac568 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
@@ -459,6 +459,37 @@
       </xs:complexType>
     </xs:element>
 
+    <xs:element name="valueLength14">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="%WSP;">
+          <xs:element name="first" type="xs:string" dfdl:lengthKind="delimited"/>
+          <xs:element name="other" type="xs:string" dfdl:lengthKind="delimited" maxOccurs="unbounded"/>
+          <xs:element name="len" type="xs:unsignedInt" dfdl:inputValueCalc='{dfdl:valueLength(../ex:other, "bytes")}'/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="valueLength15" type="xs:unsignedInt" dfdl:inputValueCalc='{dfdl:valueLength("three", "bytes")}'/>
+
+    <xs:element name="valueLength16">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="e1" maxOccurs="unbounded">
+            <xs:annotation>
+              <xs:appinfo source="http://www.ogf.org/dfdl/">
+                <dfdl:assert>{ dfdl:valueLength(., 'bytes') eq 3 }</dfdl:assert>
+              </xs:appinfo>
+            </xs:annotation>
+            <xs:complexType>
+              <xs:sequence dfdl:separator=",">
+                <xs:element name="v1" type="xs:string"/>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
     <xs:element name="contentLength">
       <xs:complexType>
         <xs:sequence dfdl:separator=",">
@@ -494,7 +525,38 @@
         </xs:sequence>
       </xs:complexType>
     </xs:element>
-    
+
+    <xs:element name="contentLength4">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="%WSP;">
+          <xs:element name="first" type="xs:string" dfdl:lengthKind="delimited"/>
+          <xs:element name="other" type="xs:string" dfdl:lengthKind="delimited" maxOccurs="unbounded"/>
+          <xs:element name="len" type="xs:unsignedInt" dfdl:inputValueCalc='{dfdl:contentLength(../ex:other, "bytes")}'/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="contentLength5" type="xs:unsignedInt" dfdl:inputValueCalc='{dfdl:contentLength("three", "bytes")}'/>
+
+    <xs:element name="contentLength6">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="e1" maxOccurs="unbounded">
+            <xs:annotation>
+              <xs:appinfo source="http://www.ogf.org/dfdl/">
+                <dfdl:assert message="{ dfdl:contentLength(., 'bytes') }">{ dfdl:contentLength(., 'bytes') eq 3 }</dfdl:assert>
+              </xs:appinfo>
+            </xs:annotation>
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="v1" type="xs:string"/>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
     <xs:element name="e_concat">
       <xs:complexType>
         <xs:sequence dfdl:separator=",">
@@ -959,8 +1021,6 @@
         </xs:sequence>
       </xs:complexType>
     </xs:element>
-
-
   </tdml:defineSchema>
 
 <!--
@@ -3897,6 +3957,77 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+<!--
+    Test Name: valueLength_14
+       Schema: Functions.dfdl.xsd
+         Root: valueLength14
+      Purpose: This test demonstrates that it is an error to get the
+               valueLength of an array
+-->
+  <tdml:parserTestCase name="valueLength_14" root="valueLength14"
+                       model="Functions.dfdl.xsd" description="Section 23 " roundTrip="false">
+
+    <tdml:document>
+      <tdml:documentPart type="text">one two three</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>cannot be a path to an array</tdml:error>
+      <tdml:error>dfdl:valueLength</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+<!--
+    Test Name: valueLength_15
+       Schema: Functions.dfdl.xsd
+         Root: valueLength15
+      Purpose: This test demonstrates that it is an error to get the
+               valueLength of a literal
+-->
+  <tdml:parserTestCase name="valueLength_15" root="valueLength15"
+                       model="Functions.dfdl.xsd" description="Section 23 " roundTrip="false">
+
+    <tdml:document>
+      <tdml:documentPart type="text">one two three</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>expected a path expression</tdml:error>
+      <tdml:error>xs:string</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+<!--
+    Test Name: valueLength_16
+       Schema: Functions.dfdl.xsd
+         Root: valueLength16
+      Purpose: This test demonstrates a valueLength on a complexType using
+               a path to self
+-->
+
+  <tdml:parserTestCase name="valueLength_16" root="valueLength16"
+                       model="Functions.dfdl.xsd" description="Section 23 " roundTrip="false">
+
+    <tdml:document>
+      <tdml:documentPart type="text">ABC,DEF,GHI</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <valueLength16>
+          <e1>
+            <v1>ABC</v1>
+          </e1>
+          <e1>
+            <v1>DEF</v1>
+          </e1>
+          <e1>
+            <v1>GHI</v1>
+          </e1>
+        </valueLength16>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
 <!--
     Test Name: valueLength_unparse_0
        Schema: Functions.dfdl.xsd
@@ -4093,6 +4224,77 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+  <!--
+      Test Name: contentLength_4
+         Schema: Functions.dfdl.xsd
+           Root: contentLength4
+        Purpose: This test demonstrates that it is an error to get the
+                 contentLength of an array
+  -->
+  <tdml:parserTestCase name="contentLength_4" root="contentLength4"
+                       model="Functions.dfdl.xsd" description="Section 23 " roundTrip="false">
+
+    <tdml:document>
+      <tdml:documentPart type="text">one two three</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>cannot be a path to an array</tdml:error>
+      <tdml:error>dfdl:contentLength</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Test Name: contentLength_5
+         Schema: Functions.dfdl.xsd
+           Root: contentLength5
+        Purpose: This test demonstrates that it is an error to get the
+                 contentLength of a literal
+  -->
+  <tdml:parserTestCase name="contentLength_5" root="contentLength5"
+                       model="Functions.dfdl.xsd" description="Section 23 " roundTrip="false">
+
+    <tdml:document>
+      <tdml:documentPart type="text">one two three</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>Expected a path expression</tdml:error>
+      <tdml:error>xs:string</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+    Test Name: contentLength_6
+       Schema: Functions.dfdl.xsd
+         Root: contentLength6
+      Purpose: This test demonstrates a contentLength on a complexType using
+               a path to self
+-->
+
+  <tdml:parserTestCase name="contentLength_6" root="contentLength6"
+                       model="Functions.dfdl.xsd" description="Section 23 " roundTrip="false">
+
+    <tdml:document>
+      <tdml:documentPart type="text">ABC,DEF,GHI</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <contentLength6>
+          <e1>
+            <v1>ABC</v1>
+          </e1>
+          <e1>
+            <v1>DEF</v1>
+          </e1>
+          <e1>
+            <v1>GHI</v1>
+          </e1>
+        </contentLength6>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
 <!--
     Test Name: valueContentLength1
        Schema: Functions.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 c6873bb7e..370786770 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
@@ -1503,6 +1503,10 @@ class TestDFDLExpressions {
   @Test def test_valueLength_4(): Unit = { runner2.runOneTest("valueLength_4") }
   @Test def test_valueLength_5(): Unit = { runner2.runOneTest("valueLength_5") }
   @Test def test_valueLength_6(): Unit = { runner2.runOneTest("valueLength_6") }
+  @Test def test_valueLength_14(): Unit = { runner2.runOneTest("valueLength_14") }
+  // DAFFODIL-2795
+  // @Test def test_valueLength_15(): Unit = { runner2.runOneTest("valueLength_15") }
+  @Test def test_valueLength_16(): Unit = { runner2.runOneTest("valueLength_16") }
   @Test def test_valueLength_sde(): Unit = { runner2.runOneTest("valueLength_sde") }
   @Test def test_valueLength_unparse_0(): Unit = { runner2.runOneTest("valueLength_unparse_0") }
   // DFDL-1516:dfdl:contentLength & dfdl:valueLength specifying lengthUnits 'characters' and variable-width encodings
@@ -1514,6 +1518,10 @@ class TestDFDLExpressions {
   @Test def test_contentLength_0(): Unit = { runner2.runOneTest("contentLength_0") }
   @Test def test_contentLength_1(): Unit = { runner2.runOneTest("contentLength_1") }
   @Test def test_contentLength_2(): Unit = { runner2.runOneTest("contentLength_2") }
+  @Test def test_contentLength_4(): Unit = { runner2.runOneTest("contentLength_4") }
+  // DAFFODIL-2795
+  // @Test def test_contentLength_5(): Unit = { runner2.runOneTest("contentLength_5") }
+  @Test def test_contentLength_6(): Unit = { runner2.runOneTest("contentLength_6") }
 
   @Test def test_valueContentLength1(): Unit = { runner2.runOneTest("valueContentLength1") }
   @Test def test_valueContentLength2(): Unit = { runner2.runOneTest("valueContentLength2") }