blob: 74b32a53e529597795e7a7d88ca3e17834093e08 [file] [log] [blame]
/* Copyright (c) 2012-2015 Tresys Technology, LLC. All rights reserved.
*
* Developed by: Tresys Technology, LLC
* http://www.tresys.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal with
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the names of Tresys Technology, nor the names of its contributors
* may be used to endorse or promote products derived from this Software
* without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
* SOFTWARE.
*/
package edu.illinois.ncsa.daffodil.dsom
import scala.xml.Node
import edu.illinois.ncsa.daffodil.exceptions.ThrowsSDE
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.TestKind
import edu.illinois.ncsa.daffodil.processors.AssertBase
/**
* The other kind of DFDL annotations are DFDL 'statements'.
* This trait is everything shared by schema components that can have
* statements.
*
* Factory for creating the corresponding DFDLAnnotation objects.
*/
trait DFDLStatementMixin extends ThrowsSDE { self: AnnotatedSchemaComponent =>
requiredEvaluations(statements)
protected final def annotationFactoryForDFDLStatement(node: Node, self: AnnotatedSchemaComponent): Option[DFDLAnnotation] = {
node match {
case <dfdl:assert>{ content @ _* }</dfdl:assert> => Some(new DFDLAssert(node, self))
case <dfdl:discriminator>{ content @ _* }</dfdl:discriminator> => Some(new DFDLDiscriminator(node, self))
case <dfdl:setVariable>{ content @ _* }</dfdl:setVariable> => Some(new DFDLSetVariable(node, self))
case <dfdl:newVariableInstance>{ content @ _* }</dfdl:newVariableInstance> => Some(new DFDLNewVariableInstance(node, self))
//
// property element annotations aren't "statements" so we don't want them back from this
// and in fact can't construct them here without causing trouble (circular definitions)
//
case <dfdl:property>{ _* }</dfdl:property> => None
case _ => SDE("Invalid DFDL annotation found: %s", node)
}
}
/**
* Validation won't check whether these are validly in place on a DFDL schema, so
* we allow any annotated object to have them, and then we can do checking on this list
* to enforce rules about which kinds of statements are allowed and where.
*
* Implement these abstract methods to do the right thing w.r.t. combining
* statements from group refs and their referenced groups, element refs and their elements,
* element decls and their simple types, simpleTypes and their base simpleTypes.
*
* The local ingredients are here for doing the needed combining and also for checking.
* E.g., dfdl:newVariableInstance isn't allowed on simpleType, can only have one discriminator per
* annotation point, and per combined annotation point, discriminators and assertions exclude each other, etc.
*/
def statements: Seq[DFDLStatement]
def newVariableInstanceStatements: Seq[DFDLNewVariableInstance]
final lazy val notNewVariableInstanceStatements = setVariableStatements ++ discriminatorStatements ++ assertStatements
def assertStatements: Seq[DFDLAssert]
def discriminatorStatements: Seq[DFDLDiscriminator]
def setVariableStatements: Seq[DFDLSetVariable]
final lazy val localStatements = this.annotationObjs.collect { case st: DFDLStatement => st }
final lazy val localNewVariableInstanceStatements = localStatements.collect { case nve: DFDLNewVariableInstance => nve }
final lazy val localNotNewVariableInstanceStatements = localStatements.diff(localNewVariableInstanceStatements)
final lazy val (localDiscriminatorStatements,
localAssertStatements) = {
val discrims = localStatements.collect { case disc: DFDLDiscriminator => disc }
val asserts = localStatements.collect { case asrt: DFDLAssert => asrt }
checkDiscriminatorsAssertsDisjoint(discrims, asserts)
}
final def checkDiscriminatorsAssertsDisjoint(discrims: Seq[DFDLDiscriminator], asserts: Seq[DFDLAssert]): (Seq[DFDLDiscriminator], Seq[DFDLAssert]) = {
schemaDefinitionUnless(discrims.size <= 1, "At most one discriminator allowed at same location: %s", discrims)
schemaDefinitionUnless(asserts == Nil || discrims == Nil,
"Cannot have both dfdl:discriminator annotations and dfdl:assert annotations at the same location.")
(discrims, asserts)
}
final 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)
svs
}
final lazy val localSetVariableStatements = {
val svs = localStatements.collect { case sv: DFDLSetVariable => sv }
checkDistinctVariableNames(svs)
}
private def getParserExprReferencedElements(s: DFDLStatement,
f: ContentValueReferencedElementInfoMixin => Set[DPathElementCompileInfo]) = {
s match {
case a: DFDLAssertionBase if (a.testKind eq TestKind.Expression) => {
a.gram match {
case ab: AssertBase => f(ab.expr)
case _ => ReferencedElementInfos.None
}
}
case _ => getUnparserExprReferencedElements(s, f)
}
}
private def getUnparserExprReferencedElements(s: DFDLStatement,
f: ContentValueReferencedElementInfoMixin => Set[DPathElementCompileInfo]) = {
s match {
case sv: DFDLSetVariable => {
val mdv = sv.defv.maybeDefaultValueExpr
if (mdv.isDefined)
f(mdv.get)
else
ReferencedElementInfos.None
}
case nv: DFDLNewVariableInstance => {
// nv.defaultValueExpr.contentReferencedElementInfos
???
}
case _ => ReferencedElementInfos.None
}
}
private def creis(rei: ContentValueReferencedElementInfoMixin) = rei.contentReferencedElementInfos
private def vreis(rei: ContentValueReferencedElementInfoMixin) = rei.valueReferencedElementInfos
private def statementReferencedElementInfos(f: DFDLStatement => Set[DPathElementCompileInfo]) = {
val stmtSets: Seq[DPathElementCompileInfo] = {
val s = statements
val sets = s.flatMap(f)
sets
}
stmtSets.toSet
}
final protected lazy val statementContentParserReferencedElementInfos =
statementReferencedElementInfos(x => getParserExprReferencedElements(x, creis(_)))
final protected lazy val statementContentUnparserReferencedElementInfos =
statementReferencedElementInfos(x => getUnparserExprReferencedElements(x, creis(_)))
final protected lazy val statementValueParserReferencedElementInfos =
statementReferencedElementInfos(x => getParserExprReferencedElements(x, vreis(_)))
final protected lazy val statementValueUnparserReferencedElementInfos =
statementReferencedElementInfos(x => getUnparserExprReferencedElements(x, vreis(_)))
}