You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
693 lines
36 KiB
693 lines
36 KiB
5 years ago
|
#
|
||
|
# minixsv, Release 0.9.0
|
||
|
# file: xsvalSchema.py
|
||
|
#
|
||
|
# Derived validator class (for validation of schema files)
|
||
|
#
|
||
|
# history:
|
||
|
# 2004-10-07 rl created
|
||
|
# 2006-08-18 rl W3C testsuite passed for supported features
|
||
|
# 2007-05-24 rl Features for release 0.8 added, several bugs fixed
|
||
|
#
|
||
|
# Copyright (c) 2004-2008 by Roland Leuthe. All rights reserved.
|
||
|
#
|
||
|
# --------------------------------------------------------------------
|
||
|
# The minixsv XML schema validator is
|
||
|
#
|
||
|
# Copyright (c) 2004-2008 by Roland Leuthe
|
||
|
#
|
||
|
# By obtaining, using, and/or copying this software and/or its
|
||
|
# associated documentation, you agree that you have read, understood,
|
||
|
# and will comply with the following terms and conditions:
|
||
|
#
|
||
|
# Permission to use, copy, modify, and distribute this software and
|
||
|
# its associated documentation for any purpose and without fee is
|
||
|
# hereby granted, provided that the above copyright notice appears in
|
||
|
# all copies, and that both that copyright notice and this permission
|
||
|
# notice appear in supporting documentation, and that the name of
|
||
|
# the author not be used in advertising or publicity
|
||
|
# pertaining to distribution of the software without specific, written
|
||
|
# prior permission.
|
||
|
#
|
||
|
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||
|
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
||
|
# ABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||
|
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
||
|
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||
|
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||
|
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||
|
# OF THIS SOFTWARE.
|
||
|
# --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
import string
|
||
|
import re
|
||
|
import os
|
||
|
from decimal import Decimal
|
||
|
from ..genxmlif.xmlifUtils import collapseString
|
||
|
from ..minixsv import *
|
||
|
from xsvalBase import XsValBase, TagException
|
||
|
from xsvalUtils import substituteSpecialEscChars
|
||
|
|
||
|
_localFacetDict = {(XSD_NAMESPACE,"list"): ("length", "minLength", "maxLength", "enumeration", "pattern", "whiteSpace"),
|
||
|
(XSD_NAMESPACE,"union"): ("enumeration", "pattern", "whiteSpace"),
|
||
|
(XSD_NAMESPACE,"anySimpleType"): ("whiteSpace"),}
|
||
|
###########################################################
|
||
|
# Derived validator class for validating one input schema file against the XML rules file
|
||
|
|
||
|
class XsValSchema (XsValBase):
|
||
|
|
||
|
########################################
|
||
|
# overloaded validate method
|
||
|
#
|
||
|
def validate (self, inputTree, xsdTree):
|
||
|
XsValBase.validate(self, inputTree, xsdTree)
|
||
|
|
||
|
self._initInternalAttributes (self.inputRoot)
|
||
|
self._updateLookupTables (self.inputRoot, self.xsdLookupDict)
|
||
|
|
||
|
self._includeAndImport (self.inputTree, self.inputTree, self.xsdIncludeDict, self.xsdLookupDict)
|
||
|
|
||
|
if not self.errorHandler.hasErrors():
|
||
|
# IDs must be unique within a schema file
|
||
|
self.xsdIdDict = {}
|
||
|
|
||
|
self._checkSchemaSecondLevel()
|
||
|
|
||
|
# FIXME: Wellknown schemas are not included in the input tree although the internal attribute has been set!
|
||
|
# Better solution required than this workaround!
|
||
|
self.inputRoot["__WellknownSchemasImported__"] = "false"
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for schema files which are not covered by "xsStructs.xsd"
|
||
|
#
|
||
|
def _checkSchemaSecondLevel(self):
|
||
|
|
||
|
targetNamespace = self.inputRoot.getAttribute("targetNamespace")
|
||
|
if targetNamespace == "":
|
||
|
self.errorHandler.raiseError("Empty string not allowed for target namespace!", self.inputRoot)
|
||
|
|
||
|
self._checkElementNodesSecondLevel()
|
||
|
self._checkNotationNodesSecondLevel()
|
||
|
self._checkAnyNodesSecondLevel()
|
||
|
self._checkGroupNodesSecondLevel()
|
||
|
self._checkAttrGroupNodesSecondLevel()
|
||
|
self._checkAttributeNodesSecondLevel()
|
||
|
self._checkAnyAttributesSecondLevel()
|
||
|
|
||
|
if self.errorHandler.hasErrors():
|
||
|
return
|
||
|
|
||
|
self._checkComplexTypesSecondLevel()
|
||
|
self._checkSimpleTypesSecondLevel()
|
||
|
|
||
|
self._checkParticlesSecondLevel()
|
||
|
|
||
|
self._checkIdentityConstraintsSecondLevel()
|
||
|
self._checkKeysSecondLevel()
|
||
|
self._checkKeyRefsSecondLevel()
|
||
|
|
||
|
########################################
|
||
|
# additional checks for element nodes
|
||
|
#
|
||
|
def _checkElementNodesSecondLevel(self):
|
||
|
elementNodes = self.inputRoot.getElementsByTagNameNS (self.inputNsURI, "element")
|
||
|
for elementNode in elementNodes:
|
||
|
if not elementNode.hasAttribute("name") and not elementNode.hasAttribute("ref"):
|
||
|
self._addError ("Element must have 'name' or 'ref' attribute!", elementNode)
|
||
|
continue
|
||
|
|
||
|
if elementNode.hasAttribute("ref"):
|
||
|
for attrName in ("name", "type", "form"):
|
||
|
if elementNode.hasAttribute(attrName):
|
||
|
self._addError ("Element with 'ref' attribute must not have %s attribute!" %repr(attrName), elementNode)
|
||
|
continue
|
||
|
|
||
|
complexTypeNode = elementNode.getFirstChildNS (self.inputNsURI, "complexType")
|
||
|
simpleTypeNode = elementNode.getFirstChildNS (self.inputNsURI, "simpleType")
|
||
|
if elementNode.hasAttribute("ref") and (complexTypeNode != None or simpleTypeNode != None):
|
||
|
self._addError ("Element with 'ref' attribute must not have type definition!", elementNode)
|
||
|
continue
|
||
|
if elementNode.hasAttribute("type") and (complexTypeNode != None or simpleTypeNode != None):
|
||
|
self._addError ("Element with 'type' attribute must not have type definition!", elementNode)
|
||
|
continue
|
||
|
|
||
|
if elementNode.hasAttribute("ref"):
|
||
|
for forbiddenAttr in ("block", "nillable", "default", "fixed"):
|
||
|
if elementNode.hasAttribute(forbiddenAttr):
|
||
|
self._addError ("Element with 'ref' attribute must not have %s attribute!" %repr(forbiddenAttr), elementNode)
|
||
|
|
||
|
self._checkReference (elementNode, self.xsdElementDict)
|
||
|
|
||
|
if elementNode.hasAttribute("type"):
|
||
|
self._checkType (elementNode, "type", self.xsdTypeDict)
|
||
|
|
||
|
self._checkNodeId(elementNode)
|
||
|
self._checkOccurs (elementNode)
|
||
|
self._checkFixedDefault(elementNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for notation nodes
|
||
|
#
|
||
|
def _checkNotationNodesSecondLevel(self):
|
||
|
notationNodes = self.inputRoot.getElementsByTagNameNS (self.inputNsURI, "notation")
|
||
|
for notationNode in notationNodes:
|
||
|
if not notationNode.hasAttribute("public") and not notationNode.hasAttribute("system"):
|
||
|
self._addError ("Notation must have 'public' or 'system' attribute!", notationNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for anyNodes
|
||
|
#
|
||
|
def _checkAnyNodesSecondLevel(self):
|
||
|
anyNodes = self.inputRoot.getElementsByTagNameNS (self.inputNsURI, "any")
|
||
|
for anyNode in anyNodes:
|
||
|
self._checkOccurs (anyNode)
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (anyNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for group nodes
|
||
|
#
|
||
|
def _checkGroupNodesSecondLevel(self):
|
||
|
groupNodes = self.inputRoot.getElementsByTagNameNS (self.inputNsURI, "group")
|
||
|
for groupNode in groupNodes:
|
||
|
self._checkNodeId(groupNode)
|
||
|
if groupNode.hasAttribute("ref"):
|
||
|
self._checkReference (groupNode, self.xsdGroupDict)
|
||
|
self._checkOccurs (groupNode)
|
||
|
if self.errorHandler.hasErrors():
|
||
|
return
|
||
|
# for groupNode in groupNodes:
|
||
|
# if groupNode.hasAttribute("name"):
|
||
|
# self._checkGroupNodeCircularDef(groupNode, {groupNode["name"]:1})
|
||
|
|
||
|
def _checkGroupNodeCircularDef(self, groupNode, groupNameDict):
|
||
|
childGroupsRefNodes, dummy, dummy = groupNode.getXPathList (".//%sgroup" %(self.inputNsPrefixString))
|
||
|
for childGroupRefNode in childGroupsRefNodes:
|
||
|
if childGroupRefNode.hasAttribute("ref"):
|
||
|
childGroupNode = self.xsdGroupDict[childGroupRefNode.getQNameAttribute("ref")]
|
||
|
if not groupNameDict.has_key(childGroupNode["name"]):
|
||
|
groupNameDict[childGroupNode["name"]] = 1
|
||
|
self._checkGroupNodeCircularDef(childGroupNode, groupNameDict)
|
||
|
else:
|
||
|
self._addError ("Circular definition of group %s!" %repr(childGroupNode["name"]), childGroupNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for attributeGroup nodes
|
||
|
#
|
||
|
def _checkAttrGroupNodesSecondLevel(self):
|
||
|
attributeGroupNodes = self.inputRoot.getElementsByTagNameNS (self.inputNsURI, "attributeGroup")
|
||
|
for attributeGroupNode in attributeGroupNodes:
|
||
|
if attributeGroupNode.hasAttribute("ref"):
|
||
|
self._checkReference (attributeGroupNode, self.xsdAttrGroupDict)
|
||
|
|
||
|
self._checkNodeId(attributeGroupNode)
|
||
|
|
||
|
########################################
|
||
|
# additional checks for attribute nodes
|
||
|
#
|
||
|
def _checkAttributeNodesSecondLevel(self):
|
||
|
attributeNodes = self.inputRoot.getElementsByTagNameNS (XSD_NAMESPACE, "attribute")
|
||
|
for attributeNode in attributeNodes:
|
||
|
if os.path.basename(attributeNode.getFilePath()) != "XMLSchema-instance.xsd":
|
||
|
# global attributes must always be "qualified"
|
||
|
if (attributeNode.getParentNode() == self.inputRoot or
|
||
|
self._getAttributeFormDefault(attributeNode) == "qualified"):
|
||
|
if self._getTargetNamespace(attributeNode) == XSI_NAMESPACE:
|
||
|
self._addError ("Target namespace of an attribute must not match '%s'!" %XSI_NAMESPACE, attributeNode)
|
||
|
|
||
|
if not attributeNode.hasAttribute("name") and not attributeNode.hasAttribute("ref"):
|
||
|
self._addError ("Attribute must have 'name' or 'ref' attribute!", attributeNode)
|
||
|
continue
|
||
|
|
||
|
if attributeNode.getAttribute("name") == "xmlns":
|
||
|
self._addError ("Attribute must not match 'xmlns'!", attributeNode)
|
||
|
|
||
|
if attributeNode.hasAttribute("ref"):
|
||
|
if attributeNode.hasAttribute("name"):
|
||
|
self._addError ("Attribute may have 'name' OR 'ref' attribute!", attributeNode)
|
||
|
if attributeNode.hasAttribute("type"):
|
||
|
self._addError ("Attribute may have 'type' OR 'ref' attribute!", attributeNode)
|
||
|
if attributeNode.hasAttribute("form"):
|
||
|
self._addError ("Attribute 'form' is not allowed in this context!", attributeNode)
|
||
|
|
||
|
if attributeNode.getFirstChildNS(XSD_NAMESPACE, "simpleType") != None:
|
||
|
self._addError ("Attribute may only have 'ref' attribute OR 'simpleType' child!", attributeNode)
|
||
|
|
||
|
self._checkReference (attributeNode, self.xsdAttributeDict)
|
||
|
|
||
|
if attributeNode.hasAttribute("type"):
|
||
|
if attributeNode.getFirstChildNS(XSD_NAMESPACE, "simpleType") != None:
|
||
|
self._addError ("Attribute may only have 'type' attribute OR 'simpleType' child!", attributeNode)
|
||
|
|
||
|
self._checkType (attributeNode, "type", self.xsdTypeDict, (XSD_NAMESPACE, "simpleType"))
|
||
|
|
||
|
use = attributeNode.getAttribute("use")
|
||
|
if use in ("required", "prohibited") and attributeNode.hasAttribute("default"):
|
||
|
self._addError ("Attribute 'default' is not allowed, because 'use' is '%s'!" %(use), attributeNode)
|
||
|
|
||
|
self._checkNodeId(attributeNode, unambiguousPerFile=0)
|
||
|
|
||
|
self._checkFixedDefault(attributeNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for attribute wildcards
|
||
|
#
|
||
|
def _checkAnyAttributesSecondLevel(self):
|
||
|
anyAttributeNodes, dummy, dummy = self.inputRoot.getXPathList (".//%sanyAttribute" %(self.inputNsPrefixString))
|
||
|
for anyAttributeNode in anyAttributeNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (anyAttributeNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for complex types
|
||
|
#
|
||
|
def _checkComplexTypesSecondLevel(self):
|
||
|
prefix = self.inputNsPrefixString
|
||
|
contentNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)scomplexContent/%(prefix)srestriction | .//%(prefix)scomplexContent/%(prefix)sextension" % vars())
|
||
|
for contentNode in contentNodes:
|
||
|
self._checkType(contentNode, "base", self.xsdTypeDict, (XSD_NAMESPACE, "complexType"))
|
||
|
|
||
|
contentNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)ssimpleContent/%(prefix)srestriction | .//%(prefix)ssimpleContent/%(prefix)sextension" % vars())
|
||
|
for contentNode in contentNodes:
|
||
|
baseNsName = contentNode.getQNameAttribute("base")
|
||
|
if baseNsName != (XSD_NAMESPACE, "anyType"):
|
||
|
typeNsName = contentNode.getParentNode().getNsName()
|
||
|
self._checkBaseType(contentNode, baseNsName, self.xsdTypeDict, typeNsName)
|
||
|
else:
|
||
|
self._addError ("Referred type must not be 'anyType'!", contentNode)
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (contentNode)
|
||
|
|
||
|
complexTypeNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)scomplexType | .//%(prefix)sextension" % vars())
|
||
|
for complexTypeNode in complexTypeNodes:
|
||
|
validAttrDict = {}
|
||
|
# check for duplicate attributes
|
||
|
self._updateAttributeDict (complexTypeNode, validAttrDict, 1)
|
||
|
# check for duplicate ID attributes
|
||
|
idAttrNode = None
|
||
|
for key, val in validAttrDict.items():
|
||
|
attrType = val["RefNode"].getQNameAttribute("type")
|
||
|
if attrType == (XSD_NAMESPACE, "ID"):
|
||
|
if not idAttrNode:
|
||
|
idAttrNode = val["Node"]
|
||
|
else:
|
||
|
# TODO: check also if attribute has a type which is derived from ID!
|
||
|
self._addError ("Two attribute declarations of complex type are IDs!", val["Node"])
|
||
|
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (complexTypeNode)
|
||
|
|
||
|
contentNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)scomplexType/%(prefix)s*" % vars())
|
||
|
for contentNode in contentNodes:
|
||
|
self._checkOccurs (contentNode)
|
||
|
|
||
|
contentNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)scomplexContent | .//%(prefix)ssimpleContent" % vars())
|
||
|
for contentNode in contentNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (contentNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for simple types
|
||
|
#
|
||
|
def _checkParticlesSecondLevel(self):
|
||
|
prefix = self.inputNsPrefixString
|
||
|
# check for duplicate element names
|
||
|
particleNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)sall | .//%(prefix)schoice | .//%(prefix)ssequence" % vars())
|
||
|
for particleNode in particleNodes:
|
||
|
elementTypeDict = {}
|
||
|
elementNameDict = {}
|
||
|
groupNameDict = {}
|
||
|
self._checkContainedElements (particleNode, particleNode.getLocalName(), elementNameDict, elementTypeDict, groupNameDict)
|
||
|
self._checkOccurs (particleNode)
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (particleNode)
|
||
|
|
||
|
|
||
|
def _checkContainedElements (self, node, particleType, elementNameDict, elementTypeDict, groupNameDict):
|
||
|
prefix = self.inputNsPrefixString
|
||
|
for childNode in node.getChildren():
|
||
|
childParticleType = childNode.getLocalName()
|
||
|
if childParticleType in ("sequence", "choice", "all"):
|
||
|
dummy = {}
|
||
|
self._checkContainedElements (childNode, childParticleType, dummy, elementTypeDict, groupNameDict)
|
||
|
elif childParticleType in ("group"):
|
||
|
if childNode["ref"] != None:
|
||
|
childGroupNode = self.xsdGroupDict[childNode.getQNameAttribute("ref")]
|
||
|
if not groupNameDict.has_key(childGroupNode["name"]):
|
||
|
groupNameDict[childGroupNode["name"]] = 1
|
||
|
for cChildNode in childGroupNode.getChildren():
|
||
|
if cChildNode.getLocalName() != "annotation":
|
||
|
self._checkContainedElements (cChildNode, particleType, elementNameDict, elementTypeDict, groupNameDict)
|
||
|
else:
|
||
|
self._addError ("Circular definition of group %s!" %repr(childGroupNode["name"]), childNode)
|
||
|
else:
|
||
|
for cChildNode in childNode.getChildren():
|
||
|
if cChildNode.getLocalName() != "annotation":
|
||
|
self._checkContainedElements (cChildNode, particleType, elementNameDict, elementTypeDict, groupNameDict)
|
||
|
else:
|
||
|
if childNode.getLocalName() == "any":
|
||
|
elementName = childNode.getAttribute("namespace")
|
||
|
else:
|
||
|
elementName = childNode.getAttributeOrDefault("name", childNode.getAttribute("ref"))
|
||
|
|
||
|
if childNode.hasAttribute("type"):
|
||
|
if not elementTypeDict.has_key(elementName):
|
||
|
elementTypeDict[elementName] = childNode["type"]
|
||
|
elif childNode["type"] != elementTypeDict[elementName]:
|
||
|
self._addError ("Element %s has identical name and different types within %s!" %(repr(elementName), repr(particleType)), childNode)
|
||
|
if particleType != "sequence":
|
||
|
if not elementNameDict.has_key(elementName):
|
||
|
elementNameDict[elementName] = 1
|
||
|
else:
|
||
|
self._addError ("Element %s is not unique within %s!" %(repr(elementName), repr(particleType)), childNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for simple types
|
||
|
#
|
||
|
def _checkSimpleTypesSecondLevel(self):
|
||
|
prefix = self.inputNsPrefixString
|
||
|
|
||
|
simpleTypeNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)ssimpleType" % vars())
|
||
|
for simpleTypeNode in simpleTypeNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (simpleTypeNode)
|
||
|
|
||
|
restrictionNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)ssimpleType/%(prefix)srestriction" % vars())
|
||
|
for restrictionNode in restrictionNodes:
|
||
|
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (restrictionNode)
|
||
|
|
||
|
if not restrictionNode.hasAttribute("base") and restrictionNode.getFirstChildNS (self.inputNsURI, "simpleType") == None:
|
||
|
self._addError ("Simple type restriction must have 'base' attribute or 'simpleType' child tag!", restrictionNode)
|
||
|
|
||
|
if restrictionNode.hasAttribute("base") and restrictionNode.getFirstChildNS (self.inputNsURI, "simpleType") != None:
|
||
|
self._addError ("Simple type restriction must not have 'base' attribute and 'simpleType' child tag!", restrictionNode)
|
||
|
|
||
|
if restrictionNode.hasAttribute("base"):
|
||
|
self._checkType(restrictionNode, "base", self.xsdTypeDict)
|
||
|
|
||
|
minExcl = restrictionNode.getFirstChildNS(self.inputNsURI, "minExclusive")
|
||
|
minIncl = restrictionNode.getFirstChildNS(self.inputNsURI, "minInclusive")
|
||
|
if minExcl != None and minIncl != None:
|
||
|
self._addError ("Restriction attributes 'minExclusive' and 'minInclusive' cannot be defined together!", restrictionNode)
|
||
|
maxExcl = restrictionNode.getFirstChildNS(self.inputNsURI, "maxExclusive")
|
||
|
maxIncl = restrictionNode.getFirstChildNS(self.inputNsURI, "maxInclusive")
|
||
|
if maxExcl != None and maxIncl != None:
|
||
|
self._addError ("Restriction attributes 'maxExclusive' and 'maxInclusive' cannot be defined together!", restrictionNode)
|
||
|
|
||
|
# check facets of associated primitive type
|
||
|
for restrictionNode in restrictionNodes:
|
||
|
try:
|
||
|
if restrictionNode.hasAttribute("base"):
|
||
|
facetNsName = self._getFacetType (restrictionNode, [restrictionNode.getParentNode(),], self.xsdTypeDict)
|
||
|
if not facetNsName:
|
||
|
continue
|
||
|
if _localFacetDict.has_key(facetNsName):
|
||
|
suppFacets = _localFacetDict[facetNsName]
|
||
|
else:
|
||
|
suppFacets, dummy, dummy = self.xsdTypeDict[facetNsName].getXPathList (".//hfp:hasFacet/@name" % vars())
|
||
|
|
||
|
specifiedFacets = {"length":None, "minLength":None, "maxLength":None,
|
||
|
"minExclusive":None, "minInclusive":None, "maxExclusive":None, "maxInclusive":None,
|
||
|
"totalDigits": None, "fractionDigits":None}
|
||
|
for childNode in restrictionNode.getChildren():
|
||
|
if childNode.getLocalName() in suppFacets:
|
||
|
if specifiedFacets.has_key(childNode.getLocalName()):
|
||
|
specifiedFacets[childNode.getLocalName()] = childNode["value"]
|
||
|
facetElementNode = self.xsdElementDict[childNode.getNsName()]
|
||
|
try:
|
||
|
self._checkElementTag (facetElementNode, restrictionNode, (childNode,), 0)
|
||
|
except TagException, errInst:
|
||
|
self._addError (errInst.errstr, errInst.node, errInst.endTag)
|
||
|
if childNode.getLocalName() in ("enumeration", "minExclusive", "minInclusive", "maxExclusive", "maxInclusive"):
|
||
|
simpleTypeReturnDict = self._checkSimpleType (restrictionNode, "base", childNode, "value", childNode["value"], None, checkAttribute=1)
|
||
|
if simpleTypeReturnDict != None and simpleTypeReturnDict.has_key("orderedValue"):
|
||
|
if childNode.getLocalName() != "enumeration":
|
||
|
specifiedFacets[childNode.getLocalName()] = simpleTypeReturnDict["orderedValue"]
|
||
|
elif childNode.getLocalName() == "enumeration":
|
||
|
self._checkSimpleType (restrictionNode, "base", childNode, "value", childNode["value"], None, checkAttribute=1)
|
||
|
elif childNode.getLocalName() != "annotation":
|
||
|
self._addError ("Facet %s not allowed for base type %s!" %(childNode.getLocalName(), repr(restrictionNode["base"])), childNode)
|
||
|
if specifiedFacets["length"] != None:
|
||
|
if specifiedFacets["minLength"] != None or specifiedFacets["maxLength"] != None:
|
||
|
self._addError ("Facet 'minLength' and 'maxLength' not allowed if facet 'length' is specified!", restrictionNode)
|
||
|
else:
|
||
|
if specifiedFacets["maxLength"] != None and specifiedFacets["minLength"] != None:
|
||
|
if int(specifiedFacets["maxLength"]) < int(specifiedFacets["minLength"]):
|
||
|
self._addError ("Facet 'maxLength' < facet 'minLength'!", restrictionNode)
|
||
|
|
||
|
if specifiedFacets["totalDigits"] != None and specifiedFacets["fractionDigits"] != None:
|
||
|
if int(specifiedFacets["totalDigits"]) < int(specifiedFacets["fractionDigits"]):
|
||
|
self._addError ("Facet 'totalDigits' must be >= 'fractionDigits'!", restrictionNode)
|
||
|
|
||
|
if specifiedFacets["minExclusive"] != None and specifiedFacets["minInclusive"] != None:
|
||
|
self._addError ("Facets 'minExclusive' and 'minInclusive' are mutually exclusive!", restrictionNode)
|
||
|
if specifiedFacets["maxExclusive"] != None and specifiedFacets["maxInclusive"] != None:
|
||
|
self._addError ("Facets 'maxExclusive' and 'maxInclusive' are mutually exclusive!", restrictionNode)
|
||
|
|
||
|
minValue = specifiedFacets["minExclusive"]
|
||
|
if specifiedFacets["minInclusive"] != None:
|
||
|
minValue = specifiedFacets["minInclusive"]
|
||
|
maxValue = specifiedFacets["maxExclusive"]
|
||
|
if specifiedFacets["maxInclusive"] != None:
|
||
|
maxValue = specifiedFacets["maxInclusive"]
|
||
|
# TODO: use orderedValue for '<' check!!
|
||
|
if minValue != None and maxValue != None and maxValue < minValue:
|
||
|
self._addError ("maxValue facet < minValue facet!", restrictionNode)
|
||
|
|
||
|
except TagException:
|
||
|
self._addError ("Primitive type for base type not found!", restrictionNode)
|
||
|
|
||
|
listNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)slist" % vars())
|
||
|
for listNode in listNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (listNode)
|
||
|
|
||
|
if not listNode.hasAttribute("itemType") and listNode.getFirstChildNS (self.inputNsURI, "simpleType") == None:
|
||
|
self._addError ("List type must have 'itemType' attribute or 'simpleType' child tag!", listNode)
|
||
|
elif listNode.hasAttribute("itemType") and listNode.getFirstChildNS (self.inputNsURI, "simpleType") != None:
|
||
|
self._addError ("List type must not have 'itemType' attribute and 'simpleType' child tag!", listNode)
|
||
|
elif listNode.hasAttribute("itemType"):
|
||
|
itemType = self._checkType(listNode, "itemType", self.xsdTypeDict)
|
||
|
if self.xsdTypeDict.has_key(itemType):
|
||
|
if self.xsdTypeDict[itemType].getLocalName() != "simpleType":
|
||
|
self._addError ("ItemType %s must be a simple type!" %(repr(itemType)), listNode)
|
||
|
elif self.xsdTypeDict[itemType].getFirstChild().getLocalName() == "list":
|
||
|
self._addError ("ItemType %s must not be a list type!" %(repr(itemType)), listNode)
|
||
|
|
||
|
unionNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)ssimpleType/%(prefix)sunion" % vars())
|
||
|
for unionNode in unionNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (unionNode)
|
||
|
|
||
|
if not unionNode.hasAttribute("memberTypes"):
|
||
|
for childNode in unionNode.getChildren():
|
||
|
if childNode.getLocalName() != "annotation":
|
||
|
break
|
||
|
else:
|
||
|
self._addError ("Union must not be empty!", unionNode)
|
||
|
else:
|
||
|
for memberType in string.split(unionNode["memberTypes"]):
|
||
|
memberNsName = unionNode.qName2NsName(memberType, 1)
|
||
|
self._checkBaseType(unionNode, memberNsName, self.xsdTypeDict)
|
||
|
if self.xsdTypeDict.has_key(memberNsName):
|
||
|
if self.xsdTypeDict[memberNsName].getLocalName() != "simpleType":
|
||
|
self._addError ("MemberType %s must be a simple type!" %(repr(memberNsName)), unionNode)
|
||
|
|
||
|
patternNodes, dummy, dummy = self.inputRoot.getXPathList (".//%(prefix)spattern" % vars())
|
||
|
for patternNode in patternNodes:
|
||
|
pattern = patternNode["value"]
|
||
|
try:
|
||
|
pattern = substituteSpecialEscChars (pattern)
|
||
|
try:
|
||
|
test = re.compile(pattern)
|
||
|
except Exception, errstr:
|
||
|
self._addError (str(errstr), patternNode)
|
||
|
self._addError ("%s is not a valid regular expression!" %(repr(patternNode["value"])), patternNode)
|
||
|
except SyntaxError, errInst:
|
||
|
self._addError (repr(errInst[0]), patternNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for keyrefs
|
||
|
#
|
||
|
def _checkIdentityConstraintsSecondLevel(self):
|
||
|
identityConstraintNodes, dummy, dummy = self.inputRoot.getXPathList (".//%sunique" %(self.inputNsPrefixString))
|
||
|
for identityConstraintNode in identityConstraintNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (identityConstraintNode)
|
||
|
|
||
|
selectorNode = identityConstraintNode.getFirstChildNS(XSD_NAMESPACE, "selector")
|
||
|
self._checkNodeId (selectorNode)
|
||
|
try:
|
||
|
completeChildList, attrNodeList, attrNsNameFirst = identityConstraintNode.getParentNode().getXPathList (selectorNode["xpath"], selectorNode)
|
||
|
if attrNsNameFirst != None:
|
||
|
self._addError ("Selection of attributes is not allowed for selector!", selectorNode)
|
||
|
except Exception, errstr:
|
||
|
self._addError (errstr, selectorNode)
|
||
|
|
||
|
try:
|
||
|
fieldNode = identityConstraintNode.getFirstChildNS(XSD_NAMESPACE, "field")
|
||
|
identityConstraintNode.getParentNode().getXPathList (fieldNode["xpath"], fieldNode)
|
||
|
self._checkNodeId (fieldNode)
|
||
|
except Exception, errstr:
|
||
|
self._addError (errstr, fieldNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for keyrefs
|
||
|
#
|
||
|
def _checkKeysSecondLevel(self):
|
||
|
keyNodes, dummy, dummy = self.inputRoot.getXPathList (".//%skey" %(self.inputNsPrefixString))
|
||
|
for keyNode in keyNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (keyNode)
|
||
|
|
||
|
fieldNode = keyNode.getFirstChildNS(XSD_NAMESPACE, "field")
|
||
|
if fieldNode != None:
|
||
|
self._checkNodeId (fieldNode)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# additional checks for keyrefs
|
||
|
#
|
||
|
def _checkKeyRefsSecondLevel(self):
|
||
|
keyrefNodes, dummy, dummy = self.inputRoot.getXPathList (".//%skeyref" %(self.inputNsPrefixString))
|
||
|
for keyrefNode in keyrefNodes:
|
||
|
# check for unique ID
|
||
|
self._checkNodeId (keyrefNode)
|
||
|
|
||
|
self._checkKeyRef(keyrefNode, self.xsdIdentityConstrDict)
|
||
|
|
||
|
|
||
|
########################################
|
||
|
# helper methods
|
||
|
#
|
||
|
|
||
|
def _checkFixedDefault(self, node):
|
||
|
if node.hasAttribute("default") and node.hasAttribute("fixed"):
|
||
|
self._addError ("%s may have 'default' OR 'fixed' attribute!" %repr(node.getLocalName()), node)
|
||
|
if node.hasAttribute("default"):
|
||
|
self._checkSimpleType (node, "type", node, "default", node["default"], None, checkAttribute=1)
|
||
|
if node.hasAttribute("fixed"):
|
||
|
self._checkSimpleType (node, "type", node, "fixed", node["fixed"], None, checkAttribute=1)
|
||
|
|
||
|
|
||
|
def _checkReference(self, node, dict):
|
||
|
baseNsName = node.getQNameAttribute("ref")
|
||
|
if dict.has_key(baseNsName):
|
||
|
refNode = dict[baseNsName]
|
||
|
fixedValue = node.getAttribute("fixed")
|
||
|
fixedRefValue = refNode.getAttribute("fixed")
|
||
|
if fixedValue != None and fixedRefValue != None and fixedValue != fixedRefValue:
|
||
|
self._addError ("Fixed value %s of attribute does not match fixed value %s of reference!" %(repr(fixedValue), repr(fixedRefValue)), node)
|
||
|
|
||
|
else:
|
||
|
self._addError ("Reference %s not found!" %(repr(baseNsName)), node)
|
||
|
|
||
|
def _checkType(self, node, typeAttrName, dict, typeNsName=None):
|
||
|
baseNsName = node.getQNameAttribute(typeAttrName)
|
||
|
self._checkBaseType(node, baseNsName, dict, typeNsName)
|
||
|
return baseNsName
|
||
|
|
||
|
def _checkBaseType(self, node, baseNsName, dict, typeNsName=None):
|
||
|
if not dict.has_key(baseNsName) and baseNsName != (XSD_NAMESPACE, "anySimpleType"):
|
||
|
self._addError ("Definition of type %s not found!" %(repr(baseNsName)), node)
|
||
|
elif typeNsName != None:
|
||
|
if typeNsName == (XSD_NAMESPACE, "simpleContent"):
|
||
|
if node.getNsName() == (XSD_NAMESPACE, "restriction"):
|
||
|
if (baseNsName != (XSD_NAMESPACE, "anySimpleType") and
|
||
|
dict[baseNsName].getNsName() == (XSD_NAMESPACE, "complexType") and
|
||
|
dict[baseNsName].getFirstChild().getNsName() == typeNsName):
|
||
|
pass
|
||
|
else:
|
||
|
self._addError ("Referred type %s must be a complex type with simple content!" %(repr(baseNsName)), node)
|
||
|
else: # extension
|
||
|
if (baseNsName == (XSD_NAMESPACE, "anySimpleType") or
|
||
|
dict[baseNsName].getNsName() == (XSD_NAMESPACE, "simpleType") or
|
||
|
(dict[baseNsName].getNsName() == (XSD_NAMESPACE, "complexType") and
|
||
|
dict[baseNsName].getFirstChild().getNsName() == typeNsName)):
|
||
|
pass
|
||
|
else:
|
||
|
self._addError ("Referred type %s must be a simple type or a complex type with simple content!" %(repr(baseNsName)), node)
|
||
|
else:
|
||
|
if typeNsName == (XSD_NAMESPACE, "simpleType") and baseNsName == (XSD_NAMESPACE, "anySimpleType"):
|
||
|
pass
|
||
|
elif dict[baseNsName].getNsName() != typeNsName:
|
||
|
self._addError ("Referred type %s must be a %s!" %(repr(baseNsName), repr(typeNsName)), node)
|
||
|
|
||
|
|
||
|
def _checkKeyRef(self, keyrefNode, dict):
|
||
|
baseNsName = keyrefNode.getQNameAttribute("refer")
|
||
|
if not dict.has_key(baseNsName):
|
||
|
self._addError ("keyref refers unknown key %s!" %(repr(baseNsName)), keyrefNode)
|
||
|
else:
|
||
|
keyNode = dict[baseNsName]["Node"]
|
||
|
if keyNode.getNsName() not in ((XSD_NAMESPACE, "key"), (XSD_NAMESPACE, "unique")):
|
||
|
self._addError ("reference to non-key constraint %s!" %(repr(baseNsName)), keyrefNode)
|
||
|
if len(keyrefNode.getChildrenNS(XSD_NAMESPACE, "field")) != len(keyNode.getChildrenNS(XSD_NAMESPACE, "field")):
|
||
|
self._addError ("key/keyref field size mismatch!", keyrefNode)
|
||
|
|
||
|
|
||
|
def _checkOccurs (self, node):
|
||
|
minOccurs = node.getAttributeOrDefault("minOccurs", "1")
|
||
|
maxOccurs = node.getAttributeOrDefault("maxOccurs", "1")
|
||
|
if maxOccurs != "unbounded":
|
||
|
if string.atoi(minOccurs) > string.atoi(maxOccurs):
|
||
|
self._addError ("Attribute minOccurs > maxOccurs!", node)
|
||
|
|
||
|
|
||
|
def _checkNodeId (self, node, unambiguousPerFile=1):
|
||
|
if node.hasAttribute("id"):
|
||
|
# id must only be unambiguous within one file
|
||
|
if unambiguousPerFile:
|
||
|
nodeId = (node.getAbsUrl(), collapseString(node["id"]))
|
||
|
else:
|
||
|
nodeId = collapseString(node["id"])
|
||
|
if not self.xsdIdDict.has_key(nodeId):
|
||
|
self.xsdIdDict[nodeId] = node
|
||
|
else:
|
||
|
self._addError ("There are multiple occurences of ID value %s!" %repr(nodeId), node)
|
||
|
|
||
|
|
||
|
def _getFacetType(self, node, parentNodeList, xsdTypeDict):
|
||
|
baseNsName = node.getQNameAttribute("base")
|
||
|
try:
|
||
|
baseNode = xsdTypeDict[baseNsName]
|
||
|
except:
|
||
|
self._addError ("Base type %s must be an atomic simple type definition or a builtin type!" %repr(baseNsName), node)
|
||
|
return None
|
||
|
|
||
|
if baseNode in parentNodeList:
|
||
|
self._addError ("Circular type definition (type is contained in its own type hierarchy)!", node)
|
||
|
return None
|
||
|
|
||
|
if baseNode.getNsName() == (XSD_NAMESPACE, "simpleType"):
|
||
|
if baseNode.getAttribute("facetType") != None:
|
||
|
facetType = baseNode.qName2NsName(baseNode["facetType"], 1)
|
||
|
node.getParentNode()["facetType"] = node.nsName2QName(facetType)
|
||
|
return facetType
|
||
|
else:
|
||
|
for baseNodeType in ("list", "union"):
|
||
|
if baseNode.getFirstChildNS (XSD_NAMESPACE, baseNodeType) != None:
|
||
|
return (XSD_NAMESPACE, baseNodeType)
|
||
|
else:
|
||
|
parentNodeList.append(node)
|
||
|
return self._getFacetType(baseNode.getFirstChildNS(XSD_NAMESPACE, "restriction"), parentNodeList, xsdTypeDict)
|
||
|
else:
|
||
|
self._addError ("Base type %s must be an atomic simple type definition or a builtin type!" %repr(baseNsName), node)
|
||
|
return None
|
||
|
|
||
|
|