You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by bt...@apache.org on 2019/06/27 13:53:43 UTC

[incubator-daffodil] branch master updated: Add dfdlx:choiceBranchKeyRanges

This is an automated email from the ASF dual-hosted git repository.

btsloane 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 efa1613  Add dfdlx:choiceBranchKeyRanges
efa1613 is described below

commit efa1613a04dd51169d5e7c3d884a640f073f09c0
Author: Brandon Sloane <bs...@tresys.com>
AuthorDate: Wed Jun 19 17:56:41 2019 -0400

    Add dfdlx:choiceBranchKeyRanges
    
    DAFFODIL-2157
---
 .../apache/daffodil/dsom/RestrictionUnion.scala    |   2 +-
 .../org/apache/daffodil/dsom/SimpleTypes.scala     |  26 +-
 .../grammar/primitives/ChoiceCombinator.scala      |  48 ++-
 .../org/apache/daffodil/cookers/Cookers.scala      |  30 ++
 .../org/apache/daffodil/util/RangeBound.scala      | 111 +++++++
 .../apache/daffodil/xsd/DFDL_part2_attributes.xsd  |   2 +
 .../resources/org/apache/daffodil/xsd/dfdlx.xsd    |   3 +
 .../daffodil/processors/TypeCalculator.scala       |  82 +----
 .../processors/parsers/ElementKindParsers.scala    |  18 +-
 .../choiceBranchRanges/choiceBranchKeyRanges.tdml  | 331 +++++++++++++++++++++
 .../daffodil/section15/choice_groups/choice.tdml   |   3 +-
 .../extensions/TestChoiceBranchKeyRanges.scala     |  45 +++
 12 files changed, 578 insertions(+), 123 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RestrictionUnion.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RestrictionUnion.scala
index 2d772ce..f49475c 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RestrictionUnion.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/RestrictionUnion.scala
@@ -31,11 +31,11 @@ import com.ibm.icu.util.TimeZone
 import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.processors.RepValueSet
 import org.apache.daffodil.processors.RepValueSetCompiler
-import org.apache.daffodil.processors.RangeBound
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.dsom.FacetTypes.ElemFacets
 import org.apache.daffodil.dsom.FacetTypes.FacetValue
 import org.apache.daffodil.processors.RepValueSet
