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 5b4abd8 Add a special unparser for empty choice branches
5b4abd8 is described below
commit 5b4abd8447a6eef642587b1cae35c6853fc746f5
Author: Steve Lawrence <[email protected]>
AuthorDate: Fri Mar 20 12:33:19 2020 -0400
Add a special unparser for empty choice branches
When unparsing a choice, it is possible that the default branch to
unparsing could be completely empty (e.g. a sequence with a parse only
assertion). In these cases, that empty branch optimizes to a
NadaUnparser. Normally NadaUnparser's are optimized out or else they
will throw an error at runtime. However, the choice combinator unparse
still expects to have an Unparser to unparse, even if it does nothing.
To support this, this creates an empty choice branch unparser that does
nothing, but allows for the choice combinator to have something to
unparse.
DAFFODIL-2296
---
.../grammar/primitives/ChoiceCombinator.scala | 12 ++++++++-
.../unparsers/ChoiceAndOtherVariousUnparsers.scala | 16 +++++++++++
.../section15/choice_groups/choice-unparse2.tdml | 31 ++++++++++++++++++++++
.../choice_groups/TestUnparseChoice2.scala | 4 ++-
4 files changed, 61 insertions(+), 2 deletions(-)
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 08ff243..8fd8cb5 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
@@ -246,7 +246,17 @@ case class ChoiceCombinator(ch: ChoiceTermBase,
alternatives: Seq[Gram])
val (eventRDMap, optDefaultBranch) = ch.choiceBranchMap
val optDefaultUnparser: Option[Unparser] = optDefaultBranch.map {
defaultBranch =>
val defaultBranchGram = defaultBranch.termContentBody
- defaultBranchGram.unparser
+ val defaultBranchUnparser = defaultBranchGram.unparser
+ if (defaultBranchUnparser.isEmpty) {
+ // This is a NadaUnparser, likely caused by a default choice branch
+ // that is just an empty sequence. NadaUnparsers throw an assertion
+ // when unparsed, but the ChoiceCombinatorUnparser still expects to
+ // have a something to unparse, so we have an unparser that just does
+ // nothing for this special case
+ new ChoiceBranchEmptyUnparser(defaultBranch.runtimeData)
+ } else {
+ defaultBranchUnparser
+ }
}
val eventUnparserMap = eventRDMap.map {
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 374e7a8..160eea8 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
@@ -64,6 +64,22 @@ case class ChoiceBranchMap(
def keys = lookupTable.keys
}
+/*
+ * Sometimes choices will have an empty branch (e.g. a sequence that just has
+ * an assert in it) that optimize to a NadaUnparser. NadaUnparsers should all
+ * be optimized out, but the ChoiceCombinatorUnparser still expects to have
+ * something in this cases. So we have a special empty branch unparser that
+ * does nothing, but gives the ChoiceCombinatorUnparsering an unparse that it
+ * can use.
+ */
+class ChoiceBranchEmptyUnparser(val context: RuntimeData)
+ extends PrimUnparser {
+
+ override lazy val runtimeDependencies = Vector()
+
+ def unparse(state: UState): Unit = {}
+}
+
class ChoiceCombinatorUnparser(
mgrd: ModelGroupRuntimeData,
eventUnparserMap: ChoiceBranchMap,
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice-unparse2.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice-unparse2.tdml
index cf7d3c7..921ee19 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice-unparse2.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice-unparse2.tdml
@@ -216,4 +216,35 @@
</tdml:infoset>
</tdml:unparserTestCase>
+ <tdml:defineSchema name="c3" elementFormDefault="unqualified">
+
+ <xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+ <dfdl:format ref="ex:GeneralFormat" lengthKind="delimited"/>
+
+ <xs:element name="choice" dfdl:terminator=";">
+ <xs:complexType>
+ <xs:choice>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:assert>{ fn:true() eq fn:true() }</dfdl:assert>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:sequence>
+ <xs:element name="impossible" type="xs:int" />
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+
+ </tdml:defineSchema>
+
+ <tdml:unparserTestCase name="choice_default_branch_is_empty" model="c3"
root="choice">
+ <tdml:document>;</tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <ex:choice />
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:unparserTestCase>
+
</tdml:testSuite>
diff --git
a/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestUnparseChoice2.scala
b/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestUnparseChoice2.scala
index caf9940..04473c7 100644
---
a/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestUnparseChoice2.scala
+++
b/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestUnparseChoice2.scala
@@ -40,4 +40,6 @@ class TestUnparseChoice2 {
@Test def test_choice_with_array_branch2() {
runner.runOneTest("choice_with_array_branch2") }
@Test def test_choice_with_array_branch3() {
runner.runOneTest("choice_with_array_branch3") }
@Test def test_choice_with_presence_bits_followed_by_array() {
runner.runOneTest("choice_with_presence_bits_followed_by_array")}
-}
\ No newline at end of file
+
+ @Test def test_choice_defaultable_branch_is_empty() {
runner.runOneTest("choice_default_branch_is_empty")}
+}