This is an automated email from the ASF dual-hosted git repository.
jadams pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git
The following commit(s) were added to refs/heads/master by this push:
new dd49907 Restructured the Variables API and implemented
newVariableInstance
dd49907 is described below
commit dd499070049b91fb8d859ac8d44fd0be088c985c
Author: Josh Adams <[email protected]>
AuthorDate: Mon Apr 13 09:34:26 2020 -0400
Restructured the Variables API and implemented newVariableInstance
Primary changes are located within VariableMap, which now is a map of
name->Stack[Variable] instead of name->List(List(Variable)). Also
necessitated some housekeeping code in the PState for to handle
instances of backtracking after modifying a Variable.
DAFFODIL-341
---
.../apache/daffodil/dsom/DFDLDefineVariable.scala | 72 ++-
.../apache/daffodil/dsom/DFDLStatementMixin.scala | 31 +-
.../scala/org/apache/daffodil/dsom/GroupDef.scala | 2 +-
.../grammar/HasStatementsGrammarMixin.scala | 18 +-
.../daffodil/grammar/ModelGroupGrammarMixin.scala | 6 +-
.../apache/daffodil/grammar/TermGrammarMixin.scala | 4 +-
.../org/apache/daffodil/grammar/VariableMap.scala | 4 +-
.../grammar/primitives/PrimitivesExpressions.scala | 8 +-
.../daffodil/dsom/TestExternalVariablesNew.scala | 2 +-
.../externalvars/TestExternalVariablesLoader.scala | 12 +-
.../scala/org/apache/daffodil/util/MStack.scala | 5 +-
.../unparsers/ExpressionEvaluatingUnparsers.scala | 15 +-
.../org/apache/daffodil/dpath/DPathRuntime.scala | 8 +-
.../externalvars/ExternalVariablesLoader.scala | 4 +-
.../apache/daffodil/processors/DataProcessor.scala | 48 +-
.../apache/daffodil/processors/RuntimeData.scala | 4 +-
.../apache/daffodil/processors/VariableMap1.scala | 301 +++++++------
.../parsers/ExpressionEvaluatingParsers.scala | 16 +-
.../parsers/InitiatedContentParsers.scala | 4 +-
.../daffodil/processors/parsers/PState.scala | 48 +-
.../daffodil/processors/parsers/Parser.scala | 4 +-
.../processors/parsers/SequenceParserBases.scala | 4 +-
.../daffodil/processors/unparsers/UState.scala | 16 +-
.../daffodil/section07/variables/variables.tdml | 493 ++++++++++++++++++++-
.../daffodil/section07/variables/variables_01.tdml | 7 +-
.../section07/variables/TestVariables.scala | 11 +
26 files changed, 880 insertions(+), 267 deletions(-)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
index 3a7d713..8931995 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
@@ -27,7 +27,7 @@ import org.apache.daffodil.xml.GlobalQName
import org.apache.daffodil.xml.QName
import org.apache.daffodil.dpath.NodeInfo.PrimType
import org.apache.daffodil.util.Maybe
-import org.apache.daffodil.grammar.primitives.SetVariable
+import org.apache.daffodil.grammar.primitives.{ SetVariable,
NewVariableInstanceStart, NewVariableInstanceEnd }
import org.apache.daffodil.schema.annotation.props.Found
import org.apache.daffodil.exceptions.Assert
@@ -100,7 +100,7 @@ abstract class VariableReference(node: Node, decl:
AnnotatedSchemaComponent)
final lazy val ref = getAttributeRequired("ref")
final lazy val varQName = resolveQName(ref)
- final def variableRuntimeData = defv.runtimeData
+ def variableRuntimeData = defv.runtimeData
final lazy val defv = decl.schemaSet.getDefineVariable(varQName).getOrElse(
this.schemaDefinitionError("Variable definition not found: %s", ref))
@@ -109,13 +109,73 @@ abstract class VariableReference(node: Node, decl:
AnnotatedSchemaComponent)
final class DFDLNewVariableInstance(node: Node, decl: AnnotatedSchemaComponent)
extends VariableReference(node, decl) // with
NewVariableInstance_AnnotationMixin
{
- lazy val defaultValue = getAttributeOption("defaultValue")
+ // newVariableInstance only allowed within group ref, sequence, or choice
+ decl match {
+ case gr: GroupRef =>
+ case grl: GroupDefLike =>
+ case _ => decl.SDE("newVariableInstance may only be used on group
reference, sequence or choice")
+ }
+
+ private lazy val attrValue = getAttributeOption("value")
+
+ private lazy val <dfdl:setVariable>{ eltChildren @ _* }</dfdl:setVariable> =
node
+
+ private lazy val eltValue = eltChildren.text.trim
+
+ private lazy val defaultValueAsAttribute = getAttributeOption("defaultValue")
+ private lazy val defaultValueAsElement = node.child.text.trim
+
+ final lazy val defaultValue = (defaultValueAsAttribute,
defaultValueAsElement) match {
+ case (None, "") => defv.defaultValue
+ case (None, str) => Some(str)
+ case (Some(str), "") => Some(str)
+ case (Some(str), v) => schemaDefinitionError("Default value of variable
was supplied both as attribute and element value: %s", node.toString)
+ }
+
+ final lazy val value = (attrValue, eltValue) match {
+ case (None, v) if (v != "") => v
+ case (Some(v), "") => v
+ case (Some(v), ev) if (ev != "") => decl.SDE("Cannot have both a value
attribute and an element value: %s", node)
+ case (None, "") => decl.SDE("Must have either a value attribute or an
element value: %s", node)
+ }
- def gram(term: Term): Gram = Assert.nyi("dfdl:newVariableInstance")
- def endGram(term: Term): Gram = Assert.nyi("dfdl:newVariableInstance")
+ lazy val maybeDefaultValueExpr = {
+ val compilationTargetType = defv.primType
+ val qn = this.qNameForProperty("defaultValue", XMLUtils.dafintURI)
+ val defaultValExpr = defaultValue.map { e =>
+ ExpressionCompilers.AnyRef.compileProperty(qn, compilationTargetType,
Found(e, this.dpathCompileInfo, "defaultValue", false), this, dpathCompileInfo)
+ }
+
+ Maybe.toMaybe(defaultValExpr)
+ }
+
+ /* Need to override variableRuntimeData so that defaultValues
+ * are read from newVariableInstance instead of the original
+ * variable definition. Also allows diagnostic messages to
+ * point to this location instead of the original definition
+ */
+ final override lazy val variableRuntimeData = {
+ val vrd = new VariableRuntimeData(
+ this.schemaFileLocation,
+ this.diagnosticDebugName,
+ this.path,
+ this.namespaces,
+ defv.external,
+ maybeDefaultValueExpr,
+ defv.typeQName,
+ defv.namedQName.asInstanceOf[GlobalQName],
+ defv.primType,
+ this.tunable.unqualifiedPathStepPolicy)
+ vrd
+ }
- lazy val newVariableInstance = defv.newVariableInstance
+ final def gram(term: Term) = LV('gram) {
+ NewVariableInstanceStart(decl, this)
+ }.value
+ final def endGram(term: Term) = LV('endGram) {
+ NewVariableInstanceEnd(decl, this)
+ }.value
}
final class DFDLSetVariable(node: Node, decl: AnnotatedSchemaComponent)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
index 38fff9c..7393a83 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
@@ -59,8 +59,11 @@ trait ResolvesDFDLStatementMixin
ReferencedElementInfos.None
}
case nv: DFDLNewVariableInstance => {
- // nv.defaultValueExpr.contentReferencedElementInfos
- s.notYetImplemented("dfdl:newVariableInstance")
+ val mdv = nv.maybeDefaultValueExpr
+ if (mdv.isDefined)
+ f(mdv.get)
+ else
+ ReferencedElementInfos.None
}
case _ => ReferencedElementInfos.None
}
@@ -177,9 +180,22 @@ trait ProvidesDFDLStatementMixin extends ThrowsSDE with
HasTermCheck { self: Ann
checkDistinctVariableNames(combinedSvs)
}
+ private lazy val patternAsserts: Seq[DFDLAssert] = combinedAsserts.filter{
st => st.testKind == TestKind.Pattern }
+ private lazy val nonPatternAsserts: Seq[DFDLAssert] =
combinedAsserts.filter{ st => st.testKind != TestKind.Pattern }
+
+ private lazy val patternDiscrims: Seq[DFDLDiscriminator] =
combinedDiscrims.filter{ st => st.testKind == TestKind.Pattern }
+ private lazy val nonPatternDiscrims: Seq[DFDLDiscriminator] =
combinedDiscrims.filter{ st => st.testKind != TestKind.Pattern }
+
+ final lazy val patternStatements: Seq[DFDLStatement] = patternAsserts ++
patternDiscrims
+
+ final lazy val lowPriorityStatements: Seq[DFDLStatement] =
setVariableStatements ++ nonPatternAsserts ++ nonPatternDiscrims
+
final protected lazy val localStatements = this.annotationObjs.collect {
case st: DFDLStatement => st }
- private lazy val localNewVariableInstanceStatements =
localStatements.collect { case nve: DFDLNewVariableInstance => nve }
+ private lazy val localNewVariableInstanceStatements = {
+ val nvis = localStatements.collect { case nve: DFDLNewVariableInstance =>
nve }
+ checkDistinctNewVariableInstances(nvis)
+ }
final protected lazy val (localDiscriminatorStatements,
localAssertStatements) = {
val discrims = localStatements.collect { case disc: DFDLDiscriminator =>
disc }
@@ -198,10 +214,17 @@ trait ProvidesDFDLStatementMixin extends ThrowsSDE with
HasTermCheck { self: Ann
private def checkDistinctVariableNames(svs: Seq[DFDLSetVariable]) = {
val names = svs.map { _.defv.globalQName }
val areAllDistinct = names.distinct.size == names.size
- schemaDefinitionUnless(areAllDistinct, "Variable names must all be
distinct at the same location: %s", names)
+ schemaDefinitionUnless(areAllDistinct, "Variables referenced by
setVariable must all be distinct at the same location: %s", names.distinct)
svs
}
+ private def checkDistinctNewVariableInstances(nvis:
Seq[DFDLNewVariableInstance]) = {
+ val names = nvis.map { _.defv.globalQName }
+ val areAllDistinct = names.distinct.size == names.size
+ schemaDefinitionUnless(areAllDistinct, "Variables referenced by
newVariableInstances must all be distinct within the same scope: %s",
names.distinct)
+ nvis
+ }
+
final protected lazy val localSetVariableStatements = {
val svs = localStatements.collect { case sv: DFDLSetVariable => sv }
checkDistinctVariableNames(svs)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
index d686925..28f38a7 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
@@ -92,7 +92,7 @@ trait GroupDefLike
}
/**
- * Global Group Defs carry annotations that are combined wiht those
+ * Global Group Defs carry annotations that are combined with those
* of the corresponding group reference that refers to them.
*
* These are not carried on the xs:group element itself, but the xs:sequence
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
index 463c998..cbe559a 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
@@ -18,13 +18,23 @@
package org.apache.daffodil.grammar
import org.apache.daffodil.dsom.Term
+import org.apache.daffodil.dsom.{ DFDLNewVariableInstance, DFDLDiscriminator,
DFDLAssert }
+import org.apache.daffodil.schema.annotation.props.gen.TestKind
trait HasStatementsGrammarMixin extends GrammarMixin { self: Term =>
- private lazy val statementGrams = statements.map { _.gram(self) }
- // TODO: statements (but specifically not newVariableInstance) can appear on
simple type definitions as well as terms.
+ // Includes setVariable as well as assert/discriminator statements that
+ // are not testKind="pattern"
+ private lazy val lowPriorityStatementGrams = lowPriorityStatements.map {
_.gram(self) }
- final lazy val dfdlStatementEvaluations = prod("dfdlStatementEvaluations",
statementGrams.length > 0) {
- statementGrams.fold(mt) { _ ~ _ }
+ final lazy val dfdlLowPriorityStatementEvaluations =
prod("dfdlStatementEvaluations", lowPriorityStatementGrams.length > 0) {
+ lowPriorityStatementGrams.fold(mt) { _ ~ _ }
+ }
+
+ // assert/discriminator statements with testKind="pattern"
+ private lazy val patternStatementGrams = patternStatements.map {
_.gram(self) }
+
+ final lazy val dfdlPatternStatementEvaluations =
prod("dfdlPatternStatementEvaluations", patternStatementGrams.length > 0) {
+ patternStatementGrams.fold(mt) { _ ~ _ }
}
}
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
index 5ad9835..89790b2f 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
@@ -42,7 +42,11 @@ trait ModelGroupGrammarMixin
private lazy val groupRightFraming = prod("groupRightFraming") {
TrailingSkipRegion(this) }
final override lazy val termContentBody = prod("termContentBody") {
- dfdlStatementEvaluations ~ groupLeftFraming ~
groupContentWithInitiatorTerminator ~ groupRightFraming
+ // See 9.5 Evaluation Order for Statement Annotations
+ dfdlPatternStatementEvaluations ~ // Assert and Discriminator statements
with testKind="pattern"
+ dfdlScopeBegin ~ // newVariableInstance
+ dfdlLowPriorityStatementEvaluations ~ // setVariable and the rest of the
Assert and Discriminator statements
+ groupLeftFraming ~ groupContentWithInitiatorTerminator ~ groupRightFraming
~ dfdlScopeEnd
}
private lazy val groupContentWithInitiatorTerminator =
prod("groupContentWithInitiatorTerminator") {
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
index 9c50459..ea318e3 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
@@ -35,9 +35,7 @@ trait TermGrammarMixin
def termContentBody: Gram
- private lazy val newVars = this.annotationObjs.filter { st =>
- st.isInstanceOf[DFDLNewVariableInstance]
- }.asInstanceOf[Seq[DFDLNewVariableInstance]]
+ private lazy val newVars = this.newVariableInstanceStatements
private lazy val newVarStarts = newVars.map { _.gram(self) }
private lazy val newVarEnds = newVars.map { _.endGram(self) }
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
index 3ced2e9..262252e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
@@ -31,8 +31,6 @@ object VariableMapFactory {
}
def setExternalVariables(currentVMap: VariableMap, bindings: Seq[Binding],
referringContext: ThrowsSDE) = {
- var newVMap = currentVMap
- bindings.foreach(b => newVMap = newVMap.setExtVariable(b.varQName,
b.varValue, referringContext))
- newVMap
+ bindings.foreach(b => currentVMap.setExtVariable(b.varQName, b.varValue,
referringContext))
}
}
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
index 42e1413..2a5efc7 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
@@ -180,15 +180,15 @@ abstract class NewVariableInstanceBase(decl:
AnnotatedSchemaComponent, stmt: DFD
case class NewVariableInstanceStart(decl: AnnotatedSchemaComponent, stmt:
DFDLNewVariableInstance)
extends NewVariableInstanceBase(decl, stmt) {
- lazy val parser: DaffodilParser = new
NewVariableInstanceStartParser(decl.runtimeData)
- override lazy val unparser: DaffodilUnparser = new
NewVariableInstanceStartUnparser(decl.runtimeData)
+ lazy val parser: DaffodilParser = new
NewVariableInstanceStartParser(stmt.variableRuntimeData)
+ override lazy val unparser: DaffodilUnparser = new
NewVariableInstanceStartUnparser(stmt.variableRuntimeData)
}
case class NewVariableInstanceEnd(decl: AnnotatedSchemaComponent, stmt:
DFDLNewVariableInstance)
extends NewVariableInstanceBase(decl, stmt) {
- lazy val parser: DaffodilParser = new
NewVariableInstanceEndParser(decl.runtimeData)
- override lazy val unparser: DaffodilUnparser = new
NewVariableInstanceEndUnparser(decl.runtimeData)
+ lazy val parser: DaffodilParser = new
NewVariableInstanceEndParser(stmt.variableRuntimeData)
+ override lazy val unparser: DaffodilUnparser = new
NewVariableInstanceEndUnparser(stmt.variableRuntimeData)
}
/**
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
index c252674..95e1620 100644
---
a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
@@ -142,7 +142,7 @@ class TestExternalVariablesNew {
case Success(None) => fail("Did not find " + keyToFind + " in the
VariableMap.")
case Success(Some(variab)) => {
// Found var1 but is the value correct?
-
assertTrue(variab.toString.contains("Variable(VariableDefined,DataValue(" +
expectedValue + ")"))
+
assertTrue(variab.toString.contains("VariableInstance(VariableDefined,DataValue("
+ expectedValue + ")"))
}
}
}
diff --git
a/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
b/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
index 6939e91..3446a89 100644
---
a/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
+++
b/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
@@ -89,11 +89,11 @@ class TestExternalVariablesLoader extends Logging {
val (sd, sset) = generateSD(topLevelAnnotations)
val initialVMap = sset.variableMap
- val lst_v_no_default = initialVMap.getVariableBindings(v_no_default)
- val lst_v_with_default = initialVMap.getVariableBindings(v_with_default)
+ val stk_v_no_default = initialVMap.getVariableBindings(v_no_default)
+ val stk_v_with_default = initialVMap.getVariableBindings(v_with_default)
- val var_v_no_default = lst_v_no_default.head.head
- val var_v_with_default = lst_v_with_default.head.head
+ val var_v_no_default = stk_v_no_default.top
+ val var_v_with_default = stk_v_with_default.top
val v_no_default_vrd = var_v_no_default.rd
val v_with_default_vrd = var_v_with_default.rd
@@ -110,9 +110,9 @@ class TestExternalVariablesLoader extends Logging {
// Verify that the external variables override the previous values
// in the VariableMap
- val (value1, _) = vmap.readVariable(v_no_default_vrd, Fakes.fakeElem)
+ val value1 = vmap.readVariable(v_no_default_vrd, Fakes.fakeElem)
assertEquals(1, value1.getAnyRef)
- val (value2, _) = vmap.readVariable(v_with_default_vrd, Fakes.fakeElem)
+ val value2 = vmap.readVariable(v_with_default_vrd, Fakes.fakeElem)
assertEquals(2, value2.getAnyRef)
}
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
b/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
index 37463e0..99afdc9 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
@@ -128,7 +128,8 @@ final class MStackOfMaybe[T <: AnyRef] {
* an object reference or null, and call Maybe(thing) explicitly outside the
* iteration. Maybe(null) is Nope, and Maybe(thing) is One(thing) if thing is
not null.
*/
-final class MStackOf[T <: AnyRef] {
+final class MStackOf[T <: AnyRef]
+ extends Serializable {
override def toString = delegate.toString
@@ -176,7 +177,7 @@ object MStackOfAnyRef {
* things.
*/
protected abstract class MStack[@specialized T] private[util] (
- arrayAllocator: (Int) => Array[T], nullValue: T) {
+ arrayAllocator: (Int) => Array[T], nullValue: T) extends Serializable {
private var index = 0
private var table: Array[T] = null
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 7906e77..8ef915b 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
@@ -42,10 +42,7 @@ final class SetVariableSuspendableExpression(
extends SuspendableExpression {
override protected def processExpressionResult(ustate: UState, v:
DataValuePrimitive) {
- val newVMap =
ustate.variableMap.setVariable(rd, v, referencingContext, ustate)
-
- ustate.setVariables(newVMap)
}
override protected def maybeKnownLengthInBits(ustate: UState) = MaybeULong(0)
@@ -90,10 +87,8 @@ class NewVariableInstanceStartUnparser(override val context:
RuntimeData)
override lazy val childProcessors = Vector()
- context.notYetImplemented("newVariableInstance")
-
- override def unparse(ustate: UState) = {
- context.notYetImplemented("newVariableInstance")
+ override def unparse(state: UState) = {
+ state.newVariableInstance(context.asInstanceOf[VariableRuntimeData])
}
}
@@ -104,10 +99,8 @@ class NewVariableInstanceEndUnparser(override val context:
RuntimeData)
override lazy val childProcessors = Vector()
- context.notYetImplemented("newVariableInstance")
-
- override def unparse(ustate: UState) = {
- context.notYetImplemented("newVariableInstance")
+ override def unparse(state: UState) = {
+ state.removeVariableInstance(context.asInstanceOf[VariableRuntimeData])
}
}
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 d904c1c..07cc4d0 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
@@ -41,6 +41,7 @@ import org.apache.daffodil.processors.ParseOrUnparseState
import org.apache.daffodil.processors.ProcessingError
import org.apache.daffodil.processors.VariableException
import org.apache.daffodil.processors.VariableRuntimeData
+import org.apache.daffodil.processors.parsers.PState
import org.apache.daffodil.util.Misc
class CompiledDPath(val ops: RecipeOp*) extends Serializable {
@@ -180,9 +181,10 @@ case class VRef(vrd: VariableRuntimeData, context:
ThrowsSDE)
override def run(dstate: DState) {
Assert.invariant(dstate.vmap != null)
- val (res, newVMap) = dstate.vmap.readVariable(vrd, context)
- dstate.setVMap(newVMap)
- dstate.setCurrentValue(res)
+ if (dstate.parseOrUnparseState.isDefined)
+ if (dstate.parseOrUnparseState.get.isInstanceOf[PState])
+ dstate.parseOrUnparseState.get.asInstanceOf[PState].variableRead(vrd)
+ dstate.setCurrentValue(dstate.vmap.readVariable(vrd, context))
}
override def toXML = toXML("$" + vrd.globalQName.toPrettyString)
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
index 162d2fc..4f551ca 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
@@ -47,8 +47,8 @@ object ExternalVariablesLoader {
def loadVariables(bindings: Seq[Binding], referringContext: ThrowsSDE, vmap:
VariableMap): VariableMap = {
Assert.usage(referringContext != null, "loadVariables expects
'referringContext' to not be null!")
- val finalVMap = VariableUtils.setExternalVariables(vmap, bindings,
referringContext)
- finalVMap
+ VariableUtils.setExternalVariables(vmap, bindings, referringContext)
+ vmap
}
// The following are methods that retrieve and transform variables into
Seq[Binding]
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
index 187df57..ca0c534 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
@@ -186,10 +186,8 @@ class DataProcessor private (
tunables:DaffodilTunables,
compilerExternalVars: Queue[Binding] = Queue.empty,
validationMode: ValidationMode.Type = ValidationMode.Off) =
- this(ssrd, tunables,
- ExternalVariablesLoader.loadVariables(compilerExternalVars, ssrd,
ssrd.originalVariables),
- false, None, validationMode,
- compilerExternalVars)
+ this(ssrd, tunables,
ExternalVariablesLoader.loadVariables(compilerExternalVars, ssrd,
ssrd.originalVariables),
+ false, None, validationMode, compilerExternalVars)
private def copy(
ssrd: SchemaSetRuntimeData = ssrd,
@@ -260,48 +258,46 @@ class DataProcessor private (
copy(areDebugging = flag, tunables = newTunables)
}
- private def newVariableMap(extVars: Map[String, String]): (VariableMap,
Queue[Binding]) = {
+ private def loadExternalVariables(extVars: Map[String, String]):
Queue[Binding] = {
val bindings = ExternalVariablesLoader.mapToBindings(extVars)
val newVars = externalVars ++ bindings
- val newMap = ExternalVariablesLoader.loadVariables(bindings, ssrd,
variableMap)
- (newMap, newVars)
+ ExternalVariablesLoader.loadVariables(bindings, ssrd, variableMap)
+ newVars
}
- private def newVariableMap(extVars: File): (VariableMap, Queue[Binding]) = {
+ private def loadExternalVariables(extVars: File): Queue[Binding] = {
val bindings = ExternalVariablesLoader.fileToBindings(extVars)
val newVars = externalVars ++ bindings
- val newMap = ExternalVariablesLoader.loadVariables(bindings, ssrd,
variableMap)
- (newMap, newVars)
+ ExternalVariablesLoader.loadVariables(bindings, ssrd, variableMap)
+ newVars
}
- private def newVariableMap(bindings: Seq[Binding]): (VariableMap,
Queue[Binding]) = {
+ private def loadExternalVariables(bindings: Seq[Binding]): Queue[Binding] =
{
val newVars = externalVars ++ bindings
- val newMap = ExternalVariablesLoader.loadVariables(bindings, ssrd,
variableMap)
- (newMap, newVars)
+ ExternalVariablesLoader.loadVariables(bindings, ssrd, variableMap)
+ newVars
}
@deprecated("Use withExternalVariables.", "2.6.0")
def setExternalVariables(extVars: Map[String, String]): Unit = {
- val (newMap, newBindings) = newVariableMap(extVars)
- variableMap = newMap
+ val newBindings = loadExternalVariables(extVars)
externalVars = newBindings
}
def withExternalVariables(extVars: Map[String, String]): DataProcessor = {
- val (newMap, newBindings) = newVariableMap(extVars)
- copy(variableMap = newMap, externalVars = newBindings)
+ val newBindings = loadExternalVariables(extVars)
+ copy(externalVars = newBindings)
}
@deprecated("Use withExternalVariables.", "2.6.0")
def setExternalVariables(extVars: File): Unit = {
- val (newMap, newBindings) = newVariableMap(extVars)
- variableMap = newMap
+ val newBindings = loadExternalVariables(extVars)
externalVars = newBindings
}
def withExternalVariables(extVars: File): DataProcessor = {
- val (newMap, newBindings) = newVariableMap(extVars)
- copy(variableMap = newMap, externalVars = newBindings)
+ val newBindings = loadExternalVariables(extVars)
+ copy(externalVars = newBindings)
}
/**
@@ -313,21 +309,19 @@ class DataProcessor private (
*/
@deprecated("Use withExternalVariables.", "2.6.0")
def setExternalVariables(extVars: File, tunable: DaffodilTunables): Unit = {
- val (newMap, newBindings) = newVariableMap(extVars)
- variableMap = newMap
+ val newBindings = loadExternalVariables(extVars)
externalVars = newBindings
}
@deprecated("Use withExternalVariables.", "2.6.0")
def setExternalVariables(extVars: Seq[Binding]): Unit = {
- val (newMap, newBindings) = newVariableMap(extVars)
- variableMap = newMap
+ val newBindings = loadExternalVariables(extVars)
externalVars = newBindings
}
def withExternalVariables(extVars: Seq[Binding]): DataProcessor = {
- val (newMap, newBindings) = newVariableMap(extVars)
- copy(variableMap = newMap, externalVars = newBindings)
+ val newBindings = loadExternalVariables(extVars)
+ copy(externalVars = newBindings)
}
@deprecated("Use withTunables.", "2.6.0")
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 22484c6..33c0ebc 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
@@ -984,7 +984,7 @@ final class VariableRuntimeData(
if (!maybeDefaultValueExpr.isDefined) VariableUndefined
else VariableDefined
- private lazy val maybeValue: DataValuePrimitiveNullable =
+ private lazy val value: DataValuePrimitiveNullable =
if (maybeDefaultValueExpr.isEmpty) DataValue.NoValue
else {
val defaultValueExpr = maybeDefaultValueExpr.get
@@ -994,6 +994,6 @@ final class VariableRuntimeData(
}
}
- def newVariableInstance: Variable = Variable(state, maybeValue, this,
maybeDefaultValueExpr)
+ def newVariableInstance: VariableInstance = VariableInstance(state, value,
this, maybeDefaultValueExpr)
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
index 3cdfce3..441fed6 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
@@ -28,9 +28,10 @@ import
org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
import org.apache.daffodil.infoset.RetryableException
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe.Nope
+import org.apache.daffodil.util.MStackOf
import org.apache.daffodil.xml.{GlobalQName, UnspecifiedNamespace, NamedQName,
QNameBase, RefQName}
-import scala.collection.immutable
+import scala.collection.mutable.Map
sealed abstract class VariableState extends Serializable
@@ -53,22 +54,49 @@ case object VariableRead extends VariableState
case object VariableInProcess extends VariableState
/**
- * Core tuple of a pure functional "state" for variables.
+ * Core tuple of a state for variables.
*/
-case class Variable(state: VariableState, value: DataValuePrimitiveNullable,
rd: VariableRuntimeData, defaultValueExpr: Maybe[CompiledExpression[AnyRef]])
extends Serializable
+case class VariableInstance(
+ var state: VariableState,
+ var value: DataValuePrimitiveNullable,
+ rd: VariableRuntimeData,
+ defaultValueExpr: Maybe[CompiledExpression[AnyRef]],
+ var priorState: VariableState = VariableUndefined,
+ var priorValue: DataValuePrimitiveNullable = DataValue.NoValue)
+ extends Serializable {
+
+ def copy(
+ state: VariableState = state,
+ value: DataValuePrimitiveNullable = value,
+ rd: VariableRuntimeData = rd,
+ defaultValueExpr: Maybe[CompiledExpression[AnyRef]] = defaultValueExpr,
+ priorState: VariableState = priorState,
+ priorValue: DataValuePrimitiveNullable = priorValue) =
+ new VariableInstance(state, value, rd, defaultValueExpr, priorState,
priorValue)
+
+ def setState(s: VariableState) = {
+ this.priorState = this.state
+ this.state = s
+ }
+
+ def setValue(v: DataValuePrimitiveNullable) = {
+ this.priorValue = this.value
+ this.value = v
+ }
+
+ def reset() = {
+ Assert.invariant(this.state != VariableUndefined)
+ this.value = this.priorValue
+ this.state = this.priorState
+ this.priorValue = DataValue.NoValue
+ this.priorState = VariableUndefined
+ }
+}
object VariableUtils {
def setExternalVariables(currentVMap: VariableMap, bindings: Seq[Binding],
referringContext: ThrowsSDE) = {
- var newVMap = currentVMap
- bindings.foreach { b =>
- val vm = newVMap
- val vqn = b.varQName
- val vv = b.varValue
- val res = vm.setExtVariable(vqn, vv, referringContext)
- newVMap = res
- }
- newVMap
+ bindings.foreach { b => currentVMap.setExtVariable(b.varQName, b.varValue,
referringContext) }
}
def convert(v: String, rd: VariableRuntimeData): DataValuePrimitive =
@@ -114,8 +142,6 @@ final class VariableBox(initialVMap: VariableMap) {
}
/**
- * Pure functional data structure for implementing DFDL's variables.
- *
* Key concepts: DFDL variables are single-assignment. Once they have been
set, they may not be set again.
* Furthermore, they have default values, and if the default value has been
read, then they may not
* subsequently be set.
@@ -127,152 +153,139 @@ final class VariableBox(initialVMap: VariableMap) {
* The DPath implementation must be made to implement the
* no-set-after-default-value-has-been-read behavior. This requires that
reading the variables causes a state transition.
*/
-class VariableMap private(vTable: Map[GlobalQName, List[List[Variable]]])
+class VariableMap private(vTable: Map[GlobalQName, MStackOf[VariableInstance]])
extends Serializable {
def this(topLevelVRDs: Seq[VariableRuntimeData] = Nil) =
- this(topLevelVRDs.map {
+ this(Map(topLevelVRDs.map {
vrd =>
val variab = vrd.newVariableInstance
- (vrd.globalQName, List(List(variab)))
- }.toMap)
+ val stack = new MStackOf[VariableInstance]
+ stack.push(variab)
+ (vrd.globalQName, stack)
+ }: _*))
override def toString(): String = {
"VariableMap(" + vTable.mkString(" | ") + ")"
}
- def find(qName: GlobalQName): Option[Variable] = {
- val optVrd = getVariableRuntimeData(qName)
- val optLists = optVrd.flatMap { vrd => vTable.get(vrd.globalQName) }
- val variab = optLists.flatMap { lists => lists.flatMap {
- _.toStream
- }.headOption
+ /**
+ * Returns a full, deep copy of the current VariableMap. This is needed as
+ * VariableInstances are mutable and cannot safely be shared across threads
+ */
+ def copy(): VariableMap = {
+ val table = Map[GlobalQName, MStackOf[VariableInstance]]()
+ vTable.foreach { case (k: GlobalQName, s: MStackOf[VariableInstance]) => {
+ val newStack= new MStackOf[VariableInstance]()
+
+ // toList provides a list in LIFO order, so we need to reverse it to
+ // maintain order
+ s.toList.reverse.foreach { case v: VariableInstance =>
newStack.push(v.copy()) }
+ table(k) = newStack
+ }}
+
+ new VariableMap(table)
+ }
+
+ def find(qName: GlobalQName): Option[VariableInstance] = {
+ val optStack = vTable.get(qName)
+ val variab = {
+ if (optStack.isDefined)
+ Some(optStack.get.top)
+ else
+ None
}
variab
}
def getVariableRuntimeData(qName: GlobalQName): Option[VariableRuntimeData]
= {
- val optLists = vTable.get(qName)
- optLists match {
- case None => None // no such variable.
- case Some(lists) => {
- val flatLists = lists.flatten
- Assert.invariant(flatLists.length > 0)
- val varObj = flatLists.head
- Some(varObj.rd)
- }
- }
+ val optVariable = find(qName)
+ if (optVariable.isDefined) Some(optVariable.get.rd) else None
}
lazy val context = Assert.invariantFailed("unused.")
- private def mkVMapForNewVariable(newVar: Variable, firstTier:
List[Variable], enclosingScopes: List[List[Variable]]) = {
- val newMap = vTable + ((newVar.rd.globalQName, (newVar :: firstTier) ::
enclosingScopes))
- new VariableMap(newMap)
- }
-
/**
* For testing mostly.
*/
- def getVariableBindings(qn: GlobalQName): List[List[Variable]] = {
+ def getVariableBindings(qn: GlobalQName): MStackOf[VariableInstance] = {
vTable.get(qn).get
}
/**
- * Returns the value of a variable, constructing also a modified variable
map which
- * shows that the variable has been read (state VariableRead), when the
variable hadn't
- * previously been read yet.
+ * Returns the value of a variable and sets the state of the variable to be
+ * VariableRead.
*/
- def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE):
(DataValuePrimitive, VariableMap) = {
+ def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE):
DataValuePrimitive = {
val referringContext: VariableRuntimeData = vrd
val varQName = vrd.globalQName
- val lists = vTable.get(varQName)
- lists match {
-
- case Some(firstTier :: enclosingScopes) =>
- firstTier match {
-
- case Variable(VariableRead, v, ctxt, _) :: rest if (v.isDefined) =>
(v.getNonNullable, this)
-
- case Variable(st, v, ctxt, defExpr) :: rest if ((v.isDefined) && (st
== VariableDefined || st == VariableSet)) => {
- val newVar = Variable(VariableRead, v, ctxt, defExpr)
- val vmap = mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
- val converted = v.getNonNullable // already converted
- (converted, vmap)
- }
-
- case _ => {
- // Fix DFDL-766
- throw new VariableHasNoValue(varQName, referringContext)
- // Runtime error:
- // referringContext.SDE(msg, varQName)
- }
+ val stack = vTable.get(varQName)
+ if (stack.isDefined) {
+ val variable = stack.get.top
+ variable.state match {
+ case VariableRead if (variable.value.isDefined) =>
variable.value.getNonNullable
+ case VariableDefined | VariableSet if (variable.value.isDefined) => {
+ variable.setState(VariableRead)
+ variable.value.getNonNullable
}
-
- case Some(Nil) => Assert.invariantFailed()
-
- case None => {
- // Compile time error:
- referringContext.SDE("Variable map (compilation): unknown variable
%s", varQName)
+ case _ => throw new VariableHasNoValue(varQName, referringContext)
}
- }
+ } else
+ referringContext.SDE("Variable map (compilation): unknown variable %s",
varQName) // Fix DFDL-766
}
/**
- * Assigns a variable, returning a new VariableMap which shows the state of
the variable.
+ * Assigns a variable and sets the variables state to VariableSet
*/
- def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive,
referringContext: ThrowsSDE, pstate: ParseOrUnparseState): VariableMap = {
+ def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive,
referringContext: ThrowsSDE, pstate: ParseOrUnparseState) = {
val varQName = vrd.globalQName
+ val stack = vTable.get(varQName)
+ if (stack.isDefined) {
+ val variable = stack.get.top
+ variable.state match {
+ case VariableSet => {
+ referringContext.SDE("Cannot set variable %s twice. State was: %s.
Existing value: %s",
+ variable.rd.globalQName, VariableSet, variable.value)
+ }
- vTable.get(varQName) match {
-
- case None => referringContext.schemaDefinitionError("unknown variable
%s", varQName)
-
- // There should always be a list with at least one tier in it (the
global tier).
- case x @ Some(firstTier :: enclosingScopes) => {
- firstTier match {
-
- case Variable(VariableDefined, v, ctxt, defaultExpr) :: rest if
(v.isDefined) => {
- val newVar = Variable(VariableSet,
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
- mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
- }
-
- case Variable(VariableUndefined, DataValue.NoValue, ctxt,
defaultExpr) :: rest => {
- val newVar = Variable(VariableSet,
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
- mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
- }
-
- case Variable(VariableSet, v, ctxt, defaultExpr) :: rest if
(v.isDefined) => {
- referringContext.SDE("Cannot set variable %s twice. State was: %s.
Existing value: %s", ctxt.globalQName, VariableSet, v)
- }
-
- case Variable(VariableRead, v, ctxt, defaultExpr) :: rest if
(v.isDefined) => {
- // referringContext.SDE
- pstate.SDW("Cannot set variable %s after reading the default
value. State was: %s. Existing value: %s", ctxt.globalQName, VariableSet, v)
- val newVar = Variable(VariableSet,
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
- mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
- }
+ case VariableRead => {
+ referringContext.SDE("Cannot set variable %s after reading the
default value. State was: %s. Existing value: %s",
+ variable.rd.globalQName, VariableSet, variable.value)
+ }
- case _ => Assert.invariantFailed("variable map internal list
structure not as expected: " + x)
+ case _ => {
+ variable.setValue(VariableUtils.convert(newValue.getAnyRef.toString,
variable.rd))
+ variable.setState(VariableSet)
}
}
- case x => Assert.invariantFailed("variables data structure not as
expected. Should not be " + x)
}
}
+ /**
+ * Creates a new instance of a variable
+ */
+ def newVariableInstance(vrd: VariableRuntimeData) = {
+ val varQName = vrd.globalQName
+ val stack = vTable.get(varQName)
+ Assert.invariant(stack.isDefined)
+ stack.get.push(vrd.newVariableInstance)
+ }
+
+ def removeVariableInstance(vrd: VariableRuntimeData) {
+ val varQName = vrd.globalQName
+ val stack = vTable.get(varQName)
+ Assert.invariant(stack.isDefined)
+ stack.get.pop
+ }
+
+
private lazy val externalVarGlobalQNames: Seq[GlobalQName] =
- vTable.map { pair => pair match {
- case (gqn, firstTier :: _) => firstTier match {
- case (variable@Variable(_, v, ctxt, _)) :: _ if (ctxt.external) =>
variable.rd.globalQName
- }
- case (_, Nil) => Assert.invariantFailed("empty tier")
- }
- }.toSeq
+ vTable.map { case (_, stack) if (stack.top.rd.external) =>
stack.top.rd.globalQName }.toSeq
/**
- * Assigns a variable, returning a new VariableMap which shows the state of
the variable.
+ * Assigns an external variable and sets the variables state to VariableSet
*/
- def setExtVariable(bindingQName: RefQName, newValue: DataValuePrimitive,
referringContext: ThrowsSDE): VariableMap = {
+ def setExtVariable(bindingQName: RefQName, newValue: DataValuePrimitive,
referringContext: ThrowsSDE) = {
val varQName: RefQName =
if (bindingQName.namespace == UnspecifiedNamespace) {
// The external variable binding was hoping to get away with not
specifying a namespace.
@@ -293,51 +306,37 @@ class VariableMap private(vTable: Map[GlobalQName,
List[List[Variable]]])
} else {
bindingQName
}
- val optMapTiers = vTable.get(varQName.toGlobalQName)
- optMapTiers match {
- case None => referringContext.schemaDefinitionError("unknown variable
%s", varQName)
- // There should always be a list with at least one tier in it (the
global tier).
- case x @ Some(firstTier :: enclosingScopes) => {
- firstTier match {
-
- case Variable(VariableDefined, v, ctxt, defaultExpr) :: rest if
(v.isDefined && ctxt.external) => {
- val newVar = Variable(VariableDefined,
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
- val newFirstTier = newVar :: rest
- mkVMapForNewVariable(newVar, newFirstTier, enclosingScopes)
- }
- case Variable(VariableDefined, v, ctxt, defaultExpr) :: rest if
(v.isDefined) => {
- referringContext.SDE("Cannot set variable %s externally. State
was: %s. Existing value: %s.", ctxt.globalQName, VariableDefined, v)
- // this // Unaltered VMap
- }
- case Variable(VariableUndefined, DataValue.NoValue, ctxt,
defaultExpr) :: rest if ctxt.external => {
- val newVar = Variable(VariableDefined,
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
- val newFirstTier = newVar :: rest
- mkVMapForNewVariable(newVar, newFirstTier, enclosingScopes)
- }
+ val stack = vTable.get(varQName.toGlobalQName)
+ if (!stack.isDefined)
+ referringContext.schemaDefinitionError("unknown variable %s", varQName)
+ else {
+ val variable = stack.get.top
+ variable.state match {
+ case VariableDefined if (!variable.rd.external) => {
+ referringContext.SDE("Cannot set variable %s externally. State was:
%s. Existing value: %s.",
+ variable.rd.globalQName, VariableDefined, variable.value)
+ }
- case Variable(VariableUndefined, DataValue.NoValue, ctxt,
defaultExpr) :: rest => {
- referringContext.SDE("Cannot set variable %s externally. State
was: %s.", ctxt.globalQName, VariableUndefined)
- // this // Unaltered VMap
- }
+ case VariableUndefined if (!variable.rd.external) => {
+ referringContext.SDE("Cannot set variable %s externally. State was:
%s.", variable.rd.globalQName, VariableUndefined)
+ }
- case Variable(VariableSet, v, ctxt, defaultExpr) :: rest if
(v.isDefined) => {
- // Shouldn't this be an impossible case? External variables should
be defined before parsing.
- // Parsing is the only point at which Set can be called?
- referringContext.SDE("Cannot externally set variable %s twice.
State was: %s. Existing value: %s", ctxt.globalQName, VariableSet, v)
- // this // Unaltered VMap
- }
+ case VariableSet => {
+ referringContext.SDE("Cannot externally set variable %s twice. State
was: %s. Existing value: %s",
+ variable.rd.globalQName, VariableSet, variable.value)
+ }
- case Variable(VariableRead, v, ctxt, defaultExpr) :: rest if
(v.isDefined) => {
- referringContext.SDE("Cannot externally set variable %s after
reading the default value. State was: %s. Existing value: %s",
ctxt.globalQName, VariableSet, v)
- // this // Unaltered VMap
- }
+ case VariableRead => {
+ referringContext.SDE("Cannot externally set variable %s after
reading the default value. State was: %s. Existing value: %s",
+ variable.rd.globalQName, VariableSet, variable.value)
+ }
- case _ => Assert.invariantFailed("variable map internal list
structure not as expected: " + x)
+ case _ => {
+ variable.setValue(VariableUtils.convert(newValue.getAnyRef.toString,
variable.rd))
+ variable.setState(VariableDefined)
}
}
- case x => Assert.invariantFailed("variables data structure not as
expected. Should not be " + x)
}
}
-
}
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 6e1c5b7..47ff959 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
@@ -153,25 +153,21 @@ class SetVariableParser(expr: CompiledExpression[AnyRef],
decl: VariableRuntimeD
}
}
-class NewVariableInstanceStartParser(
- override val context: RuntimeData)
+class NewVariableInstanceStartParser(override val context: VariableRuntimeData)
extends PrimParser {
- context.notYetImplemented("newVariableInstance")
override lazy val runtimeDependencies = Vector()
- def parse(pstate: PState) = {
- context.notYetImplemented("newVariableInstance")
+ def parse(start: PState): Unit = {
+ start.newVariableInstance(context)
}
}
-class NewVariableInstanceEndParser(
- override val context: RuntimeData)
+class NewVariableInstanceEndParser(override val context: VariableRuntimeData)
extends PrimParser {
- context.notYetImplemented("newVariableInstance")
override lazy val runtimeDependencies = Vector()
- def parse(pstate: PState) = {
- context.notYetImplemented("newVariableInstance")
+ def parse(start: PState) = {
+ start.removeVariableInstance(context)
}
}
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
index 7cb5502..578bbe1 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
@@ -49,9 +49,9 @@ final class
InitiatedContentDiscrimChoiceAndIndexGreaterThanMinParser(
def parse(start: PState): Unit = {
val top = start.discriminator
- start.popDiscriminator // pop array discriminator off of stack
+ start.popPointOfUncertainty // pop array discriminator off of stack and
any changed variables
start.setDiscriminator(true) // set the choice discriminator
- start.pushDiscriminator
+ start.pushPointOfUncertainty
start.setDiscriminator(top) // restore the array discriminator to top of
stack
if (start.arrayPos > min)
start.setDiscriminator(true) // set array discriminator only if
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
index e780bbe..ea85fca 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
@@ -55,6 +55,7 @@ import org.apache.daffodil.util.MStackOfBoolean
import org.apache.daffodil.util.MStackOfInt
import org.apache.daffodil.util.MStackOfLong
import org.apache.daffodil.util.MStackOfMaybe
+import org.apache.daffodil.util.MStackOf
import org.apache.daffodil.util.Maybe
import org.apache.daffodil.util.Maybe.Nope
import org.apache.daffodil.util.Maybe.One
@@ -66,6 +67,7 @@ import org.apache.daffodil.infoset.DISimpleState
import org.apache.daffodil.exceptions.UnsuppressableException
import org.apache.daffodil.exceptions.Abort
import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
+import org.apache.daffodil.xml.GlobalQName
object MPState {
@@ -166,6 +168,14 @@ final class PState private (
private val discriminatorStack = MStackOfBoolean()
discriminatorStack.push(false)
+ /**
+ * This stack tracks variables that have changed within the current point of
+ * uncertainty. This tracking is necessary to revert changes made to
variables
+ * when the parser needs to backtrack.
+ */
+ private val changedVariablesStack = new
MStackOf[mutable.MutableList[GlobalQName]]()
+ changedVariablesStack.push(mutable.MutableList[GlobalQName]())
+
override def dataStream = One(dataInputStream)
def saveDelimitedParseResult(result: Maybe[dfa.ParseResult]) {
@@ -205,6 +215,11 @@ final class PState private (
m.restoreInto(this)
m.clear()
markPool.returnToPool(m)
+ changedVariablesStack.top.foreach { v => {
+ val variable = variableMap.find(v)
+ if (variable.isDefined)
+ variable.get.reset
+ }}
}
def discard(m: PState.Mark) {
@@ -265,17 +280,37 @@ final class PState private (
}
def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive,
referringContext: VariableRuntimeData, pstate: PState) {
- this.setVariableMap(variableMap.setVariable(vrd, newValue,
referringContext, pstate))
+ variableMap.setVariable(vrd, newValue, referringContext, pstate)
+ changedVariablesStack.top += vrd.globalQName
+ }
+
+ /**
+ * Note that this function does not actually read the variable, it is used
+ * just to track that the variable was read in case we need to backtrack.
+ */
+ def variableRead(vrd: VariableRuntimeData) {
+ changedVariablesStack.top += vrd.globalQName
+ }
+
+ def newVariableInstance(vrd: VariableRuntimeData) {
+ variableMap.newVariableInstance(vrd)
+ changedVariablesStack.top += vrd.globalQName
}
- def pushDiscriminator {
+ def removeVariableInstance(vrd: VariableRuntimeData) {
+ variableMap.removeVariableInstance(vrd)
+ }
+
+ def pushPointOfUncertainty {
// threadCheck()
discriminatorStack.push(false)
+ changedVariablesStack.push(mutable.MutableList[GlobalQName]())
}
- def popDiscriminator {
+ def popPointOfUncertainty {
// threadCheck()
discriminatorStack.pop
+ changedVariablesStack.pop
}
def setDiscriminator(disc: Boolean) {
@@ -501,7 +536,12 @@ object PState {
output: InfosetOutputter,
dataProc: DFDL.DataProcessor): PState = {
- val variables = dataProc.variableMap
+ /**
+ * This is a full deep copy as variableMap is mutable. Reusing
+ * dataProc.VariableMap without a copy would not be thread safe.
+ */
+ val variables = dataProc.variableMap.copy
+
val diagnostics = Nil
val mutablePState = MPState()
val tunables = dataProc.getTunables()
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
index 208de71..2797403 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
@@ -196,7 +196,7 @@ class ChoiceParser(
var markLeakCausedByException = false;
try {
- pstate.pushDiscriminator
+ pstate.pushPointOfUncertainty
var diagnostics: Seq[Diagnostic] = Nil
var i = 0
var parser: Parser = null
@@ -273,7 +273,7 @@ class ChoiceParser(
log(LogLevel.Debug, "All Choice alternatives failed.")
}
- pstate.popDiscriminator
+ pstate.popPointOfUncertainty
// This is only used for unordered sequences. If we hit a failure after
parsing a discriminator,
// we need to pass this discriminator out of the make shift ChoiceParser
and back to the
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala
index 91bc796..2d692e8 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala
@@ -279,7 +279,7 @@ abstract class SequenceParserBase(
try {
checkN(pstate, parser) // check if occursIndex exceeds tunable limit.
- if (hasPoU) pstate.pushDiscriminator
+ if (hasPoU) pstate.pushPointOfUncertainty
val priorPos = pstate.bitPos0b
resultOfTry = parser.parseOne(pstate, roStatus)
@@ -289,7 +289,7 @@ abstract class SequenceParserBase(
val currentPos = pstate.bitPos0b
val wasDiscriminatorSet = pstate.discriminator
- if (hasPoU) pstate.popDiscriminator
+ if (hasPoU) pstate.popPointOfUncertainty
//
// Now we handle the result of the parse attempt.
//
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
index 8e6d1cd..2c63c88 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
@@ -56,6 +56,7 @@ import org.apache.daffodil.processors.TermRuntimeData
import org.apache.daffodil.processors.UnparseResult
import org.apache.daffodil.processors.VariableBox
import org.apache.daffodil.processors.VariableMap
+import org.apache.daffodil.processors.VariableRuntimeData
import org.apache.daffodil.processors.charset.BitsCharset
import org.apache.daffodil.processors.charset.BitsCharsetDecoder
import org.apache.daffodil.processors.charset.BitsCharsetEncoder
@@ -343,6 +344,14 @@ abstract class UState(
def regexMatchBitPositionBuffer: LongBuffer = Assert.usageError("Not to be
used.")
def documentElement: DIDocument
+
+ def newVariableInstance(vrd: VariableRuntimeData) {
+ variableMap.newVariableInstance(vrd)
+ }
+
+ def removeVariableInstance(vrd: VariableRuntimeData) {
+ variableMap.removeVariableInstance(vrd)
+ }
}
/**
@@ -678,7 +687,12 @@ object UState {
inputter: InfosetInputter): UStateMain = {
Assert.invariant(inputter.isInitialized)
- val variables = dataProc.variableMap
+ /**
+ * This is a full deep copy as variableMap is mutable. Reusing
+ * dataProc.VariableMap without a copy would not be thread safe.
+ */
+ val variables = dataProc.variableMap.copy
+
val diagnostics = Nil
val newState = new UStateMain(inputter, variables, diagnostics,
dataProc.asInstanceOf[DataProcessor], out,
new mutable.Queue[Suspension], dataProc.getTunables()) // null means no
prior UState
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
index 969e7cf..185871a 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
@@ -29,6 +29,10 @@
defaultValue="42" />
<dfdl:defineVariable name="myVar1" type="xs:int"
defaultValue="6" />
+ <dfdl:defineVariable name="myVar2" type="xs:int"
+ defaultValue="16" />
+ <dfdl:defineVariable name="myVar3" type="xs:int"
+ defaultValue="26" />
<xs:include
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
<dfdl:format ref="ex:GeneralFormat" />
<xs:element name="c">
@@ -97,7 +101,7 @@
</xs:complexType>
</xs:element>
- <xs:element name="a">
+ <xs:element name="nvi1">
<xs:complexType>
<xs:sequence>
<xs:annotation>
@@ -106,7 +110,299 @@
defaultValue="7" />
</xs:appinfo>
</xs:annotation>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi2">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="7" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ <xs:element name="nest1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="8" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="b" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ <xs:element name="nest2">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="9" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="c" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="d" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="e" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi3">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="7" />
+ <dfdl:newVariableInstance ref="ex:myVar2"
+ defaultValue="17" />
+ <dfdl:newVariableInstance ref="ex:myVar3"
+ defaultValue="27" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
<xs:element name="b" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar2 }" />
+ <xs:element name="c" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar3 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi4">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="7">8</dfdl:newVariableInstance>
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi5">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="7" />
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="8" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi6">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="7" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi7">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="a" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <dfdl:defineVariable name="s1" type="xs:string"
defaultValue="global_default1" />
+ <dfdl:defineVariable name="s2" type="xs:string"
defaultValue="global_default2" />
+ <xs:element name="nvi8">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="ex:s1">{ "global_set1"
}</dfdl:setVariable>
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="nest1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:s1"
+ defaultValue="nest1_nvi1_default1" />
+ <dfdl:newVariableInstance ref="ex:s2"
+ defaultValue="nest1_nvi1_default2" />
+ <dfdl:setVariable ref="ex:s1">{ "nest1_nvi1_set1"
}</dfdl:setVariable>
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="nest2">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:s1"
+ defaultValue="nest2_nvi2_default1" />
+ <dfdl:newVariableInstance ref="ex:s2"
+ defaultValue="nest2_nvi2_default2" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="choice">
+ <xs:complexType>
+ <xs:choice>
+ <xs:element name="c1_s2" type="xs:string"
dfdl:lengthKind="delimited">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="ex:s1">{ "c1_s1_set1"
}</dfdl:setVariable>
+ <dfdl:assert><![CDATA[{ xs:string(.) eq
"fail" }]]></dfdl:assert>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="c2_s2" type="xs:string"
dfdl:lengthKind="delimited">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="ex:s1">{ "c2_s1_set1"
}</dfdl:setVariable>
+ <dfdl:assert><![CDATA[{ xs:string(.) eq
"fail" }]]></dfdl:assert>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="c3_s2" type="xs:string"
dfdl:lengthKind="delimited">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="ex:s1">{ "c3_s1_set1"
}</dfdl:setVariable>
+ <dfdl:assert><![CDATA[{ xs:string(.) eq
"nest2_nvi2_default2" }]]></dfdl:assert>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="d" type="xs:string"
+ dfdl:inputValueCalc="{ $ex:s1 }" />
+ <xs:element name="e" type="xs:string"
+ dfdl:inputValueCalc="{ $ex:s2 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="f" type="xs:string"
+ dfdl:inputValueCalc="{ $ex:s1 }">
+ </xs:element>
+ <xs:element name="g" type="xs:string"
+ dfdl:inputValueCalc="{ $ex:s2 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="h" type="xs:string"
+ dfdl:inputValueCalc="{ $ex:s1 }" />
+ <xs:element name="i" type="xs:string"
+ dfdl:inputValueCalc="{ $ex:s2 }" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi9">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="original" type="xsd:int"
+ dfdl:inputValueCalc="{ $ex:myVar1 }" />
+ <xs:element name="choice">
+ <xs:complexType>
+ <xs:choice>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="7" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="new" type="xsd:int"
dfdl:lengthKind="explicit" dfdl:length="1">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:assert><![CDATA[{ xs:int(.) eq $ex:myVar1
}]]></dfdl:assert>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi10">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="before">
+ <xs:complexType>
+ <xs:group ref="ex:g5">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="ex:v1_no_default">{ "January"
}</dfdl:setVariable>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:group>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="after">
+ <xs:complexType>
+ <xs:group ref="ex:g5">
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:v1_no_default" />
+ <dfdl:setVariable ref="ex:v1_no_default">{ "February"
}</dfdl:setVariable>
+ </xs:appinfo>
+ </xs:annotation>
+ </xs:group>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="nvi11">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:annotation>
+ <xs:appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:newVariableInstance ref="ex:myVar1"
+ defaultValue="10" />
+ <dfdl:assert testKind="pattern" testPattern="\d"
+ message="Assertion failed for pattern \d" />
+ </xs:appinfo>
+ </xs:annotation>
+ <xs:element name="a" type="xsd:int"
dfdl:inputValueCalc="{ $ex:myVar1 }" />
</xs:sequence>
</xs:complexType>
@@ -348,9 +644,9 @@
<tdml:errors>
<tdml:error>Schema Definition Error</tdml:error>
- <tdml:error>variable</tdml:error>
- <tdml:error>twice</tdml:error>
- <tdml:error>v_no_default</tdml:error>
+ <tdml:error>Variables</tdml:error>
+ <tdml:error>distinct</tdml:error>
+ <tdml:error>same location</tdml:error>
</tdml:errors>
</tdml:parserTestCase>
@@ -363,9 +659,9 @@
<tdml:errors>
<tdml:error>Schema Definition Error</tdml:error>
- <tdml:error>variable</tdml:error>
- <tdml:error>twice</tdml:error>
- <tdml:error>v_no_default</tdml:error>
+ <tdml:error>Variables</tdml:error>
+ <tdml:error>distinct</tdml:error>
+ <tdml:error>same location</tdml:error>
</tdml:errors>
</tdml:parserTestCase>
@@ -384,20 +680,193 @@
</tdml:parserTestCase>
- <tdml:parserTestCase name="varInstance" root="a"
- model="v">
+ <tdml:parserTestCase name="varInstance_01" root="nvi1"
+ model="v"
+ description="Simple demonstration of newVariableInstance">
+
+ <tdml:document />
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <nvi1 xmlns="http://example.com">
+ <a xsi:type="xsd:int">7</a>
+ </nvi1>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_02" root="nvi2"
+ model="v"
+ description="Demonstrates that newVariableInstance correctly goes into and
out of scope">
+
+ <tdml:document />
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <nvi2 xmlns="http://example.com">
+ <a xsi:type="xsd:int">7</a>
+ <nest1>
+ <b xsi:type="xsd:int">8</b>
+ <nest2>
+ <c xsi:type="xsd:int">9</c>
+ </nest2>
+ <d xsi:type="xsd:int">8</d>
+ </nest1>
+ <e xsi:type="xsd:int">7</e>
+ </nvi2>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_03" root="nvi3"
+ model="v"
+ description="Demonstrates using multiple newVariableInstance statements
within a sequence">
<tdml:document />
<tdml:infoset>
<tdml:dfdlInfoset>
- <a xmlns="http://example.com">
- <b xsi:type="xsd:int">7</b>
- </a>
+ <nvi3 xmlns="http://example.com">
+ <a xsi:type="xsd:int">7</a>
+ <b xsi:type="xsd:int">17</b>
+ <c xsi:type="xsd:int">27</c>
+ </nvi3>
</tdml:dfdlInfoset>
</tdml:infoset>
</tdml:parserTestCase>
+ <tdml:parserTestCase name="varInstance_04" root="nvi4"
+ model="v"
+ description="SDE due to default value being set both as attribute and
element value">
+
+ <tdml:document />
+
+ <tdml:errors>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>value of variable was supplied both as attribute and element
value</tdml:error>
+ </tdml:errors>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_05" root="nvi5"
+ model="v"
+ description="SDE due to multiple newVariableInstance calls within the same
scope to the same variable">
+
+ <tdml:document />
+
+ <tdml:errors>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>newVariableInstances must all be distinct within the same
scope</tdml:error>
+ <tdml:error>ex:myVar1</tdml:error>
+ </tdml:errors>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_06" root="nvi6"
+ model="v"
+ description="SDE due to newVariableInstance being declared on an element
instead of group reference, sequence or choice">
+
+ <tdml:document />
+
+ <tdml:errors>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>newVariableInstance may only be used on group reference,
sequence or choice</tdml:error>
+ </tdml:errors>
+
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_07" root="nvi7"
+ model="v"
+ description="Demonstrates that newVariableInstance inherits default value
from original definition">
+
+ <tdml:document />
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <nvi7 xmlns="http://example.com">
+ <a xsi:type="xsd:int">6</a>
+ </nvi7>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_08" root="nvi8"
+ model="v"
+ description="Demonstrates that newVariableInstance correctly goes into and
out of scope with backtracking">
+
+ <tdml:document><![CDATA[nest2_nvi2_default2]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <nvi8 xmlns="http://example.com">
+ <nest1>
+ <nest2>
+ <choice>
+ <c3_s2>nest2_nvi2_default2</c3_s2>
+ </choice>
+ <d>c3_s1_set1</d>
+ <e>nest2_nvi2_default2</e>
+ </nest2>
+ <f>nest1_nvi1_set1</f>
+ <g>nest1_nvi1_default2</g>
+ </nest1>
+ <h>global_set1</h>
+ <i>global_default2</i>
+ </nvi8>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_09" root="nvi9"
+ model="v"
+ description="Simple demonstration of newVariableInstance on a choice">
+
+ <tdml:document><![CDATA[7]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <nvi9 xmlns="http://example.com">
+ <original xsi:type="xsd:int">6</original>
+ <choice>
+ <new xsi:type="xsd:int">7</new>
+ </choice>
+ </nvi9>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_10" root="nvi10"
+ model="v" description="setVariable on a group reference - DFDL-7-121R">
+ <tdml:document><![CDATA[2324]]></tdml:document>
+
+ <tdml:infoset>
+ <tdml:dfdlInfoset>
+ <nvi10 xmlns="http://example.com">
+ <before>
+ <month>January</month>
+ <day>23</day>
+ </before>
+ <after>
+ <month>February</month>
+ <day>24</day>
+ </after>
+ </nvi10>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="varInstance_11" root="nvi11"
+ model="v"
+ description="Demonstrates correct statement order is observed with
newVariableInstance">
+
+ <tdml:document />
+
+ <tdml:errors>
+ <tdml:error>Parse Error</tdml:error>
+ <tdml:error>Assertion failed: Assertion failed for pattern
\d</tdml:error>
+ </tdml:errors>
+ </tdml:parserTestCase>
+
<tdml:parserTestCase name="varAsSeparator" root="e1"
model="v"
description="variable referenced as a separator; the defineVariable
annotation occurs after the element declaration that references it -
DFDL-7-107R">
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
index 4466831..6d636c6 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
@@ -77,9 +77,10 @@
<tdml:document />
<tdml:errors>
- <tdml:error>variable</tdml:error>
- <tdml:error>twice</tdml:error>
- <tdml:error>v_no_default</tdml:error>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>Variables</tdml:error>
+ <tdml:error>distinct</tdml:error>
+ <tdml:error>same location</tdml:error>
</tdml:errors>
</tdml:parserTestCase>
diff --git
a/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
b/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
index 30fabf2..4dbefa5 100644
---
a/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
+++
b/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
@@ -48,6 +48,17 @@ class TestVariables {
@Test def test_multiSetErr() { runner.runOneTest("multiSetErr") }
// DFDL-1443 & DFDL-1448
// @Test def test_setAfterReadErr() { runner.runOneTest("setAfterReadErr") }
+ @Test def test_varInstance_01() { runner.runOneTest("varInstance_01") }
+ @Test def test_varInstance_02() { runner.runOneTest("varInstance_02") }
+ @Test def test_varInstance_03() { runner.runOneTest("varInstance_03") }
+ @Test def test_varInstance_04() { runner.runOneTest("varInstance_04") }
+ @Test def test_varInstance_05() { runner.runOneTest("varInstance_05") }
+ @Test def test_varInstance_06() { runner.runOneTest("varInstance_06") }
+ @Test def test_varInstance_07() { runner.runOneTest("varInstance_07") }
+ @Test def test_varInstance_08() { runner.runOneTest("varInstance_08") }
+ @Test def test_varInstance_09() { runner.runOneTest("varInstance_09") }
+ @Test def test_varInstance_10() { runner.runOneTest("varInstance_10") }
+ @Test def test_varInstance_11() { runner.runOneTest("varInstance_11") }
@Test def test_setVarChoice() { runner.runOneTest("setVarChoice") }
@Test def test_unparse_setVarChoice() {
runner.runOneTest("unparse_setVarChoice") }
@Test def test_setVarOnSeqAndElemRef() {
runner.runOneTest("setVarOnSeqAndElemRef") }