diff -r 000000000000 -r b622defdfd98 minixsv/xsvalSimpleTypes.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/minixsv/xsvalSimpleTypes.py Wed Jan 31 16:31:39 2007 +0100 @@ -0,0 +1,332 @@ +# +# # minixsv, Release 0.3 +# file: xsvalSimpleTypes.py +# +# class for validation of XML schema simple types +# +# history: +# 2004-09-09 rl created +# +# Copyright (c) 2004 by Roland Leuthe. All rights reserved. +# +# -------------------------------------------------------------------- +# The minixsv XML schema validator is +# +# Copyright (c) 2004 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 sys +import string +import re + + +class XsSimpleTypeVal: + + def __init__ (self, parent): + self.parent = parent + self.xmlIf = parent.xmlIf + self.xsdNSAlias = parent.xsdNSAlias + self.xsdTree = parent.xsdTree + + + ######################################## + # validate given value against simpleType + # + def checkSimpleType (self, attrName, typeName, attributeValue, returnDict): + localTypeName = self.xmlIf.extractLocalName(typeName) + if self.parent.xsdTypeDict.has_key(localTypeName): + typedefNode = self.parent.xsdTypeDict[localTypeName] + if typedefNode.getTagName () == self.xsdNSAlias + "simpleType": + self.checkSimpleTypeDef (typedefNode, attrName, attributeValue, returnDict) + else: + raise SimpleTypeError("Type '%s' must be simple type!" %(typeName)) + else: + try: + validateBaseType (typeName, attributeValue, returnDict) + except BaseTypeError, errstr: + raise SimpleTypeError("Value of '%s' (%s) %s" %(attrName, attributeValue, errstr)) + + + ######################################## + # validate given value against simpleType node + # + def checkSimpleTypeDef (self, xsdElement, attrName, attributeValue, returnDict): + restrictionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "restriction") + listElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "list") + unionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "union") + if restrictionElement != None: + self._checkRestrictionTag (restrictionElement, attrName, attributeValue, returnDict) + elif listElement != None: + self._checkListTag (listElement, attrName, attributeValue, returnDict) + elif unionElement != None: + self._checkUnionTag (unionElement, attrName, attributeValue, returnDict) + + ######################################## + # validate given value against restriction node + # + def _checkRestrictionTag (self, xsdElement, attrName, attributeValue, returnDict): + # first check against base type + baseType = xsdElement.getAttribute("base") + if baseType != None: + self.checkSimpleType (attrName, baseType, attributeValue, returnDict) + else: + baseTypeNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "simpleType") + self.checkSimpleTypeDef (baseTypeNode, attrName, attributeValue, returnDict) + + minExcl = xsdElement.getFirstChildNS(self.xsdNSAlias, "minExclusive") + minIncl = xsdElement.getFirstChildNS(self.xsdNSAlias, "minInclusive") + maxExcl = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxExclusive") + maxIncl = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxInclusive") + + if minExcl != None: + minExclReturnDict = {} + minExclValue = minExcl.getAttribute("value") + self.checkSimpleType (attrName, baseType, minExclValue, minExclReturnDict) + if returnDict.has_key("orderedValue") and minExclReturnDict.has_key("orderedValue"): + if returnDict["orderedValue"] <= minExclReturnDict["orderedValue"]: + raise SimpleTypeError ("Value of %s (%s) is <= minExclusive (%s)" %(attrName, attributeValue, minExclValue)) + elif minIncl != None: + minInclReturnDict = {} + minInclValue = minIncl.getAttribute("value") + self.checkSimpleType (attrName, baseType, minInclValue, minInclReturnDict) + if returnDict.has_key("orderedValue") and minInclReturnDict.has_key("orderedValue"): + if returnDict["orderedValue"] < minInclReturnDict["orderedValue"]: + raise SimpleTypeError ("Value of %s (%s) is < minInclusive (%s)" %(attrName, attributeValue, minInclValue)) + if maxExcl != None: + maxExclReturnDict = {} + maxExclValue = maxExcl.getAttribute("value") + self.checkSimpleType (attrName, baseType, maxExclValue, maxExclReturnDict) + if returnDict.has_key("orderedValue") and maxExclReturnDict.has_key("orderedValue"): + if returnDict["orderedValue"] >= maxExclReturnDict["orderedValue"]: + raise SimpleTypeError ("Value of %s (%s) is >= maxExclusive (%s)" %(attrName, attributeValue, maxExclValue)) + elif maxIncl != None: + maxInclReturnDict = {} + maxInclValue = maxIncl.getAttribute("value") + self.checkSimpleType (attrName, baseType, maxInclValue, maxInclReturnDict) + if returnDict.has_key("orderedValue") and maxInclReturnDict.has_key("orderedValue"): + if returnDict["orderedValue"] > maxInclReturnDict["orderedValue"]: + raise SimpleTypeError ("Value of %s (%s) is > maxInclusive (%s)" %(attrName, attributeValue, maxInclValue)) + + totalDigitsNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "totalDigits") + if totalDigitsNode != None: + totalDigitsValue = totalDigitsNode.getAttribute("value") + if totalDigitsNode.getAttribute("fixed") == "true": + if len(re.findall("\d" ,attributeValue)) != string.atoi(totalDigitsValue): + raise SimpleTypeError ("Total number of digits != %s for %s (%s)" %(totalDigitsValue, attrName, attributeValue)) + else: + if len(re.findall("\d" ,attributeValue)) > string.atoi(totalDigitsValue): + raise SimpleTypeError ("Total number of digits > %s for %s (%s)" %(totalDigitsValue, attrName, attributeValue)) + fractionDigitsNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "fractionDigits") + if fractionDigitsNode != None: + fractionDigitsValue = fractionDigitsNode.getAttribute("value") + result = re.search("(?P\d+)(?P\.)(?P\d+)" ,attributeValue) + if result != None: + numberOfFracDigits = len (result.group('fracDigits')) + else: + numberOfFracDigits = 0 + if fractionDigitsNode.getAttribute("fixed") == "true" and numberOfFracDigits != string.atoi(fractionDigitsValue): + raise SimpleTypeError ("Fraction number of digits != %s for %s (%s)" %(fractionDigitsValue, attrName, attributeValue)) + elif numberOfFracDigits > string.atoi(fractionDigitsValue): + raise SimpleTypeError ("Fraction number of digits > %s for %s (%s)" %(fractionDigitsValue, attrName, attributeValue)) + + if returnDict.has_key("length"): + lengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "length") + if lengthNode != None: + length = string.atoi(lengthNode.getAttribute("value")) + if returnDict["length"] != length: + raise SimpleTypeError ("Length of %s (%s) must be %d!" %(attrName, attributeValue, length)) + minLengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "minLength") + if minLengthNode != None: + minLength = string.atoi(minLengthNode.getAttribute("value")) + if returnDict["length"] < minLength: + raise SimpleTypeError ("Length of %s (%s) must be >= %d!" %(attrName, attributeValue, minLength)) + maxLengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxLength") + if maxLengthNode != None: + maxLength = string.atoi(maxLengthNode.getAttribute("value")) + if returnDict["length"] > maxLength: + raise SimpleTypeError ("Length of %s (%s) must be <= %d!" %(attrName, attributeValue, maxLength)) + + enumerationElementList = xsdElement.getChildrenNS(self.xsdNSAlias, "enumeration") + if enumerationElementList != []: + for enumeration in enumerationElementList: + if enumeration.getAttribute ("value") == attributeValue: + break + else: + raise SimpleTypeError ("Enumeration value '%s' not allowed!" %(attributeValue)) + + patternNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "pattern") + if patternNode != None: + rePattern = patternNode.getAttribute("value") + regexObj = re.match(rePattern, attributeValue) + if not regexObj or regexObj.end() != len(attributeValue): + raise SimpleTypeError ("Attribute value '%s' does not match pattern '%s'!" %(attributeValue, rePattern)) + + whiteSpace = xsdElement.getFirstChildNS(self.xsdNSAlias, "whiteSpace") + if whiteSpace != None: + wsAction = whiteSpace.getAttribute("value") + if wsAction == "replace": + normalizedValue = self._normalizeString(attributeValue) + if normalizedValue != attributeValue: + returnDict["adaptedAttrValue"] = normalizedValue + elif wsAction == "collapse": + collapsedValue = self._collapseString(attributeValue) + if collapsedValue != attributeValue: + returnDict["adaptedAttrValue"] = collapsedValue + + + ######################################## + # validate given value against list node + # + def _checkListTag (self, xsdElement, attrName, attributeValue, returnDict): + if attributeValue != "": + itemType = xsdElement.getAttribute ("itemType") + # substitute multiple whitespace characters by a single ' ' + collapsedValue = self._collapseString(attributeValue) + if collapsedValue != attributeValue: + returnDict["adaptedAttrValue"] = collapsedValue + + # divide up attributeValue => store it into list + attributeList = string.split(collapsedValue, " ") + for attrValue in attributeList: + elementReturnDict = {} + if itemType != None: + self.checkSimpleType (attrName, itemType, attrValue, elementReturnDict) + else: + itemTypeNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "simpleType") + self.checkSimpleTypeDef (itemTypeNode, attrName, attrValue, elementReturnDict) + + returnDict["length"] = len(attributeList) + else: + returnDict["length"] = 0 + + + ######################################## + # validate given value against union node + # + def _checkUnionTag (self, xsdElement, attrName, attributeValue, returnDict): + memberTypes = xsdElement.getAttribute ("memberTypes") + if memberTypes != None: + # substitute multiple whitespace characters by a single ' ' + # divide up attributeValue => store it into list + for memberType in string.split(self._collapseString(memberTypes), " "): + try: + self.checkSimpleType (attrName, memberType, attributeValue, returnDict) + return + except SimpleTypeError, errstr: + pass + + # memberTypes and additional type definitions is legal! + for childSimpleType in xsdElement.getChildrenNS(self.xsdNSAlias, "simpleType"): + try: + self.checkSimpleTypeDef (childSimpleType, attrName, attributeValue, returnDict) + return + except SimpleTypeError, errstr: + pass + + raise SimpleTypeError ("%s (%s) is no valid union member type (%s)!" %(attrName, attributeValue, memberTypes)) + + + ######################################## + # substitute multiple whitespace characters by a single ' ' + # + def _collapseString (self, strValue): + return re.sub('\s+', ' ', strValue) + + ######################################## + # substitute each whitespace characters by a single ' ' + # + def _normalizeString (self, strValue): + return re.sub('\s', ' ', strValue) + + + + +def validateBaseType (simpleType, attributeValue, returnDict): +# TODO: Many base types are not completely defined by datatypes.xsd + simpleTypeDict = {"xsd:string": _checkStringType, + "xsd:hexBinary": _checkHexBinaryType, + "xsd:integer": _checkIntegerType, + "xsd:boolean": _checkBooleanType, + "xsd:QName": _checkQNameType, + } + + simpleType = string.replace(simpleType, "xs:", "xsd:") + if simpleTypeDict.has_key (simpleType): + simpleTypeDict[simpleType] (simpleType, attributeValue, returnDict) + + elif simpleType != "xsd:anySimpleType": + if simpleType in ("xsd:decimal", "xsd:float", "xsd:double", "xsd:base64Binary", "xsd:anyURI", "xsd:NOTATION", + "xsd:duration", "xsd:dateTime", "xsd:time", "xsd:date", + "xsd:gYearMonth", "xsd:gMonthDay", "xsd:gYear", "xsd:gMonth", "xsd:gDay"): + print "INFO: Check of simple type '%s' currently not supported!" %(simpleType) + else: + # TODO: Fehler im XSD-File => Check muss an anderer Stelle erfolgen + raise BaseTypeError("uses unknown type '%s'!" %(simpleType)) + + +def _checkStringType (simpleType, attributeValue, returnDict): + # all valid?? + returnDict["length"] = len(attributeValue) + +def _checkHexBinaryType (simpleType, attributeValue, returnDict): + _checkIntegerRange (attributeValue, 16, 0, sys.maxint, returnDict) + returnDict["length"] = len(attributeValue) + +def _checkIntegerType (simpleType, attributeValue, returnDict): + _checkIntegerRange (attributeValue, 10, -sys.maxint-1, sys.maxint, returnDict) + +def _checkBooleanType (simpleType, attributeValue, returnDict): + if attributeValue not in ("true", "false", "1", "0"): + raise BaseTypeError("is not a boolean value!") + +def _checkQNameType (simpleType, attributeValue, returnDict): + # TODO: fill + returnDict["length"] = len(attributeValue) + +def _checkIntegerRange (attributeValue, base, minBase, maxBase, returnDict): + try: + value = string.atoi (attributeValue, base=base) + except: + if base == 16: + raise BaseTypeError("is not a hexadecimal value!") + else: + raise BaseTypeError("is not an integer!") + + if value < minBase or value > maxBase: + raise BaseTypeError("is out of range (%d..%d)!" %(minBase, maxBase)) + + returnDict["orderedValue"] = value + + + +######################################## +# define own exception for XML schema validation errors +# +class SimpleTypeError (StandardError): + pass + +class BaseTypeError (StandardError): + pass