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/07/03 02:18:11 UTC
[incubator-daffodil] branch master updated: Emit SDE when a type
calculator attempts to use an undefined 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 a7c8901 Emit SDE when a type calculator attempts to use an undefined type.
a7c8901 is described below
commit a7c890167f7b5d38cfbab33a4c47d902757293ea
Author: Brandon Sloane <bs...@tresys.com>
AuthorDate: Fri Jun 7 14:00:41 2019 -0400
Emit SDE when a type calculator attempts to use an undefined type.
This lays down the infrastructure which allows the DPath
interperator to lookup type calculators at compile time,
which will allow us to make their return type dependent on
the calculator they are using, instead of their functin name.
DAFFODIL-2148
---
.../daffodil/dpath/DFDLExpressionParser.scala | 20 ++++--
.../org/apache/daffodil/dsom/ElementBase.scala | 3 +-
.../org/apache/daffodil/dsom/SchemaComponent.scala | 5 +-
.../scala/org/apache/daffodil/dsom/SchemaSet.scala | 4 +-
.../org/apache/daffodil/dsom/SimpleTypes.scala | 24 +++++--
.../daffodil/dpath/DFDLXTypeCalcFunctions.scala | 7 +-
.../org/apache/daffodil/dpath/DPathRuntime.scala | 6 +-
.../scala/org/apache/daffodil/dpath/DState.scala | 54 ++++++++------
.../apache/daffodil/dsom/CompiledExpression1.scala | 51 ++++++++------
.../apache/daffodil/dsom/ExpressionCompiler.scala | 1 +
.../daffodil/processors/SchemaSetRuntimeData.scala | 3 +-
.../daffodil/processors/TypeCalculator.scala | 82 ++++++++++++++--------
.../daffodil/extensions/lookAhead/lookAhead.tdml | 4 +-
.../type_calc/typeCalcFunctionErrors.tdml | 71 ++++++++++++++++---
.../extensions/TestInputTypeValueCalc.scala | 2 +
15 files changed, 230 insertions(+), 107 deletions(-)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala
index 494d8b1..323e1ed 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dpath/DFDLExpressionParser.scala
@@ -17,14 +17,20 @@
package org.apache.daffodil.dpath
-import org.apache.daffodil.exceptions._
-import org.apache.daffodil.dsom._
-import scala.xml.NamespaceBinding
-import org.apache.daffodil.xml._
-import scala.util.parsing.input.CharSequenceReader
+import java.math.{ BigDecimal => JBigDecimal }
+import java.math.{ BigInteger => JBigInt }
+
import scala.util.parsing.combinator.RegexParsers
-import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
-import org.apache.daffodil.oolag.OOLAG._
+import scala.util.parsing.input.CharSequenceReader
+import scala.xml.NamespaceBinding
+
+import org.apache.daffodil.dsom.CompiledExpression
+import org.apache.daffodil.dsom.ConstantExpression
+import org.apache.daffodil.dsom.DPathCompileInfo
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.oolag.OOLAG.OOLAGHost
+import org.apache.daffodil.xml.NamedQName
+import org.apache.daffodil.xml.QNameRegex
/**
* Parses DPath expressions. Most real analysis is done later. This is
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
index 417434e..787020d 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
@@ -300,7 +300,8 @@ trait ElementBase
namedQName,
optPrimType,
schemaFileLocation,
- tunable)
+ tunable,
+ schemaSet.typeCalcMap)
eci
}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala
index bf30bd5..1d454b4 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaComponent.scala
@@ -62,7 +62,7 @@ trait SchemaComponent
override def oolagContextViaArgs = optLexicalParent
lazy val tunable: DaffodilTunables = optLexicalParent.get.tunable
-
+
lazy val dpathCompileInfo: DPathCompileInfo =
new DPathCompileInfo(
enclosingComponent.map { _.dpathCompileInfo },
@@ -70,7 +70,8 @@ trait SchemaComponent
namespaces,
path,
schemaFileLocation,
- tunable)
+ tunable,
+ schemaSet.typeCalcMap)
/**
* ALl non-terms get runtimeData from this definition. All Terms
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala
index dcab2e0..9c1292a 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SchemaSet.scala
@@ -40,6 +40,7 @@ import org.apache.daffodil.processors.TypeCalculator
import scala.collection.immutable.Map
import scala.collection.immutable.HashMap
import org.apache.daffodil.compiler.ProcessorFactory
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
/**
* A schema set is exactly that, a set of schemas. Each schema has
@@ -173,11 +174,10 @@ final class SchemaSet(
lazy val globalSimpleTypeDefs: Seq[GlobalSimpleTypeDef] = schemas.flatMap(_.globalSimpleTypeDefs)
- lazy val typeCalcMap: Map[GlobalQName, TypeCalculator[AnyRef, AnyRef]] = {
+ lazy val typeCalcMap: TypeCalcMap = {
val factories = globalSimpleTypeDefs
val withCalc = factories.filter(_.optTypeCalculator.isDefined)
val mappings = withCalc.map(st => (st.globalQName, st.optTypeCalculator.get))
-
mappings.toMap
}
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 c22bb7a..9d11e17 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
@@ -339,16 +339,25 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
TypeCalculatorCompiler.compileUnion(subCalculators)
})
val fromExpression: Option[TypeCalculator[AnyRef, AnyRef]] = {
- val optInputCompiled = optInputTypeCalc.toOption.map(sExpr => {
+ /*
+ * This is a minefield for circular dependencies.
+ * In order to compile expressions involve many typeCalc functions,
+ * the DPath compiler needs to look up the typeCalculator involved
+ * (to determine the src/dst type of the calculator)
+ * However, a fromExpression typeCalculator needs to compile DPath expressions,
+ * which may make use of typeCalc functions, and therefore need for the expressions
+ * to have been already compiled.
+ */
+ lazy val optInputCompiled = optInputTypeCalc.toOption.map(sExpr => {
val prop = optInputTypeCalc.asInstanceOf[Found]
- val qn = GlobalQName(Some("daf"), "inputTypeCalc", XMLUtils.dafintURI)
+ val qn = GlobalQName(Some("dfdlx"), "inputTypeCalc", XMLUtils.dafintURI)
val exprNamespaces = prop.location.namespaces
val exprComponent = prop.location.asInstanceOf[SchemaComponent]
ExpressionCompilers.AnyRef.compileExpression(
qn,
dstType, sExpr, exprNamespaces, exprComponent.dpathCompileInfo, false, this, dpathCompileInfo)
})
- val optOutputCompiled = optOutputTypeCalc.toOption.map(sExpr => {
+ lazy val optOutputCompiled = optOutputTypeCalc.toOption.map(sExpr => {
val prop = optOutputTypeCalc.asInstanceOf[Found]
val qn = GlobalQName(Some("daf"), "outputTypeCalc", XMLUtils.dafintURI)
val exprNamespaces = prop.location.namespaces
@@ -357,9 +366,12 @@ abstract class SimpleTypeDefBase(xml: Node, lexicalParent: SchemaComponent)
qn,
srcType, sExpr, exprNamespaces, exprComponent.dpathCompileInfo, false, this, dpathCompileInfo)
})
- (optInputCompiled, optOutputCompiled) match {
- case (None, None) => None
- case _ => Some(TypeCalculatorCompiler.compileExpression(optInputCompiled, optOutputCompiled, srcType, dstType))
+ val supportsParse = optInputTypeCalc.isDefined
+ val supportsUnparse = optOutputTypeCalc.isDefined
+ if (supportsParse || supportsUnparse) {
+ Some(TypeCalculatorCompiler.compileTypeCalculatorFromExpression(optInputCompiled, optOutputCompiled, srcType, dstType, supportsParse, supportsUnparse))
+ } else {
+ None
}
}
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 a4e7046..0e85725 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
@@ -44,11 +44,10 @@ trait TypeCalculatorNamedDispatch { self: FNTwoArgs =>
*/
def getCalculator(arg1: AnyRef, dstate: DState, expectedInputType: NodeInfo.Kind, expectedOutputType: NodeInfo.Kind, requireInputCalc: Boolean = false, requireOutputCalc: Boolean = false): TypeCalculator[AnyRef, AnyRef] = {
val qn = arg1.asInstanceOf[String]
- val runtimeData = dstate.runtimeData.get
- val refType = QName.resolveRef(qn, runtimeData.namespaces, runtimeData.tunable).get.toGlobalQName
- val maybeCalc = dstate.maybeSsrd.get.typeCalculators.get(refType)
+ val refType = QName.resolveRef(qn, dstate.compileInfo.namespaces, dstate.compileInfo.tunable).get.toGlobalQName
+ val maybeCalc = dstate.typeCalculators.get(refType)
if (!maybeCalc.isDefined) {
- throw FNErrorFunctionException(Maybe(runtimeData.schemaFileLocation), dstate.contextLocation, s"Simple type ${refType} does not exist or does not have a repType")
+ dstate.SDE("Simple type %s does not exist or does not have a repType", refType)
}
val calc = maybeCalc.get
if (!expectedInputType.isSubtypeOf(calc.srcType)) {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
index 3a4c870..362ea6a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
@@ -36,6 +36,8 @@ import org.apache.daffodil.processors.VariableRuntimeData
import org.apache.daffodil.util.Misc
import org.apache.daffodil.dpath.NodeInfo.PrimType
import org.apache.daffodil.dsom.DPathCompileInfo
+import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.processors.TypeCalculator
class CompiledDPath(val ops: RecipeOp*) extends Serializable {
@@ -72,7 +74,9 @@ class CompiledDPath(val ops: RecipeOp*) extends Serializable {
* TODO: constant folding really should operate on sub-expressions of expressions
* so that part of an expression can be constant, not necessarily the whole thing.
*/
- def runExpressionForConstant(sfl: SchemaFileLocation, compileInfo: DPathCompileInfo): Option[AnyRef] = {
+ def runExpressionForConstant(
+ sfl: SchemaFileLocation,
+ compileInfo: DPathCompileInfo ): Option[AnyRef] = {
//
// we use a special dummy dstate here that errors out via throw
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala
index 14c49b3..73290de 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DState.scala
@@ -17,7 +17,7 @@
package org.apache.daffodil.dpath
-import org.apache.daffodil.processors._ ; import org.apache.daffodil.infoset._
+import org.apache.daffodil.processors._; import org.apache.daffodil.infoset._
import org.apache.daffodil.exceptions._
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe._
@@ -27,6 +27,8 @@ import org.apache.daffodil.api.DataLocation
import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
import org.apache.daffodil.api.WarnID
import org.apache.daffodil.dsom.DPathCompileInfo
+import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
/**
* Modes for expression evaluation.
@@ -96,7 +98,7 @@ case object UnparserNonBlocking extends EvalMode
* if used in a forward referencing expression the expression can block until the information
* becomes available.
*/
-case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
+case class DState(val maybeSsrd: Maybe[SchemaSetRuntimeData]) {
import org.apache.daffodil.util.Numbers._
var isCompile = false
@@ -111,10 +113,10 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
* (eg. to the runtime stack), and replaces the below fields.
* At the end of the computation, it should restore the below fields.
*/
- var logicalValue:Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
-
- var repValue:Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
-
+ var logicalValue: Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
+
+ var repValue: Maybe[(AnyRef, NodeInfo.Kind)] = Maybe.Nope
+
/**
* The currentValue is used when we have a value that is not
* associated with an element of simple type. E.g., If I have
@@ -193,15 +195,15 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
def setCurrentValue(v: AnyRef) {
_currentValue = v
_currentNode = null
- }
+ }
def setCurrentValue(v: Long) {
_currentValue = asAnyRef(v)
_currentNode = null
- }
+ }
def setCurrentValue(v: Boolean) {
_currentValue = asAnyRef(v)
_currentNode = null
- }
+ }
def booleanValue: Boolean = currentValue.asInstanceOf[Boolean]
@@ -280,16 +282,16 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
def currentElement = currentNode.asInstanceOf[DIElement]
def currentArray = currentNode.asInstanceOf[DIArray]
def currentComplex = currentNode.asComplex
-
+
def nextSibling = {
val contents = currentElement.parent.asInstanceOf[DIComplex].contents
-
+
//TOOD, currentNode should really know this
val i = contents.indexOf(currentNode)
- if(i == contents.length-1){
+ if (i == contents.length - 1) {
throw new InfosetNoNextSiblingException(currentNode.asSimple, currentNode.erd.dpathElementCompileInfo)
- }else{
- contents(i+1)
+ } else {
+ contents(i + 1)
}
}
@@ -325,12 +327,12 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
def runtimeData = {
if (contextNode.isDefined) One(contextNode.get.erd)
else Nope
- }
-
+ }
+
// Overwritten in DStateForConstantFolding, so it should
// be safe to assume we have runtime data
def compileInfo = runtimeData.get.dpathCompileInfo
-
+
private var _contextNode: Maybe[DINode] = Nope
def contextNode = _contextNode
def setContextNode(node: DINode) {
@@ -357,6 +359,8 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
Nope
}
+ def typeCalculators = maybeSsrd.get.typeCalculators
+
private var _savesErrorsAndWarnings: Maybe[SavesErrorsAndWarnings] = Nope
def errorOrWarn = _savesErrorsAndWarnings
def setErrorOrWarn(s: SavesErrorsAndWarnings) {
@@ -368,7 +372,7 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
def setArrayPos(arrayPos1b: Long) {
_arrayPos = arrayPos1b
}
-
+
private var _parseOrUnparseState: Maybe[ParseOrUnparseState] = Nope
def parseOrUnparseState = _parseOrUnparseState
def setParseOrUnparseState(state: ParseOrUnparseState) {
@@ -376,8 +380,12 @@ case class DState(val maybeSsrd:Maybe[SchemaSetRuntimeData]) {
}
def SDE(formatString: String, args: Any*) = {
- Assert.usage(runtimeData.isDefined)
- errorOrWarn.get.SDE(formatString, args: _*)
+ if (isCompile) {
+ compileInfo.SDE(formatString, args: _*)
+ } else {
+ Assert.usage(runtimeData.isDefined)
+ errorOrWarn.get.SDE(formatString, args: _*)
+ }
}
// These exists so we can override it in our fake DState we use when
@@ -430,7 +438,8 @@ object DState {
}
}
-class DStateForConstantFolding(override val compileInfo: DPathCompileInfo) extends DState(Nope) {
+class DStateForConstantFolding(
+ override val compileInfo: DPathCompileInfo) extends DState(Nope) {
private def die = throw new java.lang.IllegalStateException("No infoset at compile time.")
override def currentSimple = currentNode.asInstanceOf[DISimple]
@@ -444,6 +453,7 @@ class DStateForConstantFolding(override val compileInfo: DPathCompileInfo) exten
override def fnExists() = die
override def arrayPos = die
override def arrayLength = die
-
+ override val typeCalculators = compileInfo.typeCalcMap
+
isCompile = true
}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
index 25c58d3..0b3d065 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/CompiledExpression1.scala
@@ -17,25 +17,29 @@
package org.apache.daffodil.dsom
-import org.apache.daffodil.exceptions._
-import org.apache.daffodil.processors.VariableMap
-import org.apache.daffodil.processors.Suspension
-import org.apache.daffodil.util._
-import org.apache.daffodil.xml._
-import org.apache.daffodil.dpath._
-import org.apache.daffodil.xml.NamedQName
-import org.apache.daffodil.util.PreSerialization
-import org.apache.daffodil.dpath.NodeInfo.PrimType
-import org.apache.daffodil.processors.ParseOrUnparseState
-import org.apache.daffodil.util.TransientParam
-import org.apache.daffodil.util.Maybe
-import scala.runtime.ScalaRunTime.stringOf // for printing arrays properly.
-import org.apache.daffodil.exceptions.SchemaFileLocation
-import org.apache.daffodil.exceptions.HasSchemaFileLocation
-import org.apache.daffodil.processors.ParseOrUnparseState
+import scala.runtime.ScalaRunTime.stringOf
+
import org.apache.daffodil.api.DaffodilTunables
import org.apache.daffodil.api.UnqualifiedPathStepPolicy
import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.dpath.DState
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.dpath.NodeInfo.PrimType
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.exceptions.HasSchemaFileLocation
+import org.apache.daffodil.exceptions.SchemaFileLocation
+import org.apache.daffodil.processors.ParseOrUnparseState
+import org.apache.daffodil.processors.Suspension
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
+import org.apache.daffodil.processors.VariableMap
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.MaybeULong
+import org.apache.daffodil.util.PreSerialization
+import org.apache.daffodil.util.TransientParam
+import org.apache.daffodil.xml.NS
+import org.apache.daffodil.xml.NamedQName
+import org.apache.daffodil.xml.NoNamespace
+import org.apache.daffodil.xml.StepQName
trait ContentValueReferencedElementInfoMixin {
@@ -200,14 +204,20 @@ class DPathCompileInfo(
val namespaces: scala.xml.NamespaceBinding,
val path: String,
override val schemaFileLocation: SchemaFileLocation,
- val tunable: DaffodilTunables)
+ val tunable: DaffodilTunables,
+ @TransientParam typeCalcMapArg: => TypeCalcMap)
extends ImplementsThrowsSDE with PreSerialization
with HasSchemaFileLocation {
lazy val parent = parentArg
lazy val variableMap =
variableMapArg
-
+
+ /*
+ * This map(identity) pattern appears to work around an unidentified bug with serialization.
+ */
+ lazy val typeCalcMap: TypeCalcMap = typeCalcMapArg.map(identity)
+
override def preSerialization: Any = {
parent
variableMap
@@ -305,8 +315,9 @@ class DPathElementCompileInfo(
val namedQName: NamedQName,
val optPrimType: Option[PrimType],
sfl: SchemaFileLocation,
- override val tunable: DaffodilTunables)
- extends DPathCompileInfo(parentArg, variableMap, namespaces, path, sfl, tunable)
+ override val tunable: DaffodilTunables,
+ typeCalcMap: TypeCalcMap)
+ extends DPathCompileInfo(parentArg, variableMap, namespaces, path, sfl, tunable, typeCalcMap)
with HasSchemaFileLocation {
lazy val elementChildrenCompileInfo = elementChildrenCompileInfoArg
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala
index 3638203..410690a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dsom/ExpressionCompiler.scala
@@ -22,6 +22,7 @@ import org.apache.daffodil.xml.NamedQName
import scala.xml.NamespaceBinding
import java.lang.{ Long => JLong, Boolean => JBoolean }
import org.apache.daffodil.oolag.OOLAG._
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
trait ExpressionCompilerBase[T <: AnyRef] {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala
index 08175f2..fc47d77 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/SchemaSetRuntimeData.scala
@@ -23,6 +23,7 @@ import org.apache.daffodil.api.Diagnostic
import org.apache.daffodil.processors.unparsers.Unparser
import org.apache.daffodil.processors.parsers.Parser
import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.processors.TypeCalculatorCompiler.TypeCalcMap
final class SchemaSetRuntimeData(
val parser: Parser,
@@ -31,7 +32,7 @@ final class SchemaSetRuntimeData(
val elementRuntimeData: ElementRuntimeData,
var variables: VariableMap,
var validationMode: ValidationMode.Type,
- val typeCalculators: Map[GlobalQName, TypeCalculator[AnyRef, AnyRef]])
+ val typeCalculators: TypeCalcMap)
extends Serializable with ThrowsSDE {
def tunable = elementRuntimeData.tunable
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 7bc2759..332da69 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
@@ -17,31 +17,25 @@
package org.apache.daffodil.processors
-import scala.math.BigInt
+import scala.collection.immutable.HashMap
import scala.collection.immutable.HashSet
+
+import org.apache.daffodil.dpath.DState
+import org.apache.daffodil.dpath.NodeInfo
import org.apache.daffodil.dsom.CompiledExpression
-import org.apache.daffodil.util.Maybe
-import org.apache.daffodil.oolag.OOLAG.OOLAGHost
-import org.apache.daffodil.dsom.CompiledExpression
-import org.apache.daffodil.dsom.CompiledExpression
-import scala.collection.immutable.HashMap
+import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.processors.parsers.PState
import org.apache.daffodil.processors.parsers.ParseError
-import org.apache.daffodil.processors.parsers.GeneralParseFailure
-import org.apache.daffodil.exceptions.Assert
-import java.lang.{ Long => JLong }
import org.apache.daffodil.processors.unparsers.UState
+import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe.One
-import org.apache.daffodil.dsom.CompiledExpression
-import org.apache.daffodil.dpath.DState
-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.PreSerialization
import org.apache.daffodil.util.RangeBound
+import org.apache.daffodil.util.TransientParam
+import org.apache.daffodil.xml.GlobalQName
abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Kind, val dstType: NodeInfo.Kind)
- extends Serializable {
+ extends PreSerialization {
type Error = String
/*
@@ -118,10 +112,13 @@ abstract class TypeCalculator[A <: AnyRef, B <: AnyRef](val srcType: NodeInfo.Ki
def supportsParse: Boolean = true
def supportsUnparse: Boolean = true
+
+ @throws(classOf[java.io.IOException])
+ final private def writeObject(out: java.io.ObjectOutputStream): Unit = serializeObject(out)
}
class KeysetValueTypeCalculatorOrdered[A <: AnyRef, B <: AnyRef](valueMap: HashMap[A, B], rangeTable: Seq[(RangeBound[A], RangeBound[A], B)], unparseMap: HashMap[B, A],
- srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
+ srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
extends TypeCalculator[A, B](srcType, dstType) {
override def inputTypeCalc(x: A, xType: NodeInfo.Kind): (Maybe[B], Maybe[Error]) = {
@@ -145,7 +142,7 @@ class KeysetValueTypeCalculatorOrdered[A <: AnyRef, B <: AnyRef](valueMap: HashM
override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], 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 None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
}
}
@@ -158,7 +155,7 @@ class KeysetValueTypeCalculatorUnordered[A <: AnyRef, B <: AnyRef](valueMap: Has
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 None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
}
} else {
(Maybe.Nope, One(s"Key ${x} not found in keyset-value mapping"))
@@ -168,17 +165,34 @@ class KeysetValueTypeCalculatorUnordered[A <: AnyRef, B <: AnyRef](valueMap: Has
override def outputTypeCalc(x: B, xType: NodeInfo.Kind): (Maybe[A], 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 None => (Maybe.Nope, One(s"Value ${x} not found in keyset-value mapping"))
}
}
}
-class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](maybeInputTypeCalc: Maybe[CompiledExpression[B]], maybeOutputTypeCalc: Maybe[CompiledExpression[A]],
- srcType: NodeInfo.Kind, dstType: NodeInfo.Kind)
+class ExpressionTypeCalculator[A <: AnyRef, B <: AnyRef](
+ @TransientParam maybeInputTypeCalcArg: => Maybe[CompiledExpression[B]],
+ @TransientParam maybeOutputTypeCalcArg: => Maybe[CompiledExpression[A]],
+ srcType: NodeInfo.Kind, dstType: NodeInfo.Kind,
+ override val supportsParse: Boolean, override val supportsUnparse: Boolean)
extends TypeCalculator[A, B](srcType, dstType) {
- override def supportsParse = maybeInputTypeCalc.isDefined
- override def supportsUnparse = maybeOutputTypeCalc.isDefined
+
+ /*
+ * Compiling DPath expressions may need to evaluate typeCalculators in order to lookup their srcType and dstType.
+ * To prevent circular dependencies, this means that the underlying expressions must be lazy.
+ *
+ * Since these fields must be lazy, we cannot use them to determine supportsParse or supportUnparse
+ */
+
+ lazy val maybeInputTypeCalc = maybeInputTypeCalcArg
+ lazy val maybeOutputTypeCalc = maybeOutputTypeCalcArg
+
+ override protected def preSerialization: Any = {
+ super.preSerialization
+ maybeInputTypeCalc
+ maybeOutputTypeCalc
+ }
//The class TypeValueCalc will verify that supports(Un)Parse is true when nessasary
//Therefore, if we ever call the below functions, we know that the relevent Maybe object is defined.
@@ -315,6 +329,9 @@ object RepValueSetCompiler {
}
object TypeCalculatorCompiler {
+
+ type TypeCalcMap = Map[GlobalQName, TypeCalculator[AnyRef, AnyRef]]
+
// 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] = {
Assert.invariant(!mappings.isEmpty)
@@ -349,10 +366,19 @@ object TypeCalculatorCompiler {
}
}
- def compileExpression[A <: AnyRef, B <: AnyRef](optInputTypeCalc: Option[CompiledExpression[B]], optOutputTypeCalc: Option[CompiledExpression[A]], srcType: NodeInfo.Kind, dstType: NodeInfo.Kind): ExpressionTypeCalculator[A, B] = {
- val maybeInputType: Maybe[CompiledExpression[B]] = optInputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
- val maybeOutputType: Maybe[CompiledExpression[A]] = optOutputTypeCalc.map(Maybe(_)).getOrElse(Maybe.Nope)
- new ExpressionTypeCalculator(maybeInputType, maybeOutputType, srcType, dstType)
+ /*
+ * compileExpression is already a highly overloaded name from the DPath expression compiler.
+ * 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,
+ supportsParse: Boolean, supportsUnparse: Boolean): 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)
+ new ExpressionTypeCalculator(maybeInputType, maybeOutputType, srcType, dstType, supportsParse, supportsUnparse)
}
def compileIdentity[A <: AnyRef](srcType: NodeInfo.Kind): TypeCalculator[A, A] = new IdentifyTypeCalculator(srcType)
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml
index ab86e81..c0e92dd 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/lookAhead/lookAhead.tdml
@@ -299,7 +299,7 @@
</tdml:documentPart>
</tdml:document>
<tdml:errors>
- <tdml:error>Runtime Schema Definition Error</tdml:error>
+ <tdml:error>Schema Definition Error</tdml:error>
<tdml:error>Look-ahead distance of 520 bits exceeds implementation defined limit of 512 bits</tdml:error>
</tdml:errors>
</tdml:parserTestCase>
@@ -321,7 +321,7 @@
</tdml:documentPart>
</tdml:document>
<tdml:errors>
- <tdml:error>Runtime Schema Definition Error</tdml:error>
+ <tdml:error>Schema Definition Error</tdml:error>
<tdml:error>Look-ahead distance of 513 bits exceeds implementation defined limit of 512 bits</tdml:error>
</tdml:errors>
</tdml:parserTestCase>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml
index 729877d..5aa2a7e 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/typeCalcFunctionErrors.tdml
@@ -136,7 +136,8 @@
</tdml:documentPart>
</tdml:document>
<tdml:errors>
- <tdml:error>Parse Error</tdml:error>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>Cannot convert 'a' from String type to Long</tdml:error>
</tdml:errors>
<tdml:warnings>
<tdml:warning>Schema Definition Warning</tdml:warning>
@@ -175,7 +176,8 @@
</tdml:documentPart>
</tdml:document>
<tdml:errors>
- <tdml:error>Parse Error</tdml:error>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>Cannot convert 'a' from String type to Long</tdml:error>
</tdml:errors>
<tdml:warnings>
<tdml:warning>Schema Definition Warning</tdml:warning>
@@ -246,12 +248,10 @@
<tdml:documentPart type="text">a</tdml:documentPart>
</tdml:document>
<tdml:errors>
- <tdml:error>parse Error</tdml:error>
+ <tdml:error>Parse Error</tdml:error>
+ <tdml:error>Cannot convert</tdml:error>
+ <tdml:error>from String type to Long</tdml:error>
</tdml:errors>
- <tdml:warnings>
- <tdml:warning>Schema Definition Warning</tdml:warning>
- <tdml:warning>Expression result type (String) should be manually cast to the expected type (Int)</tdml:warning>
- </tdml:warnings>
</tdml:parserTestCase>
<tdml:parserTestCase name="typeCalcDispatch_typeError_10"
@@ -285,10 +285,6 @@
<typeCalcDispatch_typeError_12>a</typeCalcDispatch_typeError_12>
</tdml:dfdlInfoset>
</tdml:infoset>
- <tdml:warnings>
- <tdml:warning>Schema Definition Warning</tdml:warning>
- <tdml:warning>Expression result type (String) should be manually cast to the expected type (Int)</tdml:warning>
- </tdml:warnings>
<tdml:errors>
<tdml:error>Unparse Error</tdml:error>
<tdml:error>Cannot convert 'a' from String type to Long</tdml:error>
@@ -372,4 +368,57 @@
</tdml:errors>
</tdml:parserTestCase>
+ <tdml:defineSchema name="nonexistantTypeCalcType_01.dfdl.xsd">
+ <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+ <dfdl:format ref="ex:GeneralFormat" lengthKind="implicit"
+ lengthUnits="bytes" encoding="UTF-8" separator="" initiator=""
+ terminator="" occursCountKind="parsed" ignoreCase="no"
+ textNumberRep="standard" representation="binary"
+ dfdlx:parseUnparsePolicy="parseOnly"
+ />
+
+ <xs:element name="root" type="xs:int" dfdl:inputValueCalc="{ dfdlx:inputTypeCalcInt('tns:nonExistant', 0) }"/>
+
+ </tdml:defineSchema>
+
+ <tdml:parserTestCase name="nonexistantTypeCalcType_01"
+ root="root" model="nonexistantTypeCalcType_01.dfdl.xsd" description="Extensions - inputTypeCalc errors">
+ <tdml:document>
+ <tdml:documentPart type="byte"></tdml:documentPart>
+ </tdml:document>
+ <tdml:errors>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>tns:nonExistant</tdml:error>
+ <tdml:error>does not exist</tdml:error>
+ </tdml:errors>
+ </tdml:parserTestCase>
+
+ <tdml:defineSchema name="nonexistantTypeCalcType_02.dfdl.xsd">
+ <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+ <dfdl:format ref="ex:GeneralFormat" lengthKind="implicit"
+ lengthUnits="bytes" encoding="UTF-8" separator="" initiator=""
+ terminator="" occursCountKind="parsed" ignoreCase="no"
+ textNumberRep="standard" representation="binary"
+ dfdlx:parseUnparsePolicy="parseOnly"
+ />
+
+ <xs:simpleType name="nonExistantTypeCalc">
+ <xs:restriction base="xs:int"/>
+ </xs:simpleType>
+ <xs:element name="root" type="xs:int" dfdl:inputValueCalc="{ dfdlx:inputTypeCalcInt('tns:nonExistantTypeCalc', 0) }"/>
+
+ </tdml:defineSchema>
+
+ <tdml:parserTestCase name="nonexistantTypeCalcType_02"
+ root="root" model="nonexistantTypeCalcType_02.dfdl.xsd" description="Extensions - inputTypeCalc errors">
+ <tdml:document>
+ <tdml:documentPart type="byte"></tdml:documentPart>
+ </tdml:document>
+ <tdml:errors>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>tns:nonExistant</tdml:error>
+ <tdml:error>does not have a repType</tdml:error>
+ </tdml:errors>
+ </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 f4b7262..11b1e60 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
@@ -96,5 +96,7 @@ class TestInputTypeValueCalc {
@Test def test_nonexistant_reptype_01() { fnErrRunner.runOneTest("nonexistant_reptype_01") }
@Test def test_nonexistantOutputTypeCalc_01() { fnErrRunner.runOneTest("nonexistantOutputTypeCalc_01") }
@Test def test_nonexistantInputTypeCalc_01() { fnErrRunner.runOneTest("nonexistantInputTypeCalc_01") }
+ @Test def test_nonexistantTypeCalcType_01() { fnErrRunner.runOneTest("nonexistantTypeCalcType_01") }
+ @Test def test_nonexistantTypeCalcType_02() { fnErrRunner.runOneTest("nonexistantTypeCalcType_02") }
}