diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala index a6d52b8a5c..50722e1360 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala @@ -21,12 +21,14 @@ import org.apache.daffodil.core.dsom.ElementBase import org.apache.daffodil.core.dsom.ModelGroup import org.apache.daffodil.core.dsom.QuasiElementDeclBase import org.apache.daffodil.core.dsom.Root +import org.apache.daffodil.core.dsom.SequenceTermBase import org.apache.daffodil.core.dsom.Term import org.apache.daffodil.lib.exceptions.Assert import org.apache.daffodil.lib.schema.annotation.props.gen.AlignmentKind import org.apache.daffodil.lib.schema.annotation.props.gen.AlignmentUnits import org.apache.daffodil.lib.schema.annotation.props.gen.LengthKind import org.apache.daffodil.lib.schema.annotation.props.gen.LengthUnits +import org.apache.daffodil.lib.schema.annotation.props.gen.SeparatorPosition import org.apache.daffodil.lib.util.Math case class AlignmentMultipleOf(nBits: Long) { @@ -65,7 +67,7 @@ trait AlignedMixin extends GrammarMixin { self: Term => final lazy val isKnownToBeAligned: Boolean = LV(Symbol("isKnownToBeAligned")) { if (!isRepresented || (alignmentKindDefaulted == AlignmentKind.Manual)) true else { - val pa = priorAlignmentWithLeadingSkipApprox + val pa = priorAlignmentWithLeftFramingApprox val aa = alignmentApprox val res = (pa % aa) == 0 res @@ -85,7 +87,7 @@ trait AlignedMixin extends GrammarMixin { self: Term => else if (isKnownEncoding) { if (knownEncodingAlignmentInBits == 1) true - else if (priorAlignmentWithLeadingSkipApprox.nBits % knownEncodingAlignmentInBits == 0) + else if (priorAlignmentWithLeftFramingApprox.nBits % knownEncodingAlignmentInBits == 0) true else false @@ -134,11 +136,14 @@ trait AlignedMixin extends GrammarMixin { self: Term => LengthExact(trailingSkipInBits) } - // FIXME: DAFFODIL-2295 - // Does not take into account that in a sequence, what may be prior may be a separator. - // The separator is text in some encoding, might not be the same as this term's encoding, and - // the alignment will be left where that text leaves it. - // + /** + * The priorAlignmentApprox doesn't need to take into account the separator + * alignment/length as that comes after the priorAlignmentApprox, but before + * the leadingSkip. + * + * TODO: DAFFODIL-3056 We need to consider the initiator MTA/length, + * as well as the prefix length + */ private lazy val priorAlignmentApprox: AlignmentMultipleOf = LV(Symbol("priorAlignmentApprox")) { if (this.isInstanceOf[Root] || this.isInstanceOf[QuasiElementDeclBase]) { @@ -201,7 +206,7 @@ trait AlignedMixin extends GrammarMixin { self: Term => // Return 0 here, unordered alignment will be handled by unorderedSequenceSelfAlignment AlignmentMultipleOf(0) } else { - ps.endingAlignmentApprox + ps.endingAlignmentWithRightFramingApprox } eaa } @@ -228,14 +233,59 @@ trait AlignedMixin extends GrammarMixin { self: Term => } }.value - private lazy val priorAlignmentWithLeadingSkipApprox: AlignmentMultipleOf = { - priorAlignmentApprox + leadingSkipApprox + private lazy val separatorPrefixMTAApprox = + this.optLexicalParent match { + case Some(s: SequenceTermBase) if s.hasSeparator => + import SeparatorPosition.* + s.separatorPosition match { + case Prefix | Infix => AlignmentMultipleOf(s.knownEncodingAlignmentInBits) + case Postfix => AlignmentMultipleOf(0) + } + case _ => AlignmentMultipleOf(0) + } + + private lazy val separatorPostfixMTAApprox = + this.optLexicalParent match { + case Some(s: SequenceTermBase) if s.hasSeparator => + import SeparatorPosition.* + s.separatorPosition match { + case Prefix | Infix => AlignmentMultipleOf(0) + case Postfix => AlignmentMultipleOf(s.knownEncodingAlignmentInBits) + } + case _ => AlignmentMultipleOf(0) + } + + private lazy val separatorLengthApprox = this.optLexicalParent match { + case Some(s: SequenceTermBase) if s.hasSeparator => + getEncodingLengthApprox(s) + case _ => LengthMultipleOf(0) + } + + private def getEncodingLengthApprox(t: Term) = { + if (t.isKnownEncoding) { + val dfdlCharset = t.charsetEv.optConstant.get + val fixedWidth = + if (dfdlCharset.maybeFixedWidth.isDefined) dfdlCharset.maybeFixedWidth.get else 8 + LengthMultipleOf(fixedWidth) + } else { + // runtime encoding, which must be a byte-length encoding + LengthMultipleOf(8) + } + } + + private lazy val priorAlignmentWithLeftFramingApprox: AlignmentMultipleOf = { + val pawls = + (priorAlignmentApprox + * separatorPrefixMTAApprox) + + separatorLengthApprox + + leadingSkipApprox + pawls } protected lazy val contentStartAlignment: AlignmentMultipleOf = { - if (priorAlignmentWithLeadingSkipApprox % alignmentApprox == 0) { + if (priorAlignmentWithLeftFramingApprox % alignmentApprox == 0) { // alignment won't be needed, continue using prior alignment as start alignment - priorAlignmentWithLeadingSkipApprox + priorAlignmentWithLeftFramingApprox } else { // alignment will be needed, it will be forced to be aligned to alignmentApprox alignmentApprox @@ -291,6 +341,18 @@ trait AlignedMixin extends GrammarMixin { self: Term => } } + /** + * The postfix separator MTA/length needs to be added after the trailing skip + * + * TODO: DAFFODIL-3057 needs to consider terminator MTA/length before trailingSkip + */ + protected lazy val endingAlignmentWithRightFramingApprox: AlignmentMultipleOf = { + val res = (endingAlignmentApprox + * separatorPostfixMTAApprox) + + separatorLengthApprox + res + } + protected lazy val elementSpecifiedLengthApprox: LengthApprox = { this match { case eb: ElementBase => { @@ -329,15 +391,7 @@ trait AlignedMixin extends GrammarMixin { self: Term => } private lazy val encodingLengthApprox: LengthApprox = { - if (isKnownEncoding) { - val dfdlCharset = charsetEv.optConstant.get - val fixedWidth = - if (dfdlCharset.maybeFixedWidth.isDefined) dfdlCharset.maybeFixedWidth.get else 8 - LengthMultipleOf(fixedWidth) - } else { - // runtime encoding, which must be a byte-length encoding - LengthMultipleOf(8) - } + getEncodingLengthApprox(this) } protected lazy val isKnownToBeByteAlignedAndByteLength: Boolean = { diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml index 0ab4ea916e..61d08de6b9 100644 --- a/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml +++ b/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml @@ -143,14 +143,6 @@ separatorPosition="infix" emptyElementParsePolicy="treatAsEmpty"/> - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + aa/Bbb + + + + + aa + bb + + + + + + + + /Aaa/Bbb + + + + + aa + bb + + + + + + + + aa/Bbb/ + + + + + + aa + bb + + + + + + + + cc-Aaa/Bbb/-dd- + + + + + + cc + aa + bb + dd + + + + + + diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala b/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala index 3ffd036121..9c27ba7261 100644 --- a/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala +++ b/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala @@ -55,4 +55,11 @@ class TestSepTests extends TdmlTests { // DAFFODIL-2791 @Test def test_treatAsAbsent_occursIndex = test + + // DAFFODIL-2295 + @Test def test_sep_alignment_1 = test + @Test def test_sep_alignment_2 = test + @Test def test_sep_alignment_3 = test + + @Test def test_sep_alignment_4 = test }