0
|
1 |
#
|
|
2 |
# # minixsv, Release 0.3
|
|
3 |
# file: xsvalSimpleTypes.py
|
|
4 |
#
|
|
5 |
# class for validation of XML schema simple types
|
|
6 |
#
|
|
7 |
# history:
|
|
8 |
# 2004-09-09 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 |
|
|
41 |
import sys
|
|
42 |
import string
|
|
43 |
import re
|
|
44 |
|
|
45 |
|
|
46 |
class XsSimpleTypeVal:
|
|
47 |
|
|
48 |
def __init__ (self, parent):
|
|
49 |
self.parent = parent
|
|
50 |
self.xmlIf = parent.xmlIf
|
|
51 |
self.xsdNSAlias = parent.xsdNSAlias
|
|
52 |
self.xsdTree = parent.xsdTree
|
|
53 |
|
|
54 |
|
|
55 |
########################################
|
|
56 |
# validate given value against simpleType
|
|
57 |
#
|
|
58 |
def checkSimpleType (self, attrName, typeName, attributeValue, returnDict):
|
|
59 |
localTypeName = self.xmlIf.extractLocalName(typeName)
|
|
60 |
if self.parent.xsdTypeDict.has_key(localTypeName):
|
|
61 |
typedefNode = self.parent.xsdTypeDict[localTypeName]
|
|
62 |
if typedefNode.getTagName () == self.xsdNSAlias + "simpleType":
|
|
63 |
self.checkSimpleTypeDef (typedefNode, attrName, attributeValue, returnDict)
|
|
64 |
else:
|
|
65 |
raise SimpleTypeError("Type '%s' must be simple type!" %(typeName))
|
|
66 |
else:
|
|
67 |
try:
|
|
68 |
validateBaseType (typeName, attributeValue, returnDict)
|
|
69 |
except BaseTypeError, errstr:
|
|
70 |
raise SimpleTypeError("Value of '%s' (%s) %s" %(attrName, attributeValue, errstr))
|
|
71 |
|
|
72 |
|
|
73 |
########################################
|
|
74 |
# validate given value against simpleType node
|
|
75 |
#
|
|
76 |
def checkSimpleTypeDef (self, xsdElement, attrName, attributeValue, returnDict):
|
|
77 |
restrictionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "restriction")
|
|
78 |
listElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "list")
|
|
79 |
unionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "union")
|
|
80 |
if restrictionElement != None:
|
|
81 |
self._checkRestrictionTag (restrictionElement, attrName, attributeValue, returnDict)
|
|
82 |
elif listElement != None:
|
|
83 |
self._checkListTag (listElement, attrName, attributeValue, returnDict)
|
|
84 |
elif unionElement != None:
|
|
85 |
self._checkUnionTag (unionElement, attrName, attributeValue, returnDict)
|
|
86 |
|
|
87 |
########################################
|
|
88 |
# validate given value against restriction node
|
|
89 |
#
|
|
90 |
def _checkRestrictionTag (self, xsdElement, attrName, attributeValue, returnDict):
|
|
91 |
# first check against base type
|
|
92 |
baseType = xsdElement.getAttribute("base")
|
|
93 |
if baseType != None:
|
|
94 |
self.checkSimpleType (attrName, baseType, attributeValue, returnDict)
|
|
95 |
else:
|
|
96 |
baseTypeNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "simpleType")
|
|
97 |
self.checkSimpleTypeDef (baseTypeNode, attrName, attributeValue, returnDict)
|
|
98 |
|
|
99 |
minExcl = xsdElement.getFirstChildNS(self.xsdNSAlias, "minExclusive")
|
|
100 |
minIncl = xsdElement.getFirstChildNS(self.xsdNSAlias, "minInclusive")
|
|
101 |
maxExcl = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxExclusive")
|
|
102 |
maxIncl = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxInclusive")
|
|
103 |
|
|
104 |
if minExcl != None:
|
|
105 |
minExclReturnDict = {}
|
|
106 |
minExclValue = minExcl.getAttribute("value")
|
|
107 |
self.checkSimpleType (attrName, baseType, minExclValue, minExclReturnDict)
|
|
108 |
if returnDict.has_key("orderedValue") and minExclReturnDict.has_key("orderedValue"):
|
|
109 |
if returnDict["orderedValue"] <= minExclReturnDict["orderedValue"]:
|
|
110 |
raise SimpleTypeError ("Value of %s (%s) is <= minExclusive (%s)" %(attrName, attributeValue, minExclValue))
|
|
111 |
elif minIncl != None:
|
|
112 |
minInclReturnDict = {}
|
|
113 |
minInclValue = minIncl.getAttribute("value")
|
|
114 |
self.checkSimpleType (attrName, baseType, minInclValue, minInclReturnDict)
|
|
115 |
if returnDict.has_key("orderedValue") and minInclReturnDict.has_key("orderedValue"):
|
|
116 |
if returnDict["orderedValue"] < minInclReturnDict["orderedValue"]:
|
|
117 |
raise SimpleTypeError ("Value of %s (%s) is < minInclusive (%s)" %(attrName, attributeValue, minInclValue))
|
|
118 |
if maxExcl != None:
|
|
119 |
maxExclReturnDict = {}
|
|
120 |
maxExclValue = maxExcl.getAttribute("value")
|
|
121 |
self.checkSimpleType (attrName, baseType, maxExclValue, maxExclReturnDict)
|
|
122 |
if returnDict.has_key("orderedValue") and maxExclReturnDict.has_key("orderedValue"):
|
|
123 |
if returnDict["orderedValue"] >= maxExclReturnDict["orderedValue"]:
|
|
124 |
raise SimpleTypeError ("Value of %s (%s) is >= maxExclusive (%s)" %(attrName, attributeValue, maxExclValue))
|
|
125 |
elif maxIncl != None:
|
|
126 |
maxInclReturnDict = {}
|
|
127 |
maxInclValue = maxIncl.getAttribute("value")
|
|
128 |
self.checkSimpleType (attrName, baseType, maxInclValue, maxInclReturnDict)
|
|
129 |
if returnDict.has_key("orderedValue") and maxInclReturnDict.has_key("orderedValue"):
|
|
130 |
if returnDict["orderedValue"] > maxInclReturnDict["orderedValue"]:
|
|
131 |
raise SimpleTypeError ("Value of %s (%s) is > maxInclusive (%s)" %(attrName, attributeValue, maxInclValue))
|
|
132 |
|
|
133 |
totalDigitsNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "totalDigits")
|
|
134 |
if totalDigitsNode != None:
|
|
135 |
totalDigitsValue = totalDigitsNode.getAttribute("value")
|
|
136 |
if totalDigitsNode.getAttribute("fixed") == "true":
|
|
137 |
if len(re.findall("\d" ,attributeValue)) != string.atoi(totalDigitsValue):
|
|
138 |
raise SimpleTypeError ("Total number of digits != %s for %s (%s)" %(totalDigitsValue, attrName, attributeValue))
|
|
139 |
else:
|
|
140 |
if len(re.findall("\d" ,attributeValue)) > string.atoi(totalDigitsValue):
|
|
141 |
raise SimpleTypeError ("Total number of digits > %s for %s (%s)" %(totalDigitsValue, attrName, attributeValue))
|
|
142 |
fractionDigitsNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "fractionDigits")
|
|
143 |
if fractionDigitsNode != None:
|
|
144 |
fractionDigitsValue = fractionDigitsNode.getAttribute("value")
|
|
145 |
result = re.search("(?P<intDigits>\d+)(?P<dot>\.)(?P<fracDigits>\d+)" ,attributeValue)
|
|
146 |
if result != None:
|
|
147 |
numberOfFracDigits = len (result.group('fracDigits'))
|
|
148 |
else:
|
|
149 |
numberOfFracDigits = 0
|
|
150 |
if fractionDigitsNode.getAttribute("fixed") == "true" and numberOfFracDigits != string.atoi(fractionDigitsValue):
|
|
151 |
raise SimpleTypeError ("Fraction number of digits != %s for %s (%s)" %(fractionDigitsValue, attrName, attributeValue))
|
|
152 |
elif numberOfFracDigits > string.atoi(fractionDigitsValue):
|
|
153 |
raise SimpleTypeError ("Fraction number of digits > %s for %s (%s)" %(fractionDigitsValue, attrName, attributeValue))
|
|
154 |
|
|
155 |
if returnDict.has_key("length"):
|
|
156 |
lengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "length")
|
|
157 |
if lengthNode != None:
|
|
158 |
length = string.atoi(lengthNode.getAttribute("value"))
|
|
159 |
if returnDict["length"] != length:
|
|
160 |
raise SimpleTypeError ("Length of %s (%s) must be %d!" %(attrName, attributeValue, length))
|
|
161 |
minLengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "minLength")
|
|
162 |
if minLengthNode != None:
|
|
163 |
minLength = string.atoi(minLengthNode.getAttribute("value"))
|
|
164 |
if returnDict["length"] < minLength:
|
|
165 |
raise SimpleTypeError ("Length of %s (%s) must be >= %d!" %(attrName, attributeValue, minLength))
|
|
166 |
maxLengthNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "maxLength")
|
|
167 |
if maxLengthNode != None:
|
|
168 |
maxLength = string.atoi(maxLengthNode.getAttribute("value"))
|
|
169 |
if returnDict["length"] > maxLength:
|
|
170 |
raise SimpleTypeError ("Length of %s (%s) must be <= %d!" %(attrName, attributeValue, maxLength))
|
|
171 |
|
|
172 |
enumerationElementList = xsdElement.getChildrenNS(self.xsdNSAlias, "enumeration")
|
|
173 |
if enumerationElementList != []:
|
|
174 |
for enumeration in enumerationElementList:
|
|
175 |
if enumeration.getAttribute ("value") == attributeValue:
|
|
176 |
break
|
|
177 |
else:
|
|
178 |
raise SimpleTypeError ("Enumeration value '%s' not allowed!" %(attributeValue))
|
|
179 |
|
|
180 |
patternNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "pattern")
|
|
181 |
if patternNode != None:
|
|
182 |
rePattern = patternNode.getAttribute("value")
|
|
183 |
regexObj = re.match(rePattern, attributeValue)
|
|
184 |
if not regexObj or regexObj.end() != len(attributeValue):
|
|
185 |
raise SimpleTypeError ("Attribute value '%s' does not match pattern '%s'!" %(attributeValue, rePattern))
|
|
186 |
|
|
187 |
whiteSpace = xsdElement.getFirstChildNS(self.xsdNSAlias, "whiteSpace")
|
|
188 |
if whiteSpace != None:
|
|
189 |
wsAction = whiteSpace.getAttribute("value")
|
|
190 |
if wsAction == "replace":
|
|
191 |
normalizedValue = self._normalizeString(attributeValue)
|
|
192 |
if normalizedValue != attributeValue:
|
|
193 |
returnDict["adaptedAttrValue"] = normalizedValue
|
|
194 |
elif wsAction == "collapse":
|
|
195 |
collapsedValue = self._collapseString(attributeValue)
|
|
196 |
if collapsedValue != attributeValue:
|
|
197 |
returnDict["adaptedAttrValue"] = collapsedValue
|
|
198 |
|
|
199 |
|
|
200 |
########################################
|
|
201 |
# validate given value against list node
|
|
202 |
#
|
|
203 |
def _checkListTag (self, xsdElement, attrName, attributeValue, returnDict):
|
|
204 |
if attributeValue != "":
|
|
205 |
itemType = xsdElement.getAttribute ("itemType")
|
|
206 |
# substitute multiple whitespace characters by a single ' '
|
|
207 |
collapsedValue = self._collapseString(attributeValue)
|
|
208 |
if collapsedValue != attributeValue:
|
|
209 |
returnDict["adaptedAttrValue"] = collapsedValue
|
|
210 |
|
|
211 |
# divide up attributeValue => store it into list
|
|
212 |
attributeList = string.split(collapsedValue, " ")
|
|
213 |
for attrValue in attributeList:
|
|
214 |
elementReturnDict = {}
|
|
215 |
if itemType != None:
|
|
216 |
self.checkSimpleType (attrName, itemType, attrValue, elementReturnDict)
|
|
217 |
else:
|
|
218 |
itemTypeNode = xsdElement.getFirstChildNS(self.xsdNSAlias, "simpleType")
|
|
219 |
self.checkSimpleTypeDef (itemTypeNode, attrName, attrValue, elementReturnDict)
|
|
220 |
|
|
221 |
returnDict["length"] = len(attributeList)
|
|
222 |
else:
|
|
223 |
returnDict["length"] = 0
|
|
224 |
|
|
225 |
|
|
226 |
########################################
|
|
227 |
# validate given value against union node
|
|
228 |
#
|
|
229 |
def _checkUnionTag (self, xsdElement, attrName, attributeValue, returnDict):
|
|
230 |
memberTypes = xsdElement.getAttribute ("memberTypes")
|
|
231 |
if memberTypes != None:
|
|
232 |
# substitute multiple whitespace characters by a single ' '
|
|
233 |
# divide up attributeValue => store it into list
|
|
234 |
for memberType in string.split(self._collapseString(memberTypes), " "):
|
|
235 |
try:
|
|
236 |
self.checkSimpleType (attrName, memberType, attributeValue, returnDict)
|
|
237 |
return
|
|
238 |
except SimpleTypeError, errstr:
|
|
239 |
pass
|
|
240 |
|
|
241 |
# memberTypes and additional type definitions is legal!
|
|
242 |
for childSimpleType in xsdElement.getChildrenNS(self.xsdNSAlias, "simpleType"):
|
|
243 |
try:
|
|
244 |
self.checkSimpleTypeDef (childSimpleType, attrName, attributeValue, returnDict)
|
|
245 |
return
|
|
246 |
except SimpleTypeError, errstr:
|
|
247 |
pass
|
|
248 |
|
|
249 |
raise SimpleTypeError ("%s (%s) is no valid union member type (%s)!" %(attrName, attributeValue, memberTypes))
|
|
250 |
|
|
251 |
|
|
252 |
########################################
|
|
253 |
# substitute multiple whitespace characters by a single ' '
|
|
254 |
#
|
|
255 |
def _collapseString (self, strValue):
|
|
256 |
return re.sub('\s+', ' ', strValue)
|
|
257 |
|
|
258 |
########################################
|
|
259 |
# substitute each whitespace characters by a single ' '
|
|
260 |
#
|
|
261 |
def _normalizeString (self, strValue):
|
|
262 |
return re.sub('\s', ' ', strValue)
|
|
263 |
|
|
264 |
|
|
265 |
|
|
266 |
|
|
267 |
def validateBaseType (simpleType, attributeValue, returnDict):
|
|
268 |
# TODO: Many base types are not completely defined by datatypes.xsd
|
|
269 |
simpleTypeDict = {"xsd:string": _checkStringType,
|
|
270 |
"xsd:hexBinary": _checkHexBinaryType,
|
|
271 |
"xsd:integer": _checkIntegerType,
|
|
272 |
"xsd:boolean": _checkBooleanType,
|
|
273 |
"xsd:QName": _checkQNameType,
|
|
274 |
}
|
|
275 |
|
|
276 |
simpleType = string.replace(simpleType, "xs:", "xsd:")
|
|
277 |
if simpleTypeDict.has_key (simpleType):
|
|
278 |
simpleTypeDict[simpleType] (simpleType, attributeValue, returnDict)
|
|
279 |
|
|
280 |
elif simpleType != "xsd:anySimpleType":
|
|
281 |
if simpleType in ("xsd:decimal", "xsd:float", "xsd:double", "xsd:base64Binary", "xsd:anyURI", "xsd:NOTATION",
|
|
282 |
"xsd:duration", "xsd:dateTime", "xsd:time", "xsd:date",
|
|
283 |
"xsd:gYearMonth", "xsd:gMonthDay", "xsd:gYear", "xsd:gMonth", "xsd:gDay"):
|
|
284 |
print "INFO: Check of simple type '%s' currently not supported!" %(simpleType)
|
|
285 |
else:
|
|
286 |
# TODO: Fehler im XSD-File => Check muss an anderer Stelle erfolgen
|
|
287 |
raise BaseTypeError("uses unknown type '%s'!" %(simpleType))
|
|
288 |
|
|
289 |
|
|
290 |
def _checkStringType (simpleType, attributeValue, returnDict):
|
|
291 |
# all valid??
|
|
292 |
returnDict["length"] = len(attributeValue)
|
|
293 |
|
|
294 |
def _checkHexBinaryType (simpleType, attributeValue, returnDict):
|
|
295 |
_checkIntegerRange (attributeValue, 16, 0, sys.maxint, returnDict)
|
|
296 |
returnDict["length"] = len(attributeValue)
|
|
297 |
|
|
298 |
def _checkIntegerType (simpleType, attributeValue, returnDict):
|
|
299 |
_checkIntegerRange (attributeValue, 10, -sys.maxint-1, sys.maxint, returnDict)
|
|
300 |
|
|
301 |
def _checkBooleanType (simpleType, attributeValue, returnDict):
|
|
302 |
if attributeValue not in ("true", "false", "1", "0"):
|
|
303 |
raise BaseTypeError("is not a boolean value!")
|
|
304 |
|
|
305 |
def _checkQNameType (simpleType, attributeValue, returnDict):
|
|
306 |
# TODO: fill
|
|
307 |
returnDict["length"] = len(attributeValue)
|
|
308 |
|
|
309 |
def _checkIntegerRange (attributeValue, base, minBase, maxBase, returnDict):
|
|
310 |
try:
|
|
311 |
value = string.atoi (attributeValue, base=base)
|
|
312 |
except:
|
|
313 |
if base == 16:
|
|
314 |
raise BaseTypeError("is not a hexadecimal value!")
|
|
315 |
else:
|
|
316 |
raise BaseTypeError("is not an integer!")
|
|
317 |
|
|
318 |
if value < minBase or value > maxBase:
|
|
319 |
raise BaseTypeError("is out of range (%d..%d)!" %(minBase, maxBase))
|
|
320 |
|
|
321 |
returnDict["orderedValue"] = value
|
|
322 |
|
|
323 |
|
|
324 |
|
|
325 |
########################################
|
|
326 |
# define own exception for XML schema validation errors
|
|
327 |
#
|
|
328 |
class SimpleTypeError (StandardError):
|
|
329 |
pass
|
|
330 |
|
|
331 |
class BaseTypeError (StandardError):
|
|
332 |
pass
|