|
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 |