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 2020/05/04 14:20:44 UTC

[incubator-daffodil] branch master updated: Implement fn:namespace-uri function

This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new c91cc42  Implement fn:namespace-uri function
c91cc42 is described below

commit c91cc4219a66e6be5b814dac8ce5a683a12c3b96
Author: John Interrante <in...@research.ge.com>
AuthorDate: Mon Apr 27 16:45:46 2020 -0400

    Implement fn:namespace-uri function
    
    Add two new cases to FunctionCallExpression.functionObject (in file
    Expression.scala) to allow expressions to call fn:namespace-uri with
    zero and one arguments and get NodeInfo.String values.  Make both
    fn:local-name and fn:namespace-uri use NodeInfo.Exists as their
    argType since these functions' arguments should be elements, not
    values.
    
    Implement fn:namespace-uri calls with FNNamespaceUri0 and
    FNNamespaceUri1 case classes in FNFunctions.scala.  These classes
    override the run() method instead of the computeValue() method since
    the sibling FNLocalName0 and FNLocalName1 case classes are doing that.
    Some parts tricky to figure out were having to prevent a NPE from
    run() calls passing DStateForConstantFolding (that's why we check
    dstate.isCompile and terminate such calls), how to get the namespace
    uri from the element, what to do when the element is in no namespace
    (return ""), and what to do when the arg is a value instead of an
    element (raise a SDE).  Make sure both FNLocalName1 and
    FNNamespaceUri1 save off and restore dstate.currentNode, not dstate
    itself.
    
    Add new test cases namespace_uri_07 and namespace_uri_08 to
    Functions.tdml to test calling fn:namespace-uri with 7) a value
    instead of an element, and 8) an element in no namespace.
    
    Make TestDFDLExpressions.scala run test_namespace_uri_03 through
    test_namespace_uri_08 now that fn:namespace-uri is implemented.  Have
    to fix another issue before we can run test_namespace_uri_01 and
    test_namespace_uri_02.
    
    DAFFODIL-1114
---
 .../org/apache/daffodil/dpath/Expression.scala     | 12 ++-
 .../org/apache/daffodil/dpath/FNFunctions.scala    | 94 +++++++++++++++++++---
 .../section23/dfdl_functions/Functions.tdml        | 56 +++++++++++--
 .../section23/dfdl_functions/home_schema.dfdl.xsd  | 10 +++
 .../dfdl_expressions/TestDFDLExpressions.scala     | 10 ++-
 5 files changed, 157 insertions(+), 25 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
