| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.daffodil.propGen |
| |
| |
| class TunableGenerator(schemaRootConfig: scala.xml.Node, schemaRootExt: scala.xml.Node) { |
| |
| val top = """ |
| |/* |
| | * Licensed to the Apache Software Foundation (ASF) under one or more |
| | * contributor license agreements. See the NOTICE file distributed with |
| | * this work for additional information regarding copyright ownership. |
| | * The ASF licenses this file to You under the Apache License, Version 2.0 |
| | * (the "License"); you may not use this file except in compliance with |
| | * the License. You may obtain a copy of the License at |
| | * |
| | * http://www.apache.org/licenses/LICENSE-2.0 |
| | * |
| | * Unless required by applicable law or agreed to in writing, software |
| | * distributed under the License is distributed on an "AS IS" BASIS, |
| | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | * See the License for the specific language governing permissions and |
| | * limitations under the License. |
| | */ |
| | |
| |package org.apache.daffodil.api |
| | |
| |//////////////////////////////////////////////////////////////////////////////////////////// |
| |// |
| |// Generated Code - Do not hand modify! |
| |// |
| |// This file is entirely generated code created from the |
| |// XML Schema files that describe Daffodil configuration files. |
| |// |
| |// Don't edit this. Go fix the generator to create what you need instead. |
| |// |
| |//////////////////////////////////////////////////////////////////////////////////////////// |
| | |
| |import org.apache.daffodil.exceptions.Assert |
| |import org.apache.daffodil.exceptions.ThrowsSDE |
| |import org.apache.daffodil.schema.annotation.props.EmptyElementParsePolicy |
| |import org.apache.daffodil.schema.annotation.props.Enum |
| |import org.apache.daffodil.util.Misc |
| |import org.apache.daffodil.xml.DaffodilXMLLoader |
| |import org.apache.daffodil.xml.XMLUtils |
| | |
| |object DaffodilTunables extends DaffodilTunablesStaticMixin { |
| | |
| | def apply(tunables: Map[String, String]): DaffodilTunables = { |
| | apply().setTunables(tunables) |
| | } |
| | |
| | def apply(tunable: String, value: String): DaffodilTunables = { |
| | apply().setTunable(tunable, value) |
| | } |
| | |
| | def apply(): DaffodilTunables = { |
| | // override tunables from the global configuration file on the class path, if it exists |
| | val (configOpt, _) = Misc.getResourceOption("/daffodil-config.xml") |
| | val configTunables: Map[String, String] = |
| | if (configOpt.isDefined) { |
| | val loader = new DaffodilXMLLoader() |
| | val node = loader.load(URISchemaSource(configOpt.get), Some(XMLUtils.dafextURI)) |
| | tunablesMap(node) |
| | } else { |
| | Map.empty |
| | } |
| | |
| | new DaffodilTunables().setTunables(configTunables) |
| | } |
| |} |
| | |
| |case class DaffodilTunables private ( |
| """.trim.stripMargin |
| |
| val middle = """ |
| | extends Serializable { |
| | |
| | def setTunables(tunables: Map[String, String]): DaffodilTunables = { |
| | tunables.foldLeft(this) { case (dafTuns, (tunable, value)) => dafTuns.setTunable(tunable, value) } |
| | } |
| | |
| | def setTunable(tunable: String, value: String): DaffodilTunables = { |
| | tunable match { |
| """.trim.stripMargin |
| |
| val bottom = """ |
| | case _ => throw new IllegalArgumentException("Unknown tunable: " + tunable) |
| | } |
| | } |
| | |
| | def notSuppressedWarning(warnID: WarnID) = |
| | !suppressSchemaDefinitionWarnings.contains(warnID) && |
| | !suppressSchemaDefinitionWarnings.contains(WarnID.All) |
| | |
| | private def throwInvalidTunableValue(tunable: String, value: String) = { |
| | throw new IllegalArgumentException("Invalid value for tunable " + tunable + ": " + value) |
| | } |
| | |
| |} |
| """.trim.stripMargin |
| |
| val tunablesRoot = (schemaRootConfig \ "element").find(_ \@ "name" == "tunables").get |
| val tunableNodes = tunablesRoot \\ "all" \ "element" |
| |
| val excludedSimpleTypes = Seq( |
| "TunableEmptyElementParsePolicy", |
| "TunableSuppressSchemaDefinitionWarnings") |
| val tunableSimpleTypeNodes = (schemaRootConfig \ "simpleType") |
| .filter { st => (st \@ "name").startsWith("Tunable") } |
| .filter { st => !excludedSimpleTypes.contains(st \@ "name") } |
| |
| def writeGeneratedCode(w: java.io.FileWriter): Unit = { |
| val tunables = |
| tunableNodes.map { tunableNode => |
| val schemaName = tunableNode \@ "name" |
| val schemaType = |
| if (tunableNode \@ "type" != "") tunableNode \@ "type" |
| else tunableNode \\ "restriction" \@ "base" |
| val schemaDefault = tunableNode \@ "default" |
| |
| if (schemaName == "") throw new Exception("Tunable missing mandatory name attribute: " + tunableNode) |
| if (schemaType == "") throw new Exception("Tunable missing mandatory type or restriction base attribute: " + schemaName) |
| if (schemaDefault == "") throw new Exception("Tunable missing mandatory default attribute: " + schemaName) |
| |
| val tunable = |
| if (schemaName == "suppressSchemaDefinitionWarnings") { |
| // special case, list of enums |
| new EnumListTunable(schemaName, schemaType, schemaDefault, "WarnID") |
| } else if (!schemaType.startsWith("xs:")) { |
| // non-primitive type, assume a single enum |
| new EnumTunable(schemaName, schemaType, schemaDefault) |
| } else if (schemaName == "tempFilePath") { |
| // special case, creates actual file object instead of string |
| new FileTunable("tempFilePath", """System.getProperty("java.io.tmpdir")""") |
| } else { |
| // primitive type |
| new PrimitiveTunable(schemaName, schemaType, schemaDefault, tunableNode) |
| } |
| tunable |
| }.sortBy(_.name) |
| |
| val definitionString = |
| tunables |
| .map(_.scalaDefinition) |
| .mkString(" ", ",\n ", ")") |
| |
| val conversionString = |
| tunables |
| .map { tunable => |
| tunable |
| .scalaConversion |
| .split("\n") |
| .filter(_.trim.length > 0) |
| .mkString(" ", "\n ", "") |
| } |
| .mkString("\n") |
| |
| w.write(top) |
| w.write("\n") |
| w.write(definitionString) |
| w.write("\n") |
| w.write(middle) |
| w.write("\n") |
| w.write(conversionString) |
| w.write("\n") |
| w.write(bottom) |
| w.write("\n") |
| w.write("\n") |
| |
| val tunableDefinitions = |
| tunableSimpleTypeNodes.map { n => |
| new TunableEnumDefinition(schemaRootConfig, schemaRootExt, n) |
| } |
| |
| w.write(tunableDefinitions.map(_.scalaEnumeration).mkString("\n")) |
| |
| w.flush(); |
| } |
| } |
| |
| abstract class TunableBase { |
| def scalaDefinition: String |
| def scalaConversion: String |
| def name: String |
| } |
| |
| class PrimitiveTunable( |
| override val name: String, |
| schemaType: String, |
| schemaDefault: String, |
| node: scala.xml.Node) |
| extends TunableBase { |
| |
| private val scalaType = schemaType match { |
| case "xs:boolean" => "Boolean" |
| case "xs:int" => "Int" |
| case "xs:long" => "Long" |
| case "xs:string" => "String" |
| case _ => throw new Exception("Type not supported for tunable: " + schemaType) |
| } |
| |
| private val scalaDefault = schemaType match { |
| case "xs:string" => "\"" + schemaDefault + "\"" |
| case _ => schemaDefault |
| } |
| |
| private def restrictionCheck(rCheck: String, rValue: String): Option[String] = { |
| if (rValue != "") { |
| Some(s""" if (!(v ${rCheck} ${rValue})) throwInvalidTunableValue(tunable, value)""") |
| } else { |
| None |
| } |
| } |
| |
| private val minInclusive = node \\ "minInclusive" \@ "value" |
| private val maxInclusive = node \\ "maxInclusive" \@ "value" |
| private val minExclusive = node \\ "minExclusive" \@ "value" |
| private val maxExclusive = node \\ "maxExclusive" \@ "value" |
| |
| private val restrictionChecks = Seq( |
| restrictionCheck(">=", minInclusive), |
| restrictionCheck("<=", maxInclusive), |
| restrictionCheck(">", minExclusive), |
| restrictionCheck("<", maxExclusive), |
| ).flatten.mkString("\n") |
| |
| override val scalaDefinition = s"""val ${name}: ${scalaType} = ${scalaDefault}""" |
| override val scalaConversion = s""" |
| |case "${name}" => { |
| | val v = scala.util.Try(value.to${scalaType}).getOrElse { throwInvalidTunableValue(tunable, value) } |
| |${restrictionChecks} |
| | this.copy(${name} = v) |
| |} |
| """.trim.stripMargin |
| } |
| |
| class FileTunable( |
| override val name: String, |
| default: String) |
| extends TunableBase { |
| |
| override val scalaDefinition = s"""val ${name}: java.io.File = new java.io.File(${default})""" |
| override val scalaConversion = s""" |
| |case "${name}" => { |
| | this.copy(${name} = new java.io.File(value)) |
| |} |
| """.trim.stripMargin |
| } |
| |
| class EnumTunable( |
| override val name: String, |
| schemaType: String, |
| schemaDefault: String) |
| extends TunableBase { |
| |
| private val scalaType = schemaType.stripPrefix("daf:Tunable") |
| private val scalaDefault = scalaType + "." + schemaDefault.head.toUpper + schemaDefault.tail |
| |
| override val scalaDefinition = s"""val ${name}: ${scalaType} = ${scalaDefault}""" |
| override val scalaConversion = s""" |
| |case "${name}" => { |
| | val vOpt = ${scalaType}.optionStringToEnum("${scalaType}", value) |
| | val v = vOpt.getOrElse { throwInvalidTunableValue(tunable, value) } |
| | this.copy(${name} = v) |
| |} |
| """.trim.stripMargin |
| } |
| |
| class EnumListTunable( |
| override val name: String, |
| schemaType: String, |
| schemaDefault: String, |
| listType: String) |
| extends TunableBase { |
| |
| val scalaDefault = { |
| val trimmedDefault = schemaDefault.trim |
| if (trimmedDefault == "") { |
| "Nil" |
| } else { |
| val defaultSeq = trimmedDefault.split("\\s+").map(d => s"${listType}.${d.head.toUpper + d.tail}") |
| s"""Seq(${defaultSeq.mkString(", ")})""" |
| } |
| |
| } |
| |
| override val scalaDefinition = s"val ${name}: Seq[${listType}] = ${scalaDefault}" |
| override val scalaConversion = s""" |
| |case "${name}" => { |
| | val values = value.split("\\\\s+").toSeq.map { v => |
| | val vOpt = ${listType}.optionStringToEnum("${listType}", v) |
| | vOpt.getOrElse { throwInvalidTunableValue(tunable, value) } |
| | } |
| | this.copy(${name} = values) |
| |} |
| """.trim.stripMargin |
| } |
| |
| class TunableEnumDefinition(schemaRootConfig: scala.xml.Node, schemaRootExt: scala.xml.Node, simpleTypeNode: scala.xml.Node) { |
| private val nodeName = (simpleTypeNode \@ "name").stripPrefix("Tunable") |
| private val scalaType = nodeName.head.toUpper + nodeName.tail |
| |
| /** |
| * Returns a list of all string values of enumerations. If a simpletype is a |
| * untion of other simple types, it recursively gets their enumeration values |
| * and combines everything. |
| */ |
| private def getAllEnumerationValues(node: scala.xml.Node): Seq[String] = { |
| val restrictions = node \\ "restriction" |
| restrictions.flatMap { r => |
| val base = r \@ "base" |
| val enumerationValues = |
| if (base.startsWith("xs:")) { |
| (r \ "enumeration").map(_ \@ "value") |
| } else { |
| val Array(pre, local) = base.split(":") |
| val schemaToSearch = pre match { |
| case "daf" => schemaRootConfig |
| case "dfdlx" => schemaRootExt |
| } |
| val restrictionSimpleTypeNode = |
| (schemaToSearch \ "simpleType").find(_ \@ "name" == base.split(":")(1)).get |
| getAllEnumerationValues(restrictionSimpleTypeNode) |
| } |
| enumerationValues |
| } |
| } |
| |
| private val allEnumerationValues = getAllEnumerationValues(simpleTypeNode) |
| |
| private val top = s""" |
| |sealed trait ${scalaType} extends ${scalaType}.Value |
| |object ${scalaType} extends Enum[${scalaType}] { |
| """.trim.stripMargin |
| |
| private val scalaEnums = { |
| val scalaEnumValues = allEnumerationValues.map { e => e.head.toUpper + e.tail } |
| scalaEnumValues.map { e => s""" case object ${e} extends ${scalaType}""" } |
| } |
| |
| private val values = { |
| val scalaEnumValues = allEnumerationValues.map { e => e.head.toUpper + e.tail } |
| scalaEnumValues.mkString(" override lazy val values = Array(", ", ", ")") |
| } |
| |
| private val bottom = s""" |
| | override def apply(name: String, context: ThrowsSDE) = Assert.usageError("not to be called. Use optionStringToEnum") |
| |} |
| """.stripMargin |
| |
| val scalaEnumeration = { |
| top + "\n" + scalaEnums.mkString("\n") + "\n" + values + "\n" + bottom |
| } |
| |
| } |