minixsv/xsvalSimpleTypes.py
author etisserant
Fri, 06 Jul 2007 18:55:52 +0200
changeset 25 8dc68e669d99
parent 0 b622defdfd98
permissions -rw-r--r--
Early implementation of STD library.
#
# # 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<intDigits>\d+)(?P<dot>\.)(?P<fracDigits>\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