You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ja...@apache.org on 2019/06/07 17:07:13 UTC
[incubator-daffodil] branch master updated: Implement
dfdl:choiceLength='explicit'
This is an automated email from the ASF dual-hosted git repository.
jadams 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 2ec2bee Implement dfdl:choiceLength='explicit'
2ec2bee is described below
commit 2ec2bee17c7d0d85eaffefc268a974d1844d265b
Author: Josh Adams <ja...@tresys.com>
AuthorDate: Mon May 13 08:23:39 2019 -0400
Implement dfdl:choiceLength='explicit'
DAFFODIL-640
---
.../org/apache/daffodil/dsom/ChoiceGroup.scala | 2 +
.../RawCommonRuntimeValuedPropertiesMixin.scala | 2 +
.../daffodil/dsom/RuntimePropertyMixins.scala | 2 +
.../daffodil/grammar/ChoiceGrammarMixin.scala | 4 +
.../grammar/primitives/ChoiceCombinator.scala | 20 +-
.../unparsers/ChoiceAndOtherVariousUnparsers.scala | 20 +-
.../processors/unparsers/SpecifiedLength2.scala | 131 ++++++-
.../unparsers/SpecifiedLengthUnparsers.scala | 3 +
.../processors/unparsers/StreamSplitterMixin.scala | 125 +++++++
.../unparsers/SuppressableSeparatorUnparser.scala | 104 +-----
.../org/apache/daffodil/processors/EvElement.scala | 11 +-
.../daffodil/processors/parsers/Parser.scala | 2 +
.../parsers/SpecifiedLengthParsers.scala | 14 +-
.../choice_groups/ChoiceLengthExplicit.tdml | 377 +++++++++++++++++++++
.../section15/choice_groups/TestChoice.scala | 19 ++
15 files changed, 710 insertions(+), 126 deletions(-)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala
index 83a7cf9..b9d6114 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala
@@ -20,6 +20,7 @@ package org.apache.daffodil.dsom
import scala.xml.Node
import scala.xml._
import org.apache.daffodil.schema.annotation.props.gen.Choice_AnnotationMixin
+import org.apache.daffodil.schema.annotation.props.gen.ChoiceAGMixin
import org.apache.daffodil.grammar.ChoiceGrammarMixin
import org.apache.daffodil.processors.RuntimeData
import org.apache.daffodil.processors.ChoiceRuntimeData
@@ -106,6 +107,7 @@ abstract class ChoiceTermBase(
with Choice_AnnotationMixin
with RawDelimitedRuntimeValuedPropertiesMixin // initiator and terminator (not separator)
with ChoiceGrammarMixin
+ with ChoiceAGMixin
with HasOptRepTypeMixinImpl {
requiredEvaluations(branchesAreNonOptional)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RawCommonRuntimeValuedPropertiesMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RawCommonRuntimeValuedPropertiesMixin.scala
index 1957ea4..1985c96 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RawCommonRuntimeValuedPropertiesMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RawCommonRuntimeValuedPropertiesMixin.scala
@@ -39,6 +39,8 @@ trait RawDelimitedRuntimeValuedPropertiesMixin
protected final lazy val initiatorRaw = requireProperty(optionInitiatorRaw)
protected final lazy val optionTerminatorRaw = findPropertyOption("terminator")
protected final lazy val terminatorRaw = requireProperty(optionTerminatorRaw)
+ protected final lazy val optionChoiceLengthRaw = findPropertyOption("choiceLength")
+ protected final lazy val choiceLengthRaw = requireProperty(optionChoiceLengthRaw)
}
trait RawElementRuntimeValuedPropertiesMixin
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RuntimePropertyMixins.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RuntimePropertyMixins.scala
index ba00b6c..8b2065c 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RuntimePropertyMixins.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RuntimePropertyMixins.scala
@@ -46,6 +46,7 @@ import org.apache.daffodil.schema.annotation.props.gen.DFDLBaseTypeMixin
import org.apache.daffodil.schema.annotation.props.gen.DFDLSimpleTypeMixin
import org.apache.daffodil.schema.annotation.props.gen.LengthAGMixin
import org.apache.daffodil.schema.annotation.props.gen.LengthKind
+import org.apache.daffodil.schema.annotation.props.gen.ChoiceAGMixin
import org.apache.daffodil.schema.annotation.props.gen.OccursAGMixin
import org.apache.daffodil.schema.annotation.props.gen.Representation
import org.apache.daffodil.schema.annotation.props.gen.Sequence_AnnotationMixin
@@ -84,6 +85,7 @@ import org.apache.daffodil.schema.annotation.props.Found
trait TermRuntimeValuedPropertiesMixin
extends DFDLBaseTypeMixin
with PropertyReferencedElementInfosMixin
+ with ChoiceAGMixin
with RawCommonRuntimeValuedPropertiesMixin { decl: Term =>
private lazy val encodingExpr = LV('encoding) {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ChoiceGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ChoiceGrammarMixin.scala
index 37375c5..9851928 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ChoiceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ChoiceGrammarMixin.scala
@@ -19,6 +19,10 @@ package org.apache.daffodil.grammar
import org.apache.daffodil.dsom.ChoiceTermBase
import org.apache.daffodil.grammar.primitives.ChoiceCombinator
+import org.apache.daffodil.processors.parsers.NadaParser
+import org.apache.daffodil.processors.unparsers.Unparser
+import org.apache.daffodil.processors.unparsers.ChoiceUnusedUnparser
+import org.apache.daffodil.util.Maybe
trait ChoiceGrammarMixin extends GrammarMixin { self: ChoiceTermBase =>
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
index 4661042..1eaed1d 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
@@ -26,6 +26,7 @@ import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.cookers.ChoiceBranchKeyCooker
import org.apache.daffodil.api.WarnID
import org.apache.daffodil.equality._
+import org.apache.daffodil.schema.annotation.props.gen.ChoiceLengthKind
import org.apache.daffodil.schema.annotation.props.gen.ChoiceKeyKindType
import org.apache.daffodil.dsom.ChoiceTermBase
import scala.util.Try
@@ -35,6 +36,7 @@ import scala.math.BigInt
import org.apache.daffodil.cookers.RepValueCooker
import org.apache.daffodil.processors.RangeBound
import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.MaybeInt
/*
* The purpose of the ChoiceCombinator (and the parsers it creates) is to
@@ -67,9 +69,19 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
lazy val optChoiceDispatchKeyKind = Some(ch.choiceDispatchKeyKind)
+ // dfdl:choiceLength is always specified in bytes
+ private lazy val choiceLengthInBits: MaybeInt = ch.choiceLengthKind match {
+ case ChoiceLengthKind.Explicit => MaybeInt(ch.choiceLength * 8)
+ case ChoiceLengthKind.Implicit => MaybeInt.Nope
+ }
+
lazy val parser: Parser = {
if (!ch.isDirectDispatch) {
- new ChoiceParser(ch.termRuntimeData, parsers.toVector)
+ val cp = new ChoiceParser(ch.termRuntimeData, parsers.toVector)
+ ch.choiceLengthKind match {
+ case ChoiceLengthKind.Implicit => cp
+ case ChoiceLengthKind.Explicit => new SpecifiedLengthChoiceParser(cp, ch.choiceRuntimeData, choiceLengthInBits.get)
+ }
} else {
val dispatchBranchKeyValueTuples: Seq[(String, Gram)] = alternatives.flatMap { alt =>
val keyTerm = alt.context.asInstanceOf[Term]
@@ -176,7 +188,6 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
new ChoiceDispatchCombinatorParser(ch.termRuntimeData, ch.choiceDispatchKeyEv, serializableMap, serializableKeyRangeMap)
case ChoiceKeyKindType.Speculative => Assert.invariantFailed("ChoiceKeyKindType==speculative while isDirectDispatch==true")
}
-
}
}
@@ -199,8 +210,9 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
val mapValues = eventUnparserMap.map { case (k, v) => v }.toSeq.filterNot(_.isEmpty)
if (mapValues.isEmpty)
new NadaUnparser(null)
- else
- new ChoiceCombinatorUnparser(ch.modelGroupRuntimeData, eventUnparserMap)
+ else {
+ new ChoiceCombinatorUnparser(ch.modelGroupRuntimeData, eventUnparserMap, choiceLengthInBits)
+ }
} else {
// Choices inside a hidden group ref are slightly different because we
// will never see events for any of the branches. Instead, we will just
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
index 0a9f432..dd36f7b 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
@@ -21,8 +21,10 @@ import org.apache.daffodil.processors._
import org.apache.daffodil.infoset._
import org.apache.daffodil.processors.RuntimeData
import org.apache.daffodil.processors.dfa.DFADelimiter
+import org.apache.daffodil.schema.annotation.props.gen.ChoiceLengthKind
import org.apache.daffodil.util.Maybe._
import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.MaybeInt
import org.apache.daffodil.util.Maybe._
class ComplexTypeUnparser(rd: RuntimeData, bodyUnparser: Unparser)
@@ -43,7 +45,10 @@ class ComplexTypeUnparser(rd: RuntimeData, bodyUnparser: Unparser)
}
}
-class ChoiceCombinatorUnparser(mgrd: ModelGroupRuntimeData, eventUnparserMap: Map[ChoiceBranchEvent, Unparser])
+class ChoiceCombinatorUnparser(
+ mgrd: ModelGroupRuntimeData,
+ eventUnparserMap: Map[ChoiceBranchEvent, Unparser],
+ choiceLengthInBits: MaybeInt)
extends CombinatorUnparser(mgrd)
with ToBriefXMLImpl {
override def nom = "Choice"
@@ -71,7 +76,18 @@ class ChoiceCombinatorUnparser(mgrd: ModelGroupRuntimeData, eventUnparserMap: Ma
UnparseError(One(mgrd.schemaFileLocation), One(state.currentLocation), "Encountered event %s. Expected one of %s.",
key, eventUnparserMap.keys.mkString(", "))
}
- childUnparser.unparse1(state)
+
+ if (choiceLengthInBits.isDefined) {
+ val suspendableOp = new ChoiceUnusedUnparserSuspendableOperation(mgrd, choiceLengthInBits.get)
+ val choiceUnusedUnparser = new ChoiceUnusedUnparser(mgrd, choiceLengthInBits.get, suspendableOp)
+
+ suspendableOp.captureDOSStartForChoiceUnused(state)
+ childUnparser.unparse1(state)
+ suspendableOp.captureDOSEndForChoiceUnused(state)
+ choiceUnusedUnparser.unparse(state)
+ } else {
+ childUnparser.unparse1(state)
+ }
}
}
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala
index db20809..b6860dc 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala
@@ -22,6 +22,8 @@ import org.apache.daffodil.infoset.DIComplex
import org.apache.daffodil.infoset.DIElement
import org.apache.daffodil.infoset.DISimple
import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.ModelGroupRuntimeData
+import org.apache.daffodil.processors.RuntimeData
import org.apache.daffodil.processors.SuspendableOperation
import org.apache.daffodil.processors.UnparseTargetLengthInBitsEv
import org.apache.daffodil.processors.charset.BitsCharset
@@ -37,6 +39,8 @@ import org.apache.daffodil.processors.LengthEv
import org.apache.daffodil.processors.Evaluatable
import org.apache.daffodil.util.MaybeJULong
import org.apache.daffodil.io.DirectOrBufferedDataOutputStream
+import org.apache.daffodil.io.ZeroLengthStatus
+import org.apache.daffodil.io.DataOutputStream
import passera.unsigned.ULong
import java.nio.charset.MalformedInputException
@@ -405,6 +409,26 @@ sealed trait NeedValueAndTargetLengthMixin {
}
}
+trait SkipTheBits { self: SuspendableOperation =>
+
+ protected val rd: RuntimeData
+
+ protected final def skipTheBits(ustate: UState, skipInBits: Long) {
+ if (skipInBits > 0) {
+ val dos = ustate.dataOutputStream
+ if (!dos.skip(skipInBits, ustate))
+ UE(ustate, "Unable to skip %s(bits).", skipInBits)
+ }
+ if (skipInBits == 0) {
+ log(LogLevel.Debug, "%s no fill for %s DOS %s.",
+ Misc.getNameFromClass(this), rd.diagnosticDebugName, ustate.dataOutputStream)
+ } else {
+ log(LogLevel.Debug, "%s filled %s bits for %s DOS %s.",
+ Misc.getNameFromClass(this), skipInBits, rd.diagnosticDebugName, ustate.dataOutputStream)
+ }
+ }
+}
+
class ElementUnusedUnparserSuspendableOperation(
override val rd: ElementRuntimeData,
override val targetLengthEv: UnparseTargetLengthInBitsEv,
@@ -412,6 +436,7 @@ class ElementUnusedUnparserSuspendableOperation(
override val maybeCharsetEv: Maybe[CharsetEv],
override val maybeLiteralNilEv: Maybe[NilStringLiteralForUnparserEv])
extends SuspendableOperation
+ with SkipTheBits
with NeedValueAndTargetLengthMixin {
/**
@@ -426,21 +451,6 @@ class ElementUnusedUnparserSuspendableOperation(
skipTheBits(ustate, skipInBits)
}
- protected final def skipTheBits(ustate: UState, skipInBits: Long) {
- if (skipInBits > 0) {
- val dos = ustate.dataOutputStream
- if (!dos.skip(skipInBits, ustate))
- UE(ustate, "Unable to skip %s(bits).", skipInBits)
- }
- if (skipInBits == 0) {
- log(LogLevel.Debug, "%s no fill for %s DOS %s.",
- Misc.getNameFromClass(this), rd.diagnosticDebugName, ustate.dataOutputStream)
- } else {
- log(LogLevel.Debug, "%s filled %s bits for %s DOS %s.",
- Misc.getNameFromClass(this), skipInBits, rd.diagnosticDebugName, ustate.dataOutputStream)
- }
- }
-
}
class ElementUnusedUnparser(
@@ -460,6 +470,97 @@ class ElementUnusedUnparser(
}
+class ChoiceUnusedUnparserSuspendableOperation(
+ override val rd: ModelGroupRuntimeData,
+ targetLengthInBits: Long)
+ extends SuspendableOperation
+ with StreamSplitter
+ with SkipTheBits {
+
+ private var zlStatus_ : ZeroLengthStatus = ZeroLengthStatus.Unknown
+
+ private var maybeDOSStart : Maybe[DataOutputStream] = Maybe.Nope
+ private var maybeDOSEnd : Maybe[DataOutputStream] = Maybe.Nope
+
+
+ def captureDOSStartForChoiceUnused(state: UState): Unit = {
+ val splitter = new RegionSplitUnparser(rd)
+ splitter.unparse(state)
+ maybeDOSStart = Maybe(splitter.dataOutputStream)
+ }
+
+ def captureDOSEndForChoiceUnused(state: UState): Unit = {
+ val splitter = new RegionSplitUnparser(rd)
+ splitter.unparse(state)
+ maybeDOSEnd = Maybe(splitter.dataOutputStream)
+ }
+
+ private lazy val dosToCheck_ = {
+ Assert.usage(maybeDOSStart.isDefined)
+ val dosForStart = maybeDOSStart.get
+ val dosForEnd = maybeDOSEnd.get
+ val primaryDOSList = getDOSFromAtoB(
+ dosForStart,
+ dosForEnd)
+
+ primaryDOSList
+ }
+
+ override def test(ustate: UState): Boolean = {
+ if (zlStatus_ ne ZeroLengthStatus.Unknown)
+ true
+ else if (maybeDOSStart.isEmpty)
+ false
+ else {
+ Assert.invariant(maybeDOSStart.isDefined)
+ if (dosToCheck_.exists { dos =>
+ val dosZLStatus = dos.zeroLengthStatus
+ dosZLStatus eq ZeroLengthStatus.NonZero
+ }) {
+ zlStatus_ = ZeroLengthStatus.NonZero
+ true
+ } else if (dosToCheck_.forall { dos =>
+ val dosZLStatus = dos.zeroLengthStatus
+ dosZLStatus eq ZeroLengthStatus.Zero
+ }) {
+ zlStatus_ = ZeroLengthStatus.Zero
+ true
+ } else {
+ Assert.invariant(zlStatus_ eq ZeroLengthStatus.Unknown)
+ false
+ }
+ }
+ }
+
+
+ /**
+ * determine delta between value length and target length
+ *
+ * and skip that many bits.
+ */
+ override def continuation(ustate: UState) {
+ val startPos0b = dosToCheck_(0).relBitPos0b
+ val endPos0b = dosToCheck_.last.relBitPos0b + startPos0b
+ val vl = (endPos0b - startPos0b).toLong
+ val skipInBits = targetLengthInBits - vl
+ if (skipInBits < 0)
+ UE(ustate, "Data too long for dfdl:choiceLength (%s bits) by %s bits. Unable to truncate.", targetLengthInBits, -skipInBits)
+ skipTheBits(ustate, skipInBits)
+ }
+}
+
+class ChoiceUnusedUnparser(
+ override val context: ModelGroupRuntimeData,
+ targetLengthInBits: Long,
+ suspendableOp: SuspendableOperation)
+ extends PrimUnparser
+ with SuspendableUnparser {
+
+ override lazy val runtimeDependencies = Vector()
+
+ override def suspendableOperation = suspendableOp
+}
+
trait PaddingUnparserMixin
extends NeedValueAndTargetLengthMixin { self: SuspendableOperation =>
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLengthUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLengthUnparsers.scala
index 3a790f3..1803851 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLengthUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLengthUnparsers.scala
@@ -26,8 +26,11 @@ import org.apache.daffodil.infoset.DISimple
import org.apache.daffodil.infoset.Infoset
import org.apache.daffodil.infoset.RetryableException
import org.apache.daffodil.io.DataOutputStream
+import org.apache.daffodil.io.ZeroLengthStatus
import org.apache.daffodil.processors.CharsetEv
import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.processors.ChoiceRuntimeData
import org.apache.daffodil.processors.Evaluatable
import org.apache.daffodil.processors.ParseOrUnparseState
import org.apache.daffodil.processors.Success
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/StreamSplitterMixin.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/StreamSplitterMixin.scala
new file mode 100644
index 0000000..bb88524
--- /dev/null
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/StreamSplitterMixin.scala
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.unparsers
+
+import org.apache.daffodil.io.DataOutputStream
+import org.apache.daffodil.io.ZeroLengthStatus
+import org.apache.daffodil.io.DirectOrBufferedDataOutputStream
+import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.processors.Processor
+import org.apache.daffodil.processors.SuspendableOperation
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.util.Maybe
+import scala.collection.mutable.Buffer
+
+trait StreamSplitter {
+ /**
+ * Given two DataOutputStream, determine the set of data output streams starting from the first,
+ * and in chain until (and including) we reach the second.
+ *
+ * If the two are the same DataOutputStream, we get back a sequence of just the one DOS.
+ */
+ def getDOSFromAtoB(beforeDOS: DataOutputStream, afterDOS: DataOutputStream): Seq[DataOutputStream] = {
+ val buf = Buffer[DataOutputStream]()
+ var maybeNext = Maybe(beforeDOS)
+ while (maybeNext.isDefined && (maybeNext.get ne afterDOS)) {
+ val thisOne = maybeNext.get
+ buf += thisOne
+ maybeNext = thisOne.maybeNextInChain
+ }
+ Assert.invariant(maybeNext.isDefined) // we MUST find a path to the afterDOS.
+ Assert.invariant(maybeNext.get eq afterDOS)
+ // we include the final afterDOS data output stream in the list
+ buf += afterDOS
+ val res = buf.toSeq
+ res
+ }
+}
+
+/**
+ * We need to isolate the regions that we have to test for ZL/notZL
+ * for separator suppression. So we want these regions to not share
+ * data output streams with anything outside them.
+ *
+ * To achieve that we split the data output stream using its buffering
+ * capabilities. Some of the splits are natural. They are one side of
+ * the (potentially suppressed) separator itself. So creating that
+ * suspendable unparser for the separator gives us a split.
+ *
+ * The other side of these regions is however, an artificial split.
+ *
+ * To avoid duplication of complex code paths, we create this
+ * suspendable unparser that exists purely to split the underlying DOS
+ * but maintain all the invariants of the underlying DOS code, i.e.,
+ * the streams still get finalized (and therefore collapsed) the regular
+ * way.
+ *
+ * We are counting on the fact that these objects live on the garbage
+ * collected heap, so have unbounded lifetimes, i.e., they aren't recycled
+ * in a pool or anything.
+ *
+ * Performance Note: These do not need to capture state like a normal
+ * suspension does (e.g., for dfdl:outputValueCalc), as they're not actually
+ * suspending any actual unparsing behavior. Just forcing a boundary in the
+ * data output streams so that we can use the data output streams to measure the
+ * length (zero/non-zero) of unparsed data.
+ */
+class RegionSplitUnparser (override val context: TermRuntimeData)
+ extends PrimUnparser
+ with SuspendableUnparser {
+
+ override val childProcessors: Vector[Processor] = Vector()
+
+ override def runtimeDependencies = Vector()
+
+ override lazy val suspendableOperation = new RegionSplitSuspendableOperation(context)
+
+ lazy val dataOutputStream = suspendableOperation.savedUstate.dataOutputStream
+}
+
+final class RegionSplitSuspendableOperation(override val rd: TermRuntimeData)
+ extends SuspendableOperation {
+
+ private var secondTime = false
+
+ /**
+ * Suspends once, since test fails the first time.
+ * When retried, the test succeeds.
+ */
+ override def test(ustate: UState): Boolean = {
+ if (secondTime) true
+ else {
+ secondTime = true
+ false
+ }
+ }
+
+ override def continuation(ustate: UState): Unit = {
+ // do nothing.
+ //
+ // The underlying suspension system will take care of
+ // finishing the DOS so everything gets unblocked.
+ }
+}
+
+object RegionSplitUnparser {
+ def apply(trd: TermRuntimeData) = {
+ val unp = new RegionSplitUnparser(trd)
+ Processor.initialize(unp)
+ unp
+ }
+}
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SuppressableSeparatorUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SuppressableSeparatorUnparser.scala
index 99c4ce9..96f7e2e 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SuppressableSeparatorUnparser.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SuppressableSeparatorUnparser.scala
@@ -38,7 +38,8 @@ import org.apache.daffodil.io.DirectOrBufferedDataOutputStream
final class SuppressableSeparatorUnparserSuspendableOperation(
sepUnparser: Unparser,
override val rd: TermRuntimeData)
- extends SuspendableOperation {
+ extends SuspendableOperation
+ with StreamSplitter {
private var zlStatus_ : ZeroLengthStatus = ZeroLengthStatus.Unknown
@@ -47,7 +48,7 @@ final class SuppressableSeparatorUnparserSuspendableOperation(
private var maybeDOSForEndOfSeparatedRegionBeforePostfixSeparator: Maybe[DataOutputStream] = Maybe.Nope
def captureStateAtEndOfPotentiallyZeroLengthRegionFollowingTheSeparator(s: UState): Unit = {
- val splitter = SuspressionRegionSplitUnparser(rd)
+ val splitter = RegionSplitUnparser(rd)
splitter.unparse(s) // splits the DOS so all the potentially ZL stuff is isolated.
maybeDOSAfterSeparatorRegion = Maybe(splitter.dataOutputStream)
}
@@ -62,13 +63,13 @@ final class SuppressableSeparatorUnparserSuspendableOperation(
* from unparsing.
*/
def captureDOSForStartOfSeparatedRegionBeforePostfixSeparator(s: UState): Unit = {
- val splitter = SuspressionRegionSplitUnparser(rd)
+ val splitter = RegionSplitUnparser(rd)
splitter.unparse(s) // splits the DOS so all the potentially ZL stuff is isolated.
maybeDOSForStartOfSeparatedRegionBeforePostfixSeparator = Maybe(splitter.dataOutputStream.maybeNextInChain.get)
}
def captureDOSForEndOfSeparatedRegionBeforePostfixSeparator(s: UState): Unit = {
- val splitter = SuspressionRegionSplitUnparser(rd)
+ val splitter = RegionSplitUnparser(rd)
splitter.unparse(s) // splits the DOS so all the potentially ZL stuff is isolated.
maybeDOSForEndOfSeparatedRegionBeforePostfixSeparator = Maybe(splitter.dataOutputStream)
}
@@ -137,28 +138,6 @@ final class SuppressableSeparatorUnparserSuspendableOperation(
}
/**
- * Given two DataOutputStream, determine the set of data output streams starting from the first,
- * and in chain until (and including) we reach the second.
- *
- * If the two are the same DataOutputStream, we get back a sequence of just the one DOS.
- */
- private def getDOSFromAtoB(beforeDOS: DataOutputStream, afterDOS: DataOutputStream): Seq[DataOutputStream] = {
- val buf = Buffer[DataOutputStream]()
- var maybeNext = Maybe(beforeDOS)
- while (maybeNext.isDefined && (maybeNext.get ne afterDOS)) {
- val thisOne = maybeNext.get
- buf += thisOne
- maybeNext = thisOne.maybeNextInChain
- }
- Assert.invariant(maybeNext.isDefined) // we MUST find a path to the afterDOS.
- Assert.invariant(maybeNext.get eq afterDOS)
- // we include the final afterDOS data output stream in the list
- buf += afterDOS
- val res = buf.toSeq
- res
- }
-
- /**
* Once we know whether the length is zero/non-zero, then we decide to unparse the
* separator or not.
*
@@ -210,76 +189,3 @@ object SuppressableSeparatorUnparser {
}
-/**
- * We need to isolate the regions that we have to test for ZL/notZL
- * for separator suppression. So we want these regions to not share
- * data output streams with anything outside them.
- *
- * To achieve that we split the data output stream using its buffering
- * capabilities. Some of the splits are natural. They are one side of
- * the (potentially suppressed) separator itself. So creating that
- * suspendable unparser for the separator gives us a split.
- *
- * The other side of these regions is however, an artificial split.
- *
- * To avoid duplication of complex code paths, we create this
- * suspendable unparser that exists purely to split the underlying DOS
- * but maintain all the invariants of the underlying DOS code, i.e.,
- * the streams still get finalized (and therefore collapsed) the regular
- * way.
- *
- * We are counting on the fact that these objects live on the garbage
- * collected heap, so have unbounded lifetimes, i.e., they aren't recycled
- * in a pool or anything.
- *
- * Performance Note: These do not need to capture state like a normal
- * suspension does (e.g., for dfdl:outputValueCalc), as they're not actually
- * suspending any actual unparsing behavior. Just forcing a boundary in the
- * data output streams so that we can use the data output streams to measure the
- * length (zero/non-zero) of unparsed data.
- */
-final class SuspressionRegionSplitUnparser private (override val context: TermRuntimeData)
- extends PrimUnparser
- with SuspendableUnparser {
-
- override val childProcessors: Vector[Processor] = Vector()
-
- override def runtimeDependencies = Vector()
-
- override lazy val suspendableOperation = new SuppressionRegionSplitSuspendableOperation(context)
-
- lazy val dataOutputStream = suspendableOperation.savedUstate.dataOutputStream
-}
-
-final class SuppressionRegionSplitSuspendableOperation(override val rd: TermRuntimeData)
- extends SuspendableOperation {
-
- private var secondTime = false
-
- /**
- * Suspends once, since test fails the first time.
- * When retried, the test succeeds.
- */
- override def test(ustate: UState): Boolean = {
- if (secondTime) true
- else {
- secondTime = true
- false
- }
- }
-
- override def continuation(ustate: UState): Unit = {
- // do nothing.
- //
- // The underlying suspension system will take care of
- // finishing the DOS so everything gets unblocked.
- }
-}
-
-object SuspressionRegionSplitUnparser {
- def apply(trd: TermRuntimeData) = {
- val unp = new SuspressionRegionSplitUnparser(trd)
- Processor.initialize(unp)
- unp
- }
-}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/EvElement.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/EvElement.scala
index b7e08ed..008693a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/EvElement.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/EvElement.scala
@@ -36,12 +36,13 @@ import org.apache.daffodil.processors.unparsers.UState
import org.apache.daffodil.schema.annotation.props.gen.LengthKind
import org.apache.daffodil.schema.annotation.props.gen.LengthUnits
import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.Maybe.Nope
import org.apache.daffodil.util.MaybeJULong
import org.apache.daffodil.util.Numbers
sealed trait LengthEv extends Evaluatable[JLong]
-class ExplicitLengthEv(expr: CompiledExpression[JLong], rd: ElementRuntimeData)
+class ExplicitLengthEv(expr: CompiledExpression[JLong], rd: RuntimeData)
extends EvaluatableExpression[JLong](
expr,
rd)
@@ -124,7 +125,7 @@ class ImplicitLengthEv(lengthValue: Long, rd: ElementRuntimeData)
* In that case, the access to the infoset throws particular exceptions
* descended from the RetryableException trait.
*/
-sealed abstract class LengthInBitsEvBase(rd: ElementRuntimeData,
+sealed abstract class LengthInBitsEvBase(rd: TermRuntimeData,
val lengthUnits: LengthUnits,
val lengthKind: LengthKind)
extends Evaluatable[MaybeJULong](rd)
@@ -167,7 +168,7 @@ class LengthInBitsEv(lengthUnits: LengthUnits,
lengthKind: LengthKind,
override val maybeCharsetEv: Maybe[CharsetEv],
val lengthEv: LengthEv,
- rd: ElementRuntimeData)
+ rd: TermRuntimeData)
extends LengthInBitsEvBase(rd, lengthUnits, lengthKind) {
override lazy val runtimeDependencies = maybeCharsetEv.toList :+ lengthEv
@@ -187,7 +188,7 @@ class LengthInBitsEv(lengthUnits: LengthUnits,
class MinLengthInBitsEv(lengthUnits: LengthUnits,
lengthKind: LengthKind,
override val maybeCharsetEv: Maybe[CharsetEv],
- minLen: Long, rd: ElementRuntimeData)
+ minLen: Long, rd: TermRuntimeData)
extends LengthInBitsEvBase(rd, lengthUnits, lengthKind) {
override lazy val runtimeDependencies = maybeCharsetEv.toList
@@ -209,7 +210,7 @@ class MinLengthInBitsEv(lengthUnits: LengthUnits,
class UnparseTargetLengthInBitsEv(
val lengthInBitsEv: LengthInBitsEv,
minLengthInBitsEv: MinLengthInBitsEv,
- rd: ElementRuntimeData)
+ rd: RuntimeData)
extends Evaluatable[MaybeJULong](rd)
with InfosetCachedEvaluatable[MaybeJULong] {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
index 961e639..e7d80a8 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
@@ -19,6 +19,7 @@ package org.apache.daffodil.processors.parsers
import java.io.StringWriter
import java.io.PrintWriter
+import java.lang.{ Long => JLong }
import org.apache.daffodil.api.Diagnostic
import org.apache.daffodil.dsom.RuntimeSchemaDefinitionError
@@ -29,6 +30,7 @@ import org.apache.daffodil.processors.ElementRuntimeData
import org.apache.daffodil.processors.Processor
import org.apache.daffodil.processors.RuntimeData
import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors.Evaluatable
import org.apache.daffodil.util.LogLevel
import org.apache.daffodil.util.Maybe.One
import org.apache.daffodil.util.MaybeULong
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala
index 94f73f4..b0078b3 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala
@@ -25,6 +25,7 @@ import passera.unsigned.ULong
import org.apache.daffodil.equality._
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.RuntimeData
import org.apache.daffodil.processors.Evaluatable
import org.apache.daffodil.processors.Success
import org.apache.daffodil.schema.annotation.props.gen.LengthUnits
@@ -35,7 +36,7 @@ import org.apache.daffodil.util.OnStack
sealed abstract class SpecifiedLengthParserBase(
eParser: Parser,
- erd: ElementRuntimeData)
+ erd: RuntimeData)
extends CombinatorParser(erd)
with CaptureParsingValueLength {
@@ -132,6 +133,17 @@ class SpecifiedLengthExplicitParser(
}
}
+class SpecifiedLengthChoiceParser(
+ eParser: Parser,
+ erd: RuntimeData,
+ choiceLengthInBits: JLong)
+ extends SpecifiedLengthParserBase(eParser, erd) {
+
+ final override def getBitLength(s: PState): MaybeULong = {
+ MaybeULong(choiceLengthInBits)
+ }
+}
+
class SpecifiedLengthImplicitParser(
eParser: Parser,
erd: ElementRuntimeData,
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoiceLengthExplicit.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoiceLengthExplicit.tdml
new file mode 100644
index 0000000..95b6077
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/ChoiceLengthExplicit.tdml
@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<tdml:testSuite suiteName="choiceExplicit" description="Tests for choice construct with explicit length"
+ xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:ct="http://w3.ibm.com/xmlns/dfdl/ctInfoset" xmlns:ex="http://example.com"
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
+ xmlns="http://example.com">
+
+ <tdml:defineSchema name="basic">
+ <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+ <dfdl:format ref="ex:GeneralFormat"/>
+
+ <xs:element name="root">
+ <xs:complexType>
+ <xs:sequence dfdl:separator=";">
+ <xs:choice>
+ <xs:element name="int" type="xs:int" dfdl:lengthKind="delimited" />
+ <xs:element name="flt" type="xs:float" dfdl:lengthKind="delimited" />
+ <xs:element name="str" type="xs:string" dfdl:lengthKind="delimited" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ch1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice dfdl:choiceLengthKind="explicit" dfdl:choiceLength="2">
+ <xs:element name="inty" type="xs:int"
+ dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:element name="stringy" type="xs:string"
+ dfdl:lengthKind="explicit" dfdl:length="2" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ch2">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice dfdl:choiceLengthKind="explicit" dfdl:choiceLength="2">
+ <xs:element name="inty" type="xs:int"
+ dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:element name="stringy" type="xs:string"
+ dfdl:lengthKind="explicit" dfdl:length="3" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ch3">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice dfdl:choiceLengthKind="explicit" dfdl:choiceLength="2">
+ <xs:element name="int1" type="xs:int"
+ dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:element name="string1" type="xs:string"
+ dfdl:lengthKind="explicit" dfdl:length="2" />
+ </xs:choice>
+ <xs:choice dfdl:choiceLengthKind="explicit" dfdl:choiceLength="4">
+ <xs:element name="int2" type="xs:int"
+ dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:element name="string2" type="xs:string"
+ dfdl:lengthKind="explicit" dfdl:length="4" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ </tdml:defineSchema>
+
+ <tdml:parserTestCase name="explicit_01" root="ch1"
+ model="basic" roundTrip="true"
+ description="simplest imaginable test of choice. No asserts, no discriminators - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[AB]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:stringy>AB</ex:stringy>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_02" root="ch1"
+ model="basic" roundTrip="true"
+ description="simplest imaginable test of choice. No asserts, no discriminators - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[1 ]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:inty>1</ex:inty>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_03" root="ch2"
+ model="basic"
+ description="simplest imaginable test of choice. No asserts, no discriminators - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[ABC]]></tdml:document>
+
+ <tdml:errors>
+ <tdml:error>Parse Error</tdml:error>
+ <tdml:error>All choice alternatives failed</tdml:error>
+ </tdml:errors>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_multiple_choices" root="ch3"
+ model="basic" roundTrip="true"
+ description="simplest imaginable test of choice. No asserts, no discriminators - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[1 TEST]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch3>
+ <ex:int1>1</ex:int1>
+ <ex:string2>TEST</ex:string2>
+ </ex:ch3>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+
+ <tdml:unparserTestCase name="explicit_unparse_01" root="ch1" model="basic">
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:inty>1</ex:inty>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document><![CDATA[1 ]]></tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:unparserTestCase name="explicit_unparse_02" root="ch1" model="basic">
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:stringy>A</ex:stringy>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document><![CDATA[A ]]></tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:defineSchema name="choice2">
+ <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+ <dfdl:format ref="ex:GeneralFormat" />
+
+ <xs:group name="simpleGroup">
+ <xs:sequence dfdl:separator=".">
+ <xs:element name="string" type="xs:string" dfdl:lengthKind="delimited"/>
+ <xs:element name="int" type="xs:int" dfdl:lengthKind="delimited"/>
+ </xs:sequence>
+ </xs:group>
+ <xs:group name="simpleGroup2">
+ <xs:sequence dfdl:separator=",">
+ <xs:element name="string" type="xs:string" dfdl:lengthKind="delimited"/>
+ <xs:element name="int" type="xs:int" dfdl:lengthKind="delimited" />
+ </xs:sequence>
+ </xs:group>
+
+ <xs:complexType name="ctype">
+ <xs:choice dfdl:choiceLengthKind="explicit" dfdl:choiceLength="3">
+ <xs:element name="inty" type="xs:int"
+ dfdl:lengthKind="explicit" dfdl:length="{ 3 }" />
+ <xs:element name="floaty" type="xs:float"
+ dfdl:lengthKind="explicit" dfdl:length="{ 3 }" />
+ <xs:element name="stringy" type="xs:string"
+ dfdl:lengthKind="explicit" dfdl:length="{ 3 }" />
+ </xs:choice>
+ </xs:complexType>
+
+ <xs:element name="ctype2">
+ <xs:complexType>
+ <xs:choice dfdl:choiceLengthKind="explicit" dfdl:choiceLength="10">
+ <xs:element name="a">
+ <xs:complexType>
+ <xs:group ref="ex:simpleGroup" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="b">
+ <xs:complexType>
+ <xs:group ref="ex:simpleGroup2" />
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="ch1">
+ <xs:complexType>
+ <xs:sequence dfdl:separator="">
+ <xs:element name="a" type="ex:ctype"
+ dfdl:lengthKind="implicit" maxOccurs="unbounded" minOccurs="0"
+ dfdl:occursCountKind="parsed" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ </tdml:defineSchema>
+
+<!--
+ Test Name: explicit_04
+ Schema: choice2
+ Root: ctype2
+ Purpose: This test demonstrates that you can have a choice with group references as branches
+-->
+
+ <tdml:parserTestCase name="explicit_04" root="ctype2"
+ model="choice2"
+ description="Section 15 - Choice Groups - group refs as branches- DFDL-15-005R.">
+
+ <tdml:document><![CDATA[string.147]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <ctype2>
+ <a>
+ <string>string</string>
+ <int>147</int>
+ </a>
+ </ctype2>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_05" root="ch1"
+ model="choice2"
+ description="Many choices one after another. 3 way branches - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[3.5AAA]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:a>
+ <ex:floaty>3.5</ex:floaty>
+ </ex:a>
+ <ex:a>
+ <ex:stringy>AAA</ex:stringy>
+ </ex:a>
+
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_06" root="ch1"
+ model="choice2"
+ description="Many choices one after another. 3 way branches - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[999888777]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:a>
+ <ex:inty>999</ex:inty>
+ </ex:a>
+ <ex:a>
+ <ex:inty>888</ex:inty>
+ </ex:a>
+ <ex:a>
+ <ex:inty>777</ex:inty>
+ </ex:a>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_07" root="ch1"
+ model="choice2"
+ description="Many choices one after another. 3 way branches - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[3.54.65.7]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:a>
+ <ex:floaty>3.5</ex:floaty>
+ </ex:a>
+ <ex:a>
+ <ex:floaty>4.6</ex:floaty>
+ </ex:a>
+ <ex:a>
+ <ex:floaty>5.7</ex:floaty>
+ </ex:a>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="explicit_08" root="ch1"
+ model="choice2"
+ description="Many choices one after another. 3 way branches - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[AAABBBCCC]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:a>
+ <ex:stringy>AAA</ex:stringy>
+ </ex:a>
+ <ex:a>
+ <ex:stringy>BBB</ex:stringy>
+ </ex:a>
+ <ex:a>
+ <ex:stringy>CCC</ex:stringy>
+ </ex:a>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+
+ <tdml:parserTestCase name="explicit_09" root="ch1"
+ model="choice2"
+ description="Many choices one after another. 3 way branches - DFDL-15-001R.">
+
+ <tdml:document><![CDATA[999AAA777]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:ch1>
+ <ex:a>
+ <ex:inty>999</ex:inty>
+ </ex:a>
+ <ex:a>
+ <ex:stringy>AAA</ex:stringy>
+ </ex:a>
+ <ex:a>
+ <ex:inty>777</ex:inty>
+ </ex:a>
+ </ex:ch1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+
+ </tdml:parserTestCase>
+
+
+</tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
index 86d5dbd..3f8e1ee 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
@@ -25,9 +25,11 @@ object TestChoice {
val testDir = "/org/apache/daffodil/section15/choice_groups/"
val runnerCH = Runner(testDir, "choice.tdml")
+ val runnerCE = Runner(testDir, "ChoiceLengthExplicit.tdml")
@AfterClass def shutDown {
runnerCH.reset
+ runnerCE.reset
}
}
@@ -121,4 +123,21 @@ class TestChoice {
@Test def test_direct_dispatch_16() { runnerCH.runOneTest("direct_dispatch_16") }
//@Test def test_choice_noBranch() { runnerCH.runOneTest("choice_noBranch") } - Test consumes no data, which causes a TDMLError
+
+
+ @Test def test_explicit_01() { runnerCE.runOneTest("explicit_01") }
+ @Test def test_explicit_02() { runnerCE.runOneTest("explicit_02") }
+ @Test def test_explicit_03() { runnerCE.runOneTest("explicit_03") }
+ @Test def test_explicit_04() { runnerCE.runOneTest("explicit_04") }
+ @Test def test_explicit_05() { runnerCE.runOneTest("explicit_05") }
+ @Test def test_explicit_06() { runnerCE.runOneTest("explicit_06") }
+ @Test def test_explicit_07() { runnerCE.runOneTest("explicit_07") }
+ @Test def test_explicit_08() { runnerCE.runOneTest("explicit_08") }
+ @Test def test_explicit_09() { runnerCE.runOneTest("explicit_09") }
+
+ @Test def test_explicit_multiple_choices() { runnerCE.runOneTest("explicit_multiple_choices") }
+
+ @Test def test_explicit_unparse_01() { runnerCE.runOneTest("explicit_unparse_01") }
+ @Test def test_explicit_unparse_02() { runnerCE.runOneTest("explicit_unparse_02") }
+
}