You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by mb...@apache.org on 2017/11/17 16:12:13 UTC
[4/6] incubator-daffodil git commit: Property Resolution - part of
fixing schema compilation
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SequenceGroup.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SequenceGroup.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SequenceGroup.scala
index ab4478c..cdc68e1 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SequenceGroup.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SequenceGroup.scala
@@ -48,9 +48,14 @@ import edu.illinois.ncsa.daffodil.Implicits.ns2String
import edu.illinois.ncsa.daffodil.grammar.SequenceGrammarMixin
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.processors.SequenceRuntimeData
-
-class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
- extends ModelGroup(xmlArg, parent, position)
+import edu.illinois.ncsa.daffodil.schema.annotation.props.Found
+import edu.illinois.ncsa.daffodil.schema.annotation.props.PropertyLookupResult
+
+abstract class SequenceTermBase(
+ final override val xml: Node,
+ final override val parent: SchemaComponent,
+ final override val position: Int)
+ extends ModelGroup
with Sequence_AnnotationMixin
with SequenceRuntimeValuedPropertiesMixin
with SequenceGrammarMixin
@@ -59,56 +64,13 @@ class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
requiredEvaluations(checkIfValidUnorderedSequence)
requiredEvaluations(modelGroupRuntimeData.preSerialization)
+ protected def apparentXMLChildren: Seq[Node]
+
final override lazy val myPeers = sequencePeers
final override lazy val hasDelimiters = hasInitiator || hasTerminator || hasSeparator
- protected final def annotationFactory(node: Node): Option[DFDLAnnotation] = {
- node match {
- case <dfdl:sequence>{ contents @ _* }</dfdl:sequence> => Some(new DFDLSequence(node, this))
- case _ => annotationFactoryForDFDLStatement(node, this)
- }
- }
-
- protected final def emptyFormatFactory = new DFDLSequence(newDFDLAnnotationXML("sequence"), this)
- protected final def isMyFormatAnnotation(a: DFDLAnnotation) = a.isInstanceOf[DFDLSequence]
-
- // The dfdl:hiddenGroupRef property cannot be scoped, nor defaulted. It's really a special
- // attribute, not a format property in the usual sense.
- // So we retrieve it by this lower-level mechanism which only combines short and long form.
- //
- // FIXME: must call findPropertyOption so that it gets a context back which
- // allows resolution of a QName using the right scope.
- final lazy val hiddenGroupRefOption = getPropertyOption("hiddenGroupRef")
-
- /**
- * We're hidden if we're inside something hidden, or we're explicitly a
- * hidden group reference (sequence with hiddenGroupRef property)
- */
- final override lazy val isHidden = {
- val res = hiddenGroupRefOption match {
- case Some(_) => true
- case None => someEnclosingComponent.isHidden
- }
- res
- }
-
- private lazy val <sequence>{ apparentXMLChildren @ _* }</sequence> = xml
-
- final def xmlChildren = LV('xmlChildren) {
- hiddenGroupRefOption match {
- case Some(qname) => {
- schemaDefinitionUnless(apparentXMLChildren.length == 0, "A sequence with hiddenGroupRef cannot have children.")
- // synthesize a group reference here.
- val contextScope = xml.asInstanceOf[Elem].scope
- val hgr = {
- (<xs:group xmlns:xs={ XMLUtils.xsdURI } ref={ qname }/>).copy(scope = contextScope)
- }
- List(hgr)
- }
- case None => apparentXMLChildren
- }
- }.value
+ protected def hiddenGroupRefOption: PropertyLookupResult
final lazy val hasStaticallyRequiredInstances = {
// true if there are syntactic features
@@ -118,16 +80,16 @@ class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
}
final override def hasKnownRequiredSyntax = LV('hasKnownRequiredSyntax) {
- lazy val memberHasRequiredSyntax = groupMembersNoRefs.exists(_.hasKnownRequiredSyntax)
+ lazy val memberHasRequiredSyntax = groupMembers.exists(_.hasKnownRequiredSyntax)
lazy val prefixOrPostfixAndStaticallyRequiredInstance =
- groupMembersNoRefs.filter { _.isRepresented }.exists { _.hasStaticallyRequiredInstances } &&
+ groupMembers.filter { _.isRepresented }.exists { _.hasStaticallyRequiredInstances } &&
(hasPrefixSep || hasPostfixSep)
lazy val infixAnd2OrMoreStaticallyRequiredInstances =
- groupMembersNoRefs.filter { m => m.isRepresented && m.hasStaticallyRequiredInstances }.length > 1 && hasInfixSep
+ groupMembers.filter { m => m.isRepresented && m.hasStaticallyRequiredInstances }.length > 1 && hasInfixSep
lazy val sepAndArryaWith2OrMoreStaticallyRequiredInstances =
- groupMembersNoRefs.filter { m =>
+ groupMembers.filter { m =>
m.isRepresented && m.hasStaticallyRequiredInstances && (m match {
- case e: LocalElementBase => e.minOccurs > 1
+ case e: ElementBase => e.minOccurs > 1
case _ => false
})
}.length > 0 && hasSeparator
@@ -153,9 +115,9 @@ class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
}
private def checkMembersHaveValidOccursCountKind: Unit = {
- val validChildren: Seq[LocalElementBase] =
+ val validChildren: Seq[ElementBase] =
groupMembers.filter { m => m.isInstanceOf[LocalElementDecl] || m.isInstanceOf[ElementRef]
- }.map(_.asInstanceOf[LocalElementBase])
+ }.map(_.asInstanceOf[ElementBase])
val invalidChildren = validChildren.filter(e => {
if (e.minOccurs == 0 | !e.isScalar) {
@@ -203,7 +165,8 @@ class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
def checkHiddenSequenceIsDefaultableOrOVC: Unit = {
if (this.isHidden) {
- val nonDefaultableOrOVC = this.childrenInHiddenGroupNotDefaultableOrOVC
+ val nonDefaultableOrOVC =
+ this.childrenInHiddenGroupNotDefaultableOrOVC
if (nonDefaultableOrOVC.length > 0) {
this.SDE("Element(s) of hidden group must define dfdl:outputValueCalc:\n%s", nonDefaultableOrOVC.mkString("\n"))
}
@@ -260,7 +223,7 @@ class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
// Constructs a sequence of choice using newContent
val newXML = {
- xmlArg match {
+ xml match {
case Elem(prefix, "sequence", attrs, scope, content @ _*) => Elem(prefix, "sequence", attrs, scope, true, newContent: _*)
case other => other
}
@@ -296,6 +259,70 @@ class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
}
+/**
+ * Captures concepts associated with definitions of Sequence groups.
+ *
+ * Used by GlobalSequenceGroupDef and local Sequence, but not by SequenceGroupRef.
+ * Used on objects that can carry DFDLSequence annotation objects.
+ */
+trait SequenceDefMixin
+ extends AnnotatedSchemaComponent
+ with GroupDefLike {
+
+ protected final def isMyFormatAnnotation(a: DFDLAnnotation) = a.isInstanceOf[DFDLSequence]
+
+ protected final def annotationFactory(node: Node): Option[DFDLAnnotation] = {
+ node match {
+ case <dfdl:sequence>{ contents @ _* }</dfdl:sequence> => Some(new DFDLSequence(node, this))
+ case _ => annotationFactoryForDFDLStatement(node, this)
+ }
+ }
+
+ protected final def emptyFormatFactory = new DFDLSequence(newDFDLAnnotationXML("sequence"), this)
+
+ final lazy val <sequence>{ apparentXMLChildren @ _* }</sequence> = (xml \\ "sequence")(0)
+
+ final def xmlChildren = apparentXMLChildren
+
+ // The dfdl:hiddenGroupRef property cannot be scoped, nor defaulted. It's really a special
+ // attribute, not a format property in the usual sense.
+ // So we retrieve it by this lower-level mechanism which only combines short and long form.
+ //
+ final lazy val hiddenGroupRefOption =
+ findPropertyOptionThisComponentOnly("hiddenGroupRef")
+
+}
+
+/**
+ * Represents a local sequence definition.
+ */
+class Sequence(xmlArg: Node, parent: SchemaComponent, position: Int)
+ extends SequenceTermBase(xmlArg, parent, position)
+ with SequenceDefMixin {
+
+ requiredEvaluations(checkHiddenGroupRefHasNoChildren)
+
+ override lazy val optReferredToComponent = None
+
+ private lazy val checkHiddenGroupRefHasNoChildren = {
+ if (hiddenGroupRefOption.isDefined) {
+ val axc = apparentXMLChildren
+ val len = axc.length
+ schemaDefinitionUnless(len == 0, "A sequence with hiddenGroupRef cannot have children.")
+ }
+ }
+
+ final lazy val hiddenGroupRefXML = LV('hiddenGroupRefXML) {
+ val Found(qname, _, _, _) = hiddenGroupRefOption
+ // synthesize a group reference here.
+ val contextScope = xml.asInstanceOf[Elem].scope
+ val hgr = {
+ (<xs:group xmlns:xs={ XMLUtils.xsdURI } ref={ qname }/>).copy(scope = contextScope)
+ }
+ hgr
+ }.value
+}
+
final class UnorderedSequence(xmlArg: Node, xmlContents: Seq[Node], parent: SchemaComponent, position: Int)
extends Sequence(xmlArg, parent, position) {
// A shell, the actual XML representation is passed in
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SimpleTypes.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SimpleTypes.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SimpleTypes.scala
index 3f84b91..1e61aec 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SimpleTypes.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/SimpleTypes.scala
@@ -43,6 +43,16 @@ trait TypeBase {
def optRestriction: Option[Restriction] = None
def optUnion: Option[Union] = None
def typeNode: NodeInfo.AnyType.Kind
+
+}
+
+trait NonPrimTypeMixin {
+ def elementDecl: ElementDeclMixin
+
+ def elementBase: ElementBase = elementDecl match {
+ case eb: ElementBase => eb
+ case ged: GlobalElementDecl => ged.elementRef
+ }
}
sealed trait SimpleTypeBase extends TypeBase {
@@ -122,15 +132,14 @@ object PrimitiveType {
}
-abstract class SimpleTypeDefBase(xmlArg: Node, override val parent: SchemaComponent)
- extends AnnotatedSchemaComponent(xmlArg, parent)
+abstract class SimpleTypeDefBase(xml: Node, parent: SchemaComponent)
+ extends AnnotatedSchemaComponentImpl(xml, parent)
with SimpleTypeBase
- with DFDLStatementMixin
+ with NonPrimTypeMixin
+ with ProvidesDFDLStatementMixin
with OverlapCheckMixin {
- requiredEvaluations(if (element.isSimpleType) simpleTypeRuntimeData.preSerialization)
-
- def element: ElementBase
+ requiredEvaluations(if (elementDecl.isSimpleType) simpleTypeRuntimeData.preSerialization)
override def typeNode = primType
@@ -141,25 +150,14 @@ abstract class SimpleTypeDefBase(xmlArg: Node, override val parent: SchemaCompon
res
}
+ /**
+ * Exclusive of self.
+ */
final lazy val bases: Seq[SimpleTypeDefBase] =
if (restrictions.isEmpty) Nil
else restrictions.tail.map { _.simpleType }
- private lazy val sTypeNonDefault: Seq[ChainPropProvider] = bases.reverse.map { _.nonDefaultFormatChain }
- private lazy val sTypeDefault: Seq[ChainPropProvider] = bases.reverse.map { _.defaultFormatChain }
-
- // want a QueueSet i.e., fifo order if iterated, but duplicates
- // kept out of the set. Will simulate by calling distinct.
- def nonDefaultPropertySources = LV('nonDefaultPropertySources) {
- val seq = (this.nonDefaultFormatChain +: sTypeNonDefault).distinct
- checkNonOverlap(seq)
- seq
- }.value
-
- def defaultPropertySources = LV('defaultPropertySources) {
- val seq = (this.defaultFormatChain +: sTypeDefault).distinct
- seq
- }.value
+ override final def optReferredToComponent = optRestriction.flatMap { _.optBaseDef }
protected final def emptyFormatFactory = new DFDLSimpleType(newDFDLAnnotationXML("simpleType"), this)
@@ -168,7 +166,7 @@ abstract class SimpleTypeDefBase(xmlArg: Node, override val parent: SchemaCompon
protected final def annotationFactory(node: Node): Option[DFDLAnnotation] = {
node match {
case <dfdl:simpleType>{ contents @ _* }</dfdl:simpleType> => Some(new DFDLSimpleType(node, this))
- case _ => annotationFactoryForDFDLStatement(node, this)
+ case _ => annotationFactoryForDFDLStatement(node, elementBase)
}
}
@@ -183,33 +181,6 @@ abstract class SimpleTypeDefBase(xmlArg: Node, override val parent: SchemaCompon
}
}
- /**
- * Combine our statements with those of our base def (if there is one)
- *
- * The order is important here. I.e., we FIRST put in each list those from our base. Then our own local ones.
- */
- final lazy val statements: Seq[DFDLStatement] =
- bases.flatMap { _.statements } ++ localStatements
-
- private lazy val optBaseDef = optRestriction.flatMap { _.optBaseDef }
-
- // TODO: refactor into shared code for combining all the annotations in the resolved set of annotations
- // for a particular annotation point, checking that there is only one format annotation, that
- // asserts and discriminators are properly excluding each-other, etc.
- // Code should be sharable for many kinds of annotation points, perhaps specialized for groups, complex type
- // elements, and simple type elements.
- //
- // See JIRA issue DFDL-481
- final lazy val newVariableInstanceStatements: Seq[DFDLNewVariableInstance] =
- optBaseDef.map { _.newVariableInstanceStatements }.getOrElse(Seq.empty) ++ localNewVariableInstanceStatements
- final lazy val (discriminatorStatements, assertStatements) = checkDiscriminatorsAssertsDisjoint(combinedDiscrims, combinedAsserts)
- private lazy val combinedAsserts: Seq[DFDLAssert] = optBaseDef.map { _.assertStatements }.getOrElse(Nil) ++ localAssertStatements
- private lazy val combinedDiscrims: Seq[DFDLDiscriminator] = optBaseDef.map { _.discriminatorStatements }.getOrElse(Nil) ++ localDiscriminatorStatements
-
- final lazy val setVariableStatements: Seq[DFDLSetVariable] = {
- val combinedSvs = optBaseDef.map { _.setVariableStatements }.getOrElse(Nil) ++ localSetVariableStatements
- checkDistinctVariableNames(combinedSvs)
- }
def toOpt[R <: AnyRef](b: Boolean, v: => R) = Misc.boolToOpt(b, v)
lazy val simpleTypeRuntimeData: SimpleTypeRuntimeData = {
@@ -219,7 +190,7 @@ abstract class SimpleTypeDefBase(xmlArg: Node, override val parent: SchemaCompon
diagnosticDebugName,
path,
namespaces,
- element.simpleType.primType,
+ primType,
noFacetChecks,
//
// TODO: Cleanup code: really these should all have been option types
@@ -250,29 +221,13 @@ abstract class SimpleTypeDefBase(xmlArg: Node, override val parent: SchemaCompon
}.getOrElse(true)
}
-sealed abstract class SimpleTypeDefFactory(xml: Node, schemaDocumentArg: SchemaDocument)
- extends SchemaComponentFactory(xml, schemaDocumentArg) {
-
-}
-
-final class LocalSimpleTypeDefFactory(xmlArg: Node, schemaDocumentArg: SchemaDocument)
- extends SimpleTypeDefFactory(xmlArg, schemaDocumentArg)
- with LocalNonElementComponentMixin {
-
- def forElement(element: ElementBase) =
- new LocalSimpleTypeDef(this, element)
-
-}
-
final class LocalSimpleTypeDef(
- val factory: LocalSimpleTypeDefFactory,
- elementArg: ElementBase)
- extends SimpleTypeDefBase(factory.xml, elementArg)
+ xmlArg: Node, elementArg: ElementDeclMixin)
+ extends SimpleTypeDefBase(xmlArg, elementArg)
with LocalNonElementComponentMixin
with NestingLexicalMixin {
- override def term = element
- override lazy val element = elementArg
+ override lazy val elementDecl = elementArg
/**
* For anonymous simple type def, uses the base name, or primitive type name
@@ -303,13 +258,13 @@ final class LocalSimpleTypeDef(
*/
final class GlobalSimpleTypeDefFactory(xmlArg: Node, schemaDocumentArg: SchemaDocument)
- extends SimpleTypeDefFactory(xmlArg, schemaDocumentArg)
+ extends SchemaComponentFactory(xmlArg, schemaDocumentArg)
with GlobalNonElementComponentMixin {
/**
* Create a private instance for this element's use.
*/
- def forElement(element: ElementBase) = new GlobalSimpleTypeDef(None, this, Some(element))
+ def forElement(elementDecl: ElementDeclMixin) = new GlobalSimpleTypeDef(None, this, Some(elementDecl))
def forDerivedType(derivedType: SimpleTypeDefBase) = new GlobalSimpleTypeDef(Some(derivedType), this, None)
}
@@ -320,12 +275,12 @@ final class GlobalSimpleTypeDefFactory(xmlArg: Node, schemaDocumentArg: SchemaDo
final class GlobalSimpleTypeDef(
derivedType: Option[SimpleTypeDefBase],
val factory: GlobalSimpleTypeDefFactory,
- val referringElement: Option[ElementBase])
+ val referringElement: Option[ElementDeclMixin])
extends SimpleTypeDefBase(factory.xml, factory.schemaDocument)
with GlobalNonElementComponentMixin
with NestingTraversesToReferenceMixin {
- override def term = element
+ // override def term = element
override lazy val referringComponent: Option[SchemaComponent] =
(derivedType, referringElement) match {
@@ -334,9 +289,9 @@ final class GlobalSimpleTypeDef(
case _ => Assert.impossible("SimpleType must either have a derivedType or an element. Not both.")
}
- override lazy val element: ElementBase = referringComponent match {
- case Some(dt: SimpleTypeDefBase) => dt.element
- case Some(e: ElementBase) => e
+ override lazy val elementDecl: ElementDeclMixin = referringComponent match {
+ case Some(dt: SimpleTypeDefBase) => dt.elementDecl
+ case Some(e: ElementDeclMixin) => e
case _ => Assert.invariantFailed("unexpected referringComponent")
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/Term.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/Term.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/Term.scala
index 1b7fae3..fad0853 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/Term.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/Term.scala
@@ -33,8 +33,6 @@
package edu.illinois.ncsa.daffodil.dsom
import java.util.UUID
-import scala.xml.Node
-import scala.xml._
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.processors.TermRuntimeData
import edu.illinois.ncsa.daffodil.grammar.TermGrammarMixin
@@ -43,19 +41,28 @@ import java.lang.{ Integer => JInt }
import edu.illinois.ncsa.daffodil.schema.annotation.props.Found
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.NilKind
-/////////////////////////////////////////////////////////////////
-// Groups System
-/////////////////////////////////////////////////////////////////
-
-// A term is content of a group
-abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
- extends AnnotatedSchemaComponent(xmlArg, parentArg)
+/**
+ * Term, and what is and isn't a Term, is a key concept in DSOM.
+ *
+ * From elements, ElementRef and LocalElementDecl are Term. A GlobalElementDecl is *not* a Term.
+ * From sequences, Sequence and SequenceGroupRef are Term. GlobalSequenceGroupDef is *not* a Term.
+ * From choices, Choice and ChoiceGroupRef are Term. GlobalChoiceGroupDef is *not* a Term.
+ *
+ * Terms are the things we actually generate parsers/unparsers for. Non-Terms just
+ * contribute information used by Terms.
+ */
+trait Term
+ extends AnnotatedSchemaComponent
+ with ResolvesProperties
+ with ResolvesDFDLStatementMixin
with TermRuntimeValuedPropertiesMixin
with TermGrammarMixin
with DelimitedRuntimeValuedPropertiesMixin
with InitiatedTerminatedMixin
with TermEncodingMixin {
+ def position: Int
+
def optIgnoreCase: Option[YesNo] = {
val ic = cachePropertyOption("ignoreCase")
ic match {
@@ -64,8 +71,6 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
}
}
- override final def term = this
-
/**
* A scalar means has no dimension. Exactly one occurrence.
*
@@ -115,15 +120,6 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
def elementChildren: Seq[ElementBase]
- override lazy val dpathCompileInfo =
- new DPathCompileInfo(
- enclosingComponent.map { _.dpathCompileInfo },
- variableMap,
- namespaces,
- path,
- schemaFileLocation,
- tunable)
-
/**
* An integer which is the alignment of this term. This takes into account the
* representation, type, charset encoding and alignment-related properties.
@@ -153,6 +149,9 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
* base class. This is to make it clear
* we're not talking about the XML structures inside the XML parent (which might
* include annotations, etc.
+ *
+ * For elements this is Nil for simple types, a single model group for
+ * complex types. For model groups there can be more children.
*/
def termChildren: Seq[Term]
@@ -166,9 +165,8 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
lazy val someEnclosingComponent = enclosingComponent.getOrElse(Assert.invariantFailed("All terms except a root element have an enclosing component."))
- lazy val referredToComponent = this // override in ElementRef and GroupRef
-
- lazy val isRepresented = true // overridden by elements, which might have inputValueCalc turning this off
+ /** Overridden as false for elements with dfdl:inputValueCalc property. */
+ lazy val isRepresented = true
/**
* nearestEnclosingSequence
@@ -182,36 +180,36 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
* This is why we have to have the GlobalXYZDefFactory stuff. Because this kind of back
* pointer (contextual sensitivity) prevents sharing.
*/
- final lazy val nearestEnclosingSequence: Option[Sequence] = enclosingTerm match {
+ final lazy val nearestEnclosingSequence: Option[SequenceTermBase] = enclosingTerm match {
case None => None
- case Some(s: Sequence) => Some(s)
+ case Some(s: SequenceTermBase) => Some(s)
case Some(_) => enclosingTerm.get.nearestEnclosingSequence
}
- final lazy val nearestEnclosingChoiceBeforeSequence: Option[Choice] = enclosingTerm match {
+ final lazy val nearestEnclosingChoiceBeforeSequence: Option[ChoiceTermBase] = enclosingTerm match {
case None => None
- case Some(s: Sequence) => None
- case Some(c: Choice) => Some(c)
+ case Some(s: SequenceTermBase) => None
+ case Some(c: ChoiceTermBase) => Some(c)
case Some(_) => enclosingTerm.get.nearestEnclosingChoiceBeforeSequence
}
- final lazy val nearestEnclosingUnorderedSequence: Option[Sequence] = enclosingTerm match {
+ final lazy val nearestEnclosingUnorderedSequence: Option[SequenceTermBase] = enclosingTerm match {
case None => None
- case Some(s: Sequence) if !s.isOrdered => Some(s)
+ case Some(s: SequenceTermBase) if !s.isOrdered => Some(s)
case Some(_) => enclosingTerm.get.nearestEnclosingUnorderedSequence
}
- final lazy val nearestEnclosingUnorderedSequenceBeforeSequence: Option[Sequence] = enclosingTerm match {
+ final lazy val nearestEnclosingUnorderedSequenceBeforeSequence: Option[SequenceTermBase] = enclosingTerm match {
case None => None
- case Some(s: Sequence) if !s.isOrdered => Some(s)
- case Some(s: Sequence) => None
+ case Some(s: SequenceTermBase) if !s.isOrdered => Some(s)
+ case Some(s: SequenceTermBase) => None
case Some(_) => enclosingTerm.get.nearestEnclosingUnorderedSequence
}
final lazy val inChoiceBeforeNearestEnclosingSequence: Boolean = enclosingTerm match {
case None => false
- case Some(s: Sequence) => false
- case Some(c: Choice) => true
+ case Some(s: SequenceTermBase) => false
+ case Some(c: ChoiceTermBase) => true
case Some(_) => enclosingTerm.get.inChoiceBeforeNearestEnclosingSequence
}
@@ -221,47 +219,6 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
case Some(_) => enclosingTerm.get.nearestEnclosingElement
}
- final lazy val nearestEnclosingElementNotRef: Option[ElementBase] = nearestEnclosingElement match {
- case None => None
- case Some(er: ElementRef) => er.nearestEnclosingElement // can't be an element ref again
- case x => x
- }
-
- protected final def thisTermNoRefs: Term = LV('thisTermNoRefs) {
- val es = nearestEnclosingSequence
-
- val thisTerm = this match {
- case eRef: ElementRef => eRef.referencedElement
- // case gd: GlobalGroupDef => gd.thisTermNoRefs // TODO: scala 2.10 compiler says this line is impossible.
- case gb: GroupBase if gb.enclosingTerm.isDefined => {
- // We're a group. We need to determine what we're enclosed by.
- gb.enclosingTerm.get match {
- case encGRef: GroupRef => {
- // We're enclosed by a GroupRef. We need to retrieve
- // what encloses that GroupRef
-
- val res = encGRef.enclosingTerm match {
- case None => encGRef.group
- case Some(encTerm) => encTerm.thisTermNoRefs
- }
- //encGRef.thisTerm
- res
- }
- case encGB: GroupBase if es.isDefined && encGB == es.get => {
- // We're an immediate child of the nearestEnclosingSequence
- // therefore we just return our self as the Term
- this
- }
- case e: LocalElementBase => e // Immediate enclosed by LocalElementBase, return it.
- case _ => gb.group
- }
- }
- case gb: GroupBase => gb.group
- case x => x
- }
- thisTerm
- }.value
-
/**
* We want to determine if we're in an unordered sequence
* at any point along our parents.
@@ -281,22 +238,16 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
final lazy val immediatelyEnclosingModelGroup: Option[ModelGroup] = {
val res = parent match {
- case c: Choice => Some(c)
- case s: Sequence => Some(s)
+ case c: ChoiceTermBase => Some(c)
+ case s: SequenceTermBase => Some(s)
case d: SchemaDocument => {
- // we're a global object. Our parent is a schema document
- // so follow backpointers to whatever is referencing us.
- this match {
- case ge: GlobalElementDecl => ge.elementRef match {
- case None => {
- // we are root. So there is no enclosing model group at all
- None
- }
- case Some(er) => er.immediatelyEnclosingModelGroup
- }
- }
+ // we must be the Root elementRef
+ Assert.invariant(this.isInstanceOf[Root])
+ None
}
- case gdd: GlobalGroupDef => gdd.groupRef.immediatelyEnclosingModelGroup
+ case gr: GroupRef => gr.asModelGroup.immediatelyEnclosingModelGroup
+ case gdd: GlobalGroupDef => gdd.groupRef.asModelGroup.immediatelyEnclosingModelGroup
+ case ged: GlobalElementDecl => ged.elementRef.immediatelyEnclosingModelGroup
case ct: ComplexTypeBase => {
None
// The above formerly was ct.element.immediatelyEnclosingModelGroup,
@@ -311,16 +262,19 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
final lazy val positionInNearestEnclosingSequence: Int = {
val res =
if (enclosingComponent == nearestEnclosingSequence) position
+ else if (this.isInstanceOf[Root]) 1
else {
enclosingComponent match {
case Some(term: Term) => term.positionInNearestEnclosingSequence
case Some(ct: ComplexTypeBase) => {
- val ctElem = ct.element
- val ctPos = ctElem.positionInNearestEnclosingSequence
+ val ctPos = ct.elementDecl match {
+ case eb: ElementBase => eb.positionInNearestEnclosingSequence
+ case ged: GlobalElementDecl => ged.elementRef.positionInNearestEnclosingSequence
+ }
ctPos
}
- case Some(ggd: GlobalGroupDef) => ggd.groupRef.positionInNearestEnclosingSequence
- case _ => Assert.invariantFailed("unable to compute position in nearest enclosing sequence")
+ case Some(ggd: GlobalGroupDef) => ggd.groupRef.asModelGroup.positionInNearestEnclosingSequence
+ case x => Assert.invariantFailed("For " + this + " unable to compute position in nearest enclosing sequence. The enclosingComponent was " + x)
}
}
res
@@ -332,7 +286,7 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
final lazy val allSiblings: Seq[Term] = {
val res = nearestEnclosingSequence.map { enc =>
- val allSiblings = enc.groupMembers.map { _.referredToComponent }
+ val allSiblings = enc.groupMembers
allSiblings
}
res.getOrElse(Nil)
@@ -420,17 +374,19 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
def isKnownRequiredElement = false
def hasKnownRequiredSyntax = false
- // Returns a tuple, where the first item in the tuple is the list of sibling
- // terms that could appear before this. The second item in the tuple is a
- // One(parent) if all siblings are optional or this element has no prior siblings
+ /**
+ * Returns a tuple, where the first item in the tuple is the list of sibling
+ * terms that could appear before this. The second item in the tuple is a
+ * One(parent) if all siblings are optional or this element has no prior siblings
+ */
lazy val potentialPriorTerms: (Seq[Term], Option[Term]) = {
val (potentialPrior, parent) = enclosingTerm match {
case None => (Seq(), None)
case Some(eb: ElementBase) => (Seq(), Some(eb))
- case Some(ch: Choice) => (Seq(), Some(ch))
- case Some(sq: Sequence) if !sq.isOrdered => (sq.groupMembersNoRefs, Some(sq))
- case Some(sq: Sequence) if sq.isOrdered => {
- val previousTerms = sq.groupMembersNoRefs.takeWhile { _ != this }
+ case Some(ch: ChoiceTermBase) => (Seq(), Some(ch))
+ case Some(sq: SequenceTermBase) if !sq.isOrdered => (sq.groupMembers, Some(sq))
+ case Some(sq: SequenceTermBase) if sq.isOrdered => {
+ val previousTerms = sq.groupMembers.takeWhile { _ != this }
if (previousTerms.isEmpty) {
// first child of seq, the seq is the only previous term
(Seq(), Some(sq))
@@ -473,14 +429,18 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
*/
lazy val childrenInHiddenGroupNotDefaultableOrOVC: Seq[ElementBase] = {
// this should only be called on hidden elements
- Assert.invariant(this.isHidden)
+ val isH = isHidden
+ Assert.invariant(isH)
val res = this match {
- case s: Sequence => {
- s.groupMembersNoRefs.flatMap { _.childrenInHiddenGroupNotDefaultableOrOVC }
+ case s: SequenceTermBase => {
+ s.groupMembers.flatMap { member =>
+ val res = member.childrenInHiddenGroupNotDefaultableOrOVC
+ res
+ }
}
- case c: Choice => {
- val branches = c.groupMembersNoRefs.map { _.childrenInHiddenGroupNotDefaultableOrOVC }
+ case c: ChoiceTermBase => {
+ val branches = c.groupMembers.map { _.childrenInHiddenGroupNotDefaultableOrOVC }
val countFullyDefaultableOrOVCBranches = branches.count { _.length == 0 }
if (countFullyDefaultableOrOVCBranches == 0) {
c.SDE("xs:choice inside a hidden group must contain a branch with all children having the dfdl:outputValueCalc property set.")
@@ -525,11 +485,226 @@ abstract class Term(xmlArg: Node, parentArg: SchemaComponent, val position: Int)
case mg: ModelGroup => {
val modelGroupCouldHaveSuspensions =
commonCouldHaveSuspensions ||
- mg.groupMembersNoRefs.exists { _.couldHaveSuspensions }
+ mg.groupMembers.exists { _.couldHaveSuspensions }
modelGroupCouldHaveSuspensions
}
}
}
+ final lazy val possibleNextTerms: Seq[Term] = LV('possibleNextTerms) {
+ val es = this.nearestEnclosingSequence
+ val eus = this.nearestEnclosingUnorderedSequenceBeforeSequence
+ val ec = this.nearestEnclosingChoiceBeforeSequence
+
+ val enclosingUnorderedGroup = {
+ (ec, eus) match {
+ case (None, None) => None
+ case (Some(choice), _) => Some(choice)
+ case (None, Some(uoSeq)) => Some(uoSeq)
+ }
+ }
+ val listOfNextTerm = (enclosingUnorderedGroup, es) match {
+ case (None, None) => Seq.empty
+ case (Some(unorderedGroup), _) => {
+ // We're in a choice or unordered sequence
+ //
+ // List must be all of our peers since (as well as our self)
+ // we could be followed by any of them plus
+ // whatever follows the unordered group.
+ val peersCouldBeNext = unorderedGroup.groupMembers
+
+ val termsUntilFirstRequiredTerm = peersCouldBeNext ++ unorderedGroup.possibleNextTerms
+ termsUntilFirstRequiredTerm
+ }
+ case (None, Some(oSeq)) => {
+ // We're in an ordered sequence
+
+ val termsUntilFirstRequiredTerm =
+ isDeclaredLastInSequence match {
+ case true => oSeq.possibleNextTerms
+ case false => {
+
+ val members = oSeq.groupMembers
+
+ val selfAndAfter = members.dropWhile(m => m ne this)
+ val after = selfAndAfter.drop(1)
+ val nextMember = after.headOption
+
+ val nextMembers =
+ nextMember match {
+ case Some(e: ElementBase) if e.isOptional => Seq(e) ++ e.possibleNextTerms
+ case Some(e: ElementBase) => Seq(e)
+ case Some(mg: ModelGroup) => Seq(mg)
+ case None => Nil // Assert.impossibleCase
+ }
+ nextMembers
+ }
+ }
+ termsUntilFirstRequiredTerm
+ }
+ }
+ listOfNextTerm
+ }.value
+
+ final def isDeclaredLastInSequence = LV('isDeclaredLastInSequence) {
+ val es = nearestEnclosingSequence
+ // how do we determine what child node we are? We search.
+ // TODO: better structure for O(1) answer to this.
+ es match {
+ case None => Assert.invariantFailed("We are not in a sequence therefore isDeclaredLastInSequence is an invalid question.")
+ case Some(s) => {
+ val members = s.groupMembers
+ if (members.last eq this) true // we want object identity comparison here, not equality.
+ else false
+ }
+ }
+ }.value
+
+ protected def possibleFirstChildTerms: Seq[Term]
+
+ /*
+ * Returns list of Elements that could be the first child in the infoset of this model group or element.
+ */
+ final def possibleFirstChildElementsInInfoset: Seq[ElementBase] = LV('possibleFirstChildElementsInInfoset) {
+ val pfct = possibleFirstChildTerms
+ val firstChildren = pfct.flatMap {
+ case e: ElementBase if e.isHidden => Nil
+ case e: ElementBase => Seq(e)
+ case s: SequenceTermBase if s.isHidden => Nil
+ case mg: ModelGroup => mg.possibleFirstChildElementsInInfoset
+ }
+ firstChildren.distinct
+ }.value
+
+ /*
+ * Returns a list of Elements that could follow this Term, including
+ * siblings, children of siblings, and siblings of the parent and their children.
+ *
+ * What stops this is when the end of an enclosing element has to be next.
+ */
+ final def possibleNextChildElementsInInfoset: Seq[ElementBase] = LV('possibleNextChildElementsInInfoset) {
+ val arrayNext = if (isArray) Seq(this.asInstanceOf[ElementBase]) else Nil
+
+ val nextSiblingElements = {
+ val poss = possibleNextSiblingTerms
+ val res = poss.flatMap {
+ possible =>
+ possible match {
+ case e: ElementBase => Seq(e)
+ case mg: ModelGroup => mg.possibleFirstChildElementsInInfoset
+ }
+ }
+ res
+ }
+
+ val nextParentElts = nextParentElements
+ val res = arrayNext ++ nextSiblingElements ++ nextParentElts
+ res
+ }.value
+
+ def nextParentElements: Seq[ElementBase]
+
+ protected def couldBeLastElementInModelGroup: Boolean
+
+ /*
+ * Returns a list of sibling Terms that could follow this term. This will not
+ * return any children of sibling Terms, or any siblings of the parent.
+ */
+ final def possibleNextSiblingTerms: Seq[Term] = LV('possibleNextSiblingTerms) {
+ val et = enclosingTerm
+ val listOfNextTerm = et match {
+ case None => Nil // root element, has no siblings
+ case Some(e: ElementBase) => Nil // complex element, cannot have another model group other than this one
+ case Some(c: ChoiceTermBase) => Nil // in choice, no other siblings could come after this one
+ case Some(s: SequenceTermBase) if !s.isOrdered => s.groupMembers // unorderd sequence, all siblings (and myself) could be next
+ case Some(s: SequenceTermBase) => {
+ // in a sequence, the next term could be any later sibling that is not
+ // or does not have a required element, up to and including the first
+ // term that is/has a required element
+ // def isOutputValueCalc(term: Term) =
+ // term match { case eb: ElementBase if eb.isOutputValueCalc => true; case _ => false }
+ val selfAndAllNextSiblings = s.groupMembers.dropWhile(_ != this)
+ val allNextSiblings = if (selfAndAllNextSiblings.length > 0) selfAndAllNextSiblings.tail else Nil
+ val nextSiblings = allNextSiblings // .dropWhile(isOutputValueCalc(_))
+ val (optional, firstRequiredAndLater) = nextSiblings.span {
+ case e: ElementBase => e.canBeAbsentFromUnparseInfoset
+ case mg: ModelGroup => !mg.mustHaveRequiredElement
+ }
+ optional ++ firstRequiredAndLater.take(1)
+ }
+ }
+ listOfNextTerm
+ }.value
+
+ /**
+ * Set of elements referenced from an expression in the scope of this term.
+ *
+ * Specific to certain function call contexts e.g., only elements referenced
+ * by dfdl:valueLength or dfdl:contentLength.
+ *
+ * Separated by parser/unparser since parsers have to derive from
+ * dfdl:inputValueCalc, and must include discriminators and assert test
+ * expressions. Unparsers must derive from dfdl:outputValueCalc and exclude
+ * discriminators and asserts. Both must include setVariable/newVariableInstance,
+ * and property expressions are nearly the same. There are some unparser-specfic
+ * properties that take runtime-valued expressions - dfdl:outputNewLine is
+ * one example.
+ */
+ final lazy val contentLengthParserReferencedElementInfos: Set[DPathElementCompileInfo] = {
+ val propRefs = propertyContentReferencedElementInfos
+ val stmtRefs = statementContentParserReferencedElementInfos
+ val calcRefs = calcContentParserReferencedElementInfos
+ val locRefs = propRefs ++ stmtRefs ++ calcRefs
+ val res = realChildren.foldLeft(locRefs) { (s, i) => s.union(i.contentLengthParserReferencedElementInfos) }
+ res
+ }
+
+ /**
+ * Any element referenced from an expression in the scope of this term
+ * is in this set.
+ */
+ final lazy val contentLengthUnparserReferencedElementInfos: Set[DPathElementCompileInfo] = {
+ val propRefs = propertyContentReferencedElementInfos
+ val stmtRefs = statementContentUnparserReferencedElementInfos
+ val calcRefs = calcContentUnparserReferencedElementInfos
+ val locRefs = propRefs ++ stmtRefs ++ calcRefs
+ val res = realChildren.foldLeft(locRefs) { (s, i) => s.union(i.contentLengthUnparserReferencedElementInfos) }
+ res
+ }
+
+ /**
+ * Any element referenced from an expression in the scope of this term
+ * is in this set.
+ */
+ final lazy val valueLengthParserReferencedElementInfos: Set[DPathElementCompileInfo] = {
+ val propRefs = propertyValueReferencedElementInfos
+ val stmtRefs = statementValueParserReferencedElementInfos
+ val calcRefs = calcValueParserReferencedElementInfos
+ val locRefs = propRefs ++ stmtRefs ++ calcRefs
+ val res = realChildren.foldLeft(locRefs) { (s, i) => s.union(i.valueLengthParserReferencedElementInfos) }
+ res
+ }
+
+ /**
+ * Any element referenced from an expression in the scope of this term
+ * is in this set.
+ */
+ final lazy val valueLengthUnparserReferencedElementInfos: Set[DPathElementCompileInfo] = {
+ val propRefs = propertyValueReferencedElementInfos
+ val stmtRefs = statementValueUnparserReferencedElementInfos
+ val calcRefs = calcValueUnparserReferencedElementInfos
+ val locRefs = propRefs ++ stmtRefs ++ calcRefs
+ val res = realChildren.foldLeft(locRefs) { (s, i) => s.union(i.valueLengthUnparserReferencedElementInfos) }
+ res
+ }
+
+ private lazy val realChildren: Seq[Term] = {
+ this match {
+ case mg: ModelGroup => mg.groupMembers.asInstanceOf[Seq[Term]]
+ case eb: ElementBase if (eb.isComplexType) => Seq(eb.complexType.group)
+ case eb: ElementBase => Seq()
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/TermEncodingMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/TermEncodingMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/TermEncodingMixin.scala
index bdb55a7..4dd1f96 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/TermEncodingMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/TermEncodingMixin.scala
@@ -38,6 +38,11 @@ import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.EncodingErrorPolic
import edu.illinois.ncsa.daffodil.processors.KnownEncodingMixin
import edu.illinois.ncsa.daffodil.io.NonByteSizeCharset
+/**
+ * Captures concepts around dfdl:encoding property and Terms.
+ *
+ * Just factored out into a trait for isolation of related code.
+ */
trait TermEncodingMixin extends KnownEncodingMixin { self: Term =>
requiredEvaluations(encodingInfo.preSerialization)
@@ -114,9 +119,6 @@ trait TermEncodingMixin extends KnownEncodingMixin { self: Term =>
mg.hasNoSkipRegions &&
hasTextAlignment
}
- case gr: GroupRef => {
- gr.group.isLocallyTextOnly
- }
}
res
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/package.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/package.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/package.scala
new file mode 100644
index 0000000..4391be1
--- /dev/null
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/dsom/package.scala
@@ -0,0 +1,101 @@
+package edu.illinois.ncsa.daffodil
+
+/**
+ * DSOM - DFDL Schema Object Model
+ *
+ * ==Overview==
+ * DSOM is the abstract syntax "tree" of a DFDL schema. It is not actually a tree, it is
+ * a graph, as there are back-pointers, and shared objects.
+ *
+ * A schema is made up of [[SchemaComponent]] objects. A [[SchemaSet]] is a collection of
+ * [[Schema]]. A schema is a collection of [[SchemaDocument]] that have a common
+ * namespace. The [[SchemaSet]] is the
+ * ultimate root of all the objects in a compilation unit. The [[Term]] class is the
+ * base for everything that can have a representation in the data stream. .
+ *
+ * Many [[SchemaComponent]] carry DFDL annotations; hence, [[AnnotatedSchemaComponent]]
+ * is a key base trait.
+ *
+ * ==UML Class Diagram==
+ *
+ * See the
+ * [[https://cwiki.apache.org/confluence/display/DAFFODIL/DFDL+Schema+Object+Model+%28DSOM%29+with+UML
+ * Daffodil Wiki]]
+ * for class diagrams.
+ *
+ * ==Terminology==
+ *
+ * Parsing - in this description we are talking about the Daffodil Schema Compiler.
+ * So when we refer to "parsing" the XML, we are referring
+ * to the recursive descent walk of the DFDL schema, with that schema represented as Scala's
+ * `scala.xml.Node` objects. Strictly speaking, the string text in files of the DFDL
+ * schema's XML is already parsed into Scala's `scala.xml.Node` objects, but
+ * it is the walk through that structure constructing the DSOM tree/graph that we
+ * refer to as "parsing" the DFDL schema.
+ *
+ * ==Principles of Operation==
+ *
+ * === Constructing the DSOM Graph===
+ *
+ * The DSOM object graph must be constructed by looking at only the XML without
+ * examining any DFDL annotations. The DSOM structure is required in order to
+ * implement DFDL's scoping rules for finding annotations including both
+ * properties (like dfdl:byteOrder) and statements (like dfdl:assert); hence,
+ * one must have the DSOM graph before one can begin
+ * accessing DFDL annotations or you end up in cycles/stack-overflows.
+ *
+ * This requires a careful consideration of class/trait members and methods that
+ * are used when constructing the DSOM graph, and those used after the DSOM
+ * graph has been created, in order to compile it into the runtime data structures.
+ *
+ * There are a few exceptions to the above. The dfdl:hiddenGroupRef attribute is one such. It
+ * must be local to the [[Sequence]] object, and has implications for the parsing
+ * of the XML as it implies there should be no children of that xs:sequence.
+ * Since it is not scoped, the DSOM graph is not needed in order to access it.
+ * Only the local [[Sequence]] object and it's [[DFDLSequence]] annotation object.
+ * The [[AnnotatedSchemaComponent]] trait provides methods for this local-only
+ * property lookup.
+ *
+ * The DSOM object graph is also needed in order to issue good diagnostic
+ * messages from the compiler; hence, Daffodil validates the DFDL schema before
+ * parsing it into the DSOM graph. Careful consideration must be given if a
+ * SchemaDefinitionError (SDE) is issued while constructing the DSOM graph.
+ *
+ * If you run into stack-overflows while the DSOM graph is being constructed, the
+ * above is a common cause of them, as the SDE diagnostic messaging uses DSOM graph
+ * information to construct context information about the error for inclusion
+ * in the messages. If the DSOM graph is still being constructed at that time, then
+ * this can be circular.
+ *
+ * ===Using the DSOM Graph===
+ *
+ * DSOM supports Daffodil schema compilation by way of the
+ * `OOLAG` pattern which
+ * is an object oriented way of using the
+ * [[https://en.wikipedia.org/wiki/Attribute_grammar attribute grammars]] compiler technique.
+ *
+ * Many attributes (in the attribute grammar sense, nothing to do with XML attributes)
+ * are simply Scala lazy val definitions, but some are declared as OOLAG
+ * attributes (using `edu.illinois.ncsa.daffodil.oolag.OOLAG.OOLAGHost.LV`) which
+ * provides for gathering of multiple diagnostic messages (`SchemaDefinitionError`)
+ * before abandoning compilation.
+ *
+ * DFDL schema compilation largely occurs by evaluating lazy val members of DSOM
+ * objects. These include the members of the grammar traits
+ * (@see [[edu.illinois.ncsa.daffodil.grammar]] package), which are mixed
+ * in to the appropriate DSOM traits/classes.
+ *
+ * ===FAQ===
+ * Q: Why invent this? Why not use XSOM, or the Apache XML Schema library?
+ *
+ * A:We had trouble with other XML-schema libraries for
+ * lack of adequate support for annotations, non-native attributes, and schema
+ * documents as first class objects. So DSOM is specific to Daffodil Basically these
+ * libraries are more about implementing XML Schema and validation, and not so
+ * much about a complex language built on the annotations of the schema. DSOM
+ * is really mostly about the annotations.
+ *
+ */
+package object dsom {
+ // This package object is just for scaladoc
+}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/AlignedMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/AlignedMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/AlignedMixin.scala
index 08bfa01..eba81ac 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/AlignedMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/AlignedMixin.scala
@@ -40,8 +40,7 @@ import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.LengthKind
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen.LengthUnits
import edu.illinois.ncsa.daffodil.util.Math
import edu.illinois.ncsa.daffodil.io.NonByteSizeCharset
-
-
+import edu.illinois.ncsa.daffodil.dsom.Root
case class AlignmentMultipleOf(nBits: Long) {
def *(that: AlignmentMultipleOf) = AlignmentMultipleOf(Math.gcd(nBits, that.nBits))
@@ -53,14 +52,12 @@ trait LengthApprox {
val nBits: Long
def +(that: LengthApprox): LengthApprox = (this, that) match {
case (l: LengthExact, r: LengthExact) => LengthExact(l.nBits + r.nBits)
- case (l, r) => LengthMultipleOf(Math.gcd(l.nBits, r.nBits))
+ case (l, r) => LengthMultipleOf(Math.gcd(l.nBits, r.nBits))
}
}
case class LengthExact(nBits: Long) extends LengthApprox
case class LengthMultipleOf(nBits: Long) extends LengthApprox
-
-
trait AlignedMixin extends GrammarMixin { self: Term =>
/**
@@ -84,7 +81,7 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
true
else
false
- } else if (this.rootElement.get.isScannable)
+ } else if (this.rootElementRef.get.isScannable)
true
else
false
@@ -98,7 +95,7 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
true
else
false
- } else if (this.rootElement.get.isScannable)
+ } else if (this.rootElementRef.get.isScannable)
true
else
false
@@ -106,7 +103,6 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
final lazy val hasNoSkipRegions = leadingSkip == 0 && trailingSkip == 0
-
private lazy val alignmentApprox: AlignmentMultipleOf = {
AlignmentMultipleOf(alignmentValueInBits.toLong)
}
@@ -134,7 +130,7 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
}
private lazy val priorAlignmentApprox: AlignmentMultipleOf = {
- if (enclosingComponent.isEmpty) {
+ if (this.isInstanceOf[Root]) {
AlignmentMultipleOf(0) // root is aligned with anything
} else {
val (priorSibs, parent) = potentialPriorTerms
@@ -175,7 +171,7 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
} else {
Seq()
}
-
+
val priorAlignmentsApprox = priorSibs.map(_.endingAlignmentApprox) ++ parent.map(_.contentStartAlignment).toSeq ++ arraySelfAlignment
priorAlignmentsApprox.reduce(_ * _)
}
@@ -233,8 +229,7 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
// multiple of 8
LengthMultipleOf(8)
}
- }
- else eb.lengthUnits match {
+ } else eb.lengthUnits match {
case LengthUnits.Bits => LengthMultipleOf(1)
case LengthUnits.Bytes => LengthMultipleOf(8)
case LengthUnits.Characters => encodingLengthApprox
@@ -242,8 +237,8 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
}
case LengthKind.Delimited => encodingLengthApprox
case LengthKind.Pattern => encodingLengthApprox
- case LengthKind.EndOfParent => LengthMultipleOf(1)// NYI
- case LengthKind.Prefixed => LengthMultipleOf(1)// NYI
+ case LengthKind.EndOfParent => LengthMultipleOf(1) // NYI
+ case LengthKind.Prefixed => LengthMultipleOf(1) // NYI
}
}
}
@@ -266,10 +261,10 @@ trait AlignedMixin extends GrammarMixin { self: Term =>
val isSkipRegionByteLength = (leadingSkipInBits % 8 == 0) && (trailingSkipInBits % 8 == 0)
val isByteLength = this match {
- case mg: ModelGroup => mg.groupMembersNoRefs.forall { _.isKnownToBeByteAlignedAndByteLength }
+ case mg: ModelGroup => mg.groupMembers.forall { _.isKnownToBeByteAlignedAndByteLength }
case eb: ElementBase => {
val isSelfByteSizeEncoding = eb.charsetEv.optConstant.map { !_.charset.isInstanceOf[NonByteSizeCharset] }.getOrElse(false)
- val isSelfByteLength =
+ val isSelfByteLength =
if (eb.isComplexType && eb.lengthKind == LengthKind.Implicit) {
eb.complexType.group.isKnownToBeByteAlignedAndByteLength
} else {
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ChoiceGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ChoiceGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ChoiceGrammarMixin.scala
index 55e8986..5309307 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ChoiceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ChoiceGrammarMixin.scala
@@ -32,10 +32,10 @@
package edu.illinois.ncsa.daffodil.grammar
-import edu.illinois.ncsa.daffodil.dsom.Choice
+import edu.illinois.ncsa.daffodil.dsom.ChoiceTermBase
import edu.illinois.ncsa.daffodil.grammar.primitives.ChoiceCombinator
-trait ChoiceGrammarMixin extends GrammarMixin { self: Choice =>
+trait ChoiceGrammarMixin extends GrammarMixin { self: ChoiceTermBase =>
override lazy val groupContent = prod("choiceContent") {
ChoiceCombinator(this, alternatives)
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
index a5f3f2f..1bc9909 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementBaseGrammarMixin.scala
@@ -37,7 +37,6 @@ import edu.illinois.ncsa.daffodil.processors._
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen._
import edu.illinois.ncsa.daffodil.dpath.NodeInfo.PrimType
import edu.illinois.ncsa.daffodil.dsom.InitiatedTerminatedMixin
-import edu.illinois.ncsa.daffodil.dsom.SimpleTypeBase
import edu.illinois.ncsa.daffodil.dsom.ElementBase
import java.lang.{ Long => JLong }
import edu.illinois.ncsa.daffodil.dpath.NodeInfo
@@ -414,7 +413,7 @@ trait ElementBaseGrammarMixin
protected lazy val elementUnused = new ElementUnused(context)
/**
- * provided by LocalElementBase for array considerations, and GlobalElementDecl - scalar only
+ * provided by ElementBase for array considerations, and GlobalElementDecl - scalar only
*/
protected def allowedValue: Gram
//
@@ -657,11 +656,7 @@ trait ElementBaseGrammarMixin
ConvertTextCombinator(this, stringValue, ConvertTextBooleanPrim(this))
}
- // shorthand
- final lazy val primType = {
- val res = typeDef.asInstanceOf[SimpleTypeBase].primType
- res
- }
+ def primType: PrimType
protected final lazy val value = prod("value", isSimpleType) {
// TODO: Consider issues with matching a stopValue. Can't say isScalar here because
@@ -1025,7 +1020,7 @@ trait ElementBaseGrammarMixin
private lazy val elementRightFraming = prod("elementRightFraming") { TrailingSkipRegion(this) }
- protected final lazy val enclosedElement = prod("enclosedElement") {
+ protected lazy val enclosedElement = prod("enclosedElement") {
//
// not isScalar, because this is reused inside arrays
// that is, we're counting on reusuing this production for array elements
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementDeclGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementDeclGrammarMixin.scala
index 31ad470..0ae7f8e 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementDeclGrammarMixin.scala
@@ -33,11 +33,11 @@
package edu.illinois.ncsa.daffodil.grammar
import edu.illinois.ncsa.daffodil.grammar.primitives.UnicodeByteOrderMark
-import edu.illinois.ncsa.daffodil.dsom.GlobalElementDecl
+import edu.illinois.ncsa.daffodil.dsom.Root
-trait GlobalElementDeclGrammarMixin
+trait RootGrammarMixin
extends LocalElementGrammarMixin // can be repeating if not root
- { self: GlobalElementDecl =>
+ { self: Root =>
final lazy val document = prod("document") {
schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
@@ -45,4 +45,5 @@ trait GlobalElementDeclGrammarMixin
}
private def documentElement = enclosedElement
+
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementReferenceGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementReferenceGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementReferenceGrammarMixin.scala
index beb84be..71c2659 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementReferenceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ElementReferenceGrammarMixin.scala
@@ -32,10 +32,8 @@
package edu.illinois.ncsa.daffodil.grammar
-import edu.illinois.ncsa.daffodil.dsom.ElementRef
+import edu.illinois.ncsa.daffodil.dsom.AbstractElementRef
-trait ElementReferenceGrammarMixin { self: ElementRef =>
-
- final override lazy val termContentBody = self.referencedElement.termContentBody
+trait ElementReferenceGrammarMixin { self: AbstractElementRef =>
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/Grammar.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/Grammar.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/Grammar.scala
index e1d43b8..e123489 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/Grammar.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/Grammar.scala
@@ -120,7 +120,17 @@ class SeqComp private (context: SchemaComponent, children: Seq[Gram]) extends Bi
else new SeqCompParser(context.runtimeData, parserChildren.toArray)
}
- lazy val unparserChildren = children.filter(x => !x.isEmpty && (x.forWhat != ForParser)).map { _.unparser }.filterNot { _.isInstanceOf[NadaUnparser] }
+ lazy val unparserChildren = {
+ val unparserKeptChildren =
+ children.filter(
+ x =>
+ !x.isEmpty && (x.forWhat != ForParser))
+ val unparsers =
+ unparserKeptChildren.map { x =>
+ x.unparser
+ }.filterNot { _.isInstanceOf[NadaUnparser] }
+ unparsers
+ }
final override lazy val unparser = {
if (unparserChildren.isEmpty) new NadaUnparser(context.runtimeData)
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarMixins.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarMixins.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarMixins.scala
index 5cf6e96..f8afb1e 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarMixins.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarMixins.scala
@@ -37,8 +37,6 @@ import edu.illinois.ncsa.daffodil.grammar.primitives.ComplexTypeCombinator
trait GroupRefGrammarMixin extends GrammarMixin { self: GroupRef =>
- override lazy val termContentBody = self.group.termContentBody
-
}
/////////////////////////////////////////////////////////////////
@@ -50,6 +48,6 @@ trait ComplexTypeBaseGrammarMixin extends GrammarMixin { self: ComplexTypeBase =
override protected final def grammarContext = this
lazy val mainGrammar = prod("mainGrammar") {
- ComplexTypeCombinator(this, modelGroup.group.asChildOfComplexType)
+ ComplexTypeCombinator(this, group.asChildOfComplexType)
}
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarTerm.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarTerm.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarTerm.scala
index 374606d..ca1aa63 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarTerm.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/GrammarTerm.scala
@@ -37,7 +37,7 @@ import edu.illinois.ncsa.daffodil.processors.parsers.Parser
import edu.illinois.ncsa.daffodil.processors.unparsers.Unparser
import edu.illinois.ncsa.daffodil.grammar.primitives.Nada
import edu.illinois.ncsa.daffodil.dsom.SchemaComponent
-import edu.illinois.ncsa.daffodil.oolag.OOLAG.OOLAGHost
+import edu.illinois.ncsa.daffodil.oolag.OOLAG.OOLAGHostImpl
import edu.illinois.ncsa.daffodil.compiler.ParserOrUnparser
import edu.illinois.ncsa.daffodil.util.Misc
import edu.illinois.ncsa.daffodil.compiler.BothParserAndUnparser
@@ -55,7 +55,7 @@ trait HasNoUnparser {
* because this one has to actually be operationalized.
*/
abstract class Gram(contextArg: SchemaComponent)
- extends OOLAGHost(contextArg) {
+ extends OOLAGHostImpl(contextArg) {
final def SDE(str: String, args: Any*): Nothing = context.SDE(str, args: _*)
final def SDW(str: String, args: Any*): Unit = context.SDW(str, args: _*)
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/HasStatementsGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/HasStatementsGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/HasStatementsGrammarMixin.scala
index dc1c6d2..03744c2 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/HasStatementsGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/HasStatementsGrammarMixin.scala
@@ -32,10 +32,9 @@
package edu.illinois.ncsa.daffodil.grammar
-import edu.illinois.ncsa.daffodil.dsom.DFDLStatementMixin
import edu.illinois.ncsa.daffodil.dsom.Term
-trait HasStatementsGrammarMixin extends GrammarMixin { self: Term with DFDLStatementMixin =>
+trait HasStatementsGrammarMixin extends GrammarMixin { self: Term =>
private lazy val statementGrams = statements.map { _.gram }
// TODO: statements (but specifically not newVariableInstance) can appear on simple type definitions as well as terms.
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/LocalElementGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/LocalElementGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/LocalElementGrammarMixin.scala
index e3e412d..84a2db3 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/LocalElementGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/LocalElementGrammarMixin.scala
@@ -33,7 +33,7 @@
package edu.illinois.ncsa.daffodil.grammar
import edu.illinois.ncsa.daffodil.schema.annotation.props._
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen._
-import edu.illinois.ncsa.daffodil.dsom.LocalElementBase
+import edu.illinois.ncsa.daffodil.dsom.ElementBase
import edu.illinois.ncsa.daffodil.equality._; object ENoWarn2 { EqualitySuppressUnusedImportWarning() }
import edu.illinois.ncsa.daffodil.grammar.primitives.StopValue
import edu.illinois.ncsa.daffodil.grammar.primitives.NotStopValue
@@ -47,11 +47,8 @@ import edu.illinois.ncsa.daffodil.grammar.primitives.OccursCountExpression
import edu.illinois.ncsa.daffodil.grammar.primitives.OptionalCombinator
import edu.illinois.ncsa.daffodil.grammar.primitives.ArrayCombinator
-trait LocalElementGrammarMixin extends GrammarMixin { self: LocalElementBase =>
+trait LocalElementGrammarMixin extends GrammarMixin { self: ElementBase =>
- /**
- * further overridden in ElementRefGrammarMixin
- */
override lazy val termContentBody = prod("termContentBody") { // override in ElementRef
(if (isScalar) enclosedElement else recurrance)
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ModelGroupGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ModelGroupGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ModelGroupGrammarMixin.scala
index 1447d2c..66c8865 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ModelGroupGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/ModelGroupGrammarMixin.scala
@@ -35,13 +35,13 @@ package edu.illinois.ncsa.daffodil.grammar
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen._
import edu.illinois.ncsa.daffodil.dsom.InitiatedTerminatedMixin
import edu.illinois.ncsa.daffodil.dsom.ModelGroup
-import edu.illinois.ncsa.daffodil.dsom.Sequence
-import edu.illinois.ncsa.daffodil.dsom.Choice
import edu.illinois.ncsa.daffodil.grammar.primitives.TrailingSkipRegion
import edu.illinois.ncsa.daffodil.grammar.primitives.LeadingSkipRegion
import edu.illinois.ncsa.daffodil.grammar.primitives.AlignmentFill
import edu.illinois.ncsa.daffodil.grammar.primitives.DelimiterStackCombinatorSequence
import edu.illinois.ncsa.daffodil.grammar.primitives.DelimiterStackCombinatorChoice
+import edu.illinois.ncsa.daffodil.dsom.SequenceTermBase
+import edu.illinois.ncsa.daffodil.dsom.ChoiceTermBase
trait ModelGroupGrammarMixin
extends InitiatedTerminatedMixin
@@ -78,8 +78,8 @@ trait ModelGroupGrammarMixin
) {
val content = initiatorRegion ~ groupContent ~ terminatorRegion
self match {
- case c: Choice => DelimiterStackCombinatorChoice(c, content)
- case s: Sequence => DelimiterStackCombinatorSequence(s, content)
+ case c: ChoiceTermBase => DelimiterStackCombinatorChoice(c, content)
+ case s: SequenceTermBase => DelimiterStackCombinatorSequence(s, content)
}
} else { groupContent }
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/SequenceGrammarMixin.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/SequenceGrammarMixin.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/SequenceGrammarMixin.scala
index 5c6ac4e..383cf53 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/SequenceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/SequenceGrammarMixin.scala
@@ -32,10 +32,10 @@
package edu.illinois.ncsa.daffodil.grammar
import edu.illinois.ncsa.daffodil.schema.annotation.props.gen._
-import edu.illinois.ncsa.daffodil.dsom.Sequence
import edu.illinois.ncsa.daffodil.grammar.primitives.SequenceCombinator
+import edu.illinois.ncsa.daffodil.dsom.SequenceTermBase
-trait SequenceGrammarMixin extends GrammarMixin { self: Sequence =>
+trait SequenceGrammarMixin extends GrammarMixin { self: SequenceTermBase =>
final override lazy val groupContent = prod("groupContent") {
self.sequenceKind match {
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/package.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/package.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/package.scala
new file mode 100644
index 0000000..0deae3a
--- /dev/null
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/package.scala
@@ -0,0 +1,104 @@
+package edu.illinois.ncsa.daffodil
+
+/**
+ * Grammar Mixins for Schema Compilation
+ *
+ * DFDL is defined using a data syntax grammar. Daffodil compiles a DFDL schema
+ * into runtime data structures using a technique that tries to be faithful to
+ * this notion of a data syntax grammar. The exact data syntax grammar used
+ * in the DFDL specification is not really suitable to base an implementation on,
+ * but where possible we have named the grammar production rules, and terminals of
+ * the grammar consistently with the DFDL specification.
+ *
+ * The grammar rules are members of the DSOM traits/classes that use a
+ * grammar notation, organized as grammar rules the applicability of which
+ * is controlled by boolean test guard expressions.
+ *
+ * In addition to the grammar rules, these mixins contain the methods that analzye
+ * the DFDL schema with its DFDL annotations, to determine whether the grammar
+ * rule is applicable. Most optimizations in the Daffodil schema compiler are
+ * implemented by way of a grammar rule or [[Terminal]] which is included or
+ * excluded (aka guarded) by an optimization.
+ *
+ * The grammar rule mixins all inherit from the [[Gram]] trait which provides
+ * operators for expressing the rules. The concrete classes are the
+ * terminals of the grammar (instances of [[Terminal]]) and these
+ * are either primitives or combinators. The primitves and combinators are
+ * defined in the [[edu.illinois.ncsa.daffodil.grammar.primitives]] package.
+ *
+ * This all works very much like Scala's `scala.util.parsing.combinator` library,
+ * which is described in the [[http://www.artima.com/pins1ed/ Programming in Scala]] book
+ * in the [[http://www.artima.com/pins1ed/combinator-parsing.html chapter on Combinator Parsing]].
+ * However, Daffodil's grammar adds the notion of rich predicate guards controlling
+ * the rules, and of course the result of the grammar is an entirely different
+ * data structure. But but the way rules are expressed and use of operators
+ * like "`~`" and "`||`" to create a little grammar composition language are similar.
+ *
+ * It is best to illustrate how the grammar works by an example drawn from model groups.
+ * This example no longer matches the code of the actual implementation, but
+ * illustrates the ideas behind the grammar:
+ * @example
+ * {{{
+ * trait ModelGroupMixin extends ... {
+ *
+ * lazy val modelGroup = groupLeftFraming ~ groupContent ~ groupRightFraming
+ *
+ * lazy val groupLeftFraming = LeadingSkipRegion(this) ~ AlignmentFill(this)
+ *
+ * lazy val groupRightFraming = TrailingSkipRegion(this)
+ * }
+ * }}}
+ * Non-terminals of the data syntax grammar are ordinary lazy val members named
+ * beginning with lower case letter. Terminals of the grammar are classes
+ * and so are named beginning with an upper case letter.
+ *
+ * A terminal like `LeadingSkipRegion` will optimize itself out by evaluating a
+ * guard predicate test. Basically, if the DFDL schema has a `dfdl:leadingSkip`
+ * property of '0', then the `LeadingSkipRegion` of the data syntax grammar is zero
+ * width, so this terminal defines itself so that the `isEmpty` method returns
+ * `true`. That enables the "`~`" operator to see that it has an empty grammar term
+ * on it's left, hence, the "`~`" operator can reduce to just the right grammar term.
+ *
+ * The right grammar term, `AlignmentFill`, requires a much more sophisticated
+ * analysis of the schema and properties, but ultimately could also decide that
+ * it is not needed, and so `isEmpty`. Then the "`~`" operator will see that both sides are
+ * empty and it itself is then empty. That allows the modelGroup rule to recognize that
+ * for this DFDL schema there is no `groupLeftFraming`.
+ *
+ * The net result of this is all the grammar regions that are not applicable disappear
+ * from the grammar. What is left is a nest of combinators and primitives suitable
+ * for this specific DFDL schema's runtime to be generated.
+ *
+ * Lazy evaluation and by-name argument passing insure that this occurs without
+ * evaluating any irrelevant grammar rules.
+ *
+ * At the end, the schema compiler generates a parser or unparser instance
+ * by "asking" the now-optimized grammar data structure for a parser or unparser.
+ * This means really by invoking the corresponding
+ * method (`parser()` or `unparser()`) on the grammar object.
+ * Recursively each combinator constructs an instance of
+ * a class that implements
+ * `edu.illinois.ncsa.daffodil.processors.parsers.Parser` (or
+ * `edu.illinois.ncsa.daffodil.processors.unparsers.Unparser`)
+ * and that instance recursively calls the parsers (or unparsers) generated
+ * from the parts of the grammar which did not optimize themselves away.
+ * Primitves construct atomic primitive parsers (and unparsers) for things like
+ * delimiters, alignment regions, or simple type values.
+ *
+ * ==Futures==
+ *
+ * Many grammar rules are defined as instances of the [[Prod]] class using the `prod` method.
+ * This is not strictly speaking needed (the example above doesn't use it), but is intended
+ * to define named extension points in the grammar for a future DFDL extension
+ * capability. The idea is that the named productions would be the places
+ * where external extensions to the grammar would be examined and utilized to
+ * augment the internal grammar rule definitions. These externally defined productions
+ * could then incorporate into the grammar, new externally defined primitives,
+ * or possibly even combinators, which ultimately generate calls to externally
+ * defined parser (or unparser) instances.
+ *
+ * Currently the use of `prod` is ad-hoc and haphazard.
+ */
+package object grammar {
+ // This object exists for scaladoc purposes.
+}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/Primitives.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/Primitives.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/Primitives.scala
index fc3c2bc..7baaf30 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/Primitives.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/Primitives.scala
@@ -37,7 +37,7 @@ import edu.illinois.ncsa.daffodil.grammar.Terminal
import edu.illinois.ncsa.daffodil.grammar.HasNoUnparser
abstract class Primitive(e: Term, guard: Boolean = false)
- extends Terminal(e, guard) {
+ extends Terminal(e, guard) {
override def toString = "Prim[" + name + "]"
}
@@ -45,14 +45,14 @@ abstract class Primitive(e: Term, guard: Boolean = false)
* For stubbing out primitives that are placeholders
*/
abstract class UnimplementedPrimitive(e: Term, guard: Boolean = false)
- extends Primitive(e, guard)
- with HasNoUnparser {
+ extends Primitive(e, guard)
+ with HasNoUnparser {
override final lazy val parser = hasNoParser
}
// base stub classes
-case class NoValue(e: GlobalElementDecl, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
+// case class NoValue(e: GlobalElementDecl, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
case class SaveInputStream(e: ElementBase, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
@@ -66,4 +66,4 @@ case class StopValue(e: ElementBase with LocalElementMixin) extends Unimplemente
case class TheDefaultValue(e: ElementBase) extends UnimplementedPrimitive(e, e.isDefaultable)
-case class UnicodeByteOrderMark(e: GlobalElementDecl) extends UnimplementedPrimitive(e, false)
+case class UnicodeByteOrderMark(e: Root) extends UnimplementedPrimitive(e, false)
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesDelimiters.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesDelimiters.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesDelimiters.scala
index 0b16b6d..138f000 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesDelimiters.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesDelimiters.scala
@@ -172,7 +172,7 @@ case class Initiator(e: Term) extends DelimiterText(e, e) {
Assert.invariant(e.hasInitiator)
val delimiterType: DelimiterTextType.Type = DelimiterTextType.Initiator
}
-case class Separator(s: Sequence, t: Term) extends DelimiterText(s, t) {
+case class Separator(s: SequenceTermBase, t: Term) extends DelimiterText(s, t) {
Assert.invariant(s.hasSeparator)
val delimiterType: DelimiterTextType.Type = DelimiterTextType.Separator
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesElementKinds.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesElementKinds.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesElementKinds.scala
index 04459ae..b2a94ef 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesElementKinds.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesElementKinds.scala
@@ -62,7 +62,7 @@ import edu.illinois.ncsa.daffodil.cookers.ChoiceBranchKeyCooker
object ENoWarn3 { EqualitySuppressUnusedImportWarning() }
-case class DelimiterStackCombinatorSequence(sq: Sequence, body: Gram) extends Terminal(sq, !body.isEmpty) {
+case class DelimiterStackCombinatorSequence(sq: SequenceTermBase, body: Gram) extends Terminal(sq, !body.isEmpty) {
lazy val pInit = if (sq.initiatorParseEv.isKnownNonEmpty) One(sq.initiatorParseEv) else Nope
lazy val pSep = if (sq.separatorParseEv.isKnownNonEmpty) One(sq.separatorParseEv) else Nope
lazy val pTerm = if (sq.terminatorParseEv.isKnownNonEmpty) One(sq.terminatorParseEv) else Nope
@@ -76,7 +76,7 @@ case class DelimiterStackCombinatorSequence(sq: Sequence, body: Gram) extends Te
override lazy val unparser: DaffodilUnparser = new DelimiterStackUnparser(uInit, uSep, uTerm, sq.runtimeData, body.unparser)
}
-case class DelimiterStackCombinatorChoice(ch: Choice, body: Gram) extends Terminal(ch, !body.isEmpty) {
+case class DelimiterStackCombinatorChoice(ch: ChoiceTermBase, body: Gram) extends Terminal(ch, !body.isEmpty) {
lazy val pInit = if (ch.initiatorParseEv.isKnownNonEmpty) One(ch.initiatorParseEv) else Nope
lazy val pTerm = if (ch.terminatorParseEv.isKnownNonEmpty) One(ch.terminatorParseEv) else Nope
@@ -113,7 +113,7 @@ case class DynamicEscapeSchemeCombinatorElement(e: ElementBase, body: Gram) exte
override lazy val unparser: DaffodilUnparser = new DynamicEscapeSchemeUnparser(schemeUnparseOpt.get, e.runtimeData, body.unparser)
}
-case class ComplexTypeCombinator(ct: ComplexTypeBase, body: Gram) extends Terminal(ct.element, !body.isEmpty) {
+case class ComplexTypeCombinator(ct: ComplexTypeBase, body: Gram) extends Terminal(ct.elementDecl, !body.isEmpty) {
override def isEmpty = body.isEmpty
@@ -123,13 +123,24 @@ case class ComplexTypeCombinator(ct: ComplexTypeBase, body: Gram) extends Termin
new ComplexTypeUnparser(ct.runtimeData, body.unparser)
}
-case class SequenceCombinator(sq: Sequence, rawTerms: Seq[Gram])
- extends Terminal(sq, !rawTerms.filterNot { _.isEmpty }.isEmpty) {
+case class SequenceCombinator(sq: SequenceTermBase, rawTerms: Seq[Gram])
+ extends Terminal(sq, true) {
+
+ override lazy val isEmpty = {
+ val rt = rawTerms
+ val frt = rt.filterNot { _.isEmpty }
+ val res = frt.isEmpty
+ res
+ }
private val mt: Gram = EmptyGram
lazy val body = rawTerms.foldRight(mt) { _ ~ _ }
+
lazy val terms = rawTerms.filterNot { _.isEmpty }
- lazy val unparsers = terms.map { _.unparser }.toVector
+
+ lazy val unparsers = terms.map { term =>
+ term.unparser
+ }.toVector
lazy val parser: DaffodilParser = new SequenceCombinatorParser(sq.termRuntimeData, body.parser)
@@ -167,7 +178,7 @@ case class OptionalCombinator(e: ElementBase, body: Gram) extends Terminal(e, !b
* need to determine which branch of the choice to take at runtime. This
* unparser uses a Map to make the determination based on the element seen.
*/
-case class ChoiceCombinator(ch: Choice, alternatives: Seq[Gram]) extends Terminal(ch, !alternatives.isEmpty) {
+case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram]) extends Terminal(ch, !alternatives.isEmpty) {
lazy val parser: DaffodilParser = {
if (!ch.isDirectDispatch) {
val folded = alternatives.map { gf => gf }.foldRight(EmptyGram.asInstanceOf[Gram]) { _ | _ }
@@ -224,7 +235,7 @@ case class ChoiceCombinator(ch: Choice, alternatives: Seq[Gram]) extends Termina
// this call is necessary since it will throw an SDE if no choice branch
// was defaultable
ch.childrenInHiddenGroupNotDefaultableOrOVC
- val defaultableBranches = ch.groupMembersNoRefs.filter { _.childrenInHiddenGroupNotDefaultableOrOVC.length == 0 }
+ val defaultableBranches = ch.groupMembers.filter { _.childrenInHiddenGroupNotDefaultableOrOVC.length == 0 }
Assert.invariant(defaultableBranches.length > 0)
if (defaultableBranches.length > 1) {
SDW("xs:choice inside a hidden group has unparse ambiguity: multiple branches exist with all children either defaulable or have the dfdl:outputValueCalc property set. The first branch will be chosen during unparse. Defaultable branches are:\n%s",
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesExpressions.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesExpressions.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesExpressions.scala
index 36d0d14..db992a5 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesExpressions.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesExpressions.scala
@@ -270,7 +270,7 @@ case class InputValueCalc(e: ElementBase,
// }
//}
-abstract class AssertPatternPrimBase(decl: AnnotatedSchemaComponent, stmt: DFDLAssertionBase)
+abstract class AssertPatternPrimBase(decl: Term, stmt: DFDLAssertionBase)
extends Terminal(decl, true) {
lazy val eName = decl.diagnosticDebugName
@@ -286,22 +286,22 @@ abstract class AssertPatternPrimBase(decl: AnnotatedSchemaComponent, stmt: DFDLA
override def unparser: DaffodilUnparser = Assert.invariantFailed("should not request unparser for asserts/discriminators")
}
-case class AssertPatternPrim(decl: AnnotatedSchemaComponent, stmt: DFDLAssert)
- extends AssertPatternPrimBase(decl, stmt) {
+case class AssertPatternPrim(term: Term, stmt: DFDLAssert)
+ extends AssertPatternPrimBase(term, stmt) {
val kindString = "AssertPatternPrim"
lazy val parser: DaffodilParser = {
- new AssertPatternParser(eName, kindString, decl.term.termRuntimeData, testPattern, stmt.message)
+ new AssertPatternParser(eName, kindString, term.termRuntimeData, testPattern, stmt.message)
}
}
-case class DiscriminatorPatternPrim(decl: AnnotatedSchemaComponent, stmt: DFDLAssertionBase)
- extends AssertPatternPrimBase(decl, stmt) {
+case class DiscriminatorPatternPrim(term: Term, stmt: DFDLAssertionBase)
+ extends AssertPatternPrimBase(term, stmt) {
val kindString = "DiscriminatorPatternPrim"
- lazy val parser: DaffodilParser = new DiscriminatorPatternParser(testPattern, eName, kindString, decl.term.termRuntimeData, stmt.message)
+ lazy val parser: DaffodilParser = new DiscriminatorPatternParser(testPattern, eName, kindString, term.termRuntimeData, stmt.message)
}
http://git-wip-us.apache.org/repos/asf/incubator-daffodil/blob/89517717/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
----------------------------------------------------------------------
diff --git a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
index 9f4f22a..469a453 100644
--- a/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
+++ b/daffodil-core/src/main/scala/edu/illinois/ncsa/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
@@ -36,7 +36,7 @@ import edu.illinois.ncsa.daffodil.grammar.Gram
import edu.illinois.ncsa.daffodil.exceptions.Assert
import edu.illinois.ncsa.daffodil.dsom.ElementBase
import edu.illinois.ncsa.daffodil.dsom.Term
-import edu.illinois.ncsa.daffodil.dsom.Sequence
+import edu.illinois.ncsa.daffodil.dsom.SequenceTermBase
import edu.illinois.ncsa.daffodil.grammar.UnaryGram
import edu.illinois.ncsa.daffodil.grammar.HasNoUnparser
import edu.illinois.ncsa.daffodil.processors.ElementRuntimeData
@@ -47,15 +47,15 @@ object UnorderedSequence {
// mandatory little optimization here. If there are no statements (most common case), then let's
// shortcut and just use the guts parser.
- Assert.usageErrorUnless(context.isInstanceOf[Sequence], "The context passed to UnorderedSequence must be a Sequence.")
+ Assert.usageErrorUnless(context.isInstanceOf[SequenceTermBase], "The context passed to UnorderedSequence must be a Sequence.")
- val ctxt = context.asInstanceOf[Sequence]
+ val ctxt = context.asInstanceOf[SequenceTermBase]
new UnorderedSequence(ctxt, eGram)
}
}
-class UnorderedSequence private (context: Sequence, eGramArg: => Gram) // private to force use of the object as factory
+class UnorderedSequence private (context: SequenceTermBase, eGramArg: => Gram) // private to force use of the object as factory
extends UnaryGram(context, eGramArg) with HasNoUnparser {
lazy val eGram = eGramArg // once only
@@ -67,7 +67,7 @@ class UnorderedSequence private (context: Sequence, eGramArg: => Gram) // privat
val sortOrder = {
val members = context.groupMembers.map(t => {
t match {
- case s: Sequence => s.groupMembers
+ case s: SequenceTermBase => s.groupMembers
case _ => Seq(t)
}
}).flatten