jadams-tresys commented on a change in pull request #207: Added support for
enumerations and TypeValueCalc
URL: https://github.com/apache/incubator-daffodil/pull/207#discussion_r275969338
##########
File path:
daffodil-core/src/main/scala/org/apache/daffodil/dsom/SimpleTypes.scala
##########
@@ -221,64 +311,399 @@ final class LocalSimpleTypeDef(
//
// TODO: implement a daf:name property to give an alternate name. If
present, use that.
//
- val rName = optRestriction.flatMap { r =>
+ val baseName = optRestriction.flatMap { r =>
r.optBaseDef.map { _.namedQName }.orElse(Some(r.primType.globalQName))
}.getOrElse(this.optUnion.map { u => u.primType.globalQName }.getOrElse(
- Assert.invariantFailed("Simple type is neither a union nor a
restriction: " + this)))
- rName.toQNameString
+ //Note that this.toString=diagnosticDebugName, so cannot be used here
+ Assert.invariantFailed("Anonymous Simple type is neither a union nor a
restriction. Enclosing element is " + this.parent)))
+ val repName = optRepTypeFactory.map(_.name)
+ repName match{
+ case None => baseName.diagnosticDebugName
+ case Some(n) => s"${n} -> ${baseName.diagnosticDebugName}"
+ }
}
}
/**
- * The factory is sharable even though the global object it creates cannot
+ * The factory is sharable even though the objects it creates cannot
* be shared.
*
- * Call forElement(element) and supply the element referring
- * to the global type, then you get back an instance that is one-to-one with
the
- * element.
+ * This allows us to compute common properties of GlobalSimpleTypes exactly
once
+ * (and makes those properties available in the absence of an instantiating
element)
*
- * This then allows attributes of the type to refer to the element in deciding
things.
- * I.e., the context is clear and kept separate for each place a global type
is used.
+ * In theory this is not useful in the case of LocalSimpleTypes, as they will
only ever be instantiated once,
+ * but we do need LocalSimpleTypes to go through a factory so they have access
to the features implemented in the factory
*/
-final class GlobalSimpleTypeDefFactory(xmlArg: Node, schemaDocumentArg:
SchemaDocument)
+trait SimpleTypeFactory {
+ def forElement(elementDecl: ElementDeclMixin): SimpleTypeBase
+ def forDerivedType(derivedType: SimpleTypeDefBase): SimpleTypeBase
+
+ def primType: PrimType
+
+ def name: String
+}
+
+abstract class SimpleTypeDefFactory(xmlArg: Node, schemaDocumentArg:
SchemaDocument)
extends SchemaComponentFactory(xmlArg, schemaDocumentArg)
- with GlobalNonElementComponentMixin {
+ with HasRepValueAttributes
+ with SimpleTypeFactory
+ with hasOptRepTypeMixinImpl
+ with ProvidesDFDLStatementMixin {
+
+ override def name = diagnosticDebugName
+
+ override final def optReferredToComponent = optRestriction.flatMap {
_.optBaseDef }
+ override final def emptyFormatFactory = new
DFDLSimpleType(newDFDLAnnotationXML("simpleType"), this)
+
+ override final def isMyFormatAnnotation(a: DFDLAnnotation) =
a.isInstanceOf[DFDLSimpleType]
+
+ override final def annotationFactory(node: Node): Option[DFDLAnnotation] = {
+ node match {
+ case <dfdl:simpleType>{ contents @ _* }</dfdl:simpleType> => Some(new
DFDLSimpleType(node, this))
+ case _ => annotationFactoryForDFDLStatement(node, this)
+ }
+ }
+
+ /*
+ * We tighten the return type of these factory methods so callers can be
sure that
+ * they are recieving a SimpleTypeDef (as opposed to a PrimitiveType).
+ *
+ * This is useful because only SimpleTypeDefs get passed to the runtime
system.
+ * By keeping this information in the type system, callers can pass elements
that they know
+ * are SimpleTypeDefs to the runtime without an explicit cast to
SimpleTypeDef
+ */
+ override def forElement(elementDecl: ElementDeclMixin): SimpleTypeDefBase
+ override def forDerivedType(derivedType: SimpleTypeDefBase):
SimpleTypeDefBase
+
+ def primType: NodeInfo.PrimType = {
+ optRestriction.map { _.primType }.getOrElse {
+ optUnion.map { _.primType }.getOrElse {
+ Assert.invariantFailed("must be either a restriction or union")
+ }
+ }
+ }
+
+ final lazy val restrictions = {
+ val thisR = optRestriction.toSeq
+ val res = thisR ++
+ thisR.flatMap { _.derivationBaseRestrictions }
+ res
+ }
+
+ /**
+ * Exclusive of self.
+ */
+ final lazy val bases: Seq[SimpleTypeDefFactory] =
+ if (restrictions.isEmpty) Nil
+ else restrictions.tail.map { _.simpleTypeFactory }
+
+ lazy val (optRestriction, optUnion) = {
+ val restrictionNodeSeq = xml \ "restriction"
+ if (restrictionNodeSeq.isEmpty) {
+ val unionNodeSeq = xml \ "union"
+ Assert.invariant(unionNodeSeq.length == 1)
+ (None, Some(new UnionFactory(unionNodeSeq(0), this)))
+ } else {
+ (Some(new RestrictionFactory(restrictionNodeSeq(0), this)), None)
+ }
+ }
+
+ lazy val optInputTypeCalc =
findPropertyOptionThisComponentOnly("inputTypeCalc")
+ lazy val optOutputTypeCalc =
findPropertyOptionThisComponentOnly("outputTypeCalc")
+
+ lazy val optTypeCalculator: Option[TypeCalculator[AnyRef, AnyRef]] = {
+ optRepTypeFactory.flatMap(repType => {
+ val srcType = repType.primType
+ val dstType = primType
+
+ val fromRestriction: Option[TypeCalculator[AnyRef, AnyRef]] =
optRestriction.flatMap({ restriction =>
+ val enumerations =
restriction.enumerations.filter(_.optRepValueSet.isDefined)
+ if (enumerations.isEmpty) {
+ /*
+ * In theory, we require srcType == dstType.
+ * But, we also compute this when we are an expression calculator.
+ * In such a case, the above invariant may not hold, which is okay,
because we
+ * will not actually use the identity calculator.
+ */
+ Some(TypeCalculatorCompiler.compileIdentity(srcType))
+ } else {
+ if (enumerations.size != restriction.enumerations.size) {
+ context.SDE("If one enumeration value defines a repValue, then all
must define a repValue")
+ }
+ val terms = enumerations.map(enum => (enum.optRepValueSet.get,
enum.canonicalRepValue.get, enum.enumValueCooked))
+ Some(TypeCalculatorCompiler.compileKeysetValue(terms, srcType,
dstType))
+ }
+ })
+ val fromUnion: Option[TypeCalculator[AnyRef, AnyRef]] = optUnion.map({
union =>
+ val subCalculators: Seq[(RepValueSet[AnyRef], RepValueSet[AnyRef],
TypeCalculator[AnyRef, AnyRef])] =
+ union.unionMemberTypes.map(subType => (subType.optRepValueSet.get,
subType.optLogicalValueSet.get, subType.optTypeCalculator.get))
+ TypeCalculatorCompiler.compileUnion(subCalculators)
+ })
+ val fromExpression: Option[TypeCalculator[AnyRef, AnyRef]] = {
+ val optInputCompiled = optInputTypeCalc.toOption.map(sExpr => {
+ val prop = optInputTypeCalc.asInstanceOf[Found]
+ val qn = GlobalQName(Some("daf"), "inputTypeCalc",
XMLUtils.dafintURI)
+ val exprNamespaces = prop.location.namespaces
+ val exprComponent = prop.location.asInstanceOf[SchemaComponent]
+ ExpressionCompilers.AnyRef.compileExpression(
+ qn,
+ dstType, sExpr, exprNamespaces, exprComponent.dpathCompileInfo,
false, this, dpathCompileInfo)
+ })
+ val optOutputCompiled = optOutputTypeCalc.toOption.map(sExpr => {
+ val prop = optOutputTypeCalc.asInstanceOf[Found]
+ val qn = GlobalQName(Some("daf"), "outputTypeCalc",
XMLUtils.dafintURI)
+ val exprNamespaces = prop.location.namespaces
+ val exprComponent = prop.location.asInstanceOf[SchemaComponent]
+ ExpressionCompilers.AnyRef.compileExpression(
+ qn,
+ srcType, sExpr, exprNamespaces, exprComponent.dpathCompileInfo,
false, this, dpathCompileInfo)
+ })
+ (optInputCompiled, optOutputCompiled) match {
+ case (None, None) => None
+ case _ =>
Some(TypeCalculatorCompiler.compileExpression(optInputCompiled,
optOutputCompiled, srcType, dstType))
+ }
+ }
+
+ val ans = (fromRestriction, fromUnion, fromExpression) match {
+ case (Some(x), None, None) => Some(x)
+ case (None, Some(x), None) => Some(x)
+ case (None, None, Some(x)) => context.SDE("Usage of inputTypeCalc and
outputTypeCalc requires an empty xs:restriction to determine the base type.")
+ case (Some(x), _, Some(y)) if
x.isInstanceOf[IdentifyTypeCalculator[AnyRef]] => Some(y)
+ case (None, None, None) => {
+ if (dstType != srcType) {
+ val repTypeName = optRepTypeDefFactory match {
+ case Some(r) => r.diagnosticDebugName
+ case None => repType.toString()
+ }
+ context.SDE(
+ "repType (%s) with primitive type (%s) used without defining a
transformation is not compatable with the baseType of (%s) with primitive type
(%s)",
+ repTypeName, srcType.name,
+ diagnosticDebugName, dstType.name)
+ }
+ None
+ }
+ case (Some(_), Some(_), _) => Assert.invariantFailed("Cannot combine
an enumeration with a union")
+ case (Some(_), _, Some(_)) => context.SDE("Cannot use
typeCalcExpressions while defining repValues of enumerations")
+ case (_, Some(_), Some(_)) => context.SDE("Cannot use
typeCalcExpressions while using a union that defines typeCalcs")
+ }
+
+ ans match {
+ case Some(idt: IdentifyTypeCalculator[AnyRef]) => {
+ if (srcType != dstType) {
+ SDE("Identity transform requires that the basetype and reptype
have a common primitive type")
+ }
+ }
+ case _ => ()
+ }
+
+ ans
+
+ })
+ }
+
+ /*
+ * We don't really need the NamedMixin. It is only used for detecting
duplicates
+ * However, since only named types can be a repType, there is no problem
+ * in requiring them to be named
+ */
+ override lazy val optRepTypeFactory: Option[SimpleTypeFactory with
NamedMixin] = {
+ lazy val fromSelf: Option[SimpleTypeFactory with NamedMixin] = {
+ val qName = findPropertyOptionThisComponentOnly("repType").toOption
+ .flatMap(QName.resolveRef(_, namespaces, tunable).toOption)
+ val optRepTypeDef = qName.flatMap(schemaSet.getGlobalSimpleTypeDef(_))
+ val optRepPrimType = qName.flatMap(schemaSet.getPrimitiveTypeFactory(_))
+ Assert.invariant(!(optRepPrimType.isDefined && optRepTypeDef.isDefined))
+ if (qName.isDefined) {
+ schemaDefinitionUnless(optRepTypeDef.isDefined ||
optRepPrimType.isDefined, s"Cannot find reptype ${qName.get}")
+ }
+ optRepTypeDef.orElse(optRepPrimType)
+ }
+ lazy val fromUnion: Option[SimpleTypeFactory with NamedMixin] =
optUnion.flatMap(union => {
+ val repTypes = union.unionMemberTypes.map(_.optRepTypeFactory)
+ //check that all repTypes are the same
+ //Because of how we inline types, we do not expect to see structual
equality,
+ //so we rely on the xml qname instead
+ val numRepTypes = repTypes.map(_.map(_.namedQName)).toSet.size
+ if (numRepTypes > 1) {
+ SDE("If any child type of a union has a repType, they all must have
the same repType")
+ }
+ if (numRepTypes == 0) {
+ None
+ } else {
+ repTypes.head
+ }
+ })
+ fromSelf.orElse(fromUnion)
+ }
+
+ override lazy val optRepValueSet: Option[RepValueSet[AnyRef]] =
optRepTypeDefFactory.flatMap(repType => {
+ val primType: PrimType = repType.primType
+
+ //TODO check for SDEs
Review comment:
Deal with these TODO's
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services