minixsv/xsvalBase.py
changeset 0 b622defdfd98
equal deleted inserted replaced
-1:000000000000 0:b622defdfd98
       
     1 #
       
     2 # minixsv, Release 0.3
       
     3 # file: xsvalBase.py
       
     4 #
       
     5 # XML schema validator base class
       
     6 #
       
     7 # history:
       
     8 # 2004-10-07 rl   created
       
     9 #
       
    10 # Copyright (c) 2004 by Roland Leuthe.  All rights reserved.
       
    11 #
       
    12 # --------------------------------------------------------------------
       
    13 # The minixsv XML schema validator is
       
    14 #
       
    15 # Copyright (c) 2004 by Roland Leuthe
       
    16 #
       
    17 # By obtaining, using, and/or copying this software and/or its
       
    18 # associated documentation, you agree that you have read, understood,
       
    19 # and will comply with the following terms and conditions:
       
    20 #
       
    21 # Permission to use, copy, modify, and distribute this software and
       
    22 # its associated documentation for any purpose and without fee is
       
    23 # hereby granted, provided that the above copyright notice appears in
       
    24 # all copies, and that both that copyright notice and this permission
       
    25 # notice appear in supporting documentation, and that the name of
       
    26 # the author not be used in advertising or publicity
       
    27 # pertaining to distribution of the software without specific, written
       
    28 # prior permission.
       
    29 #
       
    30 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
       
    31 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
       
    32 # ABILITY AND FITNESS.  IN NO EVENT SHALL THE AUTHOR
       
    33 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
       
    34 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
       
    35 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
       
    36 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
       
    37 # OF THIS SOFTWARE.
       
    38 # --------------------------------------------------------------------
       
    39 
       
    40 from xsvalErrorHandler import *
       
    41 import xsvalSimpleTypes
       
    42 
       
    43 ###########################################################
       
    44 #  Validator class for validating one input file against one XML schema file
       
    45 
       
    46 class XsValBase:
       
    47 
       
    48     def __init__(self, xmlIf, errorHandler):
       
    49         self.xmlIf         = xmlIf
       
    50         self.errorHandler  = errorHandler
       
    51 
       
    52         self._raiseError   = self.errorHandler.raiseError
       
    53         self._addError     = self.errorHandler.addError
       
    54         self._addWarning   = self.errorHandler.addWarning
       
    55 
       
    56         self.checkKeyrefList = []
       
    57 
       
    58 
       
    59     ########################################
       
    60     # validate inputTree against xsdTree
       
    61     #
       
    62     def validate (self, inputTree, xsdTree):
       
    63         self.inputTree = inputTree
       
    64         self.xsdTree   = xsdTree
       
    65 
       
    66         self.xsdRoot     = self.xsdTree.getRootNode()
       
    67         self.xsdNSAlias  = self.xmlIf.extractNamespaceAlias(self.xsdRoot.getTagName())
       
    68         self.inputRoot    = self.inputTree.getRootNode()
       
    69         self.inputNSAlias = self.xmlIf.extractNamespaceAlias(self.inputRoot.getTagName())
       
    70 
       
    71         self.simpleTypeVal = xsvalSimpleTypes.XsSimpleTypeVal(self)
       
    72 
       
    73         self._setupLookupTables()
       
    74 
       
    75         inputRootTagName = self.inputRoot.getTagName()
       
    76         inputRootLocalName = self.inputRoot.getLocalName()
       
    77         if self.xsdElementDict.has_key(inputRootLocalName):
       
    78             # start recursive schema validation
       
    79             try:
       
    80                 self._checkElementTag (self.xsdElementDict[inputRootLocalName], inputRootTagName, (self.inputRoot,), 0)
       
    81             except TagException, errInst:
       
    82                 self._addError (errInst.errstr, errInst.node, errInst.endTag)
       
    83 
       
    84             # validate keyrefs
       
    85             for inputElement, keyrefNode in self.checkKeyrefList:
       
    86                 self._checkKeyRefConstraint (keyrefNode, inputElement)
       
    87         else:
       
    88             self._raiseError ("Used root tag %s not found in schema file!" %(inputRootTagName), self.inputRoot)
       
    89 
       
    90 
       
    91     ########################################
       
    92     # setup lookup dictionaries used during validation
       
    93     #
       
    94     def _setupLookupTables (self):
       
    95         # retrieve all elements
       
    96         self.xsdElementDict = {}
       
    97         for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "element"):
       
    98             self.xsdElementDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode
       
    99 
       
   100         # retrieve all type definitions (complex and simple types)
       
   101         self.xsdTypeDict = {}
       
   102         for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "complexType"):
       
   103             self.xsdTypeDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode
       
   104 
       
   105         for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "simpleType"):
       
   106             self.xsdTypeDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode
       
   107 
       
   108         # retrieve all group definitions
       
   109         self.xsdGroupDict = {}
       
   110         for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "group"):
       
   111             self.xsdGroupDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode
       
   112 
       
   113         # retrieve all attribute group definitions
       
   114         self.xsdAttributeGroupDict = {}
       
   115         for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "attributeGroup"):
       
   116             self.xsdAttributeGroupDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode
       
   117 
       
   118         # retrieve all identity constraints
       
   119         self.xsdIdentityConstrDict = {}
       
   120         for identConstrTagName in ("unique", "key", "keyref"):
       
   121             identConstrNodeList = self.xsdRoot.getElementsByTagNameNS (self.xsdNSAlias, identConstrTagName)
       
   122             for identConstrNode in identConstrNodeList:
       
   123                 identConstrName = identConstrNode.getAttribute("name")
       
   124                 if not self.xsdIdentityConstrDict.has_key(identConstrName):
       
   125                     self.xsdIdentityConstrDict[identConstrName] = {"Node": identConstrNode, "ValueDict":{}}
       
   126                 else:
       
   127                     self._raiseError ("Duplicate identity constraint name '%s' found in schema definition!" %(identConstrName), identConstrNode)
       
   128 
       
   129 
       
   130     ########################################
       
   131     # validate inputElement against complexType node
       
   132     #
       
   133     def _checkComplexTypeTag (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType=None):
       
   134         baseTypeAttributes = {"__SPECIAL_ATTRS__":{}}
       
   135         if xsdParentElementNode.getAttribute ("nillable") == "true":
       
   136             baseTypeAttributes["__SPECIAL_ATTRS__"]["nil"] = "xsd:boolean"
       
   137 
       
   138         complexContentElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "complexContent")
       
   139         if complexContentElement != None:
       
   140             inputElementChildIndex, baseTypeAttributes = self._checkComplexContentTag (xsdParentElementNode, complexContentElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes)
       
   141         else:
       
   142             inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeContent (xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes)
       
   143         return inputElementChildIndex, baseTypeAttributes
       
   144 
       
   145     def _checkComplexContentTag (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes):
       
   146         extensionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "extension")
       
   147         if extensionElement != None:
       
   148             inputElementChildIndex, baseTypeAttributes = self._checkExtensionComplexContent (xsdParentElementNode, extensionElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes)
       
   149         else:
       
   150             restrictionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "restriction")
       
   151             if restrictionElement != None:
       
   152                 inputElementChildIndex, baseTypeAttributes = self._checkRestrictionComplexContent (xsdParentElementNode, restrictionElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes)
       
   153         return inputElementChildIndex, baseTypeAttributes
       
   154 
       
   155     def _checkExtensionComplexContent (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes):
       
   156         baseType = self.xmlIf.extractLocalName(xsdElement.getAttribute("base"))
       
   157         inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeTag (xsdParentElementNode, self.xsdTypeDict[baseType], inputElement, inputElementChildIndex, "extension")
       
   158 
       
   159         inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeContent (xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes)
       
   160         return inputElementChildIndex, baseTypeAttributes
       
   161 
       
   162     def _checkRestrictionComplexContent (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes):
       
   163         # first check against base type (retrieve only the base type attributes)
       
   164         baseType = self.xmlIf.extractLocalName(xsdElement.getAttribute("base"))
       
   165         inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeTag (xsdParentElementNode, self.xsdTypeDict[baseType], inputElement, inputElementChildIndex, "restriction")
       
   166 
       
   167         # then check input against derived complex type
       
   168         inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeContent (xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes)
       
   169         return inputElementChildIndex, baseTypeAttributes
       
   170 
       
   171     def _checkComplexTypeContent (self, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes):
       
   172         inputTagName = inputElement.getTagName()
       
   173         childTags = inputElement.getChildren()
       
   174         if usedAsBaseType in (None, "extension"):
       
   175             validChildTags = xsdElement.getChildren()
       
   176             for validChildTag in validChildTags:
       
   177                 if validChildTag.getLocalName() not in ("attribute", "attributeGroup", "anyAttribute"):
       
   178                     inputElementChildIndex = self._checkParticle (validChildTag, inputElement, childTags, inputElementChildIndex)
       
   179 
       
   180             if usedAsBaseType == None and inputElementChildIndex < len (childTags):
       
   181                 self._addError ("Unexpected child tag '%s' in tag '%s' found!" %(childTags[inputElementChildIndex].getTagName(), inputTagName), childTags[inputElementChildIndex])
       
   182 
       
   183         if usedAsBaseType in (None,):
       
   184             self._checkAttributeTags (xsdElement, inputElement, baseTypeAttributes)
       
   185 
       
   186         if usedAsBaseType in ("restriction", "extension"):
       
   187             validAttributes = xsdElement.getChildrenNS(self.xsdNSAlias, "attribute")
       
   188             for validAttrGroup in xsdElement.getChildrenNS(self.xsdNSAlias, "attributeGroup"):
       
   189                 attributeGroupRef = self.xmlIf.extractLocalName(validAttrGroup.getAttribute("ref"))
       
   190                 validAttributes.extend (self.xsdAttributeGroupDict[attributeGroupRef].getChildrenNS(self.xsdNSAlias, "attribute"))
       
   191             for validAttribute in validAttributes:
       
   192                 baseTypeAttributes[validAttribute.getAttribute("name")] = validAttribute
       
   193         return inputElementChildIndex, baseTypeAttributes
       
   194 
       
   195 
       
   196     ########################################
       
   197     # validate inputNodeList against xsdNode
       
   198     #
       
   199     def _checkList (self, elementMethod, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   200         if not xsdNode.hasAttribute("minOccurs"):
       
   201             xsdNode.setAttribute("minOccurs", "1") # default
       
   202         if not xsdNode.hasAttribute("maxOccurs"):
       
   203             xsdNode.setAttribute("maxOccurs", "1") # default
       
   204 
       
   205         minOccurs = string.atoi(xsdNode.getAttribute("minOccurs"))
       
   206         maxOccurs = -1
       
   207         if xsdNode.getAttribute("maxOccurs") != "unbounded":
       
   208             maxOccurs = string.atoi(xsdNode.getAttribute("maxOccurs"))
       
   209         occurs = 0
       
   210         while maxOccurs == -1 or occurs < maxOccurs:
       
   211             try:
       
   212                 newIndex = elementMethod (xsdNode, inputParentNode, inputNodeList, currIndex)
       
   213                 occurs += 1
       
   214                 if newIndex > currIndex:
       
   215                     currIndex = newIndex
       
   216                 else:
       
   217                     break # no suitable element found
       
   218             except TagException, errInst:
       
   219                 break
       
   220 
       
   221         if occurs == 0 and minOccurs > 0:
       
   222             raise errInst
       
   223         elif occurs < minOccurs:
       
   224             expInputTagName = xsdNode.getAttribute("name")
       
   225             if expInputTagName == None:
       
   226                 expInputTagName = self.xmlIf.extractLocalName(xsdNode.getAttribute("ref"))
       
   227 
       
   228             errInst.errstr = "Minimum number (%d) of child tags '%s' in tag '%s' not available (only %d)!" %(minOccurs, expInputTagName, inputParentNode.getTagName(), occurs)
       
   229             raise errInst
       
   230 
       
   231         return currIndex
       
   232 
       
   233     ########################################
       
   234     # validate inputNode against element node
       
   235     #
       
   236     def _checkElementTag (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   237         if xsdNode.hasAttribute("ref"):
       
   238             refAttr = self.xmlIf.extractLocalName(xsdNode.getAttribute("ref"))
       
   239             currIndex = self._checkElementTag (self.xsdElementDict[refAttr], inputParentNode, inputNodeList, currIndex)
       
   240 
       
   241         else:
       
   242             nameAttr = xsdNode.getAttribute ("name")
       
   243             if currIndex >= len (inputNodeList):
       
   244                 raise TagException ("Missing child tag '%s' in tag '%s'!" %(nameAttr, inputParentNode.getTagName()), inputParentNode, 1)
       
   245 
       
   246             inputNode = inputNodeList[currIndex]
       
   247             if nameAttr != inputNode.getLocalName():
       
   248                 raise TagException ("Missing child tag '%s' in tag '%s'!" %(nameAttr, inputParentNode.getTagName()), inputNode, 0)
       
   249 
       
   250             simpleType = None
       
   251             complexTypeNode = xsdNode.getFirstChildNS (self.xsdNSAlias, "complexType")
       
   252             simpleTypeNode  = xsdNode.getFirstChildNS (self.xsdNSAlias, "simpleType")
       
   253             if xsdNode.hasAttribute("type"):
       
   254                 typeAttr = xsdNode.getAttribute ("type")
       
   255                 localTypeAttr = self.xmlIf.extractLocalName(typeAttr)
       
   256                 if self.xsdTypeDict.has_key (localTypeAttr) and self.xsdTypeDict[localTypeAttr].getLocalName() == "complexType":
       
   257                     complexTypeNode = self.xsdTypeDict[localTypeAttr]
       
   258                 else:
       
   259                     simpleType = typeAttr
       
   260 
       
   261             if complexTypeNode != None:
       
   262                 try:
       
   263                     self._checkComplexTypeTag (xsdNode, complexTypeNode, inputNode, 0)
       
   264                 except TagException, errInst:
       
   265                     self._addError (errInst.errstr, errInst.node, errInst.endTag)
       
   266             else:
       
   267                 try:
       
   268                     simpleTypeReturnDict = {}
       
   269                     if simpleTypeNode != None:
       
   270                         self.simpleTypeVal.checkSimpleTypeDef (simpleTypeNode, inputNode.getTagName(), inputNode.getElementValue(), simpleTypeReturnDict)
       
   271                     elif simpleType != None:
       
   272                         self.simpleTypeVal.checkSimpleType (inputNode.getTagName(), simpleType, inputNode.getElementValue(), simpleTypeReturnDict)
       
   273                     # TODO: What to check if np type is specified for the element?
       
   274 
       
   275                     if simpleTypeReturnDict.has_key("adaptedAttrValue"):
       
   276                         inputNode.setElementValue(simpleTypeReturnDict["adaptedAttrValue"])
       
   277 
       
   278                 except xsvalSimpleTypes.SimpleTypeError, errstr:
       
   279                     self._addError (str(errstr), inputNode)
       
   280 
       
   281             currIndex += 1
       
   282 
       
   283             # check unique attributes and keys
       
   284             childUniqueDefList = xsdNode.getChildrenNS (self.xsdNSAlias, "unique")
       
   285             for childUniqueDef in childUniqueDefList:
       
   286                 self._checkIdentityConstraint (childUniqueDef, inputNode, "unique")
       
   287 
       
   288             childKeyDefList = xsdNode.getChildrenNS (self.xsdNSAlias, "key")
       
   289             for childKeyDef in childKeyDefList:
       
   290                 self._checkIdentityConstraint (childKeyDef, inputNode, "key")
       
   291 
       
   292             childKeyrefDefList = xsdNode.getChildrenNS (self.xsdNSAlias, "keyref")
       
   293             for childKeyrefDef in childKeyrefDefList:
       
   294                 self.checkKeyrefList.append ((inputNode, childKeyrefDef))
       
   295 
       
   296         return currIndex
       
   297 
       
   298 
       
   299     ########################################
       
   300     # validate inputNode against sequence node
       
   301     #
       
   302     def _checkSequenceTag (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   303         for xsdChildNode in xsdNode.getChildren():
       
   304             currIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex)
       
   305         return currIndex
       
   306 
       
   307     ########################################
       
   308     # validate inputNode against choice node
       
   309     #
       
   310     def _checkChoiceTag (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   311         for xsdChildNode in xsdNode.getChildren():
       
   312             try:
       
   313                 currIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex)
       
   314                 break
       
   315             except TagException, errInst:
       
   316                 pass
       
   317         else:
       
   318             if currIndex < len(inputNodeList):
       
   319                 currNode = inputNodeList[currIndex]
       
   320                 endTag = 0
       
   321             else:
       
   322                 currNode = inputParentNode
       
   323                 endTag = 1
       
   324             raise TagException ("No suitable child tag for choice found!", currNode, endTag)
       
   325 
       
   326         return currIndex
       
   327 
       
   328     ########################################
       
   329     # validate inputNode against group node
       
   330     #
       
   331     def _checkGroupTag (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   332         if xsdNode.hasAttribute("ref"):
       
   333             refAttr = self.xmlIf.extractLocalName(xsdNode.getAttribute("ref"))
       
   334             currIndex = self._checkGroupTag (self.xsdGroupDict[refAttr], inputParentNode, inputNodeList, currIndex)
       
   335         else:
       
   336             for xsdChildNode in xsdNode.getChildren():
       
   337                 currIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex)
       
   338         return currIndex
       
   339 
       
   340     ########################################
       
   341     # validate inputNode against all node
       
   342     #
       
   343     def _checkAllTag (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   344         oldIndex = currIndex
       
   345         xsdChildDict = {}
       
   346         for xsdChildNode in xsdNode.getChildren():
       
   347             xsdChildDict[xsdChildNode] = 0
       
   348         while currIndex < len(inputNodeList):
       
   349             currNode = inputNodeList[currIndex]
       
   350             for xsdChildNode in xsdChildDict.keys():
       
   351                 try:
       
   352                     newIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex)
       
   353                 except TagException, errInst:
       
   354                     continue
       
   355 
       
   356                 if xsdChildDict[xsdChildNode] == 0:
       
   357                     xsdChildDict[xsdChildNode] = 1
       
   358                     currIndex = newIndex
       
   359                     break
       
   360                 else:
       
   361                     raise TagException ("Ambiguous child tag '%s' found in all-group!" %(currNode.getTagName()), currNode)
       
   362             else:
       
   363                 raise TagException ("Unexpected child tag '%s' for all-group found!" %(currNode.getTagName()), currNode)
       
   364 
       
   365         for xsdChildNode, occurs in xsdChildDict.items():
       
   366            if xsdChildNode.getAttribute("minOccurs") != "0" and occurs == 0:
       
   367                raise TagException ("Child tag '%s' missing in all-group (%s)" %(xsdChildNode.getAttribute("name"), inputParentNode.getTagName()), inputNodeList[oldIndex])
       
   368 
       
   369         return currIndex
       
   370 
       
   371 
       
   372     ########################################
       
   373     # validate inputNode against any node
       
   374     #
       
   375     def _checkAnyTag (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   376         processContents = xsdNode.getAttribute("processContents")
       
   377         if processContents == "skip":
       
   378             pass
       
   379         elif processContents == "lax":
       
   380             # TODO: Was muss hier gecheckt werden?
       
   381             pass
       
   382         elif processContents == "strict":
       
   383             # TODO: Was muss hier gecheckt werden?
       
   384             pass
       
   385 
       
   386         if currIndex < len(inputNodeList):
       
   387             currIndex = currIndex + 1
       
   388 
       
   389         return currIndex
       
   390 
       
   391     ########################################
       
   392     # validate inputNode against particle
       
   393     #
       
   394     def _checkParticle (self, xsdNode, inputParentNode, inputNodeList, currIndex):
       
   395         xsdTagName = xsdNode.getTagName()
       
   396         if xsdTagName == self.xsdNSAlias + "element":
       
   397             currIndex = self._checkList (self._checkElementTag, xsdNode, inputParentNode, inputNodeList, currIndex)
       
   398         elif xsdTagName == self.xsdNSAlias + "choice":
       
   399             currIndex = self._checkList (self._checkChoiceTag, xsdNode, inputParentNode, inputNodeList, currIndex)
       
   400         elif xsdTagName == self.xsdNSAlias + "sequence":
       
   401             currIndex = self._checkList (self._checkSequenceTag, xsdNode, inputParentNode, inputNodeList, currIndex)
       
   402         elif xsdTagName == self.xsdNSAlias + "group":
       
   403             currIndex = self._checkList (self._checkGroupTag, xsdNode, inputParentNode, inputNodeList, currIndex)
       
   404         elif xsdTagName == self.xsdNSAlias + "all":
       
   405             currIndex = self._checkList (self._checkAllTag, xsdNode, inputParentNode, inputNodeList, currIndex)
       
   406         elif xsdTagName == self.xsdNSAlias + "any":
       
   407             currIndex = self._checkList (self._checkAnyTag, xsdNode, inputParentNode, inputNodeList, currIndex)
       
   408         elif xsdTagName == self.xsdNSAlias + "annotation":
       
   409             pass # nothing to check
       
   410         else:
       
   411             self._addError ("Internal error: Invalid tag %s found!" %(xsdTagName))
       
   412         return currIndex
       
   413 
       
   414 
       
   415     ########################################
       
   416     # validate attributes of inputNode against complexType node
       
   417     #
       
   418     def _checkAttributeTags (self, xsdNode, inputNode, validAttrDict):
       
   419         validAttributes = xsdNode.getChildrenNS(self.xsdNSAlias, "attribute")
       
   420         for validAttrGroup in xsdNode.getChildrenNS(self.xsdNSAlias, "attributeGroup"):
       
   421             attributeGroupRef = self.xmlIf.extractLocalName(validAttrGroup.getAttribute("ref"))
       
   422             validAttributes.extend (self.xsdAttributeGroupDict[attributeGroupRef].getChildrenNS(self.xsdNSAlias, "attribute"))
       
   423         for validAttribute in validAttributes:
       
   424             if validAttribute.hasAttribute("name"):
       
   425                 keyAttr = validAttribute.getAttribute("name")
       
   426             else:
       
   427                 keyAttr = self.xmlIf.extractLocalName(validAttribute.getAttribute("ref"))
       
   428             validAttrDict[keyAttr] = validAttribute
       
   429 
       
   430         inputAttrDict = {}
       
   431         for iAttrName, iAttrValue in inputNode.getAttributeDict().items():
       
   432             if self.xmlIf.extractNamespaceAlias (iAttrName) != "xmlns:" and iAttrName != "xmlns":
       
   433                 inputAttrDict[self.xmlIf.extractLocalName(iAttrName)] = iAttrValue
       
   434 
       
   435         for attrKey, validAttribute in validAttrDict.items():
       
   436             # handle special attributes, e.g. xsi:nil
       
   437             if attrKey == "__SPECIAL_ATTRS__":
       
   438                 for specialAttrName, specialAttrType in validAttribute.items():
       
   439                     if inputAttrDict.has_key(specialAttrName):
       
   440                         simpleTypeReturnDict = {}
       
   441                         try:
       
   442                             self.simpleTypeVal.checkSimpleType (specialAttrName, specialAttrType, inputAttrDict[specialAttrName], simpleTypeReturnDict)
       
   443                         except xsvalSimpleTypes.SimpleTypeError, errstr:
       
   444                             self._addError (str(errstr), inputNode)
       
   445 
       
   446                         del inputAttrDict[specialAttrName]
       
   447                 continue
       
   448 
       
   449             attrDict = validAttribute.getAttributeDict()
       
   450             if attrDict.has_key("ref"):
       
   451                 # TODO: What to do here??
       
   452                 attrRef = self.xmlIf.extractLocalName(attrDict["ref"])
       
   453                 if inputAttrDict.has_key(attrRef):
       
   454                     del inputAttrDict[attrRef]
       
   455             else:
       
   456                 attrName = attrDict["name"]
       
   457                 if not attrDict.has_key("use"):
       
   458                     attrDict["use"] = "optional"
       
   459 
       
   460                 if inputAttrDict.has_key(attrName):
       
   461                     del inputAttrDict[attrName]
       
   462                 else:
       
   463                     if attrDict["use"] == "required":
       
   464                         self._addError ("Attribute '%s' is missing for tag %s!" %(attrName, inputNode.getTagName()), inputNode)
       
   465                     elif attrDict["use"] == "optional":
       
   466                         if attrDict.has_key("default"):
       
   467                             inputNode.setAttribute(attrName, attrDict["default"])
       
   468                 if attrDict.has_key("fixed"):
       
   469                     if inputNode.getAttribute(attrName) != attrDict["fixed"]:
       
   470                         self._addError ("Attribute '%s' must have fixed value '%s'!" %(attrName, attrDict["fixed"]), inputNode)
       
   471 
       
   472                 if inputNode.hasAttribute(attrName):
       
   473                     if attrDict["use"] == "prohibited":
       
   474                         self._addError ("Attribute '%s' is prohibited in this context!" %(attrName), inputNode)
       
   475                     else:
       
   476                         attributeValue = inputNode.getAttribute(attrName)
       
   477                         try:
       
   478                             simpleTypeReturnDict = {}
       
   479                             if attrDict.has_key("type"):
       
   480                                 self.simpleTypeVal.checkSimpleType (attrName, attrDict["type"], attributeValue, simpleTypeReturnDict)
       
   481                             else:
       
   482                                 typedefNode = validAttribute.getFirstChildNS(self.xsdNSAlias, "simpleType")
       
   483                                 if typedefNode != None:
       
   484                                     self.simpleTypeVal.checkSimpleTypeDef (typedefNode, attrName, attributeValue, simpleTypeReturnDict)
       
   485                                 else:
       
   486                                     pass    # default if no type attribute is specified
       
   487 
       
   488                             if simpleTypeReturnDict.has_key("adaptedAttrValue"):
       
   489                                 inputNode.setAttribute(attrName, simpleTypeReturnDict["adaptedAttrValue"])
       
   490 
       
   491                         except xsvalSimpleTypes.SimpleTypeError, errstr:
       
   492                             self._addError (str(errstr), inputNode)
       
   493 
       
   494         for inputAttribute in inputAttrDict.keys():
       
   495             # TODO: adapt for using namespaces!!
       
   496             if self.xmlIf.extractLocalName(inputAttribute) in ("noNamespaceSchemaLocation", "schemaLocation"):
       
   497                 del inputAttrDict[inputAttribute]
       
   498 
       
   499         for inputAttribute in inputAttrDict.keys():
       
   500             if inputAttribute == "nil":
       
   501                 self._addError ("Tag '%s' hasn't been defined as nillable!" %(inputNode.getTagName()), inputNode)
       
   502             else:
       
   503                 self._addError ("Unexpected attribute '%s' in Tag '%s'!" %(inputAttribute, inputNode.getTagName()), inputNode)
       
   504 
       
   505 
       
   506     ########################################
       
   507     # validate unique and key definition
       
   508     #
       
   509     def _checkIdentityConstraint (self, identityConstrNode, inputElement, isKey):
       
   510         identConstrName = identityConstrNode.getAttribute ("name")
       
   511         selectorXPathNode = identityConstrNode.getFirstChildNS (self.xsdNSAlias, "selector")
       
   512         selectorNodeList, dummy = self._getXPath (inputElement, selectorXPathNode.getAttribute("xpath"), self.inputNSAlias)
       
   513 
       
   514         valueDict = {}
       
   515         for selectorNode in selectorNodeList:
       
   516             fieldXPathNodeList = identityConstrNode.getChildrenNS (self.xsdNSAlias, "field")
       
   517             keyValue = []
       
   518             for fieldXPathNode in fieldXPathNodeList:
       
   519                 fieldXPath = fieldXPathNode.getAttribute("xpath")
       
   520                 fieldNodeList, fieldAttributeList = self._getXPath (selectorNode, fieldXPath, self.inputNSAlias, isKey)
       
   521                 if len(fieldNodeList) > 1 or len(fieldAttributeList) > 1:
       
   522                     self._addError ("The field xPath of identity constraint '%s' must evaluate to exactly 0 or 1 node!" %(identConstrName), fieldXPathNode)
       
   523                     return
       
   524 
       
   525                 # TODO: unique and key check currently only on string base
       
   526                 for fieldNode in fieldNodeList:
       
   527                     keyValue.append (fieldNode.getElementValue ())
       
   528 
       
   529                 for attrValue in fieldAttributeList:
       
   530                     keyValue.append (attrValue)
       
   531 
       
   532             if keyValue != []:
       
   533                 keyValue = tuple(keyValue)
       
   534                 if not valueDict.has_key (keyValue):
       
   535                     valueDict[keyValue] = 1
       
   536                     self.xsdIdentityConstrDict[identConstrName]["ValueDict"][keyValue] = 1
       
   537                 else:
       
   538                     self._addError ("Duplicate identity constraint values '%s' found for identity contraint '%s'!" %(keyValue, identConstrName), selectorNode)
       
   539 
       
   540     ########################################
       
   541     # validate unique and key definition
       
   542     #
       
   543     def _checkKeyRefConstraint (self, keyrefNode, inputElement):
       
   544         keyRefName = keyrefNode.getAttribute ("name")
       
   545         keyReference = keyrefNode.getAttribute ("refer")
       
   546         if not self.xsdIdentityConstrDict.has_key (keyReference):
       
   547             self._addError ("keyref refers unknown key '%s'!" %(keyReference), keyrefNode)
       
   548             return
       
   549 
       
   550         selectorXPathNode = keyrefNode.getFirstChildNS (self.xsdNSAlias, "selector")
       
   551         selectorNodeList, dummy = self._getXPath (inputElement, selectorXPathNode.getAttribute("xpath"), self.inputNSAlias)
       
   552 
       
   553         for selectorNode in selectorNodeList:
       
   554             fieldXPathNodeList = keyrefNode.getChildrenNS(self.xsdNSAlias, "field")
       
   555             keyValue = []
       
   556             for fieldXPathNode in fieldXPathNodeList:
       
   557                 fieldXPath = fieldXPathNode.getAttribute("xpath")
       
   558                 fieldNodeList, fieldAttributeList = self._getXPath (selectorNode, fieldXPath, self.inputNSAlias, "keyref")
       
   559                 if len(fieldNodeList) > 1 or len(fieldAttributeList) > 1:
       
   560                     self._addError ("The field xPath of keyref '%s' must evaluate to exactly 0 or 1 node!" %(keyRefName), fieldXPathNode)
       
   561                     return
       
   562 
       
   563                 # TODO: unique and key check currently only on string base
       
   564                 for fieldNode in fieldNodeList:
       
   565                     keyValue.append(fieldNode.getElementValue())
       
   566 
       
   567                 for attrValue in fieldAttributeList:
       
   568                     keyValue.append(attrValue)
       
   569 
       
   570             keyValue = tuple(keyValue)
       
   571             if not self.xsdIdentityConstrDict[keyReference]["ValueDict"].has_key (keyValue):
       
   572                 self._addError ("Element value '%s' does not match key type '%s'!" %(keyValue, keyReference), selectorNode)
       
   573 
       
   574     ########################################
       
   575     # retrieve nodes/attributes specified by given xPath
       
   576     #
       
   577     def _getXPath (self, node, xPath, defaultNamespace, identityConstraint=None):
       
   578         try:
       
   579             nodeList, attributeList = node.getXPathList (xPath, defaultNamespace)
       
   580         except IOError, errstr:
       
   581             self._addError (errstr, node)
       
   582             nodeList = attributeList = []
       
   583 
       
   584         if nodeList == [] and attributeList == []:
       
   585             if identityConstraint == "key":
       
   586                 self.errorHandler.addError ("Key is missing! XPath = '%s'!" %(xPath), node)
       
   587             elif identityConstraint in ("unique", "keyref"):
       
   588                 self.errorHandler.addWarning ("Identity constraint is missing! XPath = '%s'!" %(xPath), node)
       
   589 
       
   590         return nodeList, attributeList
       
   591 
       
   592 
       
   593 ########################################
       
   594 # define own exception for XML schema validation errors
       
   595 #
       
   596 class TagException (StandardError):
       
   597     def __init__ (self, errstr="", node=None, endTag=0):
       
   598         self.node   = node
       
   599         self.errstr = errstr
       
   600         self.endTag = endTag
       
   601         StandardError.__init__(self)
       
   602