Olabusayo Kilo created DAFFODIL-3094:
----------------------------------------
Summary: Failure when prefix separator is not backtracked after
repeating complex element ends
Key: DAFFODIL-3094
URL: https://issues.apache.org/jira/browse/DAFFODIL-3094
Project: Daffodil
Issue Type: Bug
Affects Versions: 4.2.0
Reporter: Olabusayo Kilo
Fix For: 4.3.0
Attachments: data.dat, test.xsd
Schema:
{code:xml}
<xs:element name="r">
<xs:complexType>
<xs:sequence dfdl:separatorPosition="prefix" dfdl:separator="/ %NL;/"
dfdl:terminator="%NL;">
<xs:element name="foo" type="xs:string"/>
<xs:element name="GroupOfFields" maxOccurs="5"
dfdl:occursCountKind="implicit">
<xs:complexType>
<xs:sequence dfdl:separator="/ %NL;/"
dfdl:separatorSuppressionPolicy="never">
<xs:element name="c">
<xs:complexType>
<xs:choice>
<xs:element name="bar" type="xs:string"
dfdl:initiator="R:"/>
<xs:element name="ban" type="xs:string"
dfdl:initiator="N:"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="baz" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
{code}
Data:
{code:none}
/1/N:ABC/R:DEF/N:HIJ/R:LMN/2
{code}
In the above, an outer sequence is separatorPosition="prefix" and a complex
element, GroupOfFields, has occursCountKind="implicit", minOccurs=1 and
maxOccurs=5, and the data has exactly 4 real GroupOfFields occurrences
(ABC/DEF/HIJ/LMN) followed by /2; that trailing / belongs to baz's own prefix
separator, and 2 is baz's value.
Bug: since maxOccurs=5 and only 4 occurred, the parser must speculatively
attempt occurrence #5 (position 5 = maxRepeats, so
RequiredOptionalStatus.Optional). It:
1. Parses the prefix separator / before slot 5 and succeeds (bits 208→216).
2. Tries the child (GroupOfFields's inner choice bar/ban) and both initiators
fail, 0 bits consumed (isZL=true).
That failure goes through anyTypeElementFailedParseAttemptStatus
(SequenceChildParseResultHelper.scala:190-218):
{code:scala}
case _: RequiredOptionalStatus.Optional if isZL => ParseAttemptStatus.AbsentRep
{code}
AbsentRep is documented as "backtrack any elements created but retain bit
position" which is exactly what parseOneInstanceWithMaybePoU does
(SequenceParserBases.scala:413-421): it resets to the PoU (undoing the infoset
side effects) but then explicitly forces the bit position back forward past the
separator (pstate.dataInputStream.setBitPos0b(currentPos)), i.e. it keeps the
separator consumed.
That convention is correct for a simple type whose legitimate empty
representation is genuinely zero-length (classic "two adjacent separators = an
empty positional slot" idiom). But here the child is a complex type wrapping a
mandatory xs:choice with two required initiators; there is no such thing as a
valid zero-length match for it. The ZL failure isn't "the slot is legitimately
empty," it's "there is no occurrence 5 at all." Keeping the separator steals
the / that rightfully belongs to baz's own prefix separator, so baz's separator
search then fails, and ultimately the sequence terminator search fails too.
anyTypeElementFailedParseAttemptStatus conflates two different situations under
one AbsentRep result: "legitimately empty content" vs. "total structural
failure with nothing consumed"; only the former should retain the separator.
The complex-type/model-group path currently has no static "can this ever
legitimately be zero-length" flag analogous to isEmptyRepZeroLength (which
simple types have) or isModelGroupRepPossiblyZeroLength (which exists for model
groups used directly as sequence children, but isn't threaded into
element-with-complex-type result helpers).
This is a real design gap the code needs a way to know, for a complex-typed
array element, whether its content can ever be validly zero-length, and use
that to decide AbsentRep (retain separator) vs. a status that forces full
backtrack (give the separator back).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)