# # minixsv, Release 0.9.0 # file: xsvalSimpleTypes.py # # class for validation of XML schema simple types # # history: # 2004-09-09 rl created # 2006-08-18 rl W3C testsuite passed for supported features # 2007-05-24 rl Features for release 0.8 added, some bugs fixed # # Copyright (c) 2004-2007 by Roland Leuthe. All rights reserved. # # -------------------------------------------------------------------- # The minixsv XML schema validator is # # Copyright (c) 2004-2007 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 import datetime from decimal import Decimal from ..genxmlif.xmlifUtils import removeWhitespaces, collapseString, normalizeString, NsNameTupleFactory from ..minixsv import XSD_NAMESPACE from xsvalUtils import substituteSpecialEscChars ################################################### # Validator class for simple types ################################################### class XsSimpleTypeVal: def __init__ (self, parent): self.parent = parent self.xmlIf = parent.xmlIf self.xsdNsURI = parent.xsdNsURI self.xsdIdDict = parent.xsdIdDict self.xsdIdRefDict = parent.xsdIdRefDict def unlink (self): self.parent = None ######################################## # validate given value against simpleType # def checkSimpleType (self, inputNode, attrName, typeName, attributeValue, returnDict, idCheck): returnDict["adaptedAttrValue"] = attributeValue returnDict["BaseTypes"].append(str(typeName)) if _suppBaseTypeDict.has_key(typeName): try: _suppBaseTypeDict[typeName] (inputNode, typeName, attributeValue, returnDict) returnDict["primitiveType"] = typeName except BaseTypeError, errstr: raise SimpleTypeError("Value of %s (%s) %s" %(repr(attrName), repr(attributeValue), errstr)) elif self.parent.xsdTypeDict.has_key(typeName): typedefNode = self.parent.xsdTypeDict[typeName] if typedefNode.getNsName() == (XSD_NAMESPACE, "simpleType"): self.checkSimpleTypeDef (inputNode, typedefNode, attrName, attributeValue, returnDict, idCheck) elif (typedefNode.getNsName() == (XSD_NAMESPACE, "complexType") and typedefNode.getFirstChild().getNsName() == (XSD_NAMESPACE, "simpleContent")): self.checkSimpleTypeDef (inputNode, typedefNode.getFirstChild(), attrName, attributeValue, returnDict, idCheck) elif typedefNode.getAttribute("mixed") == "true": self.checkSimpleType (inputNode, attrName, (XSD_NAMESPACE, "string"), attributeValue, returnDict, idCheck) elif typeName != (XSD_NAMESPACE, "anyType"): raise SimpleTypeError("Attribute %s requires a simple type!" %repr(attrName)) if idCheck: adaptedAttrValue = returnDict["adaptedAttrValue"] if typeName == (XSD_NAMESPACE, "ID"): if not self.xsdIdDict.has_key(adaptedAttrValue): self.xsdIdDict[adaptedAttrValue] = inputNode else: raise SimpleTypeError("There are multiple occurences of ID value %s!" %repr(adaptedAttrValue)) if typeName == (XSD_NAMESPACE, "IDREF"): self.xsdIdRefDict[adaptedAttrValue] = inputNode else: # TODO: Fehler im XSD-File => Check muss an anderer Stelle erfolgen raise SimpleTypeError("%s uses unknown type %s!" %(repr(attrName), repr(typeName))) ######################################## # validate given value against simpleType node # def checkSimpleTypeDef (self, inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck): returnDict["adaptedAttrValue"] = attributeValue restrictionElement = xsdElement.getFirstChildNS(self.xsdNsURI, "restriction") extensionElement = xsdElement.getFirstChildNS(self.xsdNsURI, "extension") listElement = xsdElement.getFirstChildNS(self.xsdNsURI, "list") unionElement = xsdElement.getFirstChildNS(self.xsdNsURI, "union") if restrictionElement != None: self._checkRestrictionTag (inputNode, restrictionElement, attrName, attributeValue, returnDict, idCheck) if extensionElement != None: self._checkExtensionTag (inputNode, extensionElement, attrName, attributeValue, returnDict, idCheck) elif listElement != None: self._checkListTag (inputNode, listElement, attrName, attributeValue, returnDict, idCheck) elif unionElement != None: self._checkUnionTag (inputNode, unionElement, attrName, attributeValue, returnDict, idCheck) ######################################## # validate given value against base type # def checkBaseType (self, inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck): baseType = xsdElement.getQNameAttribute("base") if baseType != NsNameTupleFactory(None): self.checkSimpleType (inputNode, attrName, baseType, attributeValue, returnDict, idCheck) else: baseTypeNode = xsdElement.getFirstChildNS(self.xsdNsURI, "simpleType") self.checkSimpleTypeDef (inputNode, baseTypeNode, attrName, attributeValue, returnDict, idCheck) ######################################## # validate given value against restriction node # def _checkRestrictionTag (self, inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck): savedAttrValue = attributeValue # first check against base type self.checkBaseType (inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck) minExcl = xsdElement.getFirstChildNS(self.xsdNsURI, "minExclusive") minIncl = xsdElement.getFirstChildNS(self.xsdNsURI, "minInclusive") maxExcl = xsdElement.getFirstChildNS(self.xsdNsURI, "maxExclusive") maxIncl = xsdElement.getFirstChildNS(self.xsdNsURI, "maxInclusive") if minExcl != None: minExclReturnDict = {"BaseTypes":[], "primitiveType":None} minExclValue = minExcl.getAttribute("value") self.checkBaseType (inputNode, xsdElement, attrName, minExclValue, minExclReturnDict, idCheck=0) if returnDict.has_key("orderedValue") and minExclReturnDict.has_key("orderedValue"): if returnDict["orderedValue"] <= minExclReturnDict["orderedValue"]: raise SimpleTypeError ("Value of %s (%s) is <= minExclusive (%s)" %(repr(attrName), repr(attributeValue), repr(minExclValue))) elif minIncl != None: minInclReturnDict = {"BaseTypes":[], "primitiveType":None} minInclValue = minIncl.getAttribute("value") self.checkBaseType (inputNode, xsdElement, attrName, minInclValue, minInclReturnDict, idCheck=0) if returnDict.has_key("orderedValue") and minInclReturnDict.has_key("orderedValue"): if returnDict["orderedValue"] < minInclReturnDict["orderedValue"]: raise SimpleTypeError ("Value of %s (%s) is < minInclusive (%s)" %(repr(attrName), repr(attributeValue), repr(minInclValue))) if maxExcl != None: maxExclReturnDict = {"BaseTypes":[], "primitiveType":None} maxExclValue = maxExcl.getAttribute("value") self.checkBaseType (inputNode, xsdElement, attrName, maxExclValue, maxExclReturnDict, idCheck=0) if returnDict.has_key("orderedValue") and maxExclReturnDict.has_key("orderedValue"): if returnDict["orderedValue"] >= maxExclReturnDict["orderedValue"]: raise SimpleTypeError ("Value of %s (%s) is >= maxExclusive (%s)" %(repr(attrName), repr(attributeValue), repr(maxExclValue))) elif maxIncl != None: maxInclReturnDict = {"BaseTypes":[], "primitiveType":None} maxInclValue = maxIncl.getAttribute("value") self.checkBaseType (inputNode, xsdElement, attrName, maxInclValue, maxInclReturnDict, idCheck=0) if returnDict.has_key("orderedValue") and maxInclReturnDict.has_key("orderedValue"): if returnDict["orderedValue"] > maxInclReturnDict["orderedValue"]: raise SimpleTypeError ("Value of %s (%s) is > maxInclusive (%s)" %(repr(attrName), repr(attributeValue), repr(maxInclValue))) totalDigitsNode = xsdElement.getFirstChildNS(self.xsdNsURI, "totalDigits") if totalDigitsNode != None: orderedValueStr = repr(returnDict["orderedValue"]) digits = re.findall("\d" ,orderedValueStr) if digits[0] == "0" and len(digits) > 1: digits = digits[1:] totalDigitsValue = totalDigitsNode.getAttribute("value") if totalDigitsNode.getAttribute("fixed") == "true": if len(digits) != string.atoi(totalDigitsValue): raise SimpleTypeError ("Total number of digits != %s for %s (%s)" %(repr(totalDigitsValue), repr(attrName), repr(attributeValue))) else: if len(digits) > string.atoi(totalDigitsValue): raise SimpleTypeError ("Total number of digits > %s for %s (%s)" %(repr(totalDigitsValue), repr(attrName), repr(attributeValue))) fractionDigitsNode = xsdElement.getFirstChildNS(self.xsdNsURI, "fractionDigits") if fractionDigitsNode != None: orderedValueStr = repr(returnDict["orderedValue"]) fractionDigitsValue = fractionDigitsNode.getAttribute("value") result = re.search("(?P\d*)(?P\.)(?P\d+)", orderedValueStr) 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)" %(repr(fractionDigitsValue), repr(attrName), repr(attributeValue))) elif numberOfFracDigits > string.atoi(fractionDigitsValue): raise SimpleTypeError ("Fraction number of digits > %s for %s (%s)" %(repr(fractionDigitsValue), repr(attrName), repr(attributeValue))) if returnDict.has_key("length"): lengthNode = xsdElement.getFirstChildNS(self.xsdNsURI, "length") if lengthNode != None: length = string.atoi(lengthNode.getAttribute("value")) if returnDict["length"] != length: raise SimpleTypeError ("Length of %s (%s) must be %d!" %(repr(attrName), repr(attributeValue), length)) minLengthNode = xsdElement.getFirstChildNS(self.xsdNsURI, "minLength") if minLengthNode != None: minLength = string.atoi(minLengthNode.getAttribute("value")) if returnDict["length"] < minLength: raise SimpleTypeError ("Length of %s (%s) must be >= %d!" %(repr(attrName), repr(attributeValue), minLength)) maxLengthNode = xsdElement.getFirstChildNS(self.xsdNsURI, "maxLength") if maxLengthNode != None: maxLength = string.atoi(maxLengthNode.getAttribute("value")) if returnDict["length"] > maxLength: raise SimpleTypeError ("Length of %s (%s) must be <= %d!" %(repr(attrName), repr(attributeValue), maxLength)) whiteSpace = xsdElement.getFirstChildNS(self.xsdNsURI, "whiteSpace") if whiteSpace != None: returnDict["wsAction"] = whiteSpace.getAttribute("value") if returnDict["wsAction"] == "replace": normalizedValue = normalizeString(attributeValue) if normalizedValue != attributeValue: returnDict["adaptedAttrValue"] = normalizedValue elif returnDict["wsAction"] == "collapse": collapsedValue = collapseString(attributeValue) if collapsedValue != attributeValue: returnDict["adaptedAttrValue"] = collapsedValue enumerationElementList = xsdElement.getChildrenNS(self.xsdNsURI, "enumeration") if enumerationElementList != []: if returnDict.has_key("orderedValue"): attributeValue = returnDict["orderedValue"] elif returnDict.has_key("adaptedAttrValue"): attributeValue = returnDict["adaptedAttrValue"] for enumeration in enumerationElementList: enumReturnDict = {"BaseTypes":[], "primitiveType":None} enumValue = enumeration["value"] self.checkBaseType (inputNode, xsdElement, attrName, enumValue, enumReturnDict, idCheck=0) if enumReturnDict.has_key("orderedValue"): enumValue = enumReturnDict["orderedValue"] elif enumReturnDict.has_key("adaptedAttrValue"): enumValue = enumReturnDict["adaptedAttrValue"] if enumValue == attributeValue: break else: raise SimpleTypeError ("Enumeration value %s not allowed!" %repr(attributeValue)) if returnDict.has_key("adaptedAttrValue"): attributeValue = returnDict["adaptedAttrValue"] patternMatch = 1 notMatchedPatternList = [] for patternNode in xsdElement.getChildrenNS(self.xsdNsURI, "pattern"): rePattern = patternNode.getAttribute("value") intRePattern = rePattern try: intRePattern = substituteSpecialEscChars (intRePattern) except SyntaxError, errInst: raise SimpleTypeError, str(errInst) patternMatch = self._matchesPattern (intRePattern, attributeValue) if patternMatch: break else: notMatchedPatternList.append(rePattern) if not patternMatch: try: pattern = " nor ".join(notMatchedPatternList) except: pattern = "" raise SimpleTypeError ("Value of attribute %s (%s) does not match pattern %s!" %(repr(attrName), repr(attributeValue), repr(pattern))) ######################################## # checks if 'value' matches 'rePattern' completely # def _matchesPattern (self, intRePattern, attributeValue): completePatternMatch = 0 try: regexObj = re.match(intRePattern, attributeValue, re.U) if regexObj and regexObj.end() == len(attributeValue): completePatternMatch = 1 except Exception: pass return completePatternMatch ######################################## # validate given value against list node # def _checkListTag (self, inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck): if attributeValue != "": itemType = xsdElement.getQNameAttribute ("itemType") # substitute multiple whitespace characters by a single ' ' collapsedValue = collapseString(attributeValue) returnDict["wsAction"] = "collapse" returnDict["adaptedAttrValue"] = collapsedValue # divide up attributeValue => store it into list attributeList = string.split(collapsedValue, " ") for attrValue in attributeList: elementReturnDict = {"BaseTypes":[], "primitiveType":None} if itemType != (None, None): self.checkSimpleType (inputNode, attrName, itemType, attrValue, elementReturnDict, idCheck) else: itemTypeNode = xsdElement.getFirstChildNS(self.xsdNsURI, "simpleType") self.checkSimpleTypeDef (inputNode, itemTypeNode, attrName, attrValue, elementReturnDict, idCheck) returnDict["BaseTypes"].extend(elementReturnDict["BaseTypes"]) returnDict["length"] = len(attributeList) else: returnDict["length"] = 0 ######################################## # validate given value against extension node # def _checkExtensionTag (self, inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck): # first check against base type self.checkBaseType (inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck) ######################################## # validate given value against union node # def _checkUnionTag (self, inputNode, xsdElement, attrName, attributeValue, returnDict, idCheck): 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(collapseString(memberTypes), " "): try: self.checkSimpleType (inputNode, attrName, xsdElement.qName2NsName(memberType, useDefaultNs=1), attributeValue, returnDict, idCheck) return except SimpleTypeError, errstr: pass # memberTypes and additional type definitions is legal! for childSimpleType in xsdElement.getChildrenNS(self.xsdNsURI, "simpleType"): try: self.checkSimpleTypeDef (inputNode, childSimpleType, attrName, attributeValue, returnDict, idCheck) return except SimpleTypeError, errstr: pass raise SimpleTypeError ("%s (%s) is no valid union member type!" %(repr(attrName), repr(attributeValue))) ############################################################### # Base type check functions ############################################################### reDecimal = re.compile("[+-]?[0-9]*\.?[0-9]+", re.U) reInteger = re.compile("[+-]?[0-9]+", re.U) reDouble = re.compile("([+-]?[0-9]*\.?[0-9]+([eE][+\-]?[0-9]+)?)|INF|-INF|NaN", re.U) reHexBinary = re.compile("([a-fA-F0-9]{2})*", re.U) reBase64Binary = re.compile("(?P[a-zA-Z0-9+/]*)={0,3}", re.U) reQName = re.compile(substituteSpecialEscChars("\i\c*"), re.U) reDuration = re.compile("-?P(?P\d+Y)?(?P\d+M)?(?P\d+D)?(T(?P\d+H)?(?P\d+M)?((?P\d+)(?P\.\d+)?S)?)?", re.U) reDateTime = re.compile("(?P\d{4}-\d{2}-\d{2})T(?P