index c039300..0ecb5e6 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
@@ -1504,11 +1504,19 @@ case class FunctionCallExpression(functionQNameString: String, expressions: List
 
       case (RefQName(_, "local-name", FUNC), args) if args.length == 0 =>
         FNZeroArgExpr(functionQNameString, functionQName,
-          NodeInfo.String, NodeInfo.AnyAtomic, FNLocalName0(_, _))
+          NodeInfo.String, NodeInfo.Exists, FNLocalName0(_, _))
 
       case (RefQName(_, "local-name", FUNC), args) if args.length == 1 =>
         FNOneArgExpr(functionQNameString, functionQName, args,
-          NodeInfo.String, NodeInfo.AnyAtomic, FNLocalName1(_, _))
+          NodeInfo.String, NodeInfo.Exists, FNLocalName1(_, _))
+
+      case (RefQName(_, "namespace-uri", FUNC), args) if args.length == 0 =>
+        FNZeroArgExpr(functionQNameString, functionQName,
+          NodeInfo.String, NodeInfo.Exists, FNNamespaceUri0(_, _))
+
+      case (RefQName(_, "namespace-uri", FUNC), args) if args.length == 1 =>
+        FNOneArgExpr(functionQNameString, functionQName, args,
+          NodeInfo.String, NodeInfo.Exists, FNNamespaceUri1(_, _))
 
       case (RefQName(_, "string", XSD), args) =>
         FNOneArgExpr(functionQNameString, functionQName, args,
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
index fdf6c1d..05cc9df 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
@@ -43,11 +43,11 @@ import org.apache.daffodil.infoset.DataValue.DataValueBigDecimal
 import org.apache.daffodil.infoset.DataValue.DataValueBigInt
 import org.apache.daffodil.infoset.DataValue.DataValueBool
 import org.apache.daffodil.infoset.DataValue.DataValueCalendar
-import org.apache.daffodil.infoset.DataValue.DataValueInt
 import org.apache.daffodil.infoset.DataValue.DataValueLong
 import org.apache.daffodil.infoset.DataValue.DataValueNumber
 import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
 import org.apache.daffodil.infoset.DataValue.DataValueString
+import org.apache.daffodil.infoset.DataValue.DataValueURI
 import org.apache.daffodil.infoset.InfosetArrayIndexOutOfBoundsException
 import org.apache.daffodil.infoset.InfosetNoSuchChildElementException
 import org.apache.daffodil.infoset.InfosetNodeNotFinalException
@@ -683,26 +683,96 @@ case class FNLocalName1(recipe: CompiledDPath, argType: NodeInfo.Kind)
   }
 
   override def run(dstate: DState) {
-    // Save off original state, which is the original
-    // element/node that calls inputValueCalc with fn:local-name
-    //
-    val origState = dstate
+    // Save off original node, which is the original
+    // element/node that calls fn:local-name
+    val savedNode = dstate.currentNode
 
     // Execute the recipe/expression which should
-    // return a node/element whose local-name we want.
-    //
+    // return a node/element whose local-name we want
     recipe.run(dstate)
 
     val localName = dstate.currentElement.name
+    dstate.setCurrentNode(savedNode)
 
     if (localName.contains(":"))
       throw new IllegalArgumentException("fn:local-name failed. " + localName + " is not a valid NCName as it contains ':'.")
 
-    // The original state contains the node/element upon which
-    // fn:local-name was called.  This is where we should set
-    // the value.
-    //
-    origState.setCurrentValue(localName)
+    dstate.setCurrentValue(localName)
+  }
+}
+
+/**
+ * Returns the namespace URI of the expanded-QName of the context
+ * node as an xs:string value.
+ *
+ * If the context node is in no namespace, then the function returns
+ * the zero-length xs:string value instead.
+ *
+ * The following error may be raised:
+ * - If the context node is not an element, type error [err:XPTY004]
+ *
+ * This function is called when 0 arguments are provided.  We
+ * treat this as if the argument passed was "." to denote self.
+ */
+case class FNNamespaceUri0(recipe: CompiledDPath, argType: NodeInfo.Kind)
+  extends RecipeOpWithSubRecipes(recipe) {
+  override def run(dstate: DState) {
+    // Insist this is non-constant at compile time (to avoid a NPE)
+    if (dstate.isCompile)
+      throw new IllegalStateException()
+
+    // Check that the context node is really present
+    if (dstate.currentNode eq null)
+      dstate.SDE("Context node for fn:namespace-uri is not an element")
+
+    // Same as using "." to denote self.
+    val value = dstate.currentNode.namedQName.namespace.optURI match {
+      case Nope => ""
+      case uri => uri.get.toString
+    }
+    dstate.setCurrentValue(value)
+  }
+}
+
+/**
+ * Returns the namespace URI of the expanded-QName of \$arg as an
+ * xs:string value.
+ *
+ * If the element identified by \$arg is in no namespace, then the
+ * function returns the zero-length xs:string value instead.
+ *
+ * The following error may be raised:
+ * - If \$arg is not an element, type error [err:XPTY004]
+ */
+case class FNNamespaceUri1(recipe: CompiledDPath, argType: NodeInfo.Kind)
+  extends FNOneArg(recipe, argType) {
+  override def computeValue(value: DataValuePrimitive, dstate: DState) = {
+    Assert.usageError("not to be called. DPath compiler should be answering this without runtime calls.")
+  }
+
+  override def run(dstate: DState) {
+    // Insist this is non-constant at compile time (to avoid a NPE)
+    if (dstate.isCompile)
+      throw new IllegalStateException()
+
+    // Save original node that calls fn:namespace-uri and execute the
+    // recipe/expression which should return a node/element whose
+    // namespace-uri we want
+    val savedNode = dstate.currentNode
+    recipe.run(dstate)
+    val resultNode = dstate.currentNode
+    dstate.setCurrentNode(savedNode)
+
+    // Check that the expression returned an element, not a value
+    if (resultNode eq null)
+      dstate.SDE("Argument %s for fn:namespace-uri is not an element", recipe)
+
+    // Find and return the namespace-uri we want
+    val value = resultNode.namedQName.namespace.optURI match {
+      case Nope => ""
+      case uri => uri.get.toString
+    }
+    dstate.setCurrentValue(value)
   }
 }
 
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 32d7e3b..92a424d 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
@@ -11370,13 +11370,55 @@
       </tdml:dfdlInfoset>
     </tdml:infoset>
   </tdml:parserTestCase>
