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/12/19 19:51:53 UTC
[incubator-daffodil] branch master updated: Refactor type
calculator to take advantage of DataValue type
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 3808610 Refactor type calculator to take advantage of DataValue type
3808610 is described below
commit 3808610cd1354f395337b9e37116fb096b579dde
Author: Brandon Sloane <bs...@tresys.com>
AuthorDate: Mon Dec 16 10:56:49 2019 -0500
Refactor type calculator to take advantage of DataValue type
This fixes a bug where we would inadvertandly use Scala BigInts
Daffodil-2188
---
.../org/apache/daffodil/dpath/Expression.scala | 2 +-
.../org/apache/daffodil/dsom/ReptypeMixins.scala | 2 +-
.../apache/daffodil/dsom/RestrictionUnion.scala | 33 +--
.../org/apache/daffodil/dsom/SimpleTypes.scala | 116 ++++-----
.../grammar/primitives/ChoiceCombinator.scala | 79 +++---
.../org/apache/daffodil/cookers/Cookers.scala | 20 +-
.../org/apache/daffodil/util/RangeBound.scala | 111 ---------
.../unparsers/ExpressionEvaluatingUnparsers.scala | 11 +-
.../org/apache/daffodil/infoset/DataValue.scala | 8 +-
.../org/apache/daffodil/infoset/DataValue.scala | 13 +-
.../daffodil/dpath/DFDLXTypeCalcFunctions.scala | 12 +-
.../org/apache/daffodil/infoset/InfosetImpl.scala | 1 +
.../apache/daffodil/processors/RuntimeData.scala | 4 +-
.../daffodil/processors/TypeCalculator.scala | 276 ++++++++++++++-------
.../processors/parsers/ElementKindParsers.scala | 22 +-
.../parsers/ExpressionEvaluatingParsers.scala | 4 +-
.../extensions/type_calc/typeCalcFunctions.tdml | 109 ++++++++
.../extensions/TestInputTypeValueCalc.scala | 1 +
18 files changed, 481 insertions(+), 343 deletions(-)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
index 83da2c2..673b077 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
@@ -1909,7 +1909,7 @@ case class FunctionCallExpression(functionQNameString: String, expressions: List
funcObj
}
- def lookupTypeCalculator(typeCalcExpr: Expression, requiresParse: Boolean, requiresUnparse: Boolean): TypeCalculator[AnyRef, AnyRef] = {
+ def lookupTypeCalculator(typeCalcExpr: Expression, requiresParse: Boolean, requiresUnparse: Boolean): TypeCalculator = {
/*
* Every function that currently uses this defines the type calculator
* as their first arguement.
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ReptypeMixins.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ReptypeMixins.scala
index bb160c2..53ff0a3 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ReptypeMixins.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ReptypeMixins.scala
@@ -58,7 +58,7 @@ trait HasOptRepTypeMixin {
case Some(x: SimpleTypeDefBase) => Some(x)
case _ => None
}
- def optRepValueSet: Option[RepValueSet[AnyRef]]
+ def optRepValueSet: Option[RepValueSet]
def optRepTypeElement: Option[RepTypeQuasiElementDecl]
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 d3f8315..f7d0f2a 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
@@ -17,7 +17,7 @@
package org.apache.daffodil.dsom
-import java.math.BigInteger
+import java.math.{BigInteger => JBigInt}
import org.apache.daffodil.exceptions.ThrowsSDE
import org.apache.daffodil.exceptions.UnsuppressableException
import scala.xml.Node
@@ -35,7 +35,10 @@ 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
+import org.apache.daffodil.processors.RangeBound
+import org.apache.daffodil.infoset.DataValue
+import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
+import org.apache.daffodil.infoset.DataValue.DataValueBigInt
/**
* A schema component for simple type restrictions
@@ -175,17 +178,17 @@ final class Restriction(xmlArg: Node, val simpleTypeDef: SimpleTypeDefBase)
lazy val enumerations: Seq[EnumerationDefFactory] = (xml \ "enumeration").map(new EnumerationDefFactory(_, simpleTypeDef))
- lazy val facetValueSet: RepValueSet[AnyRef] = {
- val initAns: (RangeBound[BigInt], RangeBound[BigInt]) = (new RangeBound(Maybe.Nope, false), new RangeBound(Maybe.Nope, false))
+ lazy val facetValueSet: RepValueSet = {
+ val initAns: (RangeBound, RangeBound) = (new RangeBound(DataValue.NoValue, false), new RangeBound(DataValue.NoValue, false))
val range = combinedBaseFacets.foldLeft(initAns)({
case (acc, (facetType, facetValue)) =>
- lazy val valueAsBigInt = BigInt(facetValue)
+ lazy val valueAsBigInt:DataValueBigInt = new JBigInt(facetValue)
val (maybeBound, isMax, isInclusive) = facetType match {
- case Facet.maxExclusive => (Maybe(valueAsBigInt), true, false)
- case Facet.maxInclusive => (Maybe(valueAsBigInt), true, true)
- case Facet.minExclusive => (Maybe(valueAsBigInt), false, false)
- case Facet.minInclusive => (Maybe(valueAsBigInt), false, true)
- case _ => (Maybe.Nope, false, false)
+ case Facet.maxExclusive => (valueAsBigInt, true, false)
+ case Facet.maxInclusive => (valueAsBigInt, true, true)
+ case Facet.minExclusive => (valueAsBigInt, false, false)
+ case Facet.minInclusive => (valueAsBigInt, false, true)
+ case _ => (DataValue.NoValue, false, false)
}
if (maybeBound.isEmpty) {
acc
@@ -207,10 +210,10 @@ final class Restriction(xmlArg: Node, val simpleTypeDef: SimpleTypeDefBase)
}
}
})
- RepValueSetCompiler.compile(Seq(), Seq(range.asInstanceOf[(RangeBound[AnyRef], RangeBound[AnyRef])]))
+ RepValueSetCompiler.compile(Seq(), Seq(range.asInstanceOf[(RangeBound, RangeBound)]))
}
- lazy val repValueSet: RepValueSet[AnyRef] = {
+ lazy val repValueSet: RepValueSet = {
val subsets = enumerations.map(_.optRepValueSet).filter(_.isDefined).map(_.get)
if (subsets.length != 0 && subsets.length != enumerations.length) {
SDE("If one enumeration value defines a repValue, then all must define a repValue")
@@ -226,7 +229,7 @@ final class Restriction(xmlArg: Node, val simpleTypeDef: SimpleTypeDefBase)
lazy val optRepValueSet = if (repValueSet.isEmpty) None else Some(repValueSet)
- lazy val logicalValueSet: RepValueSet[AnyRef] = {
+ lazy val logicalValueSet: RepValueSet = {
val subsets = enumerations.map(_.logicalValueSet)
val fromEnums = subsets.fold(RepValueSetCompiler.empty)((a, b) => a.merge(b))
if (enumerations.length > 0) {
@@ -237,7 +240,7 @@ final class Restriction(xmlArg: Node, val simpleTypeDef: SimpleTypeDefBase)
}
}
- lazy val optLogicalValueSet: Option[RepValueSet[AnyRef]] = if (logicalValueSet.isEmpty) None else Some(logicalValueSet)
+ lazy val optLogicalValueSet: Option[RepValueSet] = if (logicalValueSet.isEmpty) None else Some(logicalValueSet)
}
/**
@@ -487,7 +490,7 @@ sealed trait TypeChecks { self: Restriction =>
protected def isInUnsignedXXXRange(value: java.math.BigDecimal, numBits: Int, typeName: String): Boolean = {
Assert.usage(numBits <= 64, "isInUnsignedXXXRange: numBits must be <= 64.")
// val min = java.math.BigDecimal.ZERO
- val max = new java.math.BigDecimal(BigInteger.ONE.shiftLeft(numBits)).subtract(new java.math.BigDecimal(1))
+ val max = new java.math.BigDecimal(JBigInt.ONE.shiftLeft(numBits)).subtract(new java.math.BigDecimal(1))
val isNegative = value.signum == -1
if (isNegative) return false
val checkMax = value.compareTo(max)
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 d80245d..393a21f 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
@@ -17,40 +17,35 @@
package org.apache.daffodil.dsom
+import java.math.{ BigInteger => JBigInt }
+
import scala.xml.Node
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.dpath.NodeInfo.PrimType
+
+import org.apache.daffodil.cookers.IntRangeCooker
+import org.apache.daffodil.cookers.RepValueCooker
import org.apache.daffodil.dpath.NodeInfo
-import org.apache.daffodil.processors.SimpleTypeRuntimeData
-import org.apache.daffodil.util.Misc
-import org.apache.daffodil.schema.annotation.props.gen.Enumeration_AnnotationMixin
-import org.apache.daffodil.schema.annotation.props.PropertyLookupResult
-import org.apache.daffodil.ExecutionMode
-import org.apache.daffodil.schema.annotation.props.Found
-import org.apache.daffodil.xml.QName
-import org.apache.daffodil.schema.annotation.props.NotFound
-import org.apache.daffodil.processors.RepValueSet
+import org.apache.daffodil.dpath.NodeInfo.PrimType
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.processors.IdentifyTypeCalculator
import org.apache.daffodil.processors.RepValueSet
+import org.apache.daffodil.processors.RepValueSetCompiler
+import org.apache.daffodil.processors.SimpleTypeRuntimeData
import org.apache.daffodil.processors.TypeCalculator
import org.apache.daffodil.processors.TypeCalculatorCompiler
-import org.apache.daffodil.processors.RepValueSetCompiler
-import org.apache.daffodil.dpath.NodeInfo.Numeric
-import org.apache.daffodil.dpath.NodeInfo.PrimType.UnsignedInt
-import org.apache.daffodil.cookers.RepValueCooker
-import org.apache.daffodil.cookers.RepValueCooker
-import scala.xml.Elem
-import scala.xml.MetaData
-import scala.xml.UnprefixedAttribute
-import scala.xml.Null
-import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.schema.annotation.props.Found
+import org.apache.daffodil.util.Misc
import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.xml.QName
import org.apache.daffodil.xml.XMLUtils
-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
+import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
+import org.apache.daffodil.processors.RangeBound
+import org.apache.daffodil.util.Maybe.One
+import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
+import org.apache.daffodil.infoset.DataValue
+import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
+import org.apache.daffodil.infoset.DataValue.DataValueBigInt
+import org.apache.daffodil.infoset.DataValue.DataValueBigInt
+import org.apache.daffodil.infoset.DataValue.DataValueString
import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
trait TypeBase {
@@ -80,9 +75,9 @@ sealed trait HasRepValueAttributes extends AnnotatedSchemaComponent
{
def optRepType: Option[SimpleTypeBase]
- def optRepValueSet: Option[RepValueSet[AnyRef]]
+ def optRepValueSet: Option[RepValueSet]
- lazy val (repValuesAttrCooked: Seq[AnyRef], repValueRangesAttrCooked: Seq[(RangeBound[BigInt], RangeBound[BigInt])]) =
+ lazy val (repValuesAttrCooked:Seq[DataValuePrimitive], repValueRangesAttrCooked:Seq[(RangeBound,RangeBound)]) =
optRepType match {
case Some(repType) => {
val repValueSetRaw = findPropertyOption("repValues").toOption
@@ -91,12 +86,16 @@ sealed trait HasRepValueAttributes extends AnnotatedSchemaComponent
repType.primType match {
case PrimType.String => {
if (repValueRangesRaw.size > 0) SDE("repValueRanges set when using a string repType")
- val repValueSetCooked = repValueSetRaw.map(RepValueCooker.convertConstant(_, this, false))
+ val repValueSetCooked = repValueSetRaw.flatMap(RepValueCooker.convertConstant(_, this, false)).map(DataValue.toDataValue)
(repValueSetCooked, Seq())
}
case _: NodeInfo.Integer.Kind => {
- val ans1 = repValueSetRaw.map(BigInt(_))
- val ans2 = IntRangeCooker.convertConstant(repValueRangesRaw, this, false)
+ val ans1 = repValueSetRaw.map(new JBigInt(_):DataValueBigInt)
+ val ans2 = IntRangeCooker.convertConstant(repValueRangesRaw, this, false).map({case (lower, upper) =>
+ (new RangeBound(lower,true), new RangeBound(upper, true))
+ }
+
+ )
(ans1, ans2)
}
case x => SDE("repType must be either String or Integer type")
@@ -105,9 +104,9 @@ sealed trait HasRepValueAttributes extends AnnotatedSchemaComponent
case None => (Seq(), Seq())
}
- lazy val optRepValueSetFromAttribute: Option[RepValueSet[AnyRef]] = optRepType.flatMap(repType => {
+ lazy val optRepValueSetFromAttribute: Option[RepValueSet] = optRepType.flatMap(repType => {
- val ans = RepValueSetCompiler.compile(repValuesAttrCooked, repValueRangesAttrCooked.asInstanceOf[Seq[(RangeBound[AnyRef], RangeBound[AnyRef])]])
+ val ans = RepValueSetCompiler.compile(repValuesAttrCooked, repValueRangesAttrCooked.asInstanceOf[Seq[(RangeBound, RangeBound)]])
if (ans.isEmpty) None else Some(ans)
})
@@ -315,12 +314,12 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
lazy val optInputTypeCalc = findPropertyOption("inputTypeCalc")
lazy val optOutputTypeCalc = findPropertyOption("outputTypeCalc")
- lazy val optTypeCalculator: Option[TypeCalculator[AnyRef, AnyRef]] = LV('optTypeCalculator) {
+ lazy val optTypeCalculator: Option[TypeCalculator] = LV('optTypeCalculator) {
optRepType.flatMap(repType => {
val srcType = repType.primType
val dstType = primType
- val fromRestriction: Option[TypeCalculator[AnyRef, AnyRef]] = optRestriction.flatMap({ restriction =>
+ val fromRestriction: Option[TypeCalculator] = optRestriction.flatMap({ restriction =>
val enumerations = restriction.enumerations.filter(_.optRepValueSet.isDefined)
if (enumerations.isEmpty) {
/*
@@ -334,16 +333,19 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
if (enumerations.size != restriction.enumerations.size) {
SDE("If one enumeration value defines a repValue, then all must define a repValue")
}
- val terms = enumerations.map(enum => (enum.optRepValueSet.get, enum.canonicalRepValue.get, enum.enumValueCooked.getAnyRef))
+ val terms = enumerations.map(enum => {
+ Assert.invariant(enum.canonicalRepValue.isDefined)
+ (enum.optRepValueSet.get, enum.canonicalRepValue.getNonNullable, enum.enumValueCooked)
+ })
Some(TypeCalculatorCompiler.compileKeysetValue(terms, srcType, dstType))
}
})
- val fromUnion: Option[TypeCalculator[AnyRef, AnyRef]] = optUnion.map({ union =>
- val subCalculators: Seq[(RepValueSet[AnyRef], RepValueSet[AnyRef], TypeCalculator[AnyRef, AnyRef])] =
+ val fromUnion: Option[TypeCalculator] = optUnion.map({ union =>
+ val subCalculators: Seq[(RepValueSet, RepValueSet, TypeCalculator)] =
union.unionMemberTypes.map(subType => (subType.optRepValueSet.get, subType.optLogicalValueSet.get, subType.optTypeCalculator.get))
TypeCalculatorCompiler.compileUnion(subCalculators)
})
- val fromExpression: Option[TypeCalculator[AnyRef, AnyRef]] = {
+ val fromExpression: Option[TypeCalculator] = {
/*
* This is a minefield for circular dependencies.
* In order to compile expressions involve many typeCalc functions,
@@ -384,7 +386,7 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
case (Some(x), None, None) => Some(x)
case (None, Some(x), None) => Some(x)
case (None, None, Some(x)) => SDE("Usage of inputTypeCalc and outputTypeCalc requires an empty xs:restriction to determine the base type.")
- case (Some(x), _, Some(y)) if x.isInstanceOf[IdentifyTypeCalculator[AnyRef]] => Some(y)
+ case (Some(x), _, Some(y)) if x.isInstanceOf[IdentifyTypeCalculator] => Some(y)
case (None, None, None) => {
if (dstType != srcType) {
val repTypeName = optRepTypeDef match {
@@ -404,7 +406,7 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
}
ans match {
- case Some(idt: IdentifyTypeCalculator[AnyRef]) => {
+ case Some(idt: IdentifyTypeCalculator) => {
if (srcType != dstType) {
SDE("Identity transform requires that the basetype and reptype have a common primitive type")
}
@@ -465,17 +467,17 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
fromSelf.orElse(fromUnion)
}.toOption.flatten
- override lazy val optRepValueSet: Option[RepValueSet[AnyRef]] = optRepTypeDef.flatMap(repType => {
+ override lazy val optRepValueSet: Option[RepValueSet] = optRepTypeDef.flatMap(repType => {
val primType: PrimType = repType.primType
- val fromRestriction: Option[RepValueSet[AnyRef]] = optRestriction.flatMap(_.optRepValueSet)
- val fromUnion: Option[RepValueSet[AnyRef]] = {
+ val fromRestriction: Option[RepValueSet] = optRestriction.flatMap(_.optRepValueSet)
+ val fromUnion: Option[RepValueSet] = {
val componentTypes = optUnion.map(_.unionMemberTypes).getOrElse(Seq())
val componentValueSets = componentTypes.flatMap(_.optRepValueSet)
val ans = componentValueSets.fold(RepValueSetCompiler.empty)((a, b) => a.merge(b))
if (ans.isEmpty) None else Some(ans)
}
- val fromSelf: Option[RepValueSet[AnyRef]] = optRepValueSetFromAttribute
+ val fromSelf: Option[RepValueSet] = optRepValueSetFromAttribute
(fromRestriction, fromUnion, fromSelf) match {
case (None, None, None) => None
@@ -489,9 +491,9 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
})
- lazy val optLogicalValueSet: Option[RepValueSet[AnyRef]] = {
- val fromRestriction: Option[RepValueSet[AnyRef]] = optRestriction.map(_.logicalValueSet)
- val fromUnion: Option[RepValueSet[AnyRef]] = optUnion.map(union => {
+ lazy val optLogicalValueSet: Option[RepValueSet] = {
+ val fromRestriction: Option[RepValueSet] = optRestriction.map(_.logicalValueSet)
+ val fromUnion: Option[RepValueSet] = optUnion.map(union => {
val subsets = union.unionMemberTypes.map(_.optLogicalValueSet).filter(_.isDefined).map(_.get)
subsets.fold(RepValueSetCompiler.empty)((a, b) => a.merge(b))
})
@@ -567,16 +569,16 @@ final class EnumerationDefFactory(
lazy val enumValueRaw: String = (xml \ "@value").head.text
lazy val enumValueCooked: DataValuePrimitive = parentType.primType.fromXMLString(enumValueRaw)
- override lazy val optRepValueSet: Option[RepValueSet[AnyRef]] = optRepValueSetFromAttribute
- lazy val logicalValueSet: RepValueSet[AnyRef] = RepValueSetCompiler.compile(Seq(enumValueCooked.getAnyRef), Seq())
- lazy val canonicalRepValue: Option[AnyRef] = {
- val ans1 = repValuesAttrCooked.headOption
- val ans2 = repValueRangesAttrCooked.headOption.map(_._1).flatMap(asBound => {
+ override lazy val optRepValueSet: Option[RepValueSet] = optRepValueSetFromAttribute
+ lazy val logicalValueSet: RepValueSet = RepValueSetCompiler.compile(Seq(enumValueCooked), Seq())
+ lazy val canonicalRepValue: DataValuePrimitiveNullable = {
+ val ans1 = repValuesAttrCooked.headOption.getOrElse(DataValue.NoValue)
+ val ans2 = repValueRangesAttrCooked.headOption.map(_._1).map(asBound => {
//TODO, currently, if the first repValue comes from an exclusive restriction we cannot
//infer a canonical repValue
- if (asBound.isInclusive) (Some(asBound.maybeBound.get)) else None
- })
- val ans = ans1.orElse(ans2)
+ if (asBound.isInclusive) asBound.maybeBound else DataValue.NoValue
+ }).getOrElse(DataValue.NoValue)
+ val ans = if(ans1.isDefined) ans1 else ans2
Assert.invariant(ans.isDefined == optRepValueSet.isDefined)
ans
}
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 e20b8f9..30e7b3b 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
@@ -17,27 +17,29 @@
package org.apache.daffodil.grammar.primitives
-import org.apache.daffodil.grammar.Terminal
+import scala.util.Failure
+import scala.util.Success
+import scala.util.Try
+
+import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.cookers.ChoiceBranchKeyCooker
+import org.apache.daffodil.cookers.IntRangeCooker
import org.apache.daffodil.dsom._
+import org.apache.daffodil.dsom.ChoiceTermBase
+import org.apache.daffodil.equality._
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.grammar.Terminal
import org.apache.daffodil.processors.parsers._
import org.apache.daffodil.processors.unparsers._
-import org.apache.daffodil.grammar.Gram
-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
-import scala.util.Failure
-import scala.util.Success
-import scala.math.BigInt
-import org.apache.daffodil.cookers.RepValueCooker
-import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.schema.annotation.props.gen.ChoiceLengthKind
import org.apache.daffodil.util.MaybeInt
-import org.apache.daffodil.util.RangeBound
-import org.apache.daffodil.cookers.IntRangeCooker
+
+import java.math.{ BigInteger => JBigInt }
+import org.apache.daffodil.processors.RangeBound
+import org.apache.daffodil.util.Maybe.One
+import org.apache.daffodil.infoset.DataValue.DataValueString
/*
* The purpose of the ChoiceCombinator (and the parsers it creates) is to
@@ -85,11 +87,11 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
}
} else {
//Verify that every alternative has some form of branch key
- alternatives.map{ alt =>
+ 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){
+ if (!hasBranchKey && !hasBranchKeyRanges && ch.defaultableChoiceBranchKeyKind != ChoiceKeyKindType.ByType) {
keyTerm.SDE("Neither dfdl:choiceBranchKey nor dfdlx:choiceBranchKeyRanges is defined.")
}
}
@@ -102,16 +104,16 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
val st = keyTerm_.simpleType
val aa = st.optRepValueSet
val repValueSet = keyTerm_.simpleType.optRepValueSet.get
- val ans = repValueSet.valueSet.toSeq.map(_.toString).mkString(" ")
+ val ans = repValueSet.valueSet.toSeq.map(_.getAnyRef.toString).mkString(" ")
ans
}
- case ChoiceKeyKindType.Explicit | ChoiceKeyKindType.Implicit => keyTerm.findPropertyOption("choiceBranchKey").toOption.getOrElse("")
+ 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){
+ if (uncookedBranchKeys.isEmpty) {
List()
- }else{
+ } else {
ChoiceBranchKeyCooker.convertConstant(uncookedBranchKeys, ch.runtimeData, forUnparse = false)
}
}
@@ -120,21 +122,26 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
//[(minKeyValue, maxKeyValue, parser, isRepresented)]
//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 dispatchBranchKeyRangeTuples: Seq[(RangeBound, RangeBound, Parser, Boolean)] = alternatives.flatMap { alt =>
val keyTerm = alt.context.asInstanceOf[Term]
- val branchKeyRanges:Seq[(RangeBound[BigInt],RangeBound[BigInt])] = ch.defaultableChoiceBranchKeyKind match {
+ val branchKeyRanges: Seq[(RangeBound, RangeBound)] = ch.defaultableChoiceBranchKeyKind match {
case ChoiceKeyKindType.ByType => {
val keyTerm_ = keyTerm.asInstanceOf[ElementBase]
- keyTerm_.simpleType.optRepValueSet.get.valueRanges.toSeq.map(x=>{
- val x_ = x.asInstanceOf[(RangeBound[BigInt],RangeBound[BigInt])]
- (x_._1,x_._2)
+ keyTerm_.simpleType.optRepValueSet.get.valueRanges.toSeq.map(x => {
+ val x_ = x.asInstanceOf[(RangeBound, RangeBound)]
+ (x_._1, x_._2)
+ })
+ }
+ case ChoiceKeyKindType.Explicit | ChoiceKeyKindType.Implicit => {
+ val bounds = IntRangeCooker.convertConstant(keyTerm.findPropertyOption("choiceBranchKeyRanges").toOption.getOrElse(""), context, false)
+ bounds.map({
+ case (lowerBound, upperBound) =>
+ (new RangeBound(lowerBound, true), new RangeBound(upperBound, true))
})
}
- 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))
+ branchKeyRanges.toSeq.map(x => (x._1, x._2, alt.parser, alt.context.enclosingTerm.get.isRepresented))
}
// check for duplicate branch keys
@@ -142,9 +149,9 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
// 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")
+ 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)
@@ -153,12 +160,12 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
if (kvs.length > 1) {
SDE(
"%s value (%s) is not unique across all branches of a direct dispatch choice. Offending branches are:\n%s",
- branchKeyAttribute, k,
+ branchKeyAttribute, k,
kvs.map(_._2.context.runtimeData).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
}
Try(k.toLong) match {
case Success(v) => {
- val asBigInt = BigInt(v)
+ val asBigInt = JBigInt.valueOf(v)
val conflictingRanges = dispatchBranchKeyRangeTuples.filter({ case (min, max, _, _) => min.testAsLower(asBigInt) && max.testAsUpper(asBigInt) })
if (conflictingRanges.length > 0) {
SDE(
@@ -197,7 +204,7 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram])
(parser, isRepresented)
})
val serializableMap: Map[String, (Parser, Boolean)] = dispatchBranchKeyMap.map(identity)
- val serializableKeyRangeMap: Vector[(RangeBound[BigInt], RangeBound[BigInt], Parser, Boolean)] = dispatchBranchKeyRangeTuples.toVector.map(identity)
+ val serializableKeyRangeMap: Vector[(RangeBound, RangeBound, Parser, Boolean)] = dispatchBranchKeyRangeTuples.toVector.map(identity)
ch.defaultableChoiceDispatchKeyKind match {
case ChoiceKeyKindType.ByType =>
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 0d8592e..63c142a 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,8 +17,8 @@
package org.apache.daffodil.cookers
-import org.apache.daffodil.util.RangeBound
-import org.apache.daffodil.util.RangeBound.Range
+import java.math.{ BigInteger => JBigInt }
+
import org.apache.daffodil.exceptions.ThrowsSDE
import org.apache.daffodil.util.Maybe
@@ -90,20 +90,18 @@ object RepValueCooker extends ListOfStringLiteralNonEmptyNoCharClassEntitiesNoBy
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])] = {
+object IntRangeCooker extends Converter[String, Seq[(JBigInt, JBigInt)]] {
+ protected def convert(input: String, context: ThrowsSDE, forUnparse: Boolean): Seq[(JBigInt, JBigInt)] = {
+ def run(xs: Seq[String]): Seq[(JBigInt, JBigInt)] = {
xs match {
case Seq() => Seq()
case a +: b +: rest => {
- val a2 = BigInt(a)
- val b2 = BigInt(b)
- if (a2.compare(b2) > 0) {
+ val a2 = new JBigInt(a)
+ val b2 = new JBigInt(b)
+ if (a2.compareTo(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)
+ (a2, b2) +: run(rest)
}
}
}
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
deleted file mode 100644
index 5a1f8e7..0000000
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/RangeBound.scala
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
index 5ee6d19..62cb5b4 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
@@ -111,7 +111,7 @@ class NewVariableInstanceEndUnparser(override val context: RuntimeData)
}
}
-class TypeValueCalcUnparser(typeCalculator: TypeCalculator[AnyRef, AnyRef], repTypeUnparser: Unparser, e: ElementRuntimeData, repTypeRuntimeData: ElementRuntimeData)
+class TypeValueCalcUnparser(typeCalculator: TypeCalculator, repTypeUnparser: Unparser, e: ElementRuntimeData, repTypeRuntimeData: ElementRuntimeData)
extends CombinatorUnparser(e) {
override def childProcessors: Vector[Unparser] = Vector(repTypeUnparser)
@@ -124,12 +124,13 @@ class TypeValueCalcUnparser(typeCalculator: TypeCalculator[AnyRef, AnyRef], repT
val currentSimple = ustate.currentInfosetNode.asSimple
- val logicalValue: DataValuePrimitiveNullable = currentSimple.dataValue
- Assert.invariant(logicalValue.isDefined)
+ val logicalValueNullable = currentSimple.dataValue
+ Assert.invariant(logicalValueNullable.isDefined)
+ val logicalValue=logicalValueNullable.getNonNullable
val logicalValueType = currentSimple.erd.optPrimType.get
val repTypeValue: DataValuePrimitiveNullable = {
- val ans = typeCalculator.outputTypeCalcUnparse(ustate, e, logicalValue.getAnyRef, logicalValueType)
- DataValue.unsafeFromMaybeAnyRef(ans)
+ val ans = typeCalculator.outputTypeCalcUnparse(ustate, e, logicalValue.getNonNullable, logicalValueType)
+ ans
}
val origInfosetElement = ustate.currentInfosetNode
diff --git a/daffodil-runtime1/src/main/scala-2.11/org/apache/daffodil/infoset/DataValue.scala b/daffodil-runtime1/src/main/scala-2.11/org/apache/daffodil/infoset/DataValue.scala
index 84bbf93..9ebeb7c 100644
--- a/daffodil-runtime1/src/main/scala-2.11/org/apache/daffodil/infoset/DataValue.scala
+++ b/daffodil-runtime1/src/main/scala-2.11/org/apache/daffodil/infoset/DataValue.scala
@@ -71,7 +71,7 @@ sealed trait DataValuePrimitiveType
* allow for automatic upcasting between these types where appropriate.
*
*/
-final class DataValue[+T <: AnyRef, +X <: AnyRef] private (val v: T) extends AnyVal {
+final class DataValue[+T <: AnyRef, +X <: AnyRef] private (val v: T) extends AnyVal with Serializable{
@inline def isEmpty = DataValue.NoValue.v eq v
@inline def isDefined = !isEmpty
@inline def value = v
@@ -99,8 +99,8 @@ final class DataValue[+T <: AnyRef, +X <: AnyRef] private (val v: T) extends Any
@inline def getString = v.asInstanceOf[JString]
@inline def getURI = v.asInstanceOf[URI]
- @inline def getNonNullable = this.asInstanceOf[DataValue[T, X with NonNullable]]
- @inline def getNullablePrimitive = this.asInstanceOf[DataValue.DataValuePrimitiveNullable]
+ @inline def getNonNullable:DataValue[T, X with NonNullable] = new DataValue(v)
+ @inline def getNullablePrimitive:DataValue.DataValuePrimitiveNullable = new DataValue(v)
@inline def getOptionAnyRef = {
if (isEmpty) {
@@ -248,6 +248,6 @@ object DataValue {
}
}
-class BoxedByteArray(val v: Array[Byte]) {
+class BoxedByteArray(val v: Array[Byte]) extends Serializable {
}
\ No newline at end of file
diff --git a/daffodil-runtime1/src/main/scala-2.12/org/apache/daffodil/infoset/DataValue.scala b/daffodil-runtime1/src/main/scala-2.12/org/apache/daffodil/infoset/DataValue.scala
index c264e3b..f33d8d9 100644
--- a/daffodil-runtime1/src/main/scala-2.12/org/apache/daffodil/infoset/DataValue.scala
+++ b/daffodil-runtime1/src/main/scala-2.12/org/apache/daffodil/infoset/DataValue.scala
@@ -67,7 +67,7 @@ sealed trait DataValuePrimitiveType
* allow for automatic upcasting between these types where appropriate.
*
*/
-final class DataValue[+T <: AnyRef, +X <: AnyRef] private (val v: T) extends AnyVal {
+final class DataValue[+T <: AnyRef, +X <: AnyRef] private (val v: T) extends AnyVal with Serializable {
@inline def isEmpty = DataValue.NoValue.v eq v
@inline def isDefined = !isEmpty
@inline def value = v
@@ -89,8 +89,8 @@ final class DataValue[+T <: AnyRef, +X <: AnyRef] private (val v: T) extends Any
@inline def getString = v.asInstanceOf[JString]
@inline def getURI = v.asInstanceOf[URI]
- @inline def getNonNullable = this.asInstanceOf[DataValue[T, X with NonNullable]]
- @inline def getNullablePrimitive = this.asInstanceOf[DataValue.DataValuePrimitiveNullable]
+ @inline def getNonNullable:DataValue[T, X with NonNullable] = new DataValue(v)
+ @inline def getNullablePrimitive:DataValue.DataValuePrimitiveNullable = new DataValue(v)
@inline def getOptionAnyRef = {
if (isEmpty) {
@@ -183,6 +183,13 @@ object DataValue {
} else {
NoValue
}
+ }
+ @inline def unsafeFromOptionAnyRef(v: Option[AnyRef]) = {
+ if (v.isDefined) {
+ new DataValue(v.get)
+ } else {
+ NoValue
+ }
}
val NoValue: DataValueEmpty = new DataValue(null)
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala
index c3bcc35..d181cea 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLXTypeCalcFunctions.scala
@@ -19,26 +19,27 @@ package org.apache.daffodil.dpath
import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
import org.apache.daffodil.processors.TypeCalculator
+import org.apache.daffodil.exceptions.Assert
-case class DFDLXInputTypeCalc(typedRecipes: List[(CompiledDPath, NodeInfo.Kind)], calc: TypeCalculator[AnyRef, AnyRef])
+case class DFDLXInputTypeCalc(typedRecipes: List[(CompiledDPath, NodeInfo.Kind)], calc: TypeCalculator)
extends FNTwoArgs(typedRecipes.map(_._1)) {
val srcType = typedRecipes(1)._2
override def computeValue(arg1: DataValuePrimitive, arg2: DataValuePrimitive, dstate: DState): DataValuePrimitive = {
- calc.inputTypeCalcRun(dstate, arg2.getAnyRef, srcType)
+ calc.inputTypeCalcRun(dstate, arg2, srcType)
dstate.currentValue.getNonNullable
}
}
-case class DFDLXOutputTypeCalc(typedRecipes: List[(CompiledDPath, NodeInfo.Kind)], calc: TypeCalculator[AnyRef, AnyRef])
+case class DFDLXOutputTypeCalc(typedRecipes: List[(CompiledDPath, NodeInfo.Kind)], calc: TypeCalculator)
extends FNTwoArgs(typedRecipes.map(_._1)) {
val srcType = typedRecipes(1)._2
def computeValue(arg1: DataValuePrimitive, arg2: DataValuePrimitive, dstate: DState): DataValuePrimitive = {
- calc.outputTypeCalcRun(dstate, arg2.getAnyRef, srcType)
+ calc.outputTypeCalcRun(dstate, arg2, srcType)
dstate.currentValue.getNonNullable
}
}
@@ -62,7 +63,8 @@ case class DFDLXOutputTypeCalcNextSibling(a: CompiledDPath, b: NodeInfo.Kind) ex
val primType = nextSibling.erd.optPrimType.get
val x = nextSibling.dataValue
- typeCalculator.outputTypeCalcRun(dstate, x.getAnyRef, primType)
+ Assert.invariant(x.isDefined)
+ typeCalculator.outputTypeCalcRun(dstate, x.getNonNullable, primType)
}
}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
index cafb663..23c1fab 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/InfosetImpl.scala
@@ -1119,6 +1119,7 @@ sealed class DISimple(override val erd: ElementRuntimeData)
// for converting to XML, or for debugging print statements.
//
val nodeKind = erd.optPrimType.get
+ val y = x.getAnyRef
x.getAnyRef match {
case xs: String => {
if (nodeKind.isInstanceOf[NodeInfo.String.Kind]) {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
index eb71513..cec73b9 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
@@ -246,8 +246,8 @@ final class SimpleTypeRuntimeData(
@TransientParam unionMemberTypesArg: => Seq[SimpleTypeRuntimeData],
@TransientParam unqualifiedPathStepPolicyArg: => UnqualifiedPathStepPolicy,
@TransientParam repTypeRuntimeDataArg: => Option[SimpleTypeRuntimeData],
- @TransientParam repValueSetArg: => Option[RepValueSet[AnyRef]],
- @TransientParam typeCalculatorArg: => Option[TypeCalculator[AnyRef, AnyRef]],
+ @TransientParam repValueSetArg: => Option[RepValueSet],
+ @TransientParam typeCalculatorArg: => Option[TypeCalculator],
@TransientParam optRepPrimTypeArg: => Option[PrimType])
extends NonTermRuntimeData(variableMapArg, schemaFileLocationArg, diagnosticDebugNameArg,
pathArg, namespacesArg, unqualifiedPathStepPolicyArg) {
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 0121cc1..b611113 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
@@ -25,18 +25,19 @@ import org.apache.daffodil.dpath.NodeInfo
import org.apache.daffodil.dsom.CompiledExpression
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.infoset.DataValue
+import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
import org.apache.daffodil.processors.parsers.PState
import org.apache.daffodil.processors.parsers.ParseError
import org.apache.daffodil.processors.unparsers.UState
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe.One
+import org.apache.daffodil.util.Numbers
import org.apache.daffodil.util.PreSerialization
-import org.apache.daffodil.util.RangeBound
import org.apache.daffodil.util.TransientParam
import org.apache.daffodil.xml.QNameBase
-abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Kind, val dstType: NodeInfo.Kind)
+abstract class TypeCalculator(val srcType: NodeInfo.Kind, val dstType: NodeInfo.Kind)
extends PreSerialization {
type Error = String
@@ -54,10 +55,11 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
* and a second interface to evaluate subexpressions which accepts a DState
*/
- def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error])
- def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error])
+ def inputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error])
+ def outputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error])
- def inputTypeCalcParse(pstate: PState, context: RuntimeData, x: A, xType: NodeInfo.Kind): DataValuePrimitiveNullable = {
+ def inputTypeCalcParse(pstate: PState, context: RuntimeData, x_in: DataValuePrimitive, xType: NodeInfo.Kind): DataValuePrimitiveNullable = {
+ val x = normalizeArg(x_in, xType)
val (ans, err) = inputTypeCalc(x, xType)
Assert.invariant(ans.isDefined ^ err.isDefined)
@@ -66,15 +68,10 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
pstate.setFailed(diag)
}
- //In event of an error, we still want to return Maybe.Nope, which happens
- //to be what ans would have
- if(ans.isDefined){
- DataValue.unsafeFromAnyRef(ans.get)
- }else{
- DataValue.NoValue
- }
+ ans
}
- def outputTypeCalcUnparse(ustate: UState, context: RuntimeData, x: B, xType: NodeInfo.Kind): Maybe[A] = {
+ def outputTypeCalcUnparse(ustate: UState, context: RuntimeData, x_in: DataValuePrimitive, xType: NodeInfo.Kind): DataValuePrimitiveNullable = {
+ val x = normalizeArg(x_in, xType)
val (ans, err) = outputTypeCalc(x, xType)
Assert.invariant(ans.isDefined ^ err.isDefined)
@@ -92,7 +89,8 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
* It appears that dpath expressions actually throw errors.
* See the definition of DAFError
*/
- def inputTypeCalcRun(dstate: DState, x: A, xType: NodeInfo.Kind): Unit = {
+ def inputTypeCalcRun(dstate: DState, x_in: DataValuePrimitive, xType: NodeInfo.Kind): Unit = {
+ val x = normalizeArg(x_in, xType)
val context = dstate.runtimeData.get
val (ans, err) = inputTypeCalc(x, xType)
Assert.invariant(ans.isDefined ^ err.isDefined)
@@ -103,10 +101,11 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
// throw FNErrorFunctionException(Maybe(context.schemaFileLocation), dstate.contextLocation, err.get)
}
- dstate.setCurrentValue(DataValue.unsafeFromAnyRef(ans.get))
+ dstate.setCurrentValue(ans)
}
- def outputTypeCalcRun(dstate: DState, x: B, xType: NodeInfo.Kind): Unit = {
+ def outputTypeCalcRun(dstate: DState, x_in: DataValuePrimitive, xType: NodeInfo.Kind): Unit = {
+ val x = normalizeArg(x_in, xType)
val context = dstate.runtimeData.get
val (ans, err) = outputTypeCalc(x, xType)
Assert.invariant(ans.isDefined ^ err.isDefined)
@@ -116,7 +115,7 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
throw diag
}
- dstate.setCurrentValue(DataValue.unsafeFromAnyRef(ans.get))
+ dstate.setCurrentValue(ans)
}
@@ -125,67 +124,85 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
@throws(classOf[java.io.IOException])
final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out)
+
+ /*
+ * In theory, this normalizeArg method should not be nessasary. We know at compile time what
+ * types a given calculator is defined in terms of, so the compiler should insert any conversion
+ * nessasary so that the type being passed in is what the calculator was defined in terms of.
+ * In practice, we are not doing that. Instead, we convert all numeric types to JBigInt.
+ */
+ protected def normalizeArg(x: DataValuePrimitive, xType: NodeInfo.Kind): DataValuePrimitive = {
+ if (xType == NodeInfo.String) {
+ x
+ } else {
+ Numbers.asBigInt(x.getAnyRef)
+ }
+ }
}
-class KeysetValueTypeCalculatorOrdered[A <: AnyRef, B <: AnyRef](valueMap: HashMap[A, B], rangeTable: Seq[(RangeBound[A], RangeBound[A], B)], unparseMap: HashMap[B, A],
+/*
+ * We define valueMap in terms of AnyRef instead of DataValuePrimitive, because HashMap is polymorphic,
+ * so using DataValuePrimitive would trigger boxing
+ */
+class KeysetValueTypeCalculatorOrdered(valueMap: HashMap[DataValuePrimitive, DataValuePrimitive], rangeTable: Seq[(RangeBound, RangeBound, DataValuePrimitive)], unparseMap: HashMap[DataValuePrimitive, DataValuePrimitive],
srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
- extends TypeCalculator[A, B](srcType, dstType) {
+ extends TypeCalculator(srcType, dstType) {
- override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error]) = {
+ override def inputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = {
if (valueMap.contains(x)) {
- (One(valueMap.get(x).get), Maybe.Nope)
+ (valueMap.get(x).get, Maybe.Nope)
} else {
- val ans1: Option[(RangeBound[A], RangeBound[A], B)] = rangeTable.find({
+ val ans1: Option[(RangeBound, RangeBound, DataValuePrimitiveNullable)] = rangeTable.find({
case (min, max, _) => {
min.testAsLower(x) && max.testAsUpper(x)
}
})
ans1 match {
case None => {
- (Maybe.Nope, One(s"Key ${x} not found in keyset-value mapping"))
+ (DataValue.NoValue, One(s"Key ${x} not found in keyset-value mapping"))
}
- case Some((_, _, v)) => (Maybe(v), Maybe.Nope)
+ case Some((_, _, v)) => (v, Maybe.Nope)
}
}
}
- override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = {
+ override def outputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = {
unparseMap.get(x) match {
- case Some(v) => (Maybe(v), Maybe.Nope)
- case None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
+ case Some(v) => (v, Maybe.Nope)
+ case None => (DataValue.NoValue, One(s"Value ${x} not found in keyset-value mapping"))
}
}
}
-class KeysetValueTypeCalculatorUnordered[A <: AnyRef, B <: AnyRef](valueMap: HashMap[A, B], unparseMap: HashMap[B, A], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
- extends TypeCalculator[A, B](srcType, dstType) {
+class KeysetValueTypeCalculatorUnordered(valueMap: HashMap[DataValuePrimitive, DataValuePrimitive], unparseMap: HashMap[DataValuePrimitive, DataValuePrimitive], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
+ extends TypeCalculator(srcType, dstType) {
- override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error]) = {
+ override def inputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = {
if (valueMap.contains(x)) {
valueMap.get(x) match {
- case Some(a) => (Maybe(a), Maybe.Nope)
- case None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
+ case Some(a) => (a, Maybe.Nope)
+ case None => (DataValue.NoValue, One(s"Value ${x} not found in keyset-value mapping"))
}
} else {
- (Maybe.Nope, One(s"Key ${x} not found in keyset-value mapping"))
+ (DataValue.NoValue, One(s"Key ${x} not found in keyset-value mapping"))
}
}
- override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = {
+ override def outputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = {
unparseMap.get(x) match {
- case Some(v) => (Maybe(v), Maybe.Nope)
- case None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
+ case Some(v) => (v, Maybe.Nope)
+ case None => (DataValue.NoValue, One(s"Value ${x} not found in keyset-value mapping"))
}
}
}
-class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](
- @TransientParam maybeInputTypeCalcArg: => Maybe[ CompiledExpression[B]],
- @TransientParam maybeOutputTypeCalcArg: => Maybe[CompiledExpression[A]],
+class ExpressionTypeCalculator(
+ @TransientParam maybeInputTypeCalcArg: => Maybe[CompiledExpression[AnyRef]],
+ @TransientParam maybeOutputTypeCalcArg: => Maybe[CompiledExpression[AnyRef]],
srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
- extends TypeCalculator[A, B](srcType, dstType) {
+ extends TypeCalculator(srcType, dstType) {
override def supportsParse = maybeInputTypeCalcArg.isDefined
override def supportsUnparse = maybeOutputTypeCalcArg.isDefined
@@ -211,46 +228,46 @@ class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](
//TODO, pass x into DPath state
- override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error]) =
+ override def inputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) =
Assert.invariantFailed("inputTypeCalc not implemented on ExpressionTypeCalculator. Call the more specialized forms directly")
- override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) =
+ override def outputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) =
Assert.invariantFailed("outputTypeCalc not implemented on ExpressionTypeCalculator. Call the more specialized forms directly")
- override def inputTypeCalcParse(state: PState, context: RuntimeData, x: A, xType: NodeInfo.Kind): DataValuePrimitiveNullable = {
+ override def inputTypeCalcParse(state: PState, context: RuntimeData, x: DataValuePrimitive, xType: NodeInfo.Kind): DataValuePrimitiveNullable = {
val dstate = state.dState
val oldRepValue = dstate.repValue
val oldLogicalValue = dstate.logicalValue
- dstate.repValue = DataValue.unsafeFromAnyRef(x)
+ dstate.repValue = x
dstate.logicalValue = DataValue.NoValue
val ans = Maybe(maybeInputTypeCalc.get.evaluate(state))
dstate.repValue = oldRepValue
dstate.logicalValue = oldLogicalValue
- if(ans.isDefined){
+ if (ans.isDefined) {
DataValue.unsafeFromAnyRef(ans.get)
- }else{
+ } else {
DataValue.NoValue;
}
}
- override def outputTypeCalcUnparse(state: UState, context: RuntimeData, x: B, xType: NodeInfo.Kind): Maybe[A] = {
+ override def outputTypeCalcUnparse(state: UState, context: RuntimeData, x: DataValuePrimitive, xType: NodeInfo.Kind): DataValuePrimitiveNullable = {
val dstate = state.dState
val oldRepValue = dstate.repValue
val oldLogicalValue = dstate.logicalValue
dstate.repValue = DataValue.NoValue
- dstate.logicalValue = DataValue.unsafeFromAnyRef(x)
+ dstate.logicalValue = x
- val ans = Maybe(maybeOutputTypeCalc.get.evaluate(state))
+ val ans = maybeOutputTypeCalc.get.evaluate(state)
dstate.repValue = oldRepValue
dstate.logicalValue = oldLogicalValue
- ans
+ DataValue.unsafeFromAnyRef(ans)
}
- override def inputTypeCalcRun(dstate: DState, x: A, xType: NodeInfo.Kind): Unit = {
+ override def inputTypeCalcRun(dstate: DState, x: DataValuePrimitive, xType: NodeInfo.Kind): Unit = {
val oldRepValue = dstate.repValue
val oldLogicalValue = dstate.logicalValue
- dstate.repValue = DataValue.unsafeFromAnyRef(x)
+ dstate.repValue = x
dstate.logicalValue = DataValue.NoValue
maybeInputTypeCalc.get.run(dstate)
@@ -259,11 +276,11 @@ class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](
dstate.logicalValue = oldLogicalValue
}
- override def outputTypeCalcRun(dstate: DState, x: B, xType: NodeInfo.Kind): Unit = {
+ override def outputTypeCalcRun(dstate: DState, x: DataValuePrimitive, xType: NodeInfo.Kind): Unit = {
val oldRepValue = dstate.repValue
val oldLogicalValue = dstate.logicalValue
dstate.repValue = DataValue.NoValue
- dstate.logicalValue = DataValue.unsafeFromAnyRef(x)
+ dstate.logicalValue = x
maybeOutputTypeCalc.get.run(dstate)
@@ -272,31 +289,32 @@ class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](
}
}
-class IdentifyTypeCalculator[A <: AnyRef](srcType: NodeInfo.Kind) extends TypeCalculator[A, A](srcType, srcType) {
- override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = (Maybe(x), Maybe.Nope)
- override def outputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = (Maybe(x), Maybe.Nope)
+class IdentifyTypeCalculator(srcType: NodeInfo.Kind) extends TypeCalculator(srcType, srcType) {
+ override def inputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = (x, Maybe.Nope)
+ override def outputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = (x, Maybe.Nope)
}
-class UnionTypeCalculator[A <: AnyRef, B <: AnyRef](subCalculators: Seq[(RepValueSet[A], RepValueSet[B], TypeCalculator[A, B])], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
- extends TypeCalculator[A, B](srcType, dstType) {
+class UnionTypeCalculator(subCalculators: Seq[(RepValueSet, RepValueSet, TypeCalculator)], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
+ extends TypeCalculator(srcType, dstType) {
//TODO, it may be worth it to pre-compute a hash table for direct dispatch,
//Similar to how keyset-value works
- override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error]) = {
+ override def inputTypeCalc(x: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = {
val subCalcSeq = subCalculators.filter(sub => sub._1.contains(x))
Assert.invariant(subCalcSeq.length <= 1)
if (subCalcSeq.isEmpty) {
- (Maybe.Nope, One(s"Key ${x} does not match any component of this simpleType union"))
+ (DataValue.NoValue, One(s"Key ${x} does not match any component of this simpleType union"))
} else {
val subCalc = subCalcSeq.head._3
subCalc.inputTypeCalc(x, xType)
}
}
- override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], Maybe[Error]) = {
+ override def outputTypeCalc(x_in: DataValuePrimitive, xType: NodeInfo.Kind): (DataValuePrimitiveNullable, Maybe[Error]) = {
+ val x = normalizeArg(x_in, xType)
val subCalcSeq = subCalculators.filter(sub => sub._2.contains(x))
Assert.invariant(subCalcSeq.length <= 1)
if (subCalcSeq.isEmpty) {
- (Maybe.Nope, One(s"Key ${x} does not match the logical values from any component of this union."))
+ (DataValue.NoValue, One(s"Key ${x} does not match the logical values from any component of this union."))
} else {
val subCalc = subCalcSeq.head._3
subCalc.outputTypeCalc(x, xType)
@@ -309,8 +327,8 @@ class UnionTypeCalculator[A <: AnyRef, B <: AnyRef](subCalculators: Seq[(RepValu
* 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
*/
-class RepValueSet[A <: AnyRef](val valueSet: HashSet[A], val valueRanges: Set[(RangeBound[A], RangeBound[A])]) extends Serializable {
- def contains(x: A): Boolean = {
+class RepValueSet(val valueSet: HashSet[DataValuePrimitive], val valueRanges: Set[(RangeBound, RangeBound)]) extends Serializable {
+ def contains(x: DataValuePrimitive): Boolean = {
val ans1 = valueSet.contains(x)
if (ans1) {
ans1
@@ -322,7 +340,7 @@ class RepValueSet[A <: AnyRef](val valueSet: HashSet[A], val valueRanges: Set[(R
}
}
- def merge(other: RepValueSet[A]): RepValueSet[A] = {
+ def merge(other: RepValueSet): RepValueSet = {
val valueSet_ = valueSet ++ other.valueSet
val valueRanges_ = valueRanges ++ other.valueRanges
new RepValueSet(valueSet_, valueRanges_)
@@ -336,38 +354,41 @@ class RepValueSet[A <: AnyRef](val valueSet: HashSet[A], val valueRanges: Set[(R
//We should check if we can safely convert them to Long
object RepValueSetCompiler {
- def compile[A <: AnyRef](valueSet: Seq[A], valuesRanges: Seq[(RangeBound[A], RangeBound[A])]): RepValueSet[A] = {
+ def compile(valueSet: Seq[DataValuePrimitive], valuesRanges: Seq[(RangeBound, RangeBound)]): RepValueSet = {
val hashSet = HashSet.empty ++ valueSet
val rangeSet = Set.empty ++ valuesRanges.filter(x => x._1.isDefined || x._2.isDefined)
new RepValueSet(hashSet, rangeSet)
}
- def empty[A <: AnyRef]: RepValueSet[A] = compile(Seq.empty, Seq.empty)
+ def empty: RepValueSet = compile(Seq.empty, Seq.empty)
}
object TypeCalculatorCompiler {
- type TypeCalcMap = Map[QNameBase, TypeCalculator[AnyRef, AnyRef]]
+ type TypeCalcMap = Map[QNameBase, TypeCalculator]
// mappings: [(keySet, canonicalKey, value)]
- def compileKeysetValue[A <: AnyRef, B <: AnyRef](mappings: Seq[(RepValueSet[A], A, B)], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind): TypeCalculator[A, B] = {
+ def compileKeysetValue(
+ mappings: Seq[(RepValueSet, DataValuePrimitive, DataValuePrimitive)],
+ srcType: NodeInfo.Kind,
+ dstType: NodeInfo.Kind): TypeCalculator = {
Assert.invariant(!mappings.isEmpty)
/*
* We need to cast to HashMap, because the type of HashMap.++ returns a generic Map
* HashMap.+ returns a HashMap, so we can avoid the case by doing the fold ourself
*/
- val valueMap: HashMap[A, B] = (HashMap.empty ++ mappings.flatMap(x => {
+ val valueMap: HashMap[DataValuePrimitive, DataValuePrimitive] = (HashMap.empty ++ mappings.flatMap(x => {
val (keySet, _, value) = x
keySet.valueSet.map((_, value))
- })).asInstanceOf[HashMap[A, B]]
- val rangeTable: Seq[(RangeBound[A], RangeBound[A], B)] = mappings.flatMap(x => {
+ })).asInstanceOf[HashMap[DataValuePrimitive, DataValuePrimitive]]
+ val rangeTable: Seq[(RangeBound, RangeBound, DataValuePrimitive)] = mappings.flatMap(x => {
val (keySet, _, value) = x
keySet.valueRanges.map({ case (min, max) => (min, max, value) })
})
- val unparseMap: HashMap[B, A] = (HashMap.empty ++ mappings.map(x => {
+ val unparseMap: HashMap[DataValuePrimitive, DataValuePrimitive] = (HashMap.empty ++ mappings.map(x => {
val (_, canonicalKey, value) = x
(value, canonicalKey)
- })).asInstanceOf[HashMap[B, A]]
+ })).asInstanceOf[HashMap[DataValuePrimitive, DataValuePrimitive]]
/*
* Type erasure makes dispatching based on if we have an ordered keyset or not difficult
@@ -387,18 +408,18 @@ object TypeCalculatorCompiler {
* While this technically overload that function, to avoid confusion, we are giving it a different
* name entirely.
*/
- def compileTypeCalculatorFromExpression[A <: AnyRef, B <: AnyRef](
- optInputTypeCalc: => Option[CompiledExpression[B]],
- optOutputTypeCalc: => Option[CompiledExpression[A]],
- srcType: NodeInfo.Kind, dstType: NodeInfo.Kind): ExpressionTypeCalculator[A, B] = {
- lazy val maybeInputType: Maybe[CompiledExpression[B]] = optInputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
- lazy val maybeOutputType: Maybe[CompiledExpression[A]] = optOutputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
+ def compileTypeCalculatorFromExpression(
+ optInputTypeCalc: => Option[CompiledExpression[AnyRef]],
+ optOutputTypeCalc: => Option[CompiledExpression[AnyRef]],
+ srcType: NodeInfo.Kind, dstType: NodeInfo.Kind): ExpressionTypeCalculator = {
+ lazy val maybeInputType: Maybe[CompiledExpression[AnyRef]] = optInputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
+ lazy val maybeOutputType: Maybe[CompiledExpression[AnyRef]] = optOutputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
new ExpressionTypeCalculator(maybeInputType, maybeOutputType, srcType, dstType)
}
- def compileIdentity[A <: AnyRef](srcType: NodeInfo.Kind): TypeCalculator[A, A] = new IdentifyTypeCalculator(srcType)
+ def compileIdentity(srcType: NodeInfo.Kind): TypeCalculator = new IdentifyTypeCalculator(srcType)
//subCalculators: Seq[(repValues, logicalValues, subCalc)]
- def compileUnion[A <: AnyRef, B <: AnyRef](subCalculators: Seq[(RepValueSet[A], RepValueSet[B], TypeCalculator[A, B])]): TypeCalculator[A, B] = {
+ def compileUnion(subCalculators: Seq[(RepValueSet, RepValueSet, TypeCalculator)]): TypeCalculator = {
//TODO, in some cases, it may be possible to merge some subCalculators
//It may also be possible to compute a direct dispatch table
val types = subCalculators.map(x => (x._3.srcType, x._3.dstType))
@@ -412,3 +433,92 @@ object TypeCalculatorCompiler {
}
}
+
+object Range {
+ type Range = (RangeBound, RangeBound)
+ def inclusive(lower:DataValuePrimitiveNullable, upper:DataValuePrimitiveNullable):Range={
+ val lower_ = new RangeBound(lower,true)
+ val upper_ = new RangeBound(upper,true)
+ (lower_,upper_)
+ }
+}
+
+/*
+ * This would be a good example of why we might want DataValue to have a more rich
+ * hierarchy. In theory, RangeBound could be defined in term of a DataValueNumeric type,
+ * from which all of the numeric types extend. In practice, getting complicated DataValue hierarchies to work
+ * turns out to be annoyingly finicky, and so may not be worth the benifit.
+ *
+ * Additionally, in the current implementation, all instance of RangeBound actually use JBigInt.
+ * This is because most of the current type calculator implementation is based on casting all numeric types
+ * to BigInt, instead of trying to keep track of what types we would be expecting at runtime.
+ */
+class RangeBound(val maybeBound: DataValuePrimitiveNullable, val isInclusive: Boolean)
+ extends Serializable {
+
+ lazy val isEmpty = maybeBound.isEmpty
+ lazy val isDefined = maybeBound.isDefined
+
+ override def toString(): String = {
+ if (maybeBound.isDefined) {
+ maybeBound.getAnyRef.toString + "_" + (if (isInclusive) "inclusive" else "exclusive")
+ } else {
+ "Nope"
+ }
+ }
+
+ def intersectsWithOtherBounds(lower: RangeBound, upper: RangeBound): Boolean = {
+ if (maybeBound.isEmpty) {
+ false
+ } else {
+ val bound = maybeBound.getNonNullable
+ 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 JJBigInt, or a Long
+ */
+
+ def testAsLower(x: DataValuePrimitive): Boolean = {
+ if (maybeBound.isEmpty) {
+ true
+ } else {
+ val bound = maybeBound.getNonNullable
+ if (isInclusive) {
+ le(bound, x)
+ } else {
+ lt(bound, x)
+ }
+ }
+ }
+ def testAsUpper(x: DataValuePrimitive): Boolean = {
+ if (maybeBound.isEmpty) {
+ true
+ } else {
+ val bound = maybeBound.getNonNullable
+ if (isInclusive) {
+ le(x, bound)
+ } else {
+ lt(x, bound)
+ }
+ }
+ }
+
+ private def le(x: DataValuePrimitive, y: DataValuePrimitive): Boolean = {
+ val x2 = Numbers.asBigInt(x.getAnyRef)
+ val y2 = Numbers.asBigInt(y.getAnyRef)
+
+ x2.compareTo(y2) <= 0
+ }
+
+ private def lt(x: DataValuePrimitive, y: DataValuePrimitive): Boolean = {
+ val x2 = Numbers.asBigInt(x.getAnyRef)
+ val y2 = Numbers.asBigInt(y.getAnyRef)
+
+ x2.compareTo(y2) < 0
+ }
+
+}
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 0e686bd..03cbf5b 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
@@ -17,11 +17,19 @@
package org.apache.daffodil.processors.parsers
-import org.apache.daffodil.processors._
+import java.math.{ BigInteger => JBigInt }
+
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.processors.ChoiceDispatchKeyEv
+import org.apache.daffodil.processors.DelimiterParseEv
+import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.EscapeSchemeParseEv
+import org.apache.daffodil.processors.RangeBound
+import org.apache.daffodil.processors.RuntimeData
+import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors.TermRuntimeData
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) {
@@ -125,7 +133,7 @@ class DynamicEscapeSchemeParser(
abstract class ChoiceDispatchCombinatorParserBase(rd: TermRuntimeData,
dispatchBranchKeyMap: Map[String, (Parser, Boolean)],
- dispatchKeyRangeMap:Vector[(RangeBound[BigInt],RangeBound[BigInt],Parser, Boolean)])
+ dispatchKeyRangeMap:Vector[(RangeBound,RangeBound,Parser, Boolean)])
extends CombinatorParser(rd) {
override def nom = "ChoiceDispatch"
@@ -179,7 +187,7 @@ abstract class ChoiceDispatchCombinatorParserBase(rd: TermRuntimeData,
} else{
if(!dispatchKeyRangeMap.isEmpty){
try{
- val keyAsBigInt = BigInt(key)
+ val keyAsBigInt = new JBigInt(key)
val optAns1= dispatchKeyRangeMap.find({case(min,max,_,_) => min.testAsLower(keyAsBigInt) && max.testAsUpper(keyAsBigInt)})
optAns1.map({case(_,_,parser,isRepresented)=>(parser,isRepresented)})
} catch{
@@ -243,13 +251,13 @@ abstract class ChoiceDispatchCombinatorParserBase(rd: TermRuntimeData,
}
class ChoiceDispatchCombinatorParser(rd: TermRuntimeData, dispatchKeyEv: ChoiceDispatchKeyEv,
- dispatchBranchKeyMap: Map[String, (Parser, Boolean)], dispatchKeyRangeMap:Vector[(RangeBound[BigInt],RangeBound[BigInt],Parser, Boolean)])
+ dispatchBranchKeyMap: Map[String, (Parser, Boolean)], dispatchKeyRangeMap:Vector[(RangeBound,RangeBound,Parser, Boolean)])
extends ChoiceDispatchCombinatorParserBase(rd, dispatchBranchKeyMap, dispatchKeyRangeMap) {
override def computeDispatchKey(pstate: PState): Maybe[String] = Maybe(dispatchKeyEv.evaluate(pstate))
}
class ChoiceDispatchCombinatorKeyByTypeParser(rd: TermRuntimeData, repTypeParser: Parser, repTypeRuntimeData: ElementRuntimeData,
- dispatchBranchKeyMap: Map[String, (Parser, Boolean)], dispatchKeyRangeMap:Vector[(RangeBound[BigInt],RangeBound[BigInt],Parser, Boolean)])
+ dispatchBranchKeyMap: Map[String, (Parser, Boolean)], dispatchKeyRangeMap:Vector[(RangeBound,RangeBound,Parser, Boolean)])
extends ChoiceDispatchCombinatorParserBase(rd, dispatchBranchKeyMap, dispatchKeyRangeMap)
with WithDetachedParser {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
index 8f56a49..c217db0 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
@@ -124,7 +124,7 @@ trait WithDetachedParser {
}
}
-class TypeValueCalcParser(typeCalculator: TypeCalculator[AnyRef, AnyRef], repTypeParser: Parser, e: ElementRuntimeData, repTypeRuntimeData: ElementRuntimeData)
+class TypeValueCalcParser(typeCalculator: TypeCalculator, repTypeParser: Parser, e: ElementRuntimeData, repTypeRuntimeData: ElementRuntimeData)
extends CombinatorParser(e)
with WithDetachedParser {
override lazy val childProcessors = Vector(repTypeParser)
@@ -136,7 +136,7 @@ class TypeValueCalcParser(typeCalculator: TypeCalculator[AnyRef, AnyRef], repTyp
pstate.dataProc.get.ssrd
if (pstate.processorStatus == Success) {
Assert.invariant(repValue.isDefined)
- val logicalValue: DataValuePrimitiveNullable = typeCalculator.inputTypeCalcParse(pstate, context, repValue.getAnyRef, repValueType)
+ val logicalValue: DataValuePrimitiveNullable = typeCalculator.inputTypeCalcParse(pstate, context, repValue.getNonNullable, repValueType)
if (pstate.processorStatus == Success) {
Assert.invariant(logicalValue.isDefined)
pstate.simpleElement.setDataValue(logicalValue)
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctions.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctions.tdml
index 8a4d856..eb32046 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctions.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctions.tdml
@@ -109,6 +109,38 @@
<xs:element name="abstractIntToStringByKeyset_01" type="xs:string" dfdl:inputValueCalc="{dfdlx:inputTypeCalc('AbstractIntToStringByKeyset',0)}"/>
+ <xs:element name="sparse_enum01">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="byte" maxOccurs="unbounded" dfdl:occursCountKind="parsed">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="raw" type="tns:uint8"/>
+ <xs:choice dfdl:choiceDispatchKey="{ xs:string(ex:raw) }">
+ <xs:sequence dfdl:choiceBranchKey="15" dfdlx:choiceBranchKeyRanges="1 3">
+ <xs:element name="enum" type="tns:sparse_lookup" dfdl:inputValueCalc="{ (dfdlx:inputTypeCalc('tns:sparse_lookup', (../ex:raw))) }"/>
+ </xs:sequence>
+ <xs:sequence dfdl:choiceBranchKey="0" dfdlx:choiceBranchKeyRanges="4 14 16 255">
+ <xs:element name="undefined" type="xs:int" dfdl:inputValueCalc="{ ../ex:raw }"/>
+ </xs:sequence>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:simpleType name="sparse_lookup" dfdlx:repType="xs:integer">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="one" dfdlx:repValues="1"/>
+ <xs:enumeration value="two" dfdlx:repValues="2"/>
+ <xs:enumeration value="three" dfdlx:repValues="3"/>
+ <xs:enumeration value="fifteen" dfdlx:repValues="15"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+
</tdml:defineSchema>
<tdml:parserTestCase name="inputTypeCalcInt_01"
@@ -281,5 +313,82 @@
</tdml:infoset>
</tdml:parserTestCase>
+ <tdml:parserTestCase name="sparse_enum_01"
+ root="sparse_enum01" model="inputTypeCalc-Embedded.dfdl.xsd" description="Extensions - inputTypeCalc keysetValue transform">
+
+ <tdml:document>
+ <tdml:documentPart type="byte"><![CDATA[
+ 00
+ 01
+ 02
+ 03
+ 04
+ 05
+ 0d
+ 0e
+ 0f
+ 10
+ fe
+ ff
+ ]]>
+ </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">
+ <sparse_enum01>
+ <byte>
+ <raw>0</raw>
+ <undefined>0</undefined>
+ </byte>
+ <byte>
+ <raw>1</raw>
+ <enum>one</enum>
+ </byte>
+ <byte>
+ <raw>2</raw>
+ <enum>two</enum>
+ </byte>
+ <byte>
+ <raw>3</raw>
+ <enum>three</enum>
+ </byte>
+ <byte>
+ <raw>4</raw>
+ <undefined>4</undefined>
+ </byte>
+ <byte>
+ <raw>5</raw>
+ <undefined>5</undefined>
+ </byte>
+ <byte>
+ <raw>13</raw>
+ <undefined>13</undefined>
+ </byte>
+ <byte>
+ <raw>14</raw>
+ <undefined>14</undefined>
+ </byte>
+ <byte>
+ <raw>15</raw>
+ <enum>fifteen</enum>
+ </byte>
+ <byte>
+ <raw>16</raw>
+ <undefined>16</undefined>
+ </byte>
+ <byte>
+ <raw>254</raw>
+ <undefined>254</undefined>
+ </byte>
+ <byte>
+ <raw>255</raw>
+ <undefined>255</undefined>
+ </byte>
+ </sparse_enum01>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
</tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala b/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala
index 30c66a7..4f175ea 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/extensions/TestInputTypeValueCalc.scala
@@ -76,6 +76,7 @@ class TestInputTypeValueCalc {
@Test def test_outputTypeCalcNextSiblingInt_01() { fnRunner.runOneTest("outputTypeCalcNextSiblingInt_01") }
@Test def test_outputTypeCalcNextSiblingString_01() { fnRunner.runOneTest("outputTypeCalcNextSiblingString_01") }
@Test def test_abstractIntToStringByKeyset_01() { fnRunner.runOneTest("abstractIntToStringByKeyset_01") }
+ @Test def test_sparse_enum_01() { fnRunner.runOneTest("sparse_enum_01") }
@Test def test_typeCalcDispatch_typeError_01() { fnErrRunner.runOneTest("typeCalcDispatch_typeError_01") }
@Test def test_typeCalcDispatch_typeError_02() { fnErrRunner.runOneTest("typeCalcDispatch_typeError_02") }