etisserant@0: # etisserant@0: # # minixsv, Release 0.3 etisserant@0: # file: xsvalSimpleTypes.py etisserant@0: # etisserant@0: # class for validation of XML schema simple types etisserant@0: # etisserant@0: # history: etisserant@0: # 2004-09-09 rl created etisserant@0: # etisserant@0: # Copyright (c) 2004 by Roland Leuthe. All rights reserved. etisserant@0: # etisserant@0: # -------------------------------------------------------------------- etisserant@0: # The minixsv XML schema validator is etisserant@0: # etisserant@0: # Copyright (c) 2004 by Roland Leuthe etisserant@0: # etisserant@0: # By obtaining, using, and/or copying this software and/or its etisserant@0: # associated documentation, you agree that you have read, understood, etisserant@0: # and will comply with the following terms and conditions: etisserant@0: # etisserant@0: # Permission to use, copy, modify, and distribute this software and etisserant@0: # its associated documentation for any purpose and without fee is etisserant@0: # hereby granted, provided that the above copyright notice appears in etisserant@0: # all copies, and that both that copyright notice and this permission etisserant@0: # notice appear in supporting documentation, and that the name of etisserant@0: # the author not be used in advertising or publicity etisserant@0: # pertaining to distribution of the software without specific, written etisserant@0: # prior permission. etisserant@0: # etisserant@0: # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD etisserant@0: # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- etisserant@0: # ABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR etisserant@0: # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY etisserant@0: # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, etisserant@0: # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS etisserant@0: # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE etisserant@0: # OF THIS SOFTWARE. etisserant@0: # -------------------------------------------------------------------- etisserant@0: etisserant@0: etisserant@0: import sys etisserant@0: import string etisserant@0: import re etisserant@0: etisserant@0: etisserant@0: class XsSimpleTypeVal: etisserant@0: etisserant@0: def __init__ (self, parent): etisserant@0: self.parent = parent etisserant@0: self.xmlIf = parent.xmlIf etisserant@0: self.xsdNSAlias = parent.xsdNSAlias etisserant@0: self.xsdTree = parent.xsdTree etisserant@0: etisserant@0: etisserant@0: ######################################## etisserant@0: # validate given value against simpleType etisserant@0: # etisserant@0: def checkSimpleType (self, attrName, typeName, attributeValue, returnDict): etisserant@0: localTypeName = self.xmlIf.extractLocalName(typeName) etisserant@0: if self.parent.xsdTypeDict.has_key(localTypeName): etisserant@0: typedefNode = self.parent.xsdTypeDict[localTypeName] etisserant@0: if typedefNode.getTagName () == self.xsdNSAlias + "simpleType": etisserant@0: self.checkSimpleTypeDef (typedefNode, attrName, attributeValue, returnDict) etisserant@0: else: etisserant@0: raise SimpleTypeError("Type '%s' must be simple type!" %(typeName)) etisserant@0: else: etisserant@0: try: etisserant@0: validateBaseType (typeName, attributeValue, returnDict) etisserant@0: except BaseTypeError, errstr: etisserant@0: raise SimpleTypeError("Value of '%s' (%s) %s" %(attrName, attributeValue, errstr)) etisserant@0: etisserant@0: etisserant@0: ######################################## etisserant@0: # validate given value against simpleType node etisserant@0: # etisserant@0: def checkSimpleTypeDef (self, xsdElement, attrName, attributeValue, returnDict): etisserant@0: restrictionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "restriction") etisserant@0: listElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "list") etisserant@0: unionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "union") etisserant@0: if restrictionElement != None: etisserant@0: self._checkRestrictionTag (restrictionElement, attrName, attributeValue, returnDict) etisserant@0: elif listElement != None: etisserant@0: self._checkListTag (listElement, attrName, attributeValue, returnDict) etisserant@0: elif unionElement != None: etisserant@0: self._checkUnionTag (unionElement, attrName, attributeValue, returnDict) etisserant@0: etisserant@0: ######################################## etisserant@0: # validate given value against restriction node etisserant@0: # etisserant@0: def _checkRestrictionTag (self, xsdElement, attrName, attributeValue, returnDict): etisserant@0: # first check against base type etisserant@0: baseType = xsdElement.getAttribute("base") etisserant@0: if baseType != None: etisserant@0: self.checkSimpleType (attrName, baseType, attributeValue, returnDict) etisserant@0: else: etisserant@0: baseTypeNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "simpleType") etisserant@0: self.checkSimpleTypeDef (baseTypeNode, attrName, attributeValue, returnDict) etisserant@0: etisserant@0: minExcl = xsdElement.getFirstChildNS(self.xsdNSAlias, "minExclusive") etisserant@0: minIncl = xsdElement.getFirstChildNS(self.xsdNSAlias, "minInclusive") etisserant@0: maxExcl = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxExclusive") etisserant@0: maxIncl = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxInclusive") etisserant@0: etisserant@0: if minExcl != None: etisserant@0: minExclReturnDict = {} etisserant@0: minExclValue = minExcl.getAttribute("value") etisserant@0: self.checkSimpleType (attrName, baseType, minExclValue, minExclReturnDict) etisserant@0: if returnDict.has_key("orderedValue") and minExclReturnDict.has_key("orderedValue"): etisserant@0: if returnDict["orderedValue"] <= minExclReturnDict["orderedValue"]: etisserant@0: raise SimpleTypeError ("Value of %s (%s) is <= minExclusive (%s)" %(attrName, attributeValue, minExclValue)) etisserant@0: elif minIncl != None: etisserant@0: minInclReturnDict = {} etisserant@0: minInclValue = minIncl.getAttribute("value") etisserant@0: self.checkSimpleType (attrName, baseType, minInclValue, minInclReturnDict) etisserant@0: if returnDict.has_key("orderedValue") and minInclReturnDict.has_key("orderedValue"): etisserant@0: if returnDict["orderedValue"] < minInclReturnDict["orderedValue"]: etisserant@0: raise SimpleTypeError ("Value of %s (%s) is < minInclusive (%s)" %(attrName, attributeValue, minInclValue)) etisserant@0: if maxExcl != None: etisserant@0: maxExclReturnDict = {} etisserant@0: maxExclValue = maxExcl.getAttribute("value") etisserant@0: self.checkSimpleType (attrName, baseType, maxExclValue, maxExclReturnDict) etisserant@0: if returnDict.has_key("orderedValue") and maxExclReturnDict.has_key("orderedValue"): etisserant@0: if returnDict["orderedValue"] >= maxExclReturnDict["orderedValue"]: etisserant@0: raise SimpleTypeError ("Value of %s (%s) is >= maxExclusive (%s)" %(attrName, attributeValue, maxExclValue)) etisserant@0: elif maxIncl != None: etisserant@0: maxInclReturnDict = {} etisserant@0: maxInclValue = maxIncl.getAttribute("value") etisserant@0: self.checkSimpleType (attrName, baseType, maxInclValue, maxInclReturnDict) etisserant@0: if returnDict.has_key("orderedValue") and maxInclReturnDict.has_key("orderedValue"): etisserant@0: if returnDict["orderedValue"] > maxInclReturnDict["orderedValue"]: etisserant@0: raise SimpleTypeError ("Value of %s (%s) is > maxInclusive (%s)" %(attrName, attributeValue, maxInclValue)) etisserant@0: etisserant@0: totalDigitsNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "totalDigits") etisserant@0: if totalDigitsNode != None: etisserant@0: totalDigitsValue = totalDigitsNode.getAttribute("value") etisserant@0: if totalDigitsNode.getAttribute("fixed") == "true": etisserant@0: if len(re.findall("\d" ,attributeValue)) != string.atoi(totalDigitsValue): etisserant@0: raise SimpleTypeError ("Total number of digits != %s for %s (%s)" %(totalDigitsValue, attrName, attributeValue)) etisserant@0: else: etisserant@0: if len(re.findall("\d" ,attributeValue)) > string.atoi(totalDigitsValue): etisserant@0: raise SimpleTypeError ("Total number of digits > %s for %s (%s)" %(totalDigitsValue, attrName, attributeValue)) etisserant@0: fractionDigitsNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "fractionDigits") etisserant@0: if fractionDigitsNode != None: etisserant@0: fractionDigitsValue = fractionDigitsNode.getAttribute("value") etisserant@0: result = re.search("(?P\d+)(?P\.)(?P\d+)" ,attributeValue) etisserant@0: if result != None: etisserant@0: numberOfFracDigits = len (result.group('fracDigits')) etisserant@0: else: etisserant@0: numberOfFracDigits = 0 etisserant@0: if fractionDigitsNode.getAttribute("fixed") == "true" and numberOfFracDigits != string.atoi(fractionDigitsValue): etisserant@0: raise SimpleTypeError ("Fraction number of digits != %s for %s (%s)" %(fractionDigitsValue, attrName, attributeValue)) etisserant@0: elif numberOfFracDigits > string.atoi(fractionDigitsValue): etisserant@0: raise SimpleTypeError ("Fraction number of digits > %s for %s (%s)" %(fractionDigitsValue, attrName, attributeValue)) etisserant@0: etisserant@0: if returnDict.has_key("length"): etisserant@0: lengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "length") etisserant@0: if lengthNode != None: etisserant@0: length = string.atoi(lengthNode.getAttribute("value")) etisserant@0: if returnDict["length"] != length: etisserant@0: raise SimpleTypeError ("Length of %s (%s) must be %d!" %(attrName, attributeValue, length)) etisserant@0: minLengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "minLength") etisserant@0: if minLengthNode != None: etisserant@0: minLength = string.atoi(minLengthNode.getAttribute("value")) etisserant@0: if returnDict["length"] < minLength: etisserant@0: raise SimpleTypeError ("Length of %s (%s) must be >= %d!" %(attrName, attributeValue, minLength)) etisserant@0: maxLengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxLength") etisserant@0: if maxLengthNode != None: etisserant@0: maxLength = string.atoi(maxLengthNode.getAttribute("value")) etisserant@0: if returnDict["length"] > maxLength: etisserant@0: raise SimpleTypeError ("Length of %s (%s) must be <= %d!" %(attrName, attributeValue, maxLength)) etisserant@0: etisserant@0: enumerationElementList = xsdElement.getChildrenNS(self.xsdNSAlias, "enumeration") etisserant@0: if enumerationElementList != []: etisserant@0: for enumeration in enumerationElementList: etisserant@0: if enumeration.getAttribute ("value") == attributeValue: etisserant@0: break etisserant@0: else: etisserant@0: raise SimpleTypeError ("Enumeration value '%s' not allowed!" %(attributeValue)) etisserant@0: etisserant@0: patternNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "pattern") etisserant@0: if patternNode != None: etisserant@0: rePattern = patternNode.getAttribute("value") etisserant@0: regexObj = re.match(rePattern, attributeValue) etisserant@0: if not regexObj or regexObj.end() != len(attributeValue): etisserant@0: raise SimpleTypeError ("Attribute value '%s' does not match pattern '%s'!" %(attributeValue, rePattern)) etisserant@0: etisserant@0: whiteSpace = xsdElement.getFirstChildNS(self.xsdNSAlias, "whiteSpace") etisserant@0: if whiteSpace != None: etisserant@0: wsAction = whiteSpace.getAttribute("value") etisserant@0: if wsAction == "replace": etisserant@0: normalizedValue = self._normalizeString(attributeValue) etisserant@0: if normalizedValue != attributeValue: etisserant@0: returnDict["adaptedAttrValue"] = normalizedValue etisserant@0: elif wsAction == "collapse": etisserant@0: collapsedValue = self._collapseString(attributeValue) etisserant@0: if collapsedValue != attributeValue: etisserant@0: returnDict["adaptedAttrValue"] = collapsedValue etisserant@0: etisserant@0: etisserant@0: ######################################## etisserant@0: # validate given value against list node etisserant@0: # etisserant@0: def _checkListTag (self, xsdElement, attrName, attributeValue, returnDict): etisserant@0: if attributeValue != "": etisserant@0: itemType = xsdElement.getAttribute ("itemType") etisserant@0: # substitute multiple whitespace characters by a single ' ' etisserant@0: collapsedValue = self._collapseString(attributeValue) etisserant@0: if collapsedValue != attributeValue: etisserant@0: returnDict["adaptedAttrValue"] = collapsedValue etisserant@0: etisserant@0: # divide up attributeValue => store it into list etisserant@0: attributeList = string.split(collapsedValue, " ") etisserant@0: for attrValue in attributeList: etisserant@0: elementReturnDict = {} etisserant@0: if itemType != None: etisserant@0: self.checkSimpleType (attrName, itemType, attrValue, elementReturnDict) etisserant@0: else: etisserant@0: itemTypeNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "simpleType") etisserant@0: self.checkSimpleTypeDef (itemTypeNode, attrName, attrValue, elementReturnDict) etisserant@0: etisserant@0: returnDict["length"] = len(attributeList) etisserant@0: else: etisserant@0: returnDict["length"] = 0 etisserant@0: etisserant@0: etisserant@0: ######################################## etisserant@0: # validate given value against union node etisserant@0: # etisserant@0: def _checkUnionTag (self, xsdElement, attrName, attributeValue, returnDict): etisserant@0: memberTypes = xsdElement.getAttribute ("memberTypes") etisserant@0: if memberTypes != None: etisserant@0: # substitute multiple whitespace characters by a single ' ' etisserant@0: # divide up attributeValue => store it into list etisserant@0: for memberType in string.split(self._collapseString(memberTypes), " "): etisserant@0: try: etisserant@0: self.checkSimpleType (attrName, memberType, attributeValue, returnDict) etisserant@0: return etisserant@0: except SimpleTypeError, errstr: etisserant@0: pass etisserant@0: etisserant@0: # memberTypes and additional type definitions is legal! etisserant@0: for childSimpleType in xsdElement.getChildrenNS(self.xsdNSAlias, "simpleType"): etisserant@0: try: etisserant@0: self.checkSimpleTypeDef (childSimpleType, attrName, attributeValue, returnDict) etisserant@0: return etisserant@0: except SimpleTypeError, errstr: etisserant@0: pass etisserant@0: etisserant@0: raise SimpleTypeError ("%s (%s) is no valid union member type (%s)!" %(attrName, attributeValue, memberTypes)) etisserant@0: etisserant@0: etisserant@0: ######################################## etisserant@0: # substitute multiple whitespace characters by a single ' ' etisserant@0: # etisserant@0: def _collapseString (self, strValue): etisserant@0: return re.sub('\s+', ' ', strValue) etisserant@0: etisserant@0: ######################################## etisserant@0: # substitute each whitespace characters by a single ' ' etisserant@0: # etisserant@0: def _normalizeString (self, strValue): etisserant@0: return re.sub('\s', ' ', strValue) etisserant@0: etisserant@0: etisserant@0: etisserant@0: etisserant@0: def validateBaseType (simpleType, attributeValue, returnDict): etisserant@0: # TODO: Many base types are not completely defined by datatypes.xsd etisserant@0: simpleTypeDict = {"xsd:string": _checkStringType, etisserant@0: "xsd:hexBinary": _checkHexBinaryType, etisserant@0: "xsd:integer": _checkIntegerType, etisserant@0: "xsd:boolean": _checkBooleanType, etisserant@0: "xsd:QName": _checkQNameType, etisserant@0: } etisserant@0: etisserant@0: simpleType = string.replace(simpleType, "xs:", "xsd:") etisserant@0: if simpleTypeDict.has_key (simpleType): etisserant@0: simpleTypeDict[simpleType] (simpleType, attributeValue, returnDict) etisserant@0: etisserant@0: elif simpleType != "xsd:anySimpleType": etisserant@0: if simpleType in ("xsd:decimal", "xsd:float", "xsd:double", "xsd:base64Binary", "xsd:anyURI", "xsd:NOTATION", etisserant@0: "xsd:duration", "xsd:dateTime", "xsd:time", "xsd:date", etisserant@0: "xsd:gYearMonth", "xsd:gMonthDay", "xsd:gYear", "xsd:gMonth", "xsd:gDay"): etisserant@0: print "INFO: Check of simple type '%s' currently not supported!" %(simpleType) etisserant@0: else: etisserant@0: # TODO: Fehler im XSD-File => Check muss an anderer Stelle erfolgen etisserant@0: raise BaseTypeError("uses unknown type '%s'!" %(simpleType)) etisserant@0: etisserant@0: etisserant@0: def _checkStringType (simpleType, attributeValue, returnDict): etisserant@0: # all valid?? etisserant@0: returnDict["length"] = len(attributeValue) etisserant@0: etisserant@0: def _checkHexBinaryType (simpleType, attributeValue, returnDict): etisserant@0: _checkIntegerRange (attributeValue, 16, 0, sys.maxint, returnDict) etisserant@0: returnDict["length"] = len(attributeValue) etisserant@0: etisserant@0: def _checkIntegerType (simpleType, attributeValue, returnDict): etisserant@0: _checkIntegerRange (attributeValue, 10, -sys.maxint-1, sys.maxint, returnDict) etisserant@0: etisserant@0: def _checkBooleanType (simpleType, attributeValue, returnDict): etisserant@0: if attributeValue not in ("true", "false", "1", "0"): etisserant@0: raise BaseTypeError("is not a boolean value!") etisserant@0: etisserant@0: def _checkQNameType (simpleType, attributeValue, returnDict): etisserant@0: # TODO: fill etisserant@0: returnDict["length"] = len(attributeValue) etisserant@0: etisserant@0: def _checkIntegerRange (attributeValue, base, minBase, maxBase, returnDict): etisserant@0: try: etisserant@0: value = string.atoi (attributeValue, base=base) etisserant@0: except: etisserant@0: if base == 16: etisserant@0: raise BaseTypeError("is not a hexadecimal value!") etisserant@0: else: etisserant@0: raise BaseTypeError("is not an integer!") etisserant@0: etisserant@0: if value < minBase or value > maxBase: etisserant@0: raise BaseTypeError("is out of range (%d..%d)!" %(minBase, maxBase)) etisserant@0: etisserant@0: returnDict["orderedValue"] = value etisserant@0: etisserant@0: etisserant@0: etisserant@0: ######################################## etisserant@0: # define own exception for XML schema validation errors etisserant@0: # etisserant@0: class SimpleTypeError (StandardError): etisserant@0: pass etisserant@0: etisserant@0: class BaseTypeError (StandardError): etisserant@0: pass