blob: 3ee2a2bd05197c7f09805fb9aa772979315e088d [file] [log] [blame]
/*
* 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.dsom
import scala.xml.Node
import org.apache.daffodil.dsom.IIUtils.IIMap
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.api.WarnID
import org.apache.daffodil.xml.XMLUtils
/**
* A schema document corresponds to one file usually named with an ".xsd" extension.
* The root element of a schema document is xsd:schema where xsd is the namespace for
* XML Schema.
*
* A schema document is important because it is the unit of lexical scoping of format
* properties in DFDL.
*
* Specifically, note that two schema documents may have the same target namespace, but
* different default formats scoped over their contents.
*
* When dealing with plain XML and XSD (not DFDL), the concept of Schema with a single
* target namespace is more used than the concept of schema document, which is usually
* only needed to issue diagnostic error messages ("In file foo.xsd, line 938...")
*
* Conversely, for DFDL, the concept of schema document is more used because schema documents
* are where default formats are specified, so it is very important what schema document
* a schema component was defined within.
*/
/**
* Handles everything about schema documents that has nothing to
* do with DFDL. Things like namespace, include, import, elementFormDefault
* etc.
*/
final class XMLSchemaDocument(
xmlArg: Node,
schemaSetArg: SchemaSet,
/**
* ii is the include or import statement DSOM object that
* lead to this schema document being imported/included.
*/
val ii: Option[IIBase],
sfArg: Option[DFDLSchemaFile],
seenBeforeArg: IIMap,
/**
* this flag lets us import into a bootstrap 'fake' document
* even though it does not have a namespace
*/
override val isBootStrapSD: Boolean)
extends SchemaComponentImpl(xmlArg, sfArg.getOrElse(schemaSetArg))
with SchemaDocIncludesAndImportsMixin {
requiredEvaluationsAlways(checkUnsupportedAttributes)
final lazy val seenBefore = seenBeforeArg
final override lazy val schemaFile = sfArg
final override lazy val xmlSchemaDocument = this
/**
* Error checks on the xs:schema element itself.
*
* E.g., we don't support the xsi:schemaLocation attribute. So we issue a warning for that.
* and some other attributes as well.
*/
private def qualOrUnqual(str: String, kind: String) = {
str match {
case "unqualified" => str
case "qualified" => str
case _ => schemaDefinitionError("Unrecognized value for %s FormDefault='%s'.", kind, str)
}
}
final lazy val elementFormDefault = {
val efdAttr = (xml \ "@elementFormDefault").text
if (efdAttr == "") "unqualified"
else qualOrUnqual(efdAttr, "element")
}
final lazy val attributeFormDefault = {
val afdAttr = (xml \ "@attributeFormDefault").text
if (afdAttr == "") "unqualified"
else qualOrUnqual(afdAttr, "attribute")
}
final def checkUnsupportedAttributes = LV('checkUnsupportedAttributes) {
val hasSchemaLocation = (xml \ "@schemaLocation").text != ""
val hasBlockDefault = (xml \ "@blockDefault").text != ""
val hasFinalDefault = (xml \ "@finalDefault").text != ""
val hasAttributeFormDefault = (xml \ "@attributeFormDefault").text != ""
schemaDefinitionWarningUnless(WarnID.UnsupportedAttributeSchemaLocation, !hasSchemaLocation, "schemaLocation is ignored.")
schemaDefinitionWarningUnless(WarnID.UnsupportedAttributeBlockDefault, !hasBlockDefault, "blockDefault is ignored")
schemaDefinitionWarningUnless(WarnID.UnsupportedAttributeFinalDefault, !hasFinalDefault, "finalDefault is ignored")
schemaDefinitionWarningUnless(WarnID.UnsupportedAttributeFormDefault, !hasAttributeFormDefault, "attributeFormDefault is not part of DFDL and will be ignored")
val res = hasSchemaLocation | hasBlockDefault | hasFinalDefault | hasAttributeFormDefault
res
}.value
/**
* True if root xs:schema element has an xmlns that uses the DFDL URI.
*
* This could be a prefix definition (Most likely xmlns:dfdl='...' but could
* be some other prefix.)
*
* Or very very unlikely, it could be the default namespace.
*/
private def hasDFDLNamespaceDefinition: Boolean = {
val scope = xml.scope
val pre = scope.getPrefix(XMLUtils.DFDL_NAMESPACE)
val hasSomePrefixForDFDLNamespace = pre ne null
lazy val hasDefaultNamespaceAsDFDLNamespace = {
val defaultNS = scope.getURI(null)
defaultNS == XMLUtils.DFDL_NAMESPACE.toString
}
val res = hasSomePrefixForDFDLNamespace || hasDefaultNamespaceAsDFDLNamespace
res
}
/**
* True if this is a DFDL schema that Daffodil should process.
* False if this schema should be ignored because it has no DFDL annotations.
*
* We will ignore this import/include if it does not use the DFDL namespace
* definition for a prefix (or the default) on the xs:schema element.
*
* We do this so that other annotation languages can co-exist with DFDL.
* That is, they can use all of XML Schema including things DFDL doesn't allow
* like attribute decls, but only when those schemas are only needed
* to process non-DFDL annotations. Since Daffodil ignores non-DFDL annotations
* entirely, Daffodil won't run into these non-DFDL allowed things like
* attribute decls. But validators like Xerces will see the regular
* import/include and process normally, which enables validation of
* all annotations, DFDL and otherwise.
*
* Further discussion - see the comments on JIRA ticket DAFFODIL-1909
*/
lazy val isDFDLSchema = hasDFDLNamespaceDefinition
}
/**
* Handles only things specific to DFDL about schema documents.
*
* I.e., default format properties, named format properties, etc.
*/
final class SchemaDocument(xmlSDoc: XMLSchemaDocument)
extends AnnotatedSchemaComponent {
final override val xml = xmlSDoc.xml
final override def optLexicalParent = Option(xmlSDoc)
final override lazy val xmlSchemaDocument = xmlSDoc
override lazy val optReferredToComponent = None
/**
* Implements the selectivity so that if you specify a root element
* to the compiler, then only that root element (and things reached from it)
* is compiled. Otherwise all top level elements are compiled.
*/
requiredEvaluationsAlways(defaultFormat)
if (schemaSet.checkAllTopLevel) {
requiredEvaluationsAlways(globalElementDecls.foreach{ _.setRequiredEvaluationsActive() })
requiredEvaluationsAlways(defineEscapeSchemes)
requiredEvaluationsAlways(defineFormats)
requiredEvaluationsAlways(defineVariables)
}
override lazy val schemaDocument = this
override lazy val schema = schemaSet.getSchema(targetNamespace).getOrElse {
Assert.invariantFailed("schema not found for schema document's namespace.")
}
protected def annotationFactory(node: Node): Option[DFDLAnnotation] = {
val res = node match {
case <dfdl:format>{ content @ _* }</dfdl:format> => new DFDLFormat(node, this)
case <dfdl:defineFormat>{ content @ _* }</dfdl:defineFormat> => new DFDLDefineFormat(node, this)
case <dfdl:defineEscapeScheme>{ content @ _* }</dfdl:defineEscapeScheme> => new DFDLDefineEscapeSchemeFactory(node, this)
case <dfdl:defineVariable>{ content @ _* }</dfdl:defineVariable> => new DFDLDefineVariable(node, this)
case _ => {
val prefix =
if (node.prefix == null || node.prefix == "") ""
else node.prefix + ":"
this.SDE("Invalid dfdl annotation found: %s", prefix + node.label)
}
}
Some(res)
}
protected lazy val emptyFormatFactory = new DFDLFormat(newDFDLAnnotationXML("format"), this)
protected def isMyFormatAnnotation(a: DFDLAnnotation) = a.isInstanceOf[DFDLFormat]
lazy val globalElementDecls = {
val xmlelts = (xml \ "element")
val decls = xmlelts.map { new GlobalElementDecl(_, this) }
decls
}
lazy val globalSimpleTypeDefs = (xml \ "simpleType").map { new GlobalSimpleTypeDef(_, this) }
lazy val globalComplexTypeDefs = (xml \ "complexType").map { new GlobalComplexTypeDef(_, this) }
lazy val globalGroupDefs = (xml \ "group").map { GlobalGroupDef(_, this) }
lazy val defaultFormat = formatAnnotation.asInstanceOf[DFDLFormat]
lazy val defineFormats = annotationObjs.collect { case df: DFDLDefineFormat => df }
lazy val defineEscapeSchemes = annotationObjs.collect { case des: DFDLDefineEscapeSchemeFactory => des }
lazy val defineVariables = annotationObjs.collect { case dv: DFDLDefineVariable => dv }
/**
* by name getters for the global things that can be referenced.
*/
def getGlobalElementDecl(name: String) = {
val geds = globalElementDecls
val res = geds.find { _.name == name }
res
}
def getGlobalSimpleTypeDef(name: String) = globalSimpleTypeDefs.find { _.name == name }
def getGlobalComplexTypeDef(name: String) = globalComplexTypeDefs.find { _.name == name }
def getGlobalGroupDef(name: String) = globalGroupDefs.find { _.name == name }
def getDefineFormat(name: String) = defineFormats.find {
df =>
val dfName = df.namedQName.local
val res = dfName == name
res
}
def getDefineVariable(name: String) = {
val res = defineVariables.find { _.name == name }
res
}
def getDefaultFormat = this.defaultFormat
def getDefineEscapeScheme(name: String) = defineEscapeSchemes.find { _.name == name }
}