-  
-<!--
-    Test Name: nilled_01
-       Schema: booleanFunctions
-         Root: nilled01
-      Purpose: This test demonstrates that the fn:nilled function returns true if arg is a node with xsi:nil = true
--->
+
+  <!--
+      Test Name: namespace_uri_07
+         Schema: home_schema.dfdl.xsd
+           Root: e5
+        Purpose: This test demonstrates the namespace-uri() function causes a SDE if given a non-element arg
+  -->
+
+  <tdml:parserTestCase name="namespace_uri_07" root="e5"
+                       model="home_schema.dfdl.xsd" description="Section 23 - Functions - fn:namespace-uri() - DFDL-23-126R">
+
+    <tdml:document>
+      <tdml:documentPart type="text"></tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>fn:namespace-uri</tdml:error>
+      <tdml:error>is not an element</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Test Name: namespace_uri_08
+         Schema: home_schema.dfdl.xsd
+           Root: e6
+        Purpose: This test demonstrates the namespace-uri() function on an element with no namespace
+  -->
+
+  <tdml:parserTestCase name="namespace_uri_08" root="e6"
+                       model="home_schema.dfdl.xsd" description="Section 23 - Functions - fn:namespace-uri() - DFDL-23-126R">
+
+    <tdml:document>
+      <tdml:documentPart type="text"></tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <e6>
+          <uri xmlns=""></uri>
+        </e6>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      Test Name: nilled_01
+         Schema: booleanFunctions
+           Root: nilled01
+        Purpose: This test demonstrates that the fn:nilled function returns true if arg is a node with xsi:nil = true
+  -->
 
   <tdml:parserTestCase name="nilled_01" root="nilled01"
     model="booleanFunctions" description="Section 23 - Functions - fn:nilled - DFDL-23-129R">
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/home_schema.dfdl.xsd b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/home_schema.dfdl.xsd
index c8b46bd..7a19f41 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/home_schema.dfdl.xsd
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/home_schema.dfdl.xsd
@@ -63,4 +63,14 @@
     </xs:complexType>
   </xs:element>
 
+  <xs:element name="e5" type="xs:string" dfdl:inputValueCalc="{ fn:namespace-uri(0) }"/>
+
+  <xs:element name="e6">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element name="uri" type="xs:string" dfdl:inputValueCalc="{ fn:namespace-uri(/home:e6/uri) }"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
 </xs:schema>
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 c010547..4741428 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
@@ -516,10 +516,12 @@ class TestDFDLExpressions {
   //@Test def test_namespace_uri_01() { runner2.runOneTest("namespace_uri_01") }
   //@Test def test_namespace_uri_02() { runner2.runOneTest("namespace_uri_02") }
   //DFDL-1114
-  //@Test def test_namespace_uri_03() { runner2.runOneTest("namespace_uri_03") }
-  //@Test def test_namespace_uri_04() { runner2.runOneTest("namespace_uri_04") }
-  //@Test def test_namespace_uri_05() { runner2.runOneTest("namespace_uri_05") }
-  //@Test def test_namespace_uri_06() { runner2.runOneTest("namespace_uri_06") }
+  @Test def test_namespace_uri_03() { runner2.runOneTest("namespace_uri_03") }
+  @Test def test_namespace_uri_04() { runner2.runOneTest("namespace_uri_04") }
+  @Test def test_namespace_uri_05() { runner2.runOneTest("namespace_uri_05") }
+  @Test def test_namespace_uri_06() { runner2.runOneTest("namespace_uri_06") }
+  @Test def test_namespace_uri_07() { runner2.runOneTest("namespace_uri_07") }
+  @Test def test_namespace_uri_08() { runner2.runOneTest("namespace_uri_08") }
 
   //DFDL-1233
   @Test def test_nilled_02() { runner2.runOneTest("nilled_02") }