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

Reply via email to