scalaxb.compiler.xsd.Args.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scalaxb_2.13 Show documentation
Show all versions of scalaxb_2.13 Show documentation
scalaxb is an XML data-binding tool for Scala that supports W3C XML Schema (xsd) and wsdl.
The newest version!
/*
* Copyright (c) 2010 e.e d3si9n
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package scalaxb.compiler.xsd
import scalaxb.compiler.Log
trait Args extends Params {
private val logger = Log.forName("xsd.Args")
def buildFromXML(typeName: String): String = "scalaxb.fromXML[" + typeName + "]"
def buildFromXML(typeName: String, selector: String, stackItem: Option[String], formatter: Option[String]): String =
buildFromXML(typeName) + s"(${selector}, ${stackItem map {
case "stack" => "stack"
case x => "scalaxb.ElemName(" + x + ") :: stack"
} getOrElse {"Nil"}})${formatter map {"(" + _ + ")"} getOrElse {""}}"
def buildToXML(typeName: String, args: String): String =
"scalaxb.toXML[" + typeName + "](" + args + ")"
def buildFromString(typeName: String, selector: String): String =
typeName + ".fromString(" + selector + ")"
// called by buildConverter
def buildArg(selector: String, typeSymbol: XsTypeSymbol, stackItem: Option[String]): String = typeSymbol match {
case AnyType(symbol) => selector
case symbol: BuiltInSimpleTypeSymbol => buildArg(buildTypeName(symbol), selector, Single, stackItem)
case ReferenceTypeSymbol(decl: SimpleTypeDecl) => buildArg(buildTypeName(baseType(decl)), selector, Single, stackItem)
case ReferenceTypeSymbol(decl: ComplexTypeDecl) =>
buildFromXML(buildTypeName(typeSymbol), selector, stackItem, None)
}
def buildArg(typeName: String, selector: String, cardinality: Cardinality,
stackItem: Option[String],
nillable: Boolean = false, defaultValue: Option[String] = None, fixedValue: Option[String] = None,
wrapForLongAll: Boolean = false, formatter: Option[String] = None): String = {
val stack = "scalaxb.ElemName(node) :: stack"
def fromU = buildFromXML(typeName, "_", stackItem, formatter)
def fromValue(x: String) = buildFromXML(typeName, "scala.xml.Text(" + quote(x) + ")", stackItem, formatter)
val retval = if (wrapForLongAll) {
// PrefixedAttribute only contains pre, so you need to pass in node to get the namespace.
if (selector.contains("@"))
(defaultValue, fixedValue) match {
case (_, Some(x)) =>
"Some(scalaxb.DataRecord(None, None, " + fromValue(x) + "))"
case (Some(x), _) =>
selector + ".headOption map { x => scalaxb.DataRecord(x, node, " +
buildFromXML(typeName, "x", stackItem, formatter) + ") } orElse " +
"Some(scalaxb.DataRecord(None, None, " + fromValue(x) + "))"
case _ =>
selector + ".headOption map { x => scalaxb.DataRecord(x, node, " +
buildFromXML(typeName, "x", stackItem, formatter) + ") }"
}
else selector + ".headOption map { x => scalaxb.DataRecord(x, " +
buildFromXML(typeName, "x", stackItem, formatter) + ") }"
} else (cardinality, nillable, (!config.varArg)) match {
case (Multiple, true, true) => selector + " map { x => scalaxb.ElemName(x).nilOption map { " + fromU + " }}"
case (Multiple, false, true) => selector + " map { " + fromU + " }"
case (Multiple, true, false) => selector + ".toSeq map { x => scalaxb.ElemName(x).nilOption map { " + fromU + " }}"
case (Multiple, false, false) => selector + ".toSeq map { " + fromU + " }"
case (Optional, true, _) => selector + ".headOption map { x => scalaxb.ElemName(x).nilOption map { " + fromU + " }}"
case (Optional, false, _) => selector + ".headOption map { " + fromU + " }"
case (Single, _, _) =>
buildSingleArg(typeName, selector, stackItem, nillable, defaultValue, fixedValue, formatter)
}
retval
}
def buildSingleArg(typeName: String, selector: String, stackItem: Option[String],
nillable: Boolean, defaultValue: Option[String],
fixedValue: Option[String], formatter: Option[String]): String = {
def fromValue(x: String) = buildFromXML(typeName, "scala.xml.Text(" + quote(x) + ")", stackItem, formatter)
def fromX(x: String) = buildFromXML(typeName, x, stackItem, formatter)
(nillable, defaultValue, fixedValue) match {
case ( _, _, Some(x)) => fromValue(x)
case (true, _, _) => "scalaxb.ElemName(" + selector + ").nilOption map { " + fromX("_") + " }"
case (_, Some(x), _) => selector + ".headOption map { " + fromX("_") + " } getOrElse { " + fromValue(x) + " }"
case (false, _, _) => fromX(selector)
}
}
def buildArg(decl: Decl): String = decl match {
case elem: ElemDecl => buildArg(elem, 0)
case attr: AttributeDecl => buildArg(attr, buildSelector(attr), Some("node"), false)
case ref: AttributeRef => buildArg(buildAttribute(ref))
case group: AttributeGroupDecl => buildAttributeGroupArg(group, false)
case _ => sys.error("GenSource#buildArg unsupported delcaration " + decl.toString)
}
def buildArgForAttribute(decl: AttributeLike, stackItem: Option[String], longAttribute: Boolean): String =
if (longAttribute) {
decl match {
case attr: AttributeDecl =>
val o = attr.copy(use = OptionalUse)
val arg = buildArg(o, buildSelector(o), stackItem, longAttribute)
if (longAttribute) {
arg + " map { " + quote(buildNodeName(o, false)) + " -> _ }"
}
else arg
case ref: AttributeRef => buildArgForAttribute(buildAttribute(ref), stackItem, longAttribute)
case group: AttributeGroupDecl => buildAttributeGroupArg(group, longAttribute)
}
} else buildArg(decl)
def toOptional(that: ElemDecl) = that.copy(minOccurs = 0, annotation = None)
// called by makeCaseClassWithType. By spec, contains only elements.
def buildArgForAll(particle: Particle, longAll: Boolean): String = {
val o = particle match {
case elem: ElemDecl => toOptional(elem)
case ref: ElemRef => toOptional(buildElement(ref))
case _ => sys.error("buildArgForAll unsupported type: " + particle)
}
val arg = buildArg(o, buildSelector(o), Some("node"), longAll)
if (longAll) arg + " map { " + quote(buildNodeName(o, true)) + " -> _ }"
else arg
}
def buildArg(elem: ElemDecl, pos: Int): String =
buildArg(elem, buildSelector(pos), Some("node"), false)
def buildArg(elem: ElemDecl, selector: String, stackItem: Option[String], wrapForLongAll: Boolean): String =
if ((isSubstitutionGroup(elem))) selector
else elem.typeSymbol match {
case symbol: BuiltInSimpleTypeSymbol => buildArg(buildTypeName(symbol), selector,
toCardinality(elem.minOccurs, elem.maxOccurs), stackItem,
elem.nillable getOrElse(false), elem.defaultValue, elem.fixedValue, wrapForLongAll)
case ReferenceTypeSymbol(decl: SimpleTypeDecl) =>
buildArg(buildTypeName(decl, false), selector,
toCardinality(elem.minOccurs, elem.maxOccurs), stackItem,
elem.nillable getOrElse(false), elem.defaultValue, elem.fixedValue, wrapForLongAll)
case ReferenceTypeSymbol(decl: ComplexTypeDecl) =>
if (compositorWrapper.contains(decl))
(toCardinality(elem.minOccurs, elem.maxOccurs), elem.nillable getOrElse {false}) match {
case (Multiple, _) if config.varArg => selector + ".toSeq"
case (Optional, true) => selector + " getOrElse { None }"
case _ => selector
}
else buildArg(buildTypeName(decl, false), selector,
toCardinality(elem.minOccurs, elem.maxOccurs), stackItem,
elem.nillable getOrElse(false), elem.defaultValue, elem.fixedValue, wrapForLongAll)
case AnyType(symbol) => buildArg(
if (elem.nillable getOrElse(false)) buildTypeName(XsNillableAny)
else buildTypeName(symbol), selector,
toCardinality(elem.minOccurs, elem.maxOccurs), stackItem,
false, elem.defaultValue, elem.fixedValue, wrapForLongAll)
case symbol: ReferenceTypeSymbol =>
if (symbol.decl == null) sys.error("GenSource#buildArg: " + elem.toString + " Invalid type " + symbol.getClass.toString + ": " +
symbol.toString + " with null decl")
else sys.error("GenSource#buildArg: " + elem.toString + " Invalid type " + symbol.getClass.toString + ": " +
symbol.toString + " with " + symbol.decl.toString)
case _ => sys.error("GenSource#buildArg: " + elem.toString + " Invalid type " + elem.typeSymbol.getClass.toString + ": " + elem.typeSymbol.toString)
}
def buildArg(attr: AttributeDecl, selector: String, stackItem: Option[String], wrapForLong: Boolean): String =
attr.typeSymbol match {
// special treatment for QName attributes
case XsQName =>
buildArg(buildTypeName(XsQName), selector, toCardinality(attr), stackItem, false,
attr.defaultValue, attr.fixedValue, wrapForLong, Some("scalaxb.XMLStandardTypes.qnameXMLFormat(node.scope)"))
case symbol: BuiltInSimpleTypeSymbol =>
buildArg(buildTypeName(symbol), selector, toCardinality(attr), stackItem, false,
attr.defaultValue, attr.fixedValue, wrapForLong)
// special treatment for QName attributes
case ReferenceTypeSymbol(decl: SimpleTypeDecl) if buildTypeName(decl, false) == buildTypeName(XsQName) =>
buildArg(buildTypeName(decl, false), selector, toCardinality(attr), stackItem, false,
attr.defaultValue, attr.fixedValue, wrapForLong, Some("scalaxb.XMLStandardTypes.qnameXMLFormat(node.scope)"))
case ReferenceTypeSymbol(decl: SimpleTypeDecl) =>
buildArg(buildTypeName(decl, false), selector, toCardinality(attr), stackItem, false,
attr.defaultValue, attr.fixedValue, wrapForLong)
case ReferenceTypeSymbol(decl: ComplexTypeDecl) =>
sys.error("Args: Attribute with complex type " + decl.toString)
case _ =>
sys.error("Args: unsupported type: " + attr.typeSymbol)
}
// called by makeCaseClassWithType
def buildArg(content: SimpleContentDecl, typeSymbol: XsTypeSymbol): String = typeSymbol match {
case AnyType(symbol) => buildArg(buildTypeName(symbol), "node", Single, Some("node"))
case base: BuiltInSimpleTypeSymbol => buildArg(buildTypeName(base), "node", Single, Some("node"))
case ReferenceTypeSymbol(decl: ComplexTypeDecl) =>
decl.content match {
case simp@SimpleContentDecl(SimpContRestrictionDecl(base: XsTypeSymbol, _, _, _)) => buildArg(simp, base)
case simp@SimpleContentDecl(SimpContExtensionDecl(base: XsTypeSymbol, _)) => buildArg(simp, base)
// http://www.w3.org/TR/xmlschema-1/#d0e7923
case comp: ComplexContentDecl => content match {
case SimpleContentDecl(SimpContRestrictionDecl(_, Some(simpleType: XsTypeSymbol), _, _)) =>
buildArg(content, simpleType)
case _ => sys.error("Args: Unsupported content " + content.toString)
}
case _ => sys.error("Args: Unsupported content " + content.toString)
}
case ReferenceTypeSymbol(decl: SimpleTypeDecl) =>
buildArg(buildTypeName(decl, false), "node", Single, Some("node"), false, None, None, false)
case _ => sys.error("Args: Unsupported type " + typeSymbol.toString)
}
// scala's \ "foo" syntax is not namespace aware, but {ns}foo is useful for long all.
def buildNodeName(elem: ElemDecl, prependNamespace: Boolean): String =
if (elem.global) elem.namespace match {
case None => elem.name
case Some(ns) =>
if (prependNamespace) s"{${ns}}" + elem.name
else elem.name
}
else elem.name
// scala's \ "@{uri}attr1" syntax requires ns, but {uri} should be ommitted for long attr.
def buildNodeName(attr: AttributeDecl, prependNamespace: Boolean): String =
if (attr.global) "@" + (attr.namespace map { "{" + _ + "}" } getOrElse {""}) + attr.name
else if (prependNamespace && attr.qualified) "@" +
(elementNamespace(attr.global, attr.namespace, attr.qualified) map { "{" + _ + "}" } getOrElse {""}) + attr.name
else "@" + attr.name
def buildNodeName(group: AttributeGroupDecl): String =
if (group.namespace == schema.targetNamespace) group.name
else (group.namespace map { "{" + _ + "}" } getOrElse {""}) + group.name
def buildSelector(elem: ElemDecl): String = buildSelector(buildNodeName(elem, false))
def buildSelector(pos: Int): String = "p" + (pos + 1)
def buildSelector(attr: AttributeDecl): String = buildSelector(buildNodeName(attr, true))
def buildSelector(nodeName: String): String = "(node \\ \"" + nodeName + "\")"
def buildArgForAnyAttribute(parent: ComplexTypeDecl, longAttribute: Boolean): String =
buildArgForAnyAttribute(flattenAttributes(parent), longAttribute)
def buildArgForAnyAttribute(parent: AttributeGroupDecl, longAttribute: Boolean): String =
buildArgForAnyAttribute(flattenAttributes(parent.attributes), longAttribute)
def buildArgForAnyAttribute(attributes: List[AttributeLike], longAttribute: Boolean): String = {
def makeCaseEntry(attr: AttributeDecl) = if (attr.global || attr.qualified)
"case scala.xml.PrefixedAttribute(pre, key, value, _) if pre == elem.scope.getPrefix(" +
(attr.namespace map { quote(_) } getOrElse { "null" }) + ") &&" + newline +
indent(8) + "key == " + quote(attr.name) + " => Nil"
else "case scala.xml.UnprefixedAttribute(key, value, _) if key == " + quote(attr.name) + " => Nil"
val xs = "node match {" + newline +
indent(5) + "case elem: scala.xml.Elem =>" + newline +
indent(5) + " elem.attributes.toList flatMap {" + newline +
attributes.collect {
case x: AttributeDecl => makeCaseEntry(x)
}.mkString(indent(7), newline + indent(7), newline) +
indent(5) + " case scala.xml.UnprefixedAttribute(key, value, _) =>" + newline +
indent(5) + " List((\"@\" + key, scalaxb.DataRecord(None, Some(key), value.text)))" + newline +
indent(5) + " case scala.xml.PrefixedAttribute(pre, key, value, _) =>" + newline +
indent(5) + " val ns = elem.scope.getURI(pre)" + newline +
indent(5) + " List((\"@{\" + ns + \"}\" + key, scalaxb.DataRecord(Option[String](ns), Some(key), value.text)))" + newline +
indent(5) + " case _ => Nil" + newline +
indent(5) + " }" + newline +
indent(5) + "case _ => Nil" + newline +
indent(4) + "}"
if (longAttribute) xs
else "scala.collection.immutable.ListMap((" + xs + "): _*)"
}
def buildArgForMixed(particle: Particle, pos: Int, ignoreSubGroup: Boolean): String =
buildArgForMixed(particle, buildSelector(pos), ignoreSubGroup)
// sub groups behave like a choice
def buildArgForMixed(particle: Particle, selector: String, ignoreSubGroup: Boolean): String = {
val cardinality = toCardinality(particle.minOccurs, particle.maxOccurs)
def isCompositor(p: Particle): Boolean = p match {
case ref: GroupRef => true
case ref: ElemRef => isCompositor(buildElement(ref))
case elem: ElemDecl =>
if (!ignoreSubGroup && isSubstitutionGroup(elem)) true
else elem.typeSymbol match {
case AnyType(symbol) => true
case ReferenceTypeSymbol(decl: ComplexTypeDecl) =>
if (compositorWrapper.contains(decl)) true
else false
case _ => false
}
case x: HasParticle => true
case _ => false
}
val retval = cardinality match {
case Multiple =>
if (isCompositor(particle)) selector + ".flatten"
else selector
case Optional =>
if (isCompositor(particle)) selector + " getOrElse {Nil}"
else selector + ".toList"
case Single =>
if (isCompositor(particle)) selector
else "Seq(" + selector + ")"
}
logger.debug("buildArgForMixed: " + cardinality + ": " + particle + ": " + retval)
retval
}
def buildArgForOptTextRecord(pos: Int): String =
buildSelector(pos) + ".toList"
def buildAttributeGroupArg(group: AttributeGroupDecl, longAttribute: Boolean): String = {
val formatterName = buildFormatterName(group)
val arg = formatterName + ".reads(node).right"
if (longAttribute) arg + ".toOption map { x => " +
quote(buildNodeName(group)) + " -> scalaxb.DataRecord(None, None, x) }"
else arg + ".get"
}
def flattenAttributes(decl: ComplexTypeDecl): List[AttributeLike] = {
val attributes = mergeAttributes(decl.content.content match {
case SimpContRestrictionDecl(ReferenceTypeSymbol(base: ComplexTypeDecl), _, _, _) => flattenAttributes(base)
case SimpContExtensionDecl(ReferenceTypeSymbol(base: ComplexTypeDecl), _) => flattenAttributes(base)
case CompContRestrictionDecl(ReferenceTypeSymbol(base: ComplexTypeDecl), _, _) => flattenAttributes(base)
case CompContExtensionDecl(ReferenceTypeSymbol(base: ComplexTypeDecl), _, _) => flattenAttributes(base)
case _ => Nil
}, flattenAttributes(decl.content.content.attributes))
// rearrange attributes so AnyAttributeDecl comes at the end.
val notAnyAttributes = attributes filter {
case any: AnyAttributeDecl => false
case _ => true
}
val anyAttributes = attributes filter {
case any: AnyAttributeDecl => true
case _ => false
}
if (anyAttributes.isEmpty) notAnyAttributes
else notAnyAttributes ::: List(anyAttributes.head)
}
// return a list of either AttributeDecl or AnyAttributeDecl
def flattenAttributes(attributes: List[AttributeLike]): List[AttributeLike] =
attributes flatMap {
case any: AnyAttributeDecl => List(any)
case attr: AttributeDecl => List(attr)
case ref: AttributeRef => List(buildAttribute(ref))
case group: AttributeGroupDecl => flattenAttributes(group.attributes)
case ref: AttributeGroupRef => flattenAttributes(buildAttributeGroup(ref).attributes)
}
def mergeAttributes(parent: List[AttributeLike],
child: List[AttributeLike]): List[AttributeLike] = child match {
case x :: xs => mergeAttributes(mergeAttributes(parent, x), xs)
case Nil => parent
}
def mergeAttributes(parent: List[AttributeLike],
child: AttributeLike): List[AttributeLike] =
if (!parent.exists(x => isSameAttribute(x, child))) parent ::: List(child)
else parent.map (x =>
if (isSameAttribute(x, child)) child match {
// since OO's hierarchy does not allow base members to be ommited,
// child overrides needs to be implemented some other way.
case attr: AttributeDecl =>
Some(x)
case _ => Some(x)
}
else Some(x) ).flatten
def isSameAttribute(lhs: AttributeLike, rhs: AttributeLike) = {
def resolveRef(attribute: AttributeLike): AttributeLike = attribute match {
case any: AnyAttributeDecl => any
case attr: AttributeDecl => attr
case ref: AttributeRef => buildAttribute(ref)
case group: AttributeGroupDecl => group
case ref: AttributeGroupRef => buildAttributeGroup(ref)
}
(resolveRef(lhs), resolveRef(rhs)) match {
case (x: AnyAttributeDecl, y: AnyAttributeDecl) => true
case (x: AttributeDecl, y: AttributeDecl) =>
x.name == y.name &&
x.global == y.global &&
(x.namespace == y.namespace ||
(!x.global && !y.global))
case (x: AttributeGroupDecl, y: AttributeGroupDecl) =>
(x.name == y.name && x.namespace == y.namespace)
case _ => false
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy