blob: 4c3075bcb6639c9eb57e5b1712cf9bd2fa7a1ed2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.daffodil.grammar.primitives
import org.apache.daffodil.grammar._
import org.apache.daffodil.dsom._
import org.apache.daffodil.dpath._
import org.apache.daffodil.xml.XMLUtils
import org.apache.daffodil.xml.GlobalQName
import org.apache.daffodil.processors.parsers.{ Parser => DaffodilParser }
import org.apache.daffodil.processors.unparsers.{ Unparser => DaffodilUnparser }
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.processors.parsers.NewVariableInstanceStartParser
import org.apache.daffodil.processors.parsers.AssertExpressionEvaluationParser
import org.apache.daffodil.dsom.ElementBase
import org.apache.daffodil.processors.parsers.NewVariableInstanceEndParser
import org.apache.daffodil.processors.parsers.SetVariableParser
import org.apache.daffodil.processors.parsers.IVCParser
import org.apache.daffodil.processors.parsers.NadaParser
import org.apache.daffodil.processors.unparsers.SetVariableUnparser
import org.apache.daffodil.processors.unparsers.NewVariableInstanceEndUnparser
import org.apache.daffodil.processors.unparsers.NewVariableInstanceStartUnparser
import org.apache.daffodil.processors.unparsers.NadaUnparser
import org.apache.daffodil.compiler.ForParser
import org.apache.daffodil.schema.annotation.props.PropertyLookupResult
import org.apache.daffodil.schema.annotation.props.Found
import org.apache.daffodil.schema.annotation.props.gen.FailureType
import org.apache.daffodil.schema.annotation.props.gen.VariableDirection
import org.apache.daffodil.dsom.ExpressionCompilers
import org.apache.daffodil.dsom.DFDLSetVariable
import org.apache.daffodil.dsom.DFDLNewVariableInstance
import org.apache.daffodil.processors.parsers.AssertPatternParser
import org.apache.daffodil.processors.parsers.TypeValueCalcParser
import org.apache.daffodil.processors.unparsers.TypeValueCalcUnparser
import org.apache.daffodil.processors.parsers.InitiatedContentDiscrimOnIndexGreaterThanMinParser
import org.apache.daffodil.processors.parsers.InitiatedContentDiscrimChoiceParser
import org.apache.daffodil.processors.parsers.InitiatedContentDiscrimChoiceAndIndexGreaterThanMinParser
import org.apache.daffodil.processors.parsers.InitiatedContentDiscrimChoiceOnlyOnFirstIndexParser
abstract class AssertBase(
decl: AnnotatedSchemaComponent,
exprWithBraces: String,
namespacesForNamespaceResolution: scala.xml.NamespaceBinding,
scWherePropertyWasLocated: AnnotatedSchemaComponent,
msgOpt: Option[String],
discrim: Boolean, // are we a discriminator or not.
assertKindName: String,
failureType: FailureType)
extends ExpressionEvaluatorBase(scWherePropertyWasLocated) {
def this(
decl: AnnotatedSchemaComponent,
foundProp: Found,
msgOpt: Option[String],
discrim: Boolean, // are we a discriminator or not.
assertKindName: String,
failureType: FailureType) =
this(decl, foundProp.value, foundProp.location.namespaces, decl, msgOpt, discrim, assertKindName, failureType)
override val baseName = assertKindName
override lazy val exprText = exprWithBraces
override lazy val exprNamespaces = namespacesForNamespaceResolution
override lazy val exprComponent = scWherePropertyWasLocated
override def nodeKind = NodeInfo.Boolean
override val forWhat = ForParser
lazy val msgExpr = {
if (msgOpt.isDefined) {
ExpressionCompilers.String.compileExpression(
qn,
NodeInfo.String, msgOpt.get, exprNamespaces, exprComponent.dpathCompileInfo, false, this, exprComponent.dpathCompileInfo)
} else {
new ConstantExpression[String](qn, NodeInfo.String, exprWithBraces + " failed")
}
}
lazy val parser: DaffodilParser = new AssertExpressionEvaluationParser(msgExpr, discrim, decl.runtimeData, expr, failureType)
override def unparser: DaffodilUnparser = hasNoUnparser
}
abstract class AssertBooleanPrimBase(
decl: AnnotatedSchemaComponent,
stmt: DFDLAssertionBase,
discrim: Boolean, // are we a discriminator or not.
assertKindName: String) extends AssertBase(decl, Found(stmt.testTxt, stmt, "test", false), stmt.messageAttrib, discrim, assertKindName, stmt.failureType)
case class AssertBooleanPrim(
decl: AnnotatedSchemaComponent,
stmt: DFDLAssertionBase)
extends AssertBooleanPrimBase(decl, stmt, false, "assert") {
}
case class DiscriminatorBooleanPrim(
decl: AnnotatedSchemaComponent,
stmt: DFDLAssertionBase)
extends AssertBooleanPrimBase(decl, stmt, true, "discriminator")
// TODO: performance wise, initiated content is supposed to be faster
// than evaluating an expression. There should be a better way to say
// "resolve this point of uncertainty" without having to introduce
// an XPath evaluator that runs fn:true() expression.
case class InitiatedContent(
mg: ModelGroup,
t: Term)
extends Terminal(t, true) {
override val forWhat = ForParser
override def parser = {
// Each of the parsers that this could create always appear immediately
// after an initiator parser. So they can all assume that the previous
// initiator was successfully parsed and thus resolve points of uncertainty
// appropriately
t match {
case eb: ElementBase if eb.isArray => {
(mg, eb.optPoUMinOccurs) match {
case (sq: SequenceTermBase, Some(min)) => {
// This is an array of elements inside a sequence with initiated
// content. There is no PoU if occursIndex <= min. All elements
// after that have a PoU, which are resolved by this parser. Note
// that min here is the value of optPoUMinOccurs, which is slightly
// different than value of minOccurs, e.g. when occursCountKind="parsed"
// this value is zero rather than the value of minOccurs.
new InitiatedContentDiscrimOnIndexGreaterThanMinParser(min, eb.erd)
}
case (ch: ChoiceTermBase, None) => {
// This branch of a choice is a fixed number of array elements.
// Because they are fixed, there is no PoU associated with the
// elements, only the choice. This must resolve the choice PoU only
// the first time so as not to resolve any other PoUs.
new InitiatedContentDiscrimChoiceOnlyOnFirstIndexParser(eb.erd)
}
case (ch: ChoiceTermBase, Some(min)) => {
// This branch of a choice is for an array of elements with a
// minimum number of occurrences. When occursIndex <= min, there
// are no PoU's for the array elements, so this parser only
// resolves PoU's once that index has been reached. However, there
// is still a PoU for the choice, which must only be resolved once
// when we parsed the first array element. Note that min here is
// the value of optPoUMinOccurs, which is slightly different than
// the value of minOccurs, e.g. when occursCountKind="parsed" this
// value is zero rather than the value of minOccurs.
new InitiatedContentDiscrimChoiceAndIndexGreaterThanMinParser(min, eb.erd)
}
case x => Assert.invariantFailed("Guard should exclude this case: " + x)
}
}
case _ => new InitiatedContentDiscrimChoiceParser(t.termRuntimeData)
}
}
override def unparser = hasNoUnparser
}
case class SetVariable(stmt: DFDLSetVariable)
extends ExpressionEvaluatorBase(stmt.annotatedSC) {
val baseName = "SetVariable[" + stmt.varQName.local + "]"
override lazy val exprText = stmt.value
override lazy val exprNamespaces = stmt.xml.scope
override lazy val exprComponent = stmt
override lazy val nodeKind = stmt.defv.primType
lazy val parser: DaffodilParser = {
if (stmt.defv.runtimeData.direction == VariableDirection.UnparseOnly)
new NadaParser(stmt.defv.runtimeData)
else
new SetVariableParser(expr, stmt.defv.runtimeData)
}
override lazy val unparser: DaffodilUnparser = {
if (stmt.defv.runtimeData.direction == VariableDirection.ParseOnly)
new NadaUnparser(stmt.defv.runtimeData)
else
new SetVariableUnparser(expr, stmt.defv.runtimeData, stmt.nonTermRuntimeData)
}
}
abstract class NewVariableInstanceBase(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
extends Terminal(decl, true) {
}
case class NewVariableInstanceStart(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
extends NewVariableInstanceBase(decl, stmt) {
lazy val parser: DaffodilParser = {
if (stmt.defv.runtimeData.direction == VariableDirection.UnparseOnly)
new NadaParser(stmt.variableRuntimeData)
else
new NewVariableInstanceStartParser(stmt.variableRuntimeData)
}
override lazy val unparser: DaffodilUnparser = {
if (stmt.defv.runtimeData.direction == VariableDirection.ParseOnly)
new NadaUnparser(stmt.variableRuntimeData)
else
new NewVariableInstanceStartUnparser(stmt.variableRuntimeData)
}
}
case class NewVariableInstanceEnd(decl: AnnotatedSchemaComponent, stmt: DFDLNewVariableInstance)
extends NewVariableInstanceBase(decl, stmt) {
lazy val parser: DaffodilParser = {
if (stmt.defv.runtimeData.direction == VariableDirection.UnparseOnly)
new NadaParser(stmt.variableRuntimeData)
else
new NewVariableInstanceEndParser(stmt.variableRuntimeData)
}
override lazy val unparser: DaffodilUnparser = {
if (stmt.defv.runtimeData.direction == VariableDirection.ParseOnly)
new NadaUnparser(stmt.variableRuntimeData)
else
new NewVariableInstanceEndUnparser(stmt.variableRuntimeData)
}
}
/**
* Refactored primitives that use expressions to put expression evaluation in one place.
* On this base (for the primitive), and a corresponding parser base class for the
* actual evaluation.
*
* That fixed a bug where a SDE wasn't being reported until the parser was run that
* could have been reported at compilation time.
*
* Anything being computed that involves the dsom or grammar objects or attributes of them,
* should be done in the grammar primitives, and NOT in the parser.
* This is important to insure errors are captured at compilation time and
* reported on relevant objects.
*/
abstract class ExpressionEvaluatorBase(e: AnnotatedSchemaComponent) extends Terminal(e, true) {
override def toString = baseName + "(" + exprText + ")"
def baseName: String
def exprNamespaces: scala.xml.NamespaceBinding
def exprComponent: SchemaComponent
def exprText: String
def nodeKind: NodeInfo.Kind
protected def qn = GlobalQName(Some("daf"), baseName, XMLUtils.dafintURI)
lazy val expr = LV('expr) {
ExpressionCompilers.AnyRef.compileExpression(
qn,
nodeKind, exprText, exprNamespaces, exprComponent.dpathCompileInfo, false, this, exprComponent.dpathCompileInfo)
}.value
}
abstract class ValueCalcBase(
e: ElementBase,
property: PropertyLookupResult)
extends ExpressionEvaluatorBase(e) {
override lazy val exprText = exprProp.value
override lazy val exprNamespaces = exprProp.location.namespaces
override lazy val exprComponent = exprProp.location.asInstanceOf[SchemaComponent]
lazy val pt = e.primType //.typeRuntimeData
override lazy val nodeKind = pt
lazy val ptn = pt.name
lazy val exprProp = property.asInstanceOf[Found]
}
case class InputValueCalc(
e: ElementBase,
property: PropertyLookupResult)
extends ValueCalcBase(e, property) {
override def baseName = "inputValueCalc"
override lazy val parser: DaffodilParser = {
new IVCParser(expr, e.elementRuntimeData)
}
override lazy val unparser = Assert.usageError("Not to be called on InputValueCalc class.")
}
case class TypeValueCalc(e: ElementBase)
extends Terminal(e, e.hasRepType) {
private lazy val simpleTypeDefBase = e.simpleType.asInstanceOf[SimpleTypeDefBase]
private lazy val typeCalculator = simpleTypeDefBase.optTypeCalculator.get
private lazy val repTypeRuntimeData = simpleTypeDefBase.optRepTypeElement.get.elementRuntimeData
private lazy val repTypeParser = simpleTypeDefBase.optRepTypeElement.get.enclosedElement.parser
private lazy val repTypeUnparser = simpleTypeDefBase.optRepTypeElement.get.enclosedElement.unparser
override lazy val parser: DaffodilParser = {
if (!typeCalculator.supportsParse) {
SDE("Parsing not defined by typeValueCalc")
}
new TypeValueCalcParser(typeCalculator, repTypeParser, e.elementRuntimeData, repTypeRuntimeData)
}
override lazy val unparser: DaffodilUnparser = {
if (!typeCalculator.supportsUnparse) {
SDE("Unparsing not defined by typeValueCalc")
}
new TypeValueCalcUnparser(typeCalculator, repTypeUnparser, e.elementRuntimeData, repTypeRuntimeData)
}
}
abstract class AssertPatternPrimBase(decl: Term, stmt: DFDLAssertionBase, discrim: Boolean)
extends ExpressionEvaluatorBase(decl) {
override val baseName = if (discrim) "Discriminator" else "Assert"
override lazy val exprText = stmt.messageAttrib.get
override lazy val exprNamespaces = decl.namespaces
override lazy val exprComponent = decl
override def nodeKind = NodeInfo.String
lazy val testPattern = {
PatternChecker.checkPattern(stmt.testTxt, decl)
stmt.testTxt
}
lazy val msgExpr =
if (stmt.messageAttrib.isDefined) {
expr
} else {
new ConstantExpression[String](qn, NodeInfo.String, testPattern + " failed")
}
override val forWhat = ForParser
lazy val parser: DaffodilParser = new AssertPatternParser(decl.termRuntimeData, discrim, testPattern, msgExpr, stmt.failureType)
override def unparser: DaffodilUnparser = Assert.invariantFailed("should not request unparser for asserts/discriminators")
}
case class AssertPatternPrim(override val term: Term, stmt: DFDLAssert)
extends AssertPatternPrimBase(term, stmt, false)
case class DiscriminatorPatternPrim(override val term: Term, stmt: DFDLDiscriminator)
extends AssertPatternPrimBase(term, stmt, true)