| /* 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(_))) |
| } |