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 d83a86086 Fix property scoping of default properties
d83a86086 is described below

commit d83a86086dec799bb4acb7e31a8617b5a597754a
Author: Steve Lawrence <[email protected]>
AuthorDate: Fri Oct 31 11:34:01 2025 -0400

    Fix property scoping of default properties
    
    Section 8.1.4, Rule 5 of the DFDL specification describes the
    scoping/resolution of default properties, saying:
    
        Obtain applicable "default" properties from a dfdl:format annotation
        on the xs:schema that contains the component (if such annotation is
        present). Combine these with the current working set of "default"
        properties, the latter overriding the former (that is, inner wins).
        Result is a new working set of "default" properties.
    
    So if a schema component does not explicitly define a property, we
    should look for default properties on the innermost components first.
    
    But Daffodil currently does the reverse, looking at the default
    properties of the outermost components first. This happens because we
    build the default component property chain list recursively, prepending
    the outermost components default chain to the list. This means wen we
    scan for default values we look at the outermost chain first.
    
    The solution to this is to reverse the default component chain so that
    when we scan it for properties the innermost components appear first and
    are given precedence.
    
    Note that it is possible that some schemas unintentionally relied on
    this buggy behavior, and it can sometimes be very difficult to figure
    out which properties changed due to the change in property resolution.
    To aid in debugging, we keep the old reversed list and when a property
    is found in the default list, we also find it in the reversed default
    list. If the property value found in the reversed list is different than
    found using the correct list, this indicates the new logic could have
    changed a relevant property value and we output a warning. In many cases
    this can actually be ignored since the property values might not
    actually matter, but when things break this can be a useful diagnostic.
    
    Deprecation/Compatibility:
    
    When resolving default properties, Daffodil now gives precedence to
    innermost referenced schema components rather than the outermost, as
    described by section 8.1.4 of the DFDL specification. If this new logic
    results in a different property value than previous versions of
    Daffodil then a schema definition warning is issued.
    
    DAFFODIL-2855
---
 .../core/dsom/AnnotatedSchemaComponent.scala       | 39 ++++++++++++++--
 ...xample_a02_targetnamespace_unqualified.dfdl.xsd |  2 +-
 .../resources/org/apache/daffodil/xsd/dafext.xsd   |  1 +
 .../daffodil/section06/namespaces/namespaces.tdml  | 16 +++----
 .../subfolder/multi_C_06_nons_valid.dfdl.xsd       |  2 +-
 .../property_scoping/PropertyScoping_01.tdml       | 14 ++++++
 .../property_scoping/PropertyScoping_06a.dfdl.xsd  | 50 +++++++++------------
 .../property_scoping/PropertyScoping_06b.dfdl.xsd  | 52 +++++++++-------------
 .../property_scoping/TestPropertyScoping.scala     |  1 +
 9 files changed, 103 insertions(+), 74 deletions(-)

diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/dsom/AnnotatedSchemaComponent.scala
 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/dsom/AnnotatedSchemaComponent.scala