+import org.apache.daffodil.util.RangeBound
 
 /**
  * A schema component for simple type restrictions
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
index 118b6ff..c22bb7a 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
@@ -44,7 +44,6 @@ import scala.xml.Elem
 import scala.xml.MetaData
 import scala.xml.UnprefixedAttribute
 import scala.xml.Null
-import org.apache.daffodil.processors.RangeBound
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.xml.GlobalQName
 import org.apache.daffodil.xml.XMLUtils
@@ -52,6 +51,8 @@ import scala.xml.NamespaceBinding
 import org.apache.daffodil.processors.IdentifyTypeCalculator
 import org.apache.daffodil.xml.NS
 import org.apache.daffodil.exceptions.SchemaFileLocation
+import org.apache.daffodil.cookers.IntRangeCooker
+import org.apache.daffodil.util.RangeBound
 
 trait TypeBase {
   def optRestriction: Option[Restriction] = None
@@ -86,33 +87,16 @@ sealed trait HasRepValueAttributes extends AnnotatedSchemaComponent
       case Some(repType) => {
         val repValueSetRaw = findPropertyOption("repValues").toOption
           .map(_.split("\\s+").toSeq).getOrElse(Seq())
-        val repValueRangesRaw = findPropertyOption("repValueRanges").toOption
-          .map(_.split("\\s+").toSeq).getOrElse(Seq())
+        val repValueRangesRaw = findPropertyOption("repValueRanges").toOption.getOrElse("")
         repType.primType match {
           case PrimType.String => {
             if (repValueRangesRaw.size > 0) SDE("repValueRanges set when using a string repType")
-            val repValueSetCooked = repValueRangesRaw.map(RepValueCooker.convertConstant(_, this, false))
+            val repValueSetCooked = repValueSetRaw.map(RepValueCooker.convertConstant(_, this, false))
             (repValueSetCooked, Seq())
           }
           case _: NodeInfo.Integer.Kind => {
-            if (repValueRangesRaw.size % 2 != 0) SDE("repValueRanges must have an even number of elements")
             val ans1 = repValueSetRaw.map(BigInt(_))
-            def cookRepValueRanges(xs: Seq[String]): Seq[(RangeBound[BigInt], RangeBound[BigInt])] = {
-              xs match {
-                case Seq() => Seq()
-                case a +: b +: rest => {
-                  val a2 = BigInt(a)
-                  val b2 = BigInt(b)
-                  if (a2.compare(b2) > 0) {
-                    SDE("min value must not be greater than max value")
-                  }
-                  val a3 = new RangeBound(Maybe(a2), true)
-                  val b3 = new RangeBound(Maybe(b2), true)
-                  (a3, b3) +: cookRepValueRanges(rest)
-                }
-              }
-            }
-            val ans2 = cookRepValueRanges(repValueRangesRaw)
+            val ans2 = IntRangeCooker.convertConstant(repValueRangesRaw, this, false)
             (ans1, ans2)
           }
           case x => SDE("repType must be either String or Integer type")
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 1eaed1d..e20b8f9 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
@@ -34,9 +34,10 @@ import scala.util.Failure
 import scala.util.Success
 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
+import org.apache.daffodil.util.RangeBound
+import org.apache.daffodil.cookers.IntRangeCooker
 
 /*
  * The purpose of the ChoiceCombinator (and the parsers it creates) is to
@@ -83,6 +84,15 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
         case ChoiceLengthKind.Explicit => new SpecifiedLengthChoiceParser(cp, ch.choiceRuntimeData, choiceLengthInBits.get)
       }
     } else {
+      //Verify that every alternative has some form of branch key
+      alternatives.map{ alt =>
+        val keyTerm = alt.context.asInstanceOf[Term]
+        val hasBranchKey = keyTerm.findPropertyOption("choiceBranchKey").isDefined
+        val hasBranchKeyRanges = keyTerm.findPropertyOption("choiceBranchKeyRanges").isDefined
+        if(!hasBranchKey && !hasBranchKeyRanges && ch.defaultableChoiceBranchKeyKind != ChoiceKeyKindType.ByType){
+          keyTerm.SDE("Neither dfdl:choiceBranchKey nor dfdlx:choiceBranchKeyRanges is defined.")
+        }
+      }
       val dispatchBranchKeyValueTuples: Seq[(String, Gram)] = alternatives.flatMap { alt =>
         val keyTerm = alt.context.asInstanceOf[Term]
         val uncookedBranchKeys =
@@ -95,9 +105,8 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
               val ans = repValueSet.valueSet.toSeq.map(_.toString).mkString(" ")
               ans
             }
-            case ChoiceKeyKindType.Explicit    => keyTerm.choiceBranchKey
-            case ChoiceKeyKindType.Implicit    => keyTerm.choiceBranchKey
-            case ChoiceKeyKindType.Speculative => keyTerm.choiceBranchKey
+            case ChoiceKeyKindType.Explicit | ChoiceKeyKindType.Implicit   => keyTerm.findPropertyOption("choiceBranchKey").toOption.getOrElse("")
+            case ChoiceKeyKindType.Speculative => Assert.invariantFailed("Cannot have choiceKeyKind==speculative with direct dispatch")
           }
         val cbks = {
           if(uncookedBranchKeys.isEmpty){
@@ -113,31 +122,39 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
       //Since there is not a choiceBranchKeyRanges attribute, this can only currently be populated by repType
       val dispatchBranchKeyRangeTuples: Seq[(RangeBound[BigInt], RangeBound[BigInt], Parser, Boolean)] = alternatives.flatMap { alt =>
         val keyTerm = alt.context.asInstanceOf[Term]
-        val branchKeyRanges:Set[(RangeBound[BigInt],RangeBound[BigInt])] = ch.defaultableChoiceBranchKeyKind match {
+        val branchKeyRanges:Seq[(RangeBound[BigInt],RangeBound[BigInt])] = ch.defaultableChoiceBranchKeyKind match {
           case ChoiceKeyKindType.ByType => {
             val keyTerm_ = keyTerm.asInstanceOf[ElementBase]
-            keyTerm_.simpleType.optRepValueSet.get.valueRanges.map(x=>{
+            keyTerm_.simpleType.optRepValueSet.get.valueRanges.toSeq.map(x=>{
               val x_ = x.asInstanceOf[(RangeBound[BigInt],RangeBound[BigInt])]
               (x_._1,x_._2)
             })
           }
-          case ChoiceKeyKindType.Explicit    => Set()
-          case ChoiceKeyKindType.Implicit    => Set()
-          case ChoiceKeyKindType.Speculative => Set()
+          case ChoiceKeyKindType.Explicit | ChoiceKeyKindType.Implicit    => 
+            IntRangeCooker.convertConstant(keyTerm.findPropertyOption("choiceBranchKeyRanges").toOption.getOrElse(""), context, false)
+          case ChoiceKeyKindType.Speculative => Assert.invariantFailed("Cannot have choiceKeyKind==speculative with direct dispatch")
         }
         branchKeyRanges.toSeq.map(x=>(x._1, x._2, alt.parser, alt.context.enclosingTerm.get.isRepresented))
       }
 
-      // check for duplicate dfdl:choiceBranchKeys
+      // check for duplicate branch keys
       // Our handling of ranges here is definantly suboptimal, but hopefully
       // we don't see enough distinct ranges on a single element for this to be an issue
+      // Additionally, at this point, the keys could be comming from either the choiceBranchKey family of attributes
+      // or the repValue family of attributes
+      val (branchKeyAttribute, branchKeyRangeAttribute) = ch.defaultableChoiceBranchKeyKind match{
+        case ChoiceKeyKindType.ByType => ("dfdlx:repValues","dfdlx:repValueRanges")
+        case ChoiceKeyKindType.Explicit | ChoiceKeyKindType.Implicit => ("dfdl:choiceBranchKey","dfdlx:choiceBranchKeyRanges")
+        case ChoiceKeyKindType.Speculative => Assert.invariantFailed("Cannot have choiceKeyKind==speculative with direct dispatch")
+      }
       val groupedByKey = dispatchBranchKeyValueTuples.groupBy(_._1)
       groupedByKey.foreach {
         case (k, kvs) =>
           if (kvs.length > 1) {
             SDE(
-              "dfdl:choiceBranchKey value (%s) is not unique across all branches of a direct dispatch choice. Offending branches are:\n%s",
-              k, kvs.map(_._2.context.runtimeData).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
+              "%s value (%s) is not unique across all branches of a direct dispatch choice. Offending branches are:\n%s",
+              branchKeyAttribute, k, 
+              kvs.map(_._2.context.runtimeData).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
           }
           Try(k.toLong) match {
             case Success(v) => {
@@ -145,8 +162,8 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
               val conflictingRanges = dispatchBranchKeyRangeTuples.filter({ case (min, max, _, _) => min.testAsLower(asBigInt) && max.testAsUpper(asBigInt) })
               if (conflictingRanges.length > 0) {
                 SDE(
-                  "dfdl:choiceBranchKey (%s) conflicts with dfdl:choiceBranchKeyRanges. Offending branches are:\n%s\n%s",
-                  k,
+                  "%s (%s) conflicts with %s. Offending branches are:\n%s\n%s",
+                  branchKeyAttribute, k, branchKeyRangeAttribute,
                   kvs.map(_._2.context.runtimeData).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""),
                   conflictingRanges.map(_._3.context).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
               }
@@ -167,7 +184,8 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
           val conflictingRanges = (conflictingRanges1 ++ conflictingRanges2).toSet
           if (conflictingRanges.size > 1) {
             SDE(
-              "dfdl:choiceBranchKeyRanges (%s, %s) conflicts with other ranges. Offending branches are:\n%s",
+              "%s (%s, %s) conflicts with other ranges. Offending branches are:\n%s",
+              branchKeyRangeAttribute,
               min, max,
               conflictingRanges.map(_._3.context).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
           }
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/cookers/Cookers.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/cookers/Cookers.scala
index 47c8fe2..0d8592e 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/cookers/Cookers.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/cookers/Cookers.scala
@@ -17,6 +17,11 @@
 
 package org.apache.daffodil.cookers
 
+import org.apache.daffodil.util.RangeBound
+import org.apache.daffodil.util.RangeBound.Range
+import org.apache.daffodil.exceptions.ThrowsSDE
+import org.apache.daffodil.util.Maybe
+
 object TextStandardInfinityRepCooker extends StringLiteralNonEmptyNoCharClassEntitiesNoByteEntities()
 
 object TextStandardNaNRepCooker extends StringLiteralNonEmptyNoCharClassEntitiesNoByteEntities()
@@ -84,3 +89,28 @@ object ChoiceBranchKeyCooker extends ListOfStringLiteralNonEmptyNoCharClassEntit
 object RepValueCooker extends ListOfStringLiteralNonEmptyNoCharClassEntitiesNoByteEntities()
 
 object UpperCaseTokenCooker extends UpperCaseToken
+
+object IntRangeCooker extends Converter[String, Seq[Range[BigInt]]] {
+  protected def convert(input: String, context: ThrowsSDE, forUnparse: Boolean): Seq[Range[BigInt]] = {
+    def run(xs: Seq[String]): Seq[(RangeBound[BigInt], RangeBound[BigInt])] = {
+      xs match {
+        case Seq() => Seq()
+        case a +: b +: rest => {
+          val a2 = BigInt(a)
+          val b2 = BigInt(b)
+          if (a2.compare(b2) > 0) {
+            context.SDE("min value (%s) is greater than max value (%s)", a2, b2)
+          }
+          val a3 = new RangeBound(Maybe(a2), true)
+          val b3 = new RangeBound(Maybe(b2), true)
+          (a3, b3) +: run(rest)
+        }
+      }
+    }
+    val asSeq = input.split("\\s+").toSeq.filter(!_.isEmpty)
+    if(asSeq.length % 2 != 0){
+      context.SDE("Integer range sets must specify an even number of endpoints")
+    }
+    run(asSeq)
+  }
+}
\ No newline at end of file
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/util/RangeBound.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/util/RangeBound.scala
new file mode 100644
index 0000000..5a1f8e7
--- /dev/null
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/util/RangeBound.scala
@@ -0,0 +1,111 @@
+/*
+ * 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.util
+
+import java.lang.{ Long => JLong }
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.cookers.Converter
+import org.apache.daffodil.exceptions.ThrowsSDE
+import org.apache.daffodil.util.RangeBound.Range
+
+object RangeBound {
+  type Range[A <: AnyRef] = (RangeBound[A], RangeBound[A])
+}
+
+class RangeBound[A <: AnyRef](val maybeBound: Maybe[A], val isInclusive: Boolean)
+  extends Serializable {
+
+  lazy val isEmpty = maybeBound.isEmpty
+  lazy val isDefined = maybeBound.isDefined
+
+  override def toString(): String = {
+    if (maybeBound.isDefined) {
+      maybeBound.get.toString + "_" + (if (isInclusive) "inclusive" else "exclusive")
+    } else {
+      "Nope"
+    }
+  }
+
+  def intersectsWithOtherBounds(lower: RangeBound[A], upper: RangeBound[A]): Boolean = {
+    if (maybeBound.isEmpty) {
+      false
+    } else {
+      val bound = maybeBound.get
+      val inBounds = lower.testAsLower(bound) && upper.testAsUpper(bound)
+      val atBoundery = maybeBound == lower.maybeBound || maybeBound == upper.maybeBound
+      inBounds && (isInclusive || !atBoundery)
+    }
+  }
+
+  /*
+   * It should be the case that x is either a BigInt, or a Long
+   */
+
+  def testAsLower(x: A): Boolean = {
+    if (maybeBound.isEmpty) {
+      true
+    } else {
+      if (isInclusive) {
+        le(maybeBound.get, x)
+      } else {
+        lt(maybeBound.get, x)
+      }
+    }
+  }
+  def testAsUpper(x: A): Boolean = {
+    if (maybeBound.isEmpty) {
+      true
+    } else {
+      val bound = maybeBound.get
+      if (isInclusive) {
+        le(x, maybeBound.get)
+      } else {
+        lt(x, maybeBound.get)
+      }
+    }
+  }
+
+  private def le(x: A, y: A): Boolean = {
+    if (x.isInstanceOf[BigInt] && y.isInstanceOf[BigInt]) {
+      x.asInstanceOf[BigInt] <= y.asInstanceOf[BigInt]
+    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[JLong]) {
+      x.asInstanceOf[JLong] <= y.asInstanceOf[JLong]
+    } else if (x.isInstanceOf[BigInt] && y.isInstanceOf[JLong]) {
+      x.asInstanceOf[BigInt] <= BigInt(y.asInstanceOf[JLong])
+    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[BigInt]) {
+      BigInt(x.asInstanceOf[JLong]) <= y.asInstanceOf[BigInt]
+    } else {
+      Assert.invariantFailed("Illegal type passed to RangeBound.le")
+    }
+  }
+
+  private def lt(x: A, y: A): Boolean = {
+    if (x.isInstanceOf[BigInt] && y.isInstanceOf[BigInt]) {
+      x.asInstanceOf[BigInt] < y.asInstanceOf[BigInt]
+    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[JLong]) {
+      x.asInstanceOf[JLong] < y.asInstanceOf[JLong]
+    } else if (x.isInstanceOf[BigInt] && y.isInstanceOf[JLong]) {
+      x.asInstanceOf[BigInt] < BigInt(y.asInstanceOf[JLong])
+    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[BigInt]) {
+      BigInt(x.asInstanceOf[JLong]) < y.asInstanceOf[BigInt]
+    } else {
+      Assert.invariantFailed("Illegal type passed to RangeBound.lt")
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
index 32271e2..cebc507 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part2_attributes.xsd
@@ -108,6 +108,7 @@
     <xsd:attribute name="encodingErrorPolicy" type="dfdl:EncodingErrorPolicyEnum"/>
     <xsd:attribute name="ignoreCase" type="dfdl:YesNoEnum" />
     <xsd:attribute name="choiceBranchKey" type="dfdl:ListOfDFDLStringLiteral" />
+    <xsd:attribute ref="dfdlx:choiceBranchKeyRanges" />
     <xsd:attribute name="textBidi" type="dfdl:YesNoEnum"/>
   </xsd:attributeGroup>
 
@@ -680,6 +681,7 @@
       type="dfdl:YesNoEnum" />
     <xsd:attribute form="qualified" name="choiceBranchKey"
       type="dfdl:ListOfDFDLStringLiteral" />
+    <xsd:attribute ref="dfdlx:choiceBranchKeyRanges" />
   </xsd:attributeGroup>
 
   <!-- 12.1 Aligned Data -->
diff --git a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
index 2d743c1..7bee32b 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dfdlx.xsd
@@ -35,6 +35,7 @@
   <xs:simpleType name="PropertyNameType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="dfdlx:choiceBranchKeyKind" />
+      <xs:enumeration value="dfdlx:choiceBranchKeyRanges" />
       <xs:enumeration value="dfdlx:choiceDispatchKeyKind" />
       <xs:enumeration value="dfdlx:emptyElementParsePolicy"/>
       <xs:enumeration value="dfdlx:inputTypeCalc"/>
@@ -73,6 +74,8 @@
       <xs:enumeration value="implicit"/>
     </xs:restriction>
   </xs:simpleType>
+  
+  <xs:attribute name="choiceBranchKeyRanges" type="dfdl:NonEmptyListOfInteger" />
 
   <xs:attributeGroup name="SimpleTypeValueCalcAG">
     <xs:attribute form="qualified" name="repType" type="dfdl:DFDLQName" />
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala
index 5ebce0c..7bc2759 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/TypeCalculator.scala
@@ -38,6 +38,7 @@ import org.apache.daffodil.processors.parsers.ParseError
 import org.apache.daffodil.util.Maybe.One
 import org.apache.daffodil.dpath.NodeInfo.Kind
 import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.util.RangeBound
 
 abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Kind, val dstType: NodeInfo.Kind)
   extends Serializable {
@@ -274,87 +275,6 @@ class UnionTypeCalculator[A <: AnyRef, B <: AnyRef](subCalculators: Seq[(RepValu
 
 }
 
-class RangeBound[A <: AnyRef](val maybeBound: Maybe[A], val isInclusive: Boolean) extends Serializable {
-  lazy val isEmpty = maybeBound.isEmpty
-  lazy val isDefined = maybeBound.isDefined
-
-  override def toString(): String = {
-    if (maybeBound.isDefined) {
-      maybeBound.get.toString + "_" + (if (isInclusive) "inclusive" else "exclusive")
-    } else {
-      "Nope"
-    }
-  }
-
-  def intersectsWithOtherBounds(lower: RangeBound[A], upper: RangeBound[A]): Boolean = {
-    if (maybeBound.isEmpty) {
-      false
-    } else {
-      val bound = maybeBound.get
-      val inBounds = lower.testAsLower(bound) && upper.testAsUpper(bound)
-      val atBoundery = maybeBound == lower.maybeBound || maybeBound == upper.maybeBound
-      inBounds && (isInclusive || !atBoundery)
-    }
-  }
-
-  /*
-   * It should be the case that x is either a BigInt, or a Long
-   */
-
-  def testAsLower(x: A): Boolean = {
-    if (maybeBound.isEmpty) {
-      true
-    } else {
-      if (isInclusive) {
-        le(maybeBound.get, x)
-      } else {
-        lt(maybeBound.get, x)
-      }
-    }
-  }
-  def testAsUpper(x: A): Boolean = {
-    if (maybeBound.isEmpty) {
-      true
-    } else {
-      val bound = maybeBound.get
-      if (isInclusive) {
-        le(x, maybeBound.get)
-      } else {
-        lt(x, maybeBound.get)
-      }
-    }
-  }
-
-  private def le(x: A, y: A): Boolean = {
-    if (x.isInstanceOf[BigInt] && y.isInstanceOf[BigInt]) {
-      x.asInstanceOf[BigInt] <= y.asInstanceOf[BigInt]
-    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[JLong]) {
-      x.asInstanceOf[JLong] <= y.asInstanceOf[JLong]
-    } else if (x.isInstanceOf[BigInt] && y.isInstanceOf[JLong]) {
-      x.asInstanceOf[BigInt] <= BigInt(y.asInstanceOf[JLong])
-    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[BigInt]) {
-      BigInt(x.asInstanceOf[JLong]) <= y.asInstanceOf[BigInt]
-    } else {
-      Assert.invariantFailed("Illegal type passed to RangeBound.le")
-    }
-  }
-
-  private def lt(x: A, y: A): Boolean = {
-    if (x.isInstanceOf[BigInt] && y.isInstanceOf[BigInt]) {
-      x.asInstanceOf[BigInt] < y.asInstanceOf[BigInt]
-    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[JLong]) {
-      x.asInstanceOf[JLong] < y.asInstanceOf[JLong]
-    } else if (x.isInstanceOf[BigInt] && y.isInstanceOf[JLong]) {
-      x.asInstanceOf[BigInt] < BigInt(y.asInstanceOf[JLong])
-    } else if (x.isInstanceOf[JLong] && y.isInstanceOf[BigInt]) {
-      BigInt(x.asInstanceOf[JLong]) < y.asInstanceOf[BigInt]
-    } else {
-      Assert.invariantFailed("Illegal type passed to RangeBound.lt")
-    }
-  }
-
-}
-
 /*
  * Since we can inherit the restriction from xsd facets, we also need to be able to support an
  * aribitrary subset of: minInclusive, minExclusive, maxInclusive, and maxExclusive
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
index 793ebe8..06c5ee4 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
@@ -21,6 +21,7 @@ import org.apache.daffodil.processors._
 import org.apache.daffodil.util.LogLevel
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.util.RangeBound
 
 class ComplexTypeParser(rd: RuntimeData, bodyParser: Parser)
   extends CombinatorParser(rd) {
@@ -177,9 +178,13 @@ abstract class ChoiceDispatchCombinatorParserBase(rd: TermRuntimeData,
             parserOpt1
           } else{
             if(!dispatchKeyRangeMap.isEmpty){
-              val keyAsBigInt = BigInt(key)
-              val optAns1= dispatchKeyRangeMap.filter({case(min,max,_,_) => min.testAsLower(keyAsBigInt) && max.testAsUpper(keyAsBigInt)}).headOption
-              optAns1.map({case(_,_,parser,isRepresented)=>(parser,isRepresented)})
+              try{
+                val keyAsBigInt = BigInt(key)
+                val optAns1= dispatchKeyRangeMap.find({case(min,max,_,_) => min.testAsLower(keyAsBigInt) && max.testAsUpper(keyAsBigInt)})
+                optAns1.map({case(_,_,parser,isRepresented)=>(parser,isRepresented)})
+              } catch{
+                case _:NumberFormatException => None
+              }
             }else{
               None
             }
@@ -221,11 +226,16 @@ abstract class ChoiceDispatchCombinatorParserBase(rd: TermRuntimeData,
       }
     } finally {
       if (!freedInitialState) {
-        Assert.invariant(pstate.processorStatus ne Success)
         //Since we failed, it does not matter what state we are actually in, as our caller will backtrack anyway
         //What does matter is that we return the mark to the pool
         pstate.discard(initialState)
         freedInitialState = true
+        
+        /* It is important to put this check after cleaning up the initial state.
+         * If this check fails, before cleaning up the mark, then the error will be
+         * hidden by another error complaining about us not cleaning up the mark
+         */
+        Assert.invariant(pstate.processorStatus ne Success)
       }
     }
 
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/choiceBranchRanges/choiceBranchKeyRanges.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/choiceBranchRanges/choiceBranchKeyRanges.tdml
new file mode 100644
index 0000000..1b9f2ee
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/choiceBranchRanges/choiceBranchKeyRanges.tdml
@@ -0,0 +1,331 @@
+<?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 xmlns:ex="http://example.com" xmlns="http://example.com"
+  xmlns:tns="http://example.com"
+  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:dfdlx="http://www.ogf.org/dfdl/dfdl-1.0/extensions"
+  xmlns:fn="http://www.w3.org/2005/xpath-functions"
+  >
+
+  <tdml:defineSchema name="choiceBranchKeyRanges-Embedded.dfdl.xsd">
+
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format ref="ex:GeneralFormat"
+      lengthUnits="bits"
+      dfdlx:emptyElementParsePolicy="treatAsEmpty"
+      representation="binary"
+      />
+
+    <xs:simpleType name="uint8" dfdl:lengthKind="explicit" dfdl:length="8">
+      <xs:restriction base="xs:unsignedInt"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="emptyElt" dfdl:lengthKind="explicit" dfdl:length="0">
+      <xs:restriction base="xs:hexBinary"/>
+    </xs:simpleType>
+
+    <xs:element name="choiceBranchKeyRanges_01">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte" maxOccurs="unbounded" dfdl:occursCountKind="parsed">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="tag" type="tns:uint8"/>
+                <xs:choice dfdl:choiceDispatchKey="{ tns:tag }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdl:choiceBranchKey="0"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdl:choiceBranchKey="1 5" dfdlx:choiceBranchKeyRanges="10 100 110 255"/>
+                  <xs:element name="c" type="tns:emptyElt" dfdl:choiceBranchKey="2 3 4"/>
+                  <xs:element name="d" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="6 9"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_overlap_01">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="tag" type="tns:uint8"/>
+                <xs:choice dfdl:choiceDispatchKey="{ tns:tag }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdl:choiceBranchKey="0"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 255"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_overlap_02">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="tag" type="tns:uint8"/>
+                <xs:choice dfdl:choiceDispatchKey="{ tns:tag }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdl:choiceBranchKey="10"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 4 7 20 25 100"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_overlap_03">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="tag" type="tns:uint8"/>
+                <xs:choice dfdl:choiceDispatchKey="{ tns:tag }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="5 24 100 255"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 4 25 100"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_oddLength_01">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="tag" type="tns:uint8"/>
+                <xs:choice dfdl:choiceDispatchKey="{ tns:tag }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="5 24 25 100 255"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 4 25 100"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_badOrder_01">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="tag" type="tns:uint8"/>
+                <xs:choice dfdl:choiceDispatchKey="{ tns:tag }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="24 5 100 255"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 4 25 100"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_nonintDispatch_01">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:choice dfdl:choiceDispatchKey="{ 'x' }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdl:choiceBranchKey="x" dfdlx:choiceBranchKeyRanges="5 24 100 255"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 4 25 99"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="choiceBranchKeyRanges_nonintDispatch_02">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="byte">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:choice dfdl:choiceDispatchKey="{ 'x' }" >
+                  <xs:element name="a" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="5 24 100 255"/>
+                  <xs:element name="b" type="tns:emptyElt" dfdlx:choiceBranchKeyRanges="0 4 25 99"/>
+                </xs:choice>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_01"
+    root="choiceBranchKeyRanges_01" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    00 
+    01 05 0a 32 64 6e c8 ff
+    02 03 04
+    06 07 08 09
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset xmlns:xs="http://www.w3.org/2001/XMLSchema"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <choiceBranchKeyRanges_01>
+          <byte><tag>0</tag><a/></byte>
+          
+          <byte><tag>1</tag><b/></byte>
+          <byte><tag>5</tag><b/></byte>
+          <byte><tag>10</tag><b/></byte>
+          <byte><tag>50</tag><b/></byte>
+          <byte><tag>100</tag><b/></byte>
+          <byte><tag>110</tag><b/></byte>
+          <byte><tag>200</tag><b/></byte>
+          <byte><tag>255</tag><b/></byte>
+          
+          <byte><tag>2</tag><c/></byte>
+          <byte><tag>3</tag><c/></byte>
+          <byte><tag>4</tag><c/></byte>
+
+          <byte><tag>6</tag><d/></byte>
+          <byte><tag>7</tag><d/></byte>
+          <byte><tag>8</tag><d/></byte>
+          <byte><tag>9</tag><d/></byte>
+        </choiceBranchKeyRanges_01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_overlap_01"
+    root="choiceBranchKeyRanges_overlap_01" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>dfdl:choiceBranchKey</tdml:error>
+      <tdml:error>conflicts with</tdml:error>
+      <tdml:error>dfdlx:choiceBranchKeyRanges</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_overlap_02"
+    root="choiceBranchKeyRanges_overlap_02" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>dfdl:choiceBranchKey</tdml:error>
+      <tdml:error>conflicts with</tdml:error>
+      <tdml:error>dfdlx:choiceBranchKeyRanges</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_overlap_03"
+    root="choiceBranchKeyRanges_overlap_03" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>dfdlx:choiceBranchKeyRanges</tdml:error>
+      <tdml:error>conflicts with</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_oddLength_01"
+    root="choiceBranchKeyRanges_oddLength_01" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>Integer range</tdml:error>
+      <tdml:error>even</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_badOrder_01"
+    root="choiceBranchKeyRanges_badOrder_01" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>min value (24)</tdml:error>
+      <tdml:error>max value (5)</tdml:error>
+      <tdml:error>greater</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_nonintDispatch_01"
+    root="choiceBranchKeyRanges_nonintDispatch_01" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <choiceBranchKeyRanges_nonintDispatch_01>
+          <byte><a/></byte>
+        </choiceBranchKeyRanges_nonintDispatch_01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="choiceBranchKeyRanges_nonintDispatch_02"
+    root="choiceBranchKeyRanges_nonintDispatch_02" model="choiceBranchKeyRanges-Embedded.dfdl.xsd" description="Extensions - choiceBranchKeyRanges">
+
+    <tdml:document>
+    <tdml:documentPart type="byte">
+    </tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>Choice dispatch key (x) failed to match any of the branch keys</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
index 165ec38..e08bc78 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
@@ -1838,7 +1838,8 @@ it sure is/
     <tdml:errors>
       <tdml:error>Schema Definition Error</tdml:error>
       <tdml:error>choiceBranchKey</tdml:error>
-      <tdml:error>not defined</tdml:error>
+      <tdml:error>choiceBranchKeyRanges</tdml:error>
+      <tdml:error>defined</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
 
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestChoiceBranchKeyRanges.scala b/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestChoiceBranchKeyRanges.scala
new file mode 100644
index 0000000..4f43047
--- /dev/null
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestChoiceBranchKeyRanges.scala
@@ -0,0 +1,45 @@
+/*
+ * 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.extensions
+
+import org.apache.daffodil.tdml.Runner
+import org.junit.AfterClass
+import org.junit.Test
+
+object TestChoiceBranchKeyRanges {
+  val testDir = "/org/apache/daffodil/extensions/choiceBranchRanges/"
+
+  val runner = Runner(testDir, "choiceBranchKeyRanges.tdml", validateTDMLFile = true)
+
+  @AfterClass def shutDown {
+    runner.reset
+  }
+
+}
+
+class TestChoiceBranchKeyRanges {
+  import TestChoiceBranchKeyRanges._
+
+  @Test def test_choiceBranchKeyRanges_01() { runner.runOneTest("choiceBranchKeyRanges_01") }
+  @Test def test_choiceBranchKeyRanges_overlap_01() { runner.runOneTest("choiceBranchKeyRanges_overlap_01") }
+  @Test def test_choiceBranchKeyRanges_overlap_02() { runner.runOneTest("choiceBranchKeyRanges_overlap_02") }
+  @Test def test_choiceBranchKeyRanges_overlap_03() { runner.runOneTest("choiceBranchKeyRanges_overlap_03") }
+  @Test def test_choiceBranchKeyRanges_oddLength_01() { runner.runOneTest("choiceBranchKeyRanges_oddLength_01") }
+  @Test def test_choiceBranchKeyRanges_badOrder_01() { runner.runOneTest("choiceBranchKeyRanges_badOrder_01") }
+  @Test def test_choiceBranchKeyRanges_nonintDispatch_01() { runner.runOneTest("choiceBranchKeyRanges_nonintDispatch_01") }
+  @Test def test_choiceBranchKeyRanges_nonintDispatch_02() { runner.runOneTest("choiceBranchKeyRanges_nonintDispatch_02") }
+}
\ No newline at end of file