|
1 # |
|
2 # minixsv, Release 0.3 |
|
3 # file: xsvalBase.py |
|
4 # |
|
5 # XML schema validator base class |
|
6 # |
|
7 # history: |
|
8 # 2004-10-07 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 from xsvalErrorHandler import * |
|
41 import xsvalSimpleTypes |
|
42 |
|
43 ########################################################### |
|
44 # Validator class for validating one input file against one XML schema file |
|
45 |
|
46 class XsValBase: |
|
47 |
|
48 def __init__(self, xmlIf, errorHandler): |
|
49 self.xmlIf = xmlIf |
|
50 self.errorHandler = errorHandler |
|
51 |
|
52 self._raiseError = self.errorHandler.raiseError |
|
53 self._addError = self.errorHandler.addError |
|
54 self._addWarning = self.errorHandler.addWarning |
|
55 |
|
56 self.checkKeyrefList = [] |
|
57 |
|
58 |
|
59 ######################################## |
|
60 # validate inputTree against xsdTree |
|
61 # |
|
62 def validate (self, inputTree, xsdTree): |
|
63 self.inputTree = inputTree |
|
64 self.xsdTree = xsdTree |
|
65 |
|
66 self.xsdRoot = self.xsdTree.getRootNode() |
|
67 self.xsdNSAlias = self.xmlIf.extractNamespaceAlias(self.xsdRoot.getTagName()) |
|
68 self.inputRoot = self.inputTree.getRootNode() |
|
69 self.inputNSAlias = self.xmlIf.extractNamespaceAlias(self.inputRoot.getTagName()) |
|
70 |
|
71 self.simpleTypeVal = xsvalSimpleTypes.XsSimpleTypeVal(self) |
|
72 |
|
73 self._setupLookupTables() |
|
74 |
|
75 inputRootTagName = self.inputRoot.getTagName() |
|
76 inputRootLocalName = self.inputRoot.getLocalName() |
|
77 if self.xsdElementDict.has_key(inputRootLocalName): |
|
78 # start recursive schema validation |
|
79 try: |
|
80 self._checkElementTag (self.xsdElementDict[inputRootLocalName], inputRootTagName, (self.inputRoot,), 0) |
|
81 except TagException, errInst: |
|
82 self._addError (errInst.errstr, errInst.node, errInst.endTag) |
|
83 |
|
84 # validate keyrefs |
|
85 for inputElement, keyrefNode in self.checkKeyrefList: |
|
86 self._checkKeyRefConstraint (keyrefNode, inputElement) |
|
87 else: |
|
88 self._raiseError ("Used root tag %s not found in schema file!" %(inputRootTagName), self.inputRoot) |
|
89 |
|
90 |
|
91 ######################################## |
|
92 # setup lookup dictionaries used during validation |
|
93 # |
|
94 def _setupLookupTables (self): |
|
95 # retrieve all elements |
|
96 self.xsdElementDict = {} |
|
97 for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "element"): |
|
98 self.xsdElementDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode |
|
99 |
|
100 # retrieve all type definitions (complex and simple types) |
|
101 self.xsdTypeDict = {} |
|
102 for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "complexType"): |
|
103 self.xsdTypeDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode |
|
104 |
|
105 for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "simpleType"): |
|
106 self.xsdTypeDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode |
|
107 |
|
108 # retrieve all group definitions |
|
109 self.xsdGroupDict = {} |
|
110 for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "group"): |
|
111 self.xsdGroupDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode |
|
112 |
|
113 # retrieve all attribute group definitions |
|
114 self.xsdAttributeGroupDict = {} |
|
115 for xsdTypeNode in self.xsdRoot.getChildrenNS(self.xsdNSAlias, "attributeGroup"): |
|
116 self.xsdAttributeGroupDict[xsdTypeNode.getAttribute("name")] = xsdTypeNode |
|
117 |
|
118 # retrieve all identity constraints |
|
119 self.xsdIdentityConstrDict = {} |
|
120 for identConstrTagName in ("unique", "key", "keyref"): |
|
121 identConstrNodeList = self.xsdRoot.getElementsByTagNameNS (self.xsdNSAlias, identConstrTagName) |
|
122 for identConstrNode in identConstrNodeList: |
|
123 identConstrName = identConstrNode.getAttribute("name") |
|
124 if not self.xsdIdentityConstrDict.has_key(identConstrName): |
|
125 self.xsdIdentityConstrDict[identConstrName] = {"Node": identConstrNode, "ValueDict":{}} |
|
126 else: |
|
127 self._raiseError ("Duplicate identity constraint name '%s' found in schema definition!" %(identConstrName), identConstrNode) |
|
128 |
|
129 |
|
130 ######################################## |
|
131 # validate inputElement against complexType node |
|
132 # |
|
133 def _checkComplexTypeTag (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType=None): |
|
134 baseTypeAttributes = {"__SPECIAL_ATTRS__":{}} |
|
135 if xsdParentElementNode.getAttribute ("nillable") == "true": |
|
136 baseTypeAttributes["__SPECIAL_ATTRS__"]["nil"] = "xsd:boolean" |
|
137 |
|
138 complexContentElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "complexContent") |
|
139 if complexContentElement != None: |
|
140 inputElementChildIndex, baseTypeAttributes = self._checkComplexContentTag (xsdParentElementNode, complexContentElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes) |
|
141 else: |
|
142 inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeContent (xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes) |
|
143 return inputElementChildIndex, baseTypeAttributes |
|
144 |
|
145 def _checkComplexContentTag (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes): |
|
146 extensionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "extension") |
|
147 if extensionElement != None: |
|
148 inputElementChildIndex, baseTypeAttributes = self._checkExtensionComplexContent (xsdParentElementNode, extensionElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes) |
|
149 else: |
|
150 restrictionElement = xsdElement.getFirstChildNS(self.xsdNSAlias, "restriction") |
|
151 if restrictionElement != None: |
|
152 inputElementChildIndex, baseTypeAttributes = self._checkRestrictionComplexContent (xsdParentElementNode, restrictionElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes) |
|
153 return inputElementChildIndex, baseTypeAttributes |
|
154 |
|
155 def _checkExtensionComplexContent (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes): |
|
156 baseType = self.xmlIf.extractLocalName(xsdElement.getAttribute("base")) |
|
157 inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeTag (xsdParentElementNode, self.xsdTypeDict[baseType], inputElement, inputElementChildIndex, "extension") |
|
158 |
|
159 inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeContent (xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes) |
|
160 return inputElementChildIndex, baseTypeAttributes |
|
161 |
|
162 def _checkRestrictionComplexContent (self, xsdParentElementNode, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes): |
|
163 # first check against base type (retrieve only the base type attributes) |
|
164 baseType = self.xmlIf.extractLocalName(xsdElement.getAttribute("base")) |
|
165 inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeTag (xsdParentElementNode, self.xsdTypeDict[baseType], inputElement, inputElementChildIndex, "restriction") |
|
166 |
|
167 # then check input against derived complex type |
|
168 inputElementChildIndex, baseTypeAttributes = self._checkComplexTypeContent (xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes) |
|
169 return inputElementChildIndex, baseTypeAttributes |
|
170 |
|
171 def _checkComplexTypeContent (self, xsdElement, inputElement, inputElementChildIndex, usedAsBaseType, baseTypeAttributes): |
|
172 inputTagName = inputElement.getTagName() |
|
173 childTags = inputElement.getChildren() |
|
174 if usedAsBaseType in (None, "extension"): |
|
175 validChildTags = xsdElement.getChildren() |
|
176 for validChildTag in validChildTags: |
|
177 if validChildTag.getLocalName() not in ("attribute", "attributeGroup", "anyAttribute"): |
|
178 inputElementChildIndex = self._checkParticle (validChildTag, inputElement, childTags, inputElementChildIndex) |
|
179 |
|
180 if usedAsBaseType == None and inputElementChildIndex < len (childTags): |
|
181 self._addError ("Unexpected child tag '%s' in tag '%s' found!" %(childTags[inputElementChildIndex].getTagName(), inputTagName), childTags[inputElementChildIndex]) |
|
182 |
|
183 if usedAsBaseType in (None,): |
|
184 self._checkAttributeTags (xsdElement, inputElement, baseTypeAttributes) |
|
185 |
|
186 if usedAsBaseType in ("restriction", "extension"): |
|
187 validAttributes = xsdElement.getChildrenNS(self.xsdNSAlias, "attribute") |
|
188 for validAttrGroup in xsdElement.getChildrenNS(self.xsdNSAlias, "attributeGroup"): |
|
189 attributeGroupRef = self.xmlIf.extractLocalName(validAttrGroup.getAttribute("ref")) |
|
190 validAttributes.extend (self.xsdAttributeGroupDict[attributeGroupRef].getChildrenNS(self.xsdNSAlias, "attribute")) |
|
191 for validAttribute in validAttributes: |
|
192 baseTypeAttributes[validAttribute.getAttribute("name")] = validAttribute |
|
193 return inputElementChildIndex, baseTypeAttributes |
|
194 |
|
195 |
|
196 ######################################## |
|
197 # validate inputNodeList against xsdNode |
|
198 # |
|
199 def _checkList (self, elementMethod, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
200 if not xsdNode.hasAttribute("minOccurs"): |
|
201 xsdNode.setAttribute("minOccurs", "1") # default |
|
202 if not xsdNode.hasAttribute("maxOccurs"): |
|
203 xsdNode.setAttribute("maxOccurs", "1") # default |
|
204 |
|
205 minOccurs = string.atoi(xsdNode.getAttribute("minOccurs")) |
|
206 maxOccurs = -1 |
|
207 if xsdNode.getAttribute("maxOccurs") != "unbounded": |
|
208 maxOccurs = string.atoi(xsdNode.getAttribute("maxOccurs")) |
|
209 occurs = 0 |
|
210 while maxOccurs == -1 or occurs < maxOccurs: |
|
211 try: |
|
212 newIndex = elementMethod (xsdNode, inputParentNode, inputNodeList, currIndex) |
|
213 occurs += 1 |
|
214 if newIndex > currIndex: |
|
215 currIndex = newIndex |
|
216 else: |
|
217 break # no suitable element found |
|
218 except TagException, errInst: |
|
219 break |
|
220 |
|
221 if occurs == 0 and minOccurs > 0: |
|
222 raise errInst |
|
223 elif occurs < minOccurs: |
|
224 expInputTagName = xsdNode.getAttribute("name") |
|
225 if expInputTagName == None: |
|
226 expInputTagName = self.xmlIf.extractLocalName(xsdNode.getAttribute("ref")) |
|
227 |
|
228 errInst.errstr = "Minimum number (%d) of child tags '%s' in tag '%s' not available (only %d)!" %(minOccurs, expInputTagName, inputParentNode.getTagName(), occurs) |
|
229 raise errInst |
|
230 |
|
231 return currIndex |
|
232 |
|
233 ######################################## |
|
234 # validate inputNode against element node |
|
235 # |
|
236 def _checkElementTag (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
237 if xsdNode.hasAttribute("ref"): |
|
238 refAttr = self.xmlIf.extractLocalName(xsdNode.getAttribute("ref")) |
|
239 currIndex = self._checkElementTag (self.xsdElementDict[refAttr], inputParentNode, inputNodeList, currIndex) |
|
240 |
|
241 else: |
|
242 nameAttr = xsdNode.getAttribute ("name") |
|
243 if currIndex >= len (inputNodeList): |
|
244 raise TagException ("Missing child tag '%s' in tag '%s'!" %(nameAttr, inputParentNode.getTagName()), inputParentNode, 1) |
|
245 |
|
246 inputNode = inputNodeList[currIndex] |
|
247 if nameAttr != inputNode.getLocalName(): |
|
248 raise TagException ("Missing child tag '%s' in tag '%s'!" %(nameAttr, inputParentNode.getTagName()), inputNode, 0) |
|
249 |
|
250 simpleType = None |
|
251 complexTypeNode = xsdNode.getFirstChildNS (self.xsdNSAlias, "complexType") |
|
252 simpleTypeNode = xsdNode.getFirstChildNS (self.xsdNSAlias, "simpleType") |
|
253 if xsdNode.hasAttribute("type"): |
|
254 typeAttr = xsdNode.getAttribute ("type") |
|
255 localTypeAttr = self.xmlIf.extractLocalName(typeAttr) |
|
256 if self.xsdTypeDict.has_key (localTypeAttr) and self.xsdTypeDict[localTypeAttr].getLocalName() == "complexType": |
|
257 complexTypeNode = self.xsdTypeDict[localTypeAttr] |
|
258 else: |
|
259 simpleType = typeAttr |
|
260 |
|
261 if complexTypeNode != None: |
|
262 try: |
|
263 self._checkComplexTypeTag (xsdNode, complexTypeNode, inputNode, 0) |
|
264 except TagException, errInst: |
|
265 self._addError (errInst.errstr, errInst.node, errInst.endTag) |
|
266 else: |
|
267 try: |
|
268 simpleTypeReturnDict = {} |
|
269 if simpleTypeNode != None: |
|
270 self.simpleTypeVal.checkSimpleTypeDef (simpleTypeNode, inputNode.getTagName(), inputNode.getElementValue(), simpleTypeReturnDict) |
|
271 elif simpleType != None: |
|
272 self.simpleTypeVal.checkSimpleType (inputNode.getTagName(), simpleType, inputNode.getElementValue(), simpleTypeReturnDict) |
|
273 # TODO: What to check if np type is specified for the element? |
|
274 |
|
275 if simpleTypeReturnDict.has_key("adaptedAttrValue"): |
|
276 inputNode.setElementValue(simpleTypeReturnDict["adaptedAttrValue"]) |
|
277 |
|
278 except xsvalSimpleTypes.SimpleTypeError, errstr: |
|
279 self._addError (str(errstr), inputNode) |
|
280 |
|
281 currIndex += 1 |
|
282 |
|
283 # check unique attributes and keys |
|
284 childUniqueDefList = xsdNode.getChildrenNS (self.xsdNSAlias, "unique") |
|
285 for childUniqueDef in childUniqueDefList: |
|
286 self._checkIdentityConstraint (childUniqueDef, inputNode, "unique") |
|
287 |
|
288 childKeyDefList = xsdNode.getChildrenNS (self.xsdNSAlias, "key") |
|
289 for childKeyDef in childKeyDefList: |
|
290 self._checkIdentityConstraint (childKeyDef, inputNode, "key") |
|
291 |
|
292 childKeyrefDefList = xsdNode.getChildrenNS (self.xsdNSAlias, "keyref") |
|
293 for childKeyrefDef in childKeyrefDefList: |
|
294 self.checkKeyrefList.append ((inputNode, childKeyrefDef)) |
|
295 |
|
296 return currIndex |
|
297 |
|
298 |
|
299 ######################################## |
|
300 # validate inputNode against sequence node |
|
301 # |
|
302 def _checkSequenceTag (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
303 for xsdChildNode in xsdNode.getChildren(): |
|
304 currIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex) |
|
305 return currIndex |
|
306 |
|
307 ######################################## |
|
308 # validate inputNode against choice node |
|
309 # |
|
310 def _checkChoiceTag (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
311 for xsdChildNode in xsdNode.getChildren(): |
|
312 try: |
|
313 currIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex) |
|
314 break |
|
315 except TagException, errInst: |
|
316 pass |
|
317 else: |
|
318 if currIndex < len(inputNodeList): |
|
319 currNode = inputNodeList[currIndex] |
|
320 endTag = 0 |
|
321 else: |
|
322 currNode = inputParentNode |
|
323 endTag = 1 |
|
324 raise TagException ("No suitable child tag for choice found!", currNode, endTag) |
|
325 |
|
326 return currIndex |
|
327 |
|
328 ######################################## |
|
329 # validate inputNode against group node |
|
330 # |
|
331 def _checkGroupTag (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
332 if xsdNode.hasAttribute("ref"): |
|
333 refAttr = self.xmlIf.extractLocalName(xsdNode.getAttribute("ref")) |
|
334 currIndex = self._checkGroupTag (self.xsdGroupDict[refAttr], inputParentNode, inputNodeList, currIndex) |
|
335 else: |
|
336 for xsdChildNode in xsdNode.getChildren(): |
|
337 currIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex) |
|
338 return currIndex |
|
339 |
|
340 ######################################## |
|
341 # validate inputNode against all node |
|
342 # |
|
343 def _checkAllTag (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
344 oldIndex = currIndex |
|
345 xsdChildDict = {} |
|
346 for xsdChildNode in xsdNode.getChildren(): |
|
347 xsdChildDict[xsdChildNode] = 0 |
|
348 while currIndex < len(inputNodeList): |
|
349 currNode = inputNodeList[currIndex] |
|
350 for xsdChildNode in xsdChildDict.keys(): |
|
351 try: |
|
352 newIndex = self._checkParticle (xsdChildNode, inputParentNode, inputNodeList, currIndex) |
|
353 except TagException, errInst: |
|
354 continue |
|
355 |
|
356 if xsdChildDict[xsdChildNode] == 0: |
|
357 xsdChildDict[xsdChildNode] = 1 |
|
358 currIndex = newIndex |
|
359 break |
|
360 else: |
|
361 raise TagException ("Ambiguous child tag '%s' found in all-group!" %(currNode.getTagName()), currNode) |
|
362 else: |
|
363 raise TagException ("Unexpected child tag '%s' for all-group found!" %(currNode.getTagName()), currNode) |
|
364 |
|
365 for xsdChildNode, occurs in xsdChildDict.items(): |
|
366 if xsdChildNode.getAttribute("minOccurs") != "0" and occurs == 0: |
|
367 raise TagException ("Child tag '%s' missing in all-group (%s)" %(xsdChildNode.getAttribute("name"), inputParentNode.getTagName()), inputNodeList[oldIndex]) |
|
368 |
|
369 return currIndex |
|
370 |
|
371 |
|
372 ######################################## |
|
373 # validate inputNode against any node |
|
374 # |
|
375 def _checkAnyTag (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
376 processContents = xsdNode.getAttribute("processContents") |
|
377 if processContents == "skip": |
|
378 pass |
|
379 elif processContents == "lax": |
|
380 # TODO: Was muss hier gecheckt werden? |
|
381 pass |
|
382 elif processContents == "strict": |
|
383 # TODO: Was muss hier gecheckt werden? |
|
384 pass |
|
385 |
|
386 if currIndex < len(inputNodeList): |
|
387 currIndex = currIndex + 1 |
|
388 |
|
389 return currIndex |
|
390 |
|
391 ######################################## |
|
392 # validate inputNode against particle |
|
393 # |
|
394 def _checkParticle (self, xsdNode, inputParentNode, inputNodeList, currIndex): |
|
395 xsdTagName = xsdNode.getTagName() |
|
396 if xsdTagName == self.xsdNSAlias + "element": |
|
397 currIndex = self._checkList (self._checkElementTag, xsdNode, inputParentNode, inputNodeList, currIndex) |
|
398 elif xsdTagName == self.xsdNSAlias + "choice": |
|
399 currIndex = self._checkList (self._checkChoiceTag, xsdNode, inputParentNode, inputNodeList, currIndex) |
|
400 elif xsdTagName == self.xsdNSAlias + "sequence": |
|
401 currIndex = self._checkList (self._checkSequenceTag, xsdNode, inputParentNode, inputNodeList, currIndex) |
|
402 elif xsdTagName == self.xsdNSAlias + "group": |
|
403 currIndex = self._checkList (self._checkGroupTag, xsdNode, inputParentNode, inputNodeList, currIndex) |
|
404 elif xsdTagName == self.xsdNSAlias + "all": |
|
405 currIndex = self._checkList (self._checkAllTag, xsdNode, inputParentNode, inputNodeList, currIndex) |
|
406 elif xsdTagName == self.xsdNSAlias + "any": |
|
407 currIndex = self._checkList (self._checkAnyTag, xsdNode, inputParentNode, inputNodeList, currIndex) |
|
408 elif xsdTagName == self.xsdNSAlias + "annotation": |
|
409 pass # nothing to check |
|
410 else: |
|
411 self._addError ("Internal error: Invalid tag %s found!" %(xsdTagName)) |
|
412 return currIndex |
|
413 |
|
414 |
|
415 ######################################## |
|
416 # validate attributes of inputNode against complexType node |
|
417 # |
|
418 def _checkAttributeTags (self, xsdNode, inputNode, validAttrDict): |
|
419 validAttributes = xsdNode.getChildrenNS(self.xsdNSAlias, "attribute") |
|
420 for validAttrGroup in xsdNode.getChildrenNS(self.xsdNSAlias, "attributeGroup"): |
|
421 attributeGroupRef = self.xmlIf.extractLocalName(validAttrGroup.getAttribute("ref")) |
|
422 validAttributes.extend (self.xsdAttributeGroupDict[attributeGroupRef].getChildrenNS(self.xsdNSAlias, "attribute")) |
|
423 for validAttribute in validAttributes: |
|
424 if validAttribute.hasAttribute("name"): |
|
425 keyAttr = validAttribute.getAttribute("name") |
|
426 else: |
|
427 keyAttr = self.xmlIf.extractLocalName(validAttribute.getAttribute("ref")) |
|
428 validAttrDict[keyAttr] = validAttribute |
|
429 |
|
430 inputAttrDict = {} |
|
431 for iAttrName, iAttrValue in inputNode.getAttributeDict().items(): |
|
432 if self.xmlIf.extractNamespaceAlias (iAttrName) != "xmlns:" and iAttrName != "xmlns": |
|
433 inputAttrDict[self.xmlIf.extractLocalName(iAttrName)] = iAttrValue |
|
434 |
|
435 for attrKey, validAttribute in validAttrDict.items(): |
|
436 # handle special attributes, e.g. xsi:nil |
|
437 if attrKey == "__SPECIAL_ATTRS__": |
|
438 for specialAttrName, specialAttrType in validAttribute.items(): |
|
439 if inputAttrDict.has_key(specialAttrName): |
|
440 simpleTypeReturnDict = {} |
|
441 try: |
|
442 self.simpleTypeVal.checkSimpleType (specialAttrName, specialAttrType, inputAttrDict[specialAttrName], simpleTypeReturnDict) |
|
443 except xsvalSimpleTypes.SimpleTypeError, errstr: |
|
444 self._addError (str(errstr), inputNode) |
|
445 |
|
446 del inputAttrDict[specialAttrName] |
|
447 continue |
|
448 |
|
449 attrDict = validAttribute.getAttributeDict() |
|
450 if attrDict.has_key("ref"): |
|
451 # TODO: What to do here?? |
|
452 attrRef = self.xmlIf.extractLocalName(attrDict["ref"]) |
|
453 if inputAttrDict.has_key(attrRef): |
|
454 del inputAttrDict[attrRef] |
|
455 else: |
|
456 attrName = attrDict["name"] |
|
457 if not attrDict.has_key("use"): |
|
458 attrDict["use"] = "optional" |
|
459 |
|
460 if inputAttrDict.has_key(attrName): |
|
461 del inputAttrDict[attrName] |
|
462 else: |
|
463 if attrDict["use"] == "required": |
|
464 self._addError ("Attribute '%s' is missing for tag %s!" %(attrName, inputNode.getTagName()), inputNode) |
|
465 elif attrDict["use"] == "optional": |
|
466 if attrDict.has_key("default"): |
|
467 inputNode.setAttribute(attrName, attrDict["default"]) |
|
468 if attrDict.has_key("fixed"): |
|
469 if inputNode.getAttribute(attrName) != attrDict["fixed"]: |
|
470 self._addError ("Attribute '%s' must have fixed value '%s'!" %(attrName, attrDict["fixed"]), inputNode) |
|
471 |
|
472 if inputNode.hasAttribute(attrName): |
|
473 if attrDict["use"] == "prohibited": |
|
474 self._addError ("Attribute '%s' is prohibited in this context!" %(attrName), inputNode) |
|
475 else: |
|
476 attributeValue = inputNode.getAttribute(attrName) |
|
477 try: |
|
478 simpleTypeReturnDict = {} |
|
479 if attrDict.has_key("type"): |
|
480 self.simpleTypeVal.checkSimpleType (attrName, attrDict["type"], attributeValue, simpleTypeReturnDict) |
|
481 else: |
|
482 typedefNode = validAttribute.getFirstChildNS(self.xsdNSAlias, "simpleType") |
|
483 if typedefNode != None: |
|
484 self.simpleTypeVal.checkSimpleTypeDef (typedefNode, attrName, attributeValue, simpleTypeReturnDict) |
|
485 else: |
|
486 pass # default if no type attribute is specified |
|
487 |
|
488 if simpleTypeReturnDict.has_key("adaptedAttrValue"): |
|
489 inputNode.setAttribute(attrName, simpleTypeReturnDict["adaptedAttrValue"]) |
|
490 |
|
491 except xsvalSimpleTypes.SimpleTypeError, errstr: |
|
492 self._addError (str(errstr), inputNode) |
|
493 |
|
494 for inputAttribute in inputAttrDict.keys(): |
|
495 # TODO: adapt for using namespaces!! |
|
496 if self.xmlIf.extractLocalName(inputAttribute) in ("noNamespaceSchemaLocation", "schemaLocation"): |
|
497 del inputAttrDict[inputAttribute] |
|
498 |
|
499 for inputAttribute in inputAttrDict.keys(): |
|
500 if inputAttribute == "nil": |
|
501 self._addError ("Tag '%s' hasn't been defined as nillable!" %(inputNode.getTagName()), inputNode) |
|
502 else: |
|
503 self._addError ("Unexpected attribute '%s' in Tag '%s'!" %(inputAttribute, inputNode.getTagName()), inputNode) |
|
504 |
|
505 |
|
506 ######################################## |
|
507 # validate unique and key definition |
|
508 # |
|
509 def _checkIdentityConstraint (self, identityConstrNode, inputElement, isKey): |
|
510 identConstrName = identityConstrNode.getAttribute ("name") |
|
511 selectorXPathNode = identityConstrNode.getFirstChildNS (self.xsdNSAlias, "selector") |
|
512 selectorNodeList, dummy = self._getXPath (inputElement, selectorXPathNode.getAttribute("xpath"), self.inputNSAlias) |
|
513 |
|
514 valueDict = {} |
|
515 for selectorNode in selectorNodeList: |
|
516 fieldXPathNodeList = identityConstrNode.getChildrenNS (self.xsdNSAlias, "field") |
|
517 keyValue = [] |
|
518 for fieldXPathNode in fieldXPathNodeList: |
|
519 fieldXPath = fieldXPathNode.getAttribute("xpath") |
|
520 fieldNodeList, fieldAttributeList = self._getXPath (selectorNode, fieldXPath, self.inputNSAlias, isKey) |
|
521 if len(fieldNodeList) > 1 or len(fieldAttributeList) > 1: |
|
522 self._addError ("The field xPath of identity constraint '%s' must evaluate to exactly 0 or 1 node!" %(identConstrName), fieldXPathNode) |
|
523 return |
|
524 |
|
525 # TODO: unique and key check currently only on string base |
|
526 for fieldNode in fieldNodeList: |
|
527 keyValue.append (fieldNode.getElementValue ()) |
|
528 |
|
529 for attrValue in fieldAttributeList: |
|
530 keyValue.append (attrValue) |
|
531 |
|
532 if keyValue != []: |
|
533 keyValue = tuple(keyValue) |
|
534 if not valueDict.has_key (keyValue): |
|
535 valueDict[keyValue] = 1 |
|
536 self.xsdIdentityConstrDict[identConstrName]["ValueDict"][keyValue] = 1 |
|
537 else: |
|
538 self._addError ("Duplicate identity constraint values '%s' found for identity contraint '%s'!" %(keyValue, identConstrName), selectorNode) |
|
539 |
|
540 ######################################## |
|
541 # validate unique and key definition |
|
542 # |
|
543 def _checkKeyRefConstraint (self, keyrefNode, inputElement): |
|
544 keyRefName = keyrefNode.getAttribute ("name") |
|
545 keyReference = keyrefNode.getAttribute ("refer") |
|
546 if not self.xsdIdentityConstrDict.has_key (keyReference): |
|
547 self._addError ("keyref refers unknown key '%s'!" %(keyReference), keyrefNode) |
|
548 return |
|
549 |
|
550 selectorXPathNode = keyrefNode.getFirstChildNS (self.xsdNSAlias, "selector") |
|
551 selectorNodeList, dummy = self._getXPath (inputElement, selectorXPathNode.getAttribute("xpath"), self.inputNSAlias) |
|
552 |
|
553 for selectorNode in selectorNodeList: |
|
554 fieldXPathNodeList = keyrefNode.getChildrenNS(self.xsdNSAlias, "field") |
|
555 keyValue = [] |
|
556 for fieldXPathNode in fieldXPathNodeList: |
|
557 fieldXPath = fieldXPathNode.getAttribute("xpath") |
|
558 fieldNodeList, fieldAttributeList = self._getXPath (selectorNode, fieldXPath, self.inputNSAlias, "keyref") |
|
559 if len(fieldNodeList) > 1 or len(fieldAttributeList) > 1: |
|
560 self._addError ("The field xPath of keyref '%s' must evaluate to exactly 0 or 1 node!" %(keyRefName), fieldXPathNode) |
|
561 return |
|
562 |
|
563 # TODO: unique and key check currently only on string base |
|
564 for fieldNode in fieldNodeList: |
|
565 keyValue.append(fieldNode.getElementValue()) |
|
566 |
|
567 for attrValue in fieldAttributeList: |
|
568 keyValue.append(attrValue) |
|
569 |
|
570 keyValue = tuple(keyValue) |
|
571 if not self.xsdIdentityConstrDict[keyReference]["ValueDict"].has_key (keyValue): |
|
572 self._addError ("Element value '%s' does not match key type '%s'!" %(keyValue, keyReference), selectorNode) |
|
573 |
|
574 ######################################## |
|
575 # retrieve nodes/attributes specified by given xPath |
|
576 # |
|
577 def _getXPath (self, node, xPath, defaultNamespace, identityConstraint=None): |
|
578 try: |
|
579 nodeList, attributeList = node.getXPathList (xPath, defaultNamespace) |
|
580 except IOError, errstr: |
|
581 self._addError (errstr, node) |
|
582 nodeList = attributeList = [] |
|
583 |
|
584 if nodeList == [] and attributeList == []: |
|
585 if identityConstraint == "key": |
|
586 self.errorHandler.addError ("Key is missing! XPath = '%s'!" %(xPath), node) |
|
587 elif identityConstraint in ("unique", "keyref"): |
|
588 self.errorHandler.addWarning ("Identity constraint is missing! XPath = '%s'!" %(xPath), node) |
|
589 |
|
590 return nodeList, attributeList |
|
591 |
|
592 |
|
593 ######################################## |
|
594 # define own exception for XML schema validation errors |
|
595 # |
|
596 class TagException (StandardError): |
|
597 def __init__ (self, errstr="", node=None, endTag=0): |
|
598 self.node = node |
|
599 self.errstr = errstr |
|
600 self.endTag = endTag |
|
601 StandardError.__init__(self) |
|
602 |