index b69324a48..4f3e1f60c 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/dsom/AnnotatedSchemaComponent.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/dsom/AnnotatedSchemaComponent.scala
@@ -107,10 +107,24 @@ trait ResolvesScopedProperties extends FindPropertyMixin 
{ self: Term =>
   private def findDefaultProperty(pname: String): PropertyLookupResult = {
     val result = findPropertyInSources(pname, defaultPropertySources)
     val fixup = result match {
-      case Found(value, loc, pname, _) =>
+      case Found(value, loc, pname, _) => {
+        // we found the property in default format annotation. Daffodil 4.1.0 
correct the order
+        // we resolve default property sources to match the DFDL 
specification. Fortunately,
+        // this does not affect most schemas, but for those that it does, it 
can be very
+        // difficult to determine what the old value was. So we lookup 
property sources in the
+        // old incorrect reversed order and see if we find the same value and 
warn if not
+        val oldResult = findPropertyInSources(pname, 
defaultPropertySourcesReversed)
+        val oldValue = oldResult.toOption.get
+        schemaDefinitionWarningWhen(
+          WarnID.ChangedDefaultPropertyResolution,
+          oldValue != value,
+          s"""Value of property $pname changed from "$oldValue" to "$value" 
due to corrections in default property resolution."""
+        )
+
         // found as a default property.
         // supply constructor's last arg is boolean indicating it's a default 
property
         Found(value, loc, pname, true)
+      }
       case NotFound(nd, d, pn) =>
         Assert.invariant(d.isEmpty)
         NotFound(
@@ -357,16 +371,33 @@ trait AnnotatedSchemaComponent
       }
     }.value
 
-  final protected lazy val defaultPropertySources: Seq[ChainPropProvider] =
-    LV(Symbol("defaultPropertySources")) {
+  /**
+   * The default property sources we should use for resolving property values 
are calculated by
+   * combining the default chain of referred components with our own default 
chain. We do this
+   * by prepending our own defaultFormatChain to the chain of referred 
components. Prepends are
+   * efficient, but this creates the chain in the reverse order, since we want 
to give
+   * precedence to the innermost referred components (see section 8.1.4 of the 
DFDL
+   * specification). We could reverse it now, but the reversed list is 
actually useful to keep
+   * around. For example, Daffodil 4.1.0 fixed a bug to correctly use the 
non-reversed order,
+   * but some schemas relied on this buggy behavior. By storing the old 
reversed order, we can
+   * use this to detect and warn if schemas are relying on the buggy behavior.
+   */
+  final protected lazy val defaultPropertySourcesReversed: 
Seq[ChainPropProvider] =
+    LV(Symbol("defaultPropertySourcesReversed")) {
       val refTo = refersToForPropertyCombining
-      val chainFromReferredTo = refTo.toSeq.map { _.defaultPropertySources 
}.distinct.flatten
+      val chainFromReferredTo =
+        refTo.toSeq.map { _.defaultPropertySourcesReversed }.distinct.flatten
       val completeDefaultFormatChain =
         defaultFormatChain +: chainFromReferredTo
       val seq = completeDefaultFormatChain.distinct
       seq
     }.value
 
+  final protected lazy val defaultPropertySources: Seq[ChainPropProvider] =
+    LV(Symbol("defaultPropertySources")) {
+      defaultPropertySourcesReversed.reverse
+    }.value
+
   final protected lazy val nonDefaultFormatChain: ChainPropProvider = {
     val fa = formatAnnotation
     val fc = fa.formatChain
diff --git 
a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
 
b/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
index 5ebc23108..040ad788d 100644
--- 
a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
+++ 
b/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
@@ -43,7 +43,7 @@
   </xs:annotation>
 
   <xs:element name="inty" type="xs:int" dfdl:lengthKind="delimited"/>
-  <xs:element name="intx" type="xs:int" nillable="true" 
dfdl:nilKind="literalValue" dfdl:nilValue="^"/>
+  <xs:element name="intx" type="xs:int" dfdl:lengthKind="delimited" 
nillable="true" dfdl:nilKind="literalValue" dfdl:nilValue="^"/>
 
 
 </xs:schema>
diff --git 
a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd 
b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
index befb240b6..f82ea9b06 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
@@ -713,6 +713,7 @@
               <xs:documentation>Deprecated.</xs:documentation>
             </xs:annotation>
           </xs:enumeration>
+          <xs:enumeration value="changedDefaultPropertyResolution" />
           <xs:enumeration value="deprecatedBuiltInFormats" />
           <xs:enumeration value="deprecatedEncodingNameUSASCII7BitPacked" />
           <xs:enumeration value="deprecatedExpressionResultCoercion" />
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/namespaces.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/namespaces.tdml
index 0052c6adf..f8c8552d0 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/namespaces.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/namespaces.tdml
@@ -744,18 +744,18 @@
     as a restriction base.
     - In Schema B, an element is declared of the simpleType in C.
     - In Schema A, a sequence of B elements is declared
-    In this case, each Schema file in the chain, starting at F, imposes its 
-    own terminator value, named
-    after the schema itself (Schema E's terminator is "E", B's is "B", etc.). 
-    Each time a new schema is used
-    in the chain, the separator is overwritten. Schema B is the last schema 
-    in the chain to set this value,
-    so the terminator for our data set will be "B".
+    In this case, each Schema file in the chain, starting at F, sets a default
+    terminator value, named after the schema itself (Schema E's terminator is
+    "E", B's is "B", etc.). 
+    Each time a new schema is used in the chain, the separator is overwritten.
+    Default properties are resolved from the defaults of the innermost schema
+    components, which in this case is schema E, so the terminator for our data
+    set will be "E"
   -->
 
   <tdml:parserTestCase name="long_chain_06" root="nestSequence6"
     model="multi_A_03.dfdl.xsd" description="import a schema - DFDL-6-007R">
-    <tdml:document><![CDATA[5632B]]></tdml:document>
+    <tdml:document><![CDATA[5632E]]></tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
         <nestSequence6>
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/subfolder/multi_C_06_nons_valid.dfdl.xsd
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/subfolder/multi_C_06_nons_valid.dfdl.xsd
index c6925cf5b..e19ab0bb9 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/subfolder/multi_C_06_nons_valid.dfdl.xsd
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section06/namespaces/subfolder/multi_C_06_nons_valid.dfdl.xsd
@@ -33,6 +33,6 @@
     </xs:appinfo>
   </xs:annotation>
 
-  <xs:element name="cElem" type="xs:string" dfdl:initiator="c:" 
dfdl:outputValueCalc="{ 'done' }" />
+  <xs:element name="cElem" type="xs:string" dfdl:lengthKind="delimited" 
dfdl:initiator="c:" dfdl:outputValueCalc="{ 'done' }" />
 
 </xs:schema>
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_01.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_01.tdml
index 9d5bf5851..9f9811ccf 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_01.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_01.tdml
@@ -686,4 +686,18 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+  <tdml:parserTestCase name="property_scoping_12"
+    model="PropertyScoping_06a.dfdl.xsd"
+    description="Section 8 Default properties inherited from referenced 
components">
+
+    <tdml:document>
+      <tdml:documentPart type="byte">00000001</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:int01>1</tns:int01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git 
a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_06a.dfdl.xsd
similarity index 51%
copy from 
daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
copy to 
daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_06a.dfdl.xsd
index 5ebc23108..8cb900dd7 100644
--- 
a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_06a.dfdl.xsd
@@ -16,34 +16,24 @@
   limitations under the License.
 -->
 
-<xs:schema targetNamespace="http://a02.com";
-  xmlns:xs="http://www.w3.org/2001/XMLSchema";
+<schema xmlns="http://www.w3.org/2001/XMLSchema";
   xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/";
-  xmlns:a2="http://a02.com";>
-
-  <xs:include 
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
-
-  <xs:annotation>
-    <xs:appinfo source="http://www.ogf.org/dfdl/";>
-      <dfdl:format ref="a2:GeneralFormat"
-        separator=""
-        initiator=""
-        separatorPosition="infix"
-        ignoreCase="no"
-        separatorSuppressionPolicy="anyEmpty"
-        terminator=""
-        occursCountKind="parsed"
-        initiatedContent="no"
-        representation="text"
-        textNumberRep="standard"
-        encoding="ASCII"
-        textTrimKind="none"
-        leadingSkip='0'/>
-    </xs:appinfo>
-  </xs:annotation>
-
-  <xs:element name="inty" type="xs:int" dfdl:lengthKind="delimited"/>
-  <xs:element name="intx" type="xs:int" nillable="true" 
dfdl:nilKind="literalValue" dfdl:nilValue="^"/>
-
-
-</xs:schema>
+  xmlns:xs="http://www.w3.org/2001/XMLSchema";
+  xmlns:ex="http://example.com";
+  targetNamespace="http://example.com";>
+
+  <include 
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />
+
+  <annotation>
+    <appinfo source="http://www.ogf.org/dfdl/";>
+      <dfdl:format ref="ex:GeneralFormat" 
+        representation="binary"
+        byteOrder="littleEndian" />
+    </appinfo>
+  </annotation>
+
+  <include schemaLocation="PropertyScoping_06b.dfdl.xsd" />
+
+  <element name="int01" type="ex:int" />
+
+</schema>
diff --git 
a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_06b.dfdl.xsd
similarity index 51%
copy from 
daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
copy to 
daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_06b.dfdl.xsd
index 5ebc23108..e798a39a4 100644
--- 
a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section08/property_scoping/PropertyScoping_06b.dfdl.xsd
@@ -16,34 +16,26 @@
   limitations under the License.
 -->
 
-<xs:schema targetNamespace="http://a02.com";
-  xmlns:xs="http://www.w3.org/2001/XMLSchema";
+<schema xmlns="http://www.w3.org/2001/XMLSchema";
   xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/";
-  xmlns:a2="http://a02.com";>
-
-  <xs:include 
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
-
-  <xs:annotation>
-    <xs:appinfo source="http://www.ogf.org/dfdl/";>
-      <dfdl:format ref="a2:GeneralFormat"
-        separator=""
-        initiator=""
-        separatorPosition="infix"
-        ignoreCase="no"
-        separatorSuppressionPolicy="anyEmpty"
-        terminator=""
-        occursCountKind="parsed"
-        initiatedContent="no"
-        representation="text"
-        textNumberRep="standard"
-        encoding="ASCII"
-        textTrimKind="none"
-        leadingSkip='0'/>
-    </xs:appinfo>
-  </xs:annotation>
-
-  <xs:element name="inty" type="xs:int" dfdl:lengthKind="delimited"/>
-  <xs:element name="intx" type="xs:int" nillable="true" 
dfdl:nilKind="literalValue" dfdl:nilValue="^"/>
-
-
-</xs:schema>
+  xmlns:xs="http://www.w3.org/2001/XMLSchema";
+  xmlns:ex="http://example.com";
+  targetNamespace="http://example.com";>
+
+  <include 
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd" />
+
+  <annotation>
+    <appinfo source="http://www.ogf.org/dfdl/";>
+      <dfdl:format ref="ex:GeneralFormat"
+        representation="binary"
+        byteOrder="bigEndian" />
+    </appinfo>
+  </annotation>
+
+  <include schemaLocation="PropertyScoping_06b.dfdl.xsd" />
+
+  <simpleType name="int">
+    <restriction base="xs:int" />
+  </simpleType>
+
+</schema>
diff --git 
a/daffodil-test/src/test/scala/org/apache/daffodil/section08/property_scoping/TestPropertyScoping.scala
 
b/daffodil-test/src/test/scala/org/apache/daffodil/section08/property_scoping/TestPropertyScoping.scala
index 7f39399f8..98e5c62ee 100644
--- 
a/daffodil-test/src/test/scala/org/apache/daffodil/section08/property_scoping/TestPropertyScoping.scala
+++ 
b/daffodil-test/src/test/scala/org/apache/daffodil/section08/property_scoping/TestPropertyScoping.scala
@@ -79,6 +79,7 @@ class TestPropertyScoping01 extends TdmlTests {
   @Test def unparse_property_scoping_11 = test
   @Test def unparse_property_scoping_12 = test
   @Test def NearestEnclosingSequenceElementRef = test
+  @Test def property_scoping_12 = test
 
   @Test def refElementFormFail = test
 }

Reply via email to