Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | """\ |
2 | minidom.py -- a lightweight DOM implementation. | |
3 | ||
4 | parse("foo.xml") | |
5 | ||
6 | parseString("<foo><bar/></foo>") | |
7 | ||
8 | Todo: | |
9 | ===== | |
10 | * convenience methods for getting elements and text. | |
11 | * more testing | |
12 | * bring some of the writer and linearizer code into conformance with this | |
13 | interface | |
14 | * SAX 2 namespaces | |
15 | """ | |
16 | ||
17 | import xml.dom | |
18 | ||
19 | from xml.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE, domreg | |
20 | from xml.dom.minicompat import * | |
21 | from xml.dom.xmlbuilder import DOMImplementationLS, DocumentLS | |
22 | ||
23 | _TupleType = type(()) | |
24 | ||
25 | # This is used by the ID-cache invalidation checks; the list isn't | |
26 | # actually complete, since the nodes being checked will never be the | |
27 | # DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE. (The node being checked is | |
28 | # the node being added or removed, not the node being modified.) | |
29 | # | |
30 | _nodeTypes_with_children = (xml.dom.Node.ELEMENT_NODE, | |
31 | xml.dom.Node.ENTITY_REFERENCE_NODE) | |
32 | ||
33 | ||
34 | class Node(xml.dom.Node, GetattrMagic): | |
35 | namespaceURI = None # this is non-null only for elements and attributes | |
36 | parentNode = None | |
37 | ownerDocument = None | |
38 | nextSibling = None | |
39 | previousSibling = None | |
40 | ||
41 | prefix = EMPTY_PREFIX # non-null only for NS elements and attributes | |
42 | ||
43 | def __nonzero__(self): | |
44 | return True | |
45 | ||
46 | def toxml(self, encoding = None): | |
47 | return self.toprettyxml("", "", encoding) | |
48 | ||
49 | def toprettyxml(self, indent="\t", newl="\n", encoding = None): | |
50 | # indent = the indentation string to prepend, per level | |
51 | # newl = the newline string to append | |
52 | writer = _get_StringIO() | |
53 | if encoding is not None: | |
54 | import codecs | |
55 | # Can't use codecs.getwriter to preserve 2.0 compatibility | |
56 | writer = codecs.lookup(encoding)[3](writer) | |
57 | if self.nodeType == Node.DOCUMENT_NODE: | |
58 | # Can pass encoding only to document, to put it into XML header | |
59 | self.writexml(writer, "", indent, newl, encoding) | |
60 | else: | |
61 | self.writexml(writer, "", indent, newl) | |
62 | return writer.getvalue() | |
63 | ||
64 | def hasChildNodes(self): | |
65 | if self.childNodes: | |
66 | return True | |
67 | else: | |
68 | return False | |
69 | ||
70 | def _get_childNodes(self): | |
71 | return self.childNodes | |
72 | ||
73 | def _get_firstChild(self): | |
74 | if self.childNodes: | |
75 | return self.childNodes[0] | |
76 | ||
77 | def _get_lastChild(self): | |
78 | if self.childNodes: | |
79 | return self.childNodes[-1] | |
80 | ||
81 | def insertBefore(self, newChild, refChild): | |
82 | if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE: | |
83 | for c in tuple(newChild.childNodes): | |
84 | self.insertBefore(c, refChild) | |
85 | ### The DOM does not clearly specify what to return in this case | |
86 | return newChild | |
87 | if newChild.nodeType not in self._child_node_types: | |
88 | raise xml.dom.HierarchyRequestErr( | |
89 | "%s cannot be child of %s" % (repr(newChild), repr(self))) | |
90 | if newChild.parentNode is not None: | |
91 | newChild.parentNode.removeChild(newChild) | |
92 | if refChild is None: | |
93 | self.appendChild(newChild) | |
94 | else: | |
95 | try: | |
96 | index = self.childNodes.index(refChild) | |
97 | except ValueError: | |
98 | raise xml.dom.NotFoundErr() | |
99 | if newChild.nodeType in _nodeTypes_with_children: | |
100 | _clear_id_cache(self) | |
101 | self.childNodes.insert(index, newChild) | |
102 | newChild.nextSibling = refChild | |
103 | refChild.previousSibling = newChild | |
104 | if index: | |
105 | node = self.childNodes[index-1] | |
106 | node.nextSibling = newChild | |
107 | newChild.previousSibling = node | |
108 | else: | |
109 | newChild.previousSibling = None | |
110 | newChild.parentNode = self | |
111 | return newChild | |
112 | ||
113 | def appendChild(self, node): | |
114 | if node.nodeType == self.DOCUMENT_FRAGMENT_NODE: | |
115 | for c in tuple(node.childNodes): | |
116 | self.appendChild(c) | |
117 | ### The DOM does not clearly specify what to return in this case | |
118 | return node | |
119 | if node.nodeType not in self._child_node_types: | |
120 | raise xml.dom.HierarchyRequestErr( | |
121 | "%s cannot be child of %s" % (repr(node), repr(self))) | |
122 | elif node.nodeType in _nodeTypes_with_children: | |
123 | _clear_id_cache(self) | |
124 | if node.parentNode is not None: | |
125 | node.parentNode.removeChild(node) | |
126 | _append_child(self, node) | |
127 | node.nextSibling = None | |
128 | return node | |
129 | ||
130 | def replaceChild(self, newChild, oldChild): | |
131 | if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE: | |
132 | refChild = oldChild.nextSibling | |
133 | self.removeChild(oldChild) | |
134 | return self.insertBefore(newChild, refChild) | |
135 | if newChild.nodeType not in self._child_node_types: | |
136 | raise xml.dom.HierarchyRequestErr( | |
137 | "%s cannot be child of %s" % (repr(newChild), repr(self))) | |
138 | if newChild.parentNode is not None: | |
139 | newChild.parentNode.removeChild(newChild) | |
140 | if newChild is oldChild: | |
141 | return | |
142 | try: | |
143 | index = self.childNodes.index(oldChild) | |
144 | except ValueError: | |
145 | raise xml.dom.NotFoundErr() | |
146 | self.childNodes[index] = newChild | |
147 | newChild.parentNode = self | |
148 | oldChild.parentNode = None | |
149 | if (newChild.nodeType in _nodeTypes_with_children | |
150 | or oldChild.nodeType in _nodeTypes_with_children): | |
151 | _clear_id_cache(self) | |
152 | newChild.nextSibling = oldChild.nextSibling | |
153 | newChild.previousSibling = oldChild.previousSibling | |
154 | oldChild.nextSibling = None | |
155 | oldChild.previousSibling = None | |
156 | if newChild.previousSibling: | |
157 | newChild.previousSibling.nextSibling = newChild | |
158 | if newChild.nextSibling: | |
159 | newChild.nextSibling.previousSibling = newChild | |
160 | return oldChild | |
161 | ||
162 | def removeChild(self, oldChild): | |
163 | try: | |
164 | self.childNodes.remove(oldChild) | |
165 | except ValueError: | |
166 | raise xml.dom.NotFoundErr() | |
167 | if oldChild.nextSibling is not None: | |
168 | oldChild.nextSibling.previousSibling = oldChild.previousSibling | |
169 | if oldChild.previousSibling is not None: | |
170 | oldChild.previousSibling.nextSibling = oldChild.nextSibling | |
171 | oldChild.nextSibling = oldChild.previousSibling = None | |
172 | if oldChild.nodeType in _nodeTypes_with_children: | |
173 | _clear_id_cache(self) | |
174 | ||
175 | oldChild.parentNode = None | |
176 | return oldChild | |
177 | ||
178 | def normalize(self): | |
179 | L = [] | |
180 | for child in self.childNodes: | |
181 | if child.nodeType == Node.TEXT_NODE: | |
182 | data = child.data | |
183 | if data and L and L[-1].nodeType == child.nodeType: | |
184 | # collapse text node | |
185 | node = L[-1] | |
186 | node.data = node.data + child.data | |
187 | node.nextSibling = child.nextSibling | |
188 | child.unlink() | |
189 | elif data: | |
190 | if L: | |
191 | L[-1].nextSibling = child | |
192 | child.previousSibling = L[-1] | |
193 | else: | |
194 | child.previousSibling = None | |
195 | L.append(child) | |
196 | else: | |
197 | # empty text node; discard | |
198 | child.unlink() | |
199 | else: | |
200 | if L: | |
201 | L[-1].nextSibling = child | |
202 | child.previousSibling = L[-1] | |
203 | else: | |
204 | child.previousSibling = None | |
205 | L.append(child) | |
206 | if child.nodeType == Node.ELEMENT_NODE: | |
207 | child.normalize() | |
208 | self.childNodes[:] = L | |
209 | ||
210 | def cloneNode(self, deep): | |
211 | return _clone_node(self, deep, self.ownerDocument or self) | |
212 | ||
213 | def isSupported(self, feature, version): | |
214 | return self.ownerDocument.implementation.hasFeature(feature, version) | |
215 | ||
216 | def _get_localName(self): | |
217 | # Overridden in Element and Attr where localName can be Non-Null | |
218 | return None | |
219 | ||
220 | # Node interfaces from Level 3 (WD 9 April 2002) | |
221 | ||
222 | def isSameNode(self, other): | |
223 | return self is other | |
224 | ||
225 | def getInterface(self, feature): | |
226 | if self.isSupported(feature, None): | |
227 | return self | |
228 | else: | |
229 | return None | |
230 | ||
231 | # The "user data" functions use a dictionary that is only present | |
232 | # if some user data has been set, so be careful not to assume it | |
233 | # exists. | |
234 | ||
235 | def getUserData(self, key): | |
236 | try: | |
237 | return self._user_data[key][0] | |
238 | except (AttributeError, KeyError): | |
239 | return None | |
240 | ||
241 | def setUserData(self, key, data, handler): | |
242 | old = None | |
243 | try: | |
244 | d = self._user_data | |
245 | except AttributeError: | |
246 | d = {} | |
247 | self._user_data = d | |
248 | if d.has_key(key): | |
249 | old = d[key][0] | |
250 | if data is None: | |
251 | # ignore handlers passed for None | |
252 | handler = None | |
253 | if old is not None: | |
254 | del d[key] | |
255 | else: | |
256 | d[key] = (data, handler) | |
257 | return old | |
258 | ||
259 | def _call_user_data_handler(self, operation, src, dst): | |
260 | if hasattr(self, "_user_data"): | |
261 | for key, (data, handler) in self._user_data.items(): | |
262 | if handler is not None: | |
263 | handler.handle(operation, key, data, src, dst) | |
264 | ||
265 | # minidom-specific API: | |
266 | ||
267 | def unlink(self): | |
268 | self.parentNode = self.ownerDocument = None | |
269 | if self.childNodes: | |
270 | for child in self.childNodes: | |
271 | child.unlink() | |
272 | self.childNodes = NodeList() | |
273 | self.previousSibling = None | |
274 | self.nextSibling = None | |
275 | ||
276 | defproperty(Node, "firstChild", doc="First child node, or None.") | |
277 | defproperty(Node, "lastChild", doc="Last child node, or None.") | |
278 | defproperty(Node, "localName", doc="Namespace-local name of this node.") | |
279 | ||
280 | ||
281 | def _append_child(self, node): | |
282 | # fast path with less checks; usable by DOM builders if careful | |
283 | childNodes = self.childNodes | |
284 | if childNodes: | |
285 | last = childNodes[-1] | |
286 | node.__dict__["previousSibling"] = last | |
287 | last.__dict__["nextSibling"] = node | |
288 | childNodes.append(node) | |
289 | node.__dict__["parentNode"] = self | |
290 | ||
291 | def _in_document(node): | |
292 | # return True iff node is part of a document tree | |
293 | while node is not None: | |
294 | if node.nodeType == Node.DOCUMENT_NODE: | |
295 | return True | |
296 | node = node.parentNode | |
297 | return False | |
298 | ||
299 | def _write_data(writer, data): | |
300 | "Writes datachars to writer." | |
301 | data = data.replace("&", "&").replace("<", "<") | |
302 | data = data.replace("\"", """).replace(">", ">") | |
303 | writer.write(data) | |
304 | ||
305 | def _get_elements_by_tagName_helper(parent, name, rc): | |
306 | for node in parent.childNodes: | |
307 | if node.nodeType == Node.ELEMENT_NODE and \ | |
308 | (name == "*" or node.tagName == name): | |
309 | rc.append(node) | |
310 | _get_elements_by_tagName_helper(node, name, rc) | |
311 | return rc | |
312 | ||
313 | def _get_elements_by_tagName_ns_helper(parent, nsURI, localName, rc): | |
314 | for node in parent.childNodes: | |
315 | if node.nodeType == Node.ELEMENT_NODE: | |
316 | if ((localName == "*" or node.localName == localName) and | |
317 | (nsURI == "*" or node.namespaceURI == nsURI)): | |
318 | rc.append(node) | |
319 | _get_elements_by_tagName_ns_helper(node, nsURI, localName, rc) | |
320 | return rc | |
321 | ||
322 | class DocumentFragment(Node): | |
323 | nodeType = Node.DOCUMENT_FRAGMENT_NODE | |
324 | nodeName = "#document-fragment" | |
325 | nodeValue = None | |
326 | attributes = None | |
327 | parentNode = None | |
328 | _child_node_types = (Node.ELEMENT_NODE, | |
329 | Node.TEXT_NODE, | |
330 | Node.CDATA_SECTION_NODE, | |
331 | Node.ENTITY_REFERENCE_NODE, | |
332 | Node.PROCESSING_INSTRUCTION_NODE, | |
333 | Node.COMMENT_NODE, | |
334 | Node.NOTATION_NODE) | |
335 | ||
336 | def __init__(self): | |
337 | self.childNodes = NodeList() | |
338 | ||
339 | ||
340 | class Attr(Node): | |
341 | nodeType = Node.ATTRIBUTE_NODE | |
342 | attributes = None | |
343 | ownerElement = None | |
344 | specified = False | |
345 | _is_id = False | |
346 | ||
347 | _child_node_types = (Node.TEXT_NODE, Node.ENTITY_REFERENCE_NODE) | |
348 | ||
349 | def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None, | |
350 | prefix=None): | |
351 | # skip setattr for performance | |
352 | d = self.__dict__ | |
353 | d["nodeName"] = d["name"] = qName | |
354 | d["namespaceURI"] = namespaceURI | |
355 | d["prefix"] = prefix | |
356 | d['childNodes'] = NodeList() | |
357 | ||
358 | # Add the single child node that represents the value of the attr | |
359 | self.childNodes.append(Text()) | |
360 | ||
361 | # nodeValue and value are set elsewhere | |
362 | ||
363 | def _get_localName(self): | |
364 | return self.nodeName.split(":", 1)[-1] | |
365 | ||
366 | def _get_name(self): | |
367 | return self.name | |
368 | ||
369 | def _get_specified(self): | |
370 | return self.specified | |
371 | ||
372 | def __setattr__(self, name, value): | |
373 | d = self.__dict__ | |
374 | if name in ("value", "nodeValue"): | |
375 | d["value"] = d["nodeValue"] = value | |
376 | d2 = self.childNodes[0].__dict__ | |
377 | d2["data"] = d2["nodeValue"] = value | |
378 | if self.ownerElement is not None: | |
379 | _clear_id_cache(self.ownerElement) | |
380 | elif name in ("name", "nodeName"): | |
381 | d["name"] = d["nodeName"] = value | |
382 | if self.ownerElement is not None: | |
383 | _clear_id_cache(self.ownerElement) | |
384 | else: | |
385 | d[name] = value | |
386 | ||
387 | def _set_prefix(self, prefix): | |
388 | nsuri = self.namespaceURI | |
389 | if prefix == "xmlns": | |
390 | if nsuri and nsuri != XMLNS_NAMESPACE: | |
391 | raise xml.dom.NamespaceErr( | |
392 | "illegal use of 'xmlns' prefix for the wrong namespace") | |
393 | d = self.__dict__ | |
394 | d['prefix'] = prefix | |
395 | if prefix is None: | |
396 | newName = self.localName | |
397 | else: | |
398 | newName = "%s:%s" % (prefix, self.localName) | |
399 | if self.ownerElement: | |
400 | _clear_id_cache(self.ownerElement) | |
401 | d['nodeName'] = d['name'] = newName | |
402 | ||
403 | def _set_value(self, value): | |
404 | d = self.__dict__ | |
405 | d['value'] = d['nodeValue'] = value | |
406 | if self.ownerElement: | |
407 | _clear_id_cache(self.ownerElement) | |
408 | self.childNodes[0].data = value | |
409 | ||
410 | def unlink(self): | |
411 | # This implementation does not call the base implementation | |
412 | # since most of that is not needed, and the expense of the | |
413 | # method call is not warranted. We duplicate the removal of | |
414 | # children, but that's all we needed from the base class. | |
415 | elem = self.ownerElement | |
416 | if elem is not None: | |
417 | del elem._attrs[self.nodeName] | |
418 | del elem._attrsNS[(self.namespaceURI, self.localName)] | |
419 | if self._is_id: | |
420 | self._is_id = False | |
421 | elem._magic_id_nodes -= 1 | |
422 | self.ownerDocument._magic_id_count -= 1 | |
423 | for child in self.childNodes: | |
424 | child.unlink() | |
425 | del self.childNodes[:] | |
426 | ||
427 | def _get_isId(self): | |
428 | if self._is_id: | |
429 | return True | |
430 | doc = self.ownerDocument | |
431 | elem = self.ownerElement | |
432 | if doc is None or elem is None: | |
433 | return False | |
434 | ||
435 | info = doc._get_elem_info(elem) | |
436 | if info is None: | |
437 | return False | |
438 | if self.namespaceURI: | |
439 | return info.isIdNS(self.namespaceURI, self.localName) | |
440 | else: | |
441 | return info.isId(self.nodeName) | |
442 | ||
443 | def _get_schemaType(self): | |
444 | doc = self.ownerDocument | |
445 | elem = self.ownerElement | |
446 | if doc is None or elem is None: | |
447 | return _no_type | |
448 | ||
449 | info = doc._get_elem_info(elem) | |
450 | if info is None: | |
451 | return _no_type | |
452 | if self.namespaceURI: | |
453 | return info.getAttributeTypeNS(self.namespaceURI, self.localName) | |
454 | else: | |
455 | return info.getAttributeType(self.nodeName) | |
456 | ||
457 | defproperty(Attr, "isId", doc="True if this attribute is an ID.") | |
458 | defproperty(Attr, "localName", doc="Namespace-local name of this attribute.") | |
459 | defproperty(Attr, "schemaType", doc="Schema type for this attribute.") | |
460 | ||
461 | ||
462 | class NamedNodeMap(NewStyle, GetattrMagic): | |
463 | """The attribute list is a transient interface to the underlying | |
464 | dictionaries. Mutations here will change the underlying element's | |
465 | dictionary. | |
466 | ||
467 | Ordering is imposed artificially and does not reflect the order of | |
468 | attributes as found in an input document. | |
469 | """ | |
470 | ||
471 | __slots__ = ('_attrs', '_attrsNS', '_ownerElement') | |
472 | ||
473 | def __init__(self, attrs, attrsNS, ownerElement): | |
474 | self._attrs = attrs | |
475 | self._attrsNS = attrsNS | |
476 | self._ownerElement = ownerElement | |
477 | ||
478 | def _get_length(self): | |
479 | return len(self._attrs) | |
480 | ||
481 | def item(self, index): | |
482 | try: | |
483 | return self[self._attrs.keys()[index]] | |
484 | except IndexError: | |
485 | return None | |
486 | ||
487 | def items(self): | |
488 | L = [] | |
489 | for node in self._attrs.values(): | |
490 | L.append((node.nodeName, node.value)) | |
491 | return L | |
492 | ||
493 | def itemsNS(self): | |
494 | L = [] | |
495 | for node in self._attrs.values(): | |
496 | L.append(((node.namespaceURI, node.localName), node.value)) | |
497 | return L | |
498 | ||
499 | def has_key(self, key): | |
500 | if isinstance(key, StringTypes): | |
501 | return self._attrs.has_key(key) | |
502 | else: | |
503 | return self._attrsNS.has_key(key) | |
504 | ||
505 | def keys(self): | |
506 | return self._attrs.keys() | |
507 | ||
508 | def keysNS(self): | |
509 | return self._attrsNS.keys() | |
510 | ||
511 | def values(self): | |
512 | return self._attrs.values() | |
513 | ||
514 | def get(self, name, value=None): | |
515 | return self._attrs.get(name, value) | |
516 | ||
517 | __len__ = _get_length | |
518 | ||
519 | def __cmp__(self, other): | |
520 | if self._attrs is getattr(other, "_attrs", None): | |
521 | return 0 | |
522 | else: | |
523 | return cmp(id(self), id(other)) | |
524 | ||
525 | def __getitem__(self, attname_or_tuple): | |
526 | if isinstance(attname_or_tuple, _TupleType): | |
527 | return self._attrsNS[attname_or_tuple] | |
528 | else: | |
529 | return self._attrs[attname_or_tuple] | |
530 | ||
531 | # same as set | |
532 | def __setitem__(self, attname, value): | |
533 | if isinstance(value, StringTypes): | |
534 | try: | |
535 | node = self._attrs[attname] | |
536 | except KeyError: | |
537 | node = Attr(attname) | |
538 | node.ownerDocument = self._ownerElement.ownerDocument | |
539 | self.setNamedItem(node) | |
540 | node.value = value | |
541 | else: | |
542 | if not isinstance(value, Attr): | |
543 | raise TypeError, "value must be a string or Attr object" | |
544 | node = value | |
545 | self.setNamedItem(node) | |
546 | ||
547 | def getNamedItem(self, name): | |
548 | try: | |
549 | return self._attrs[name] | |
550 | except KeyError: | |
551 | return None | |
552 | ||
553 | def getNamedItemNS(self, namespaceURI, localName): | |
554 | try: | |
555 | return self._attrsNS[(namespaceURI, localName)] | |
556 | except KeyError: | |
557 | return None | |
558 | ||
559 | def removeNamedItem(self, name): | |
560 | n = self.getNamedItem(name) | |
561 | if n is not None: | |
562 | _clear_id_cache(self._ownerElement) | |
563 | del self._attrs[n.nodeName] | |
564 | del self._attrsNS[(n.namespaceURI, n.localName)] | |
565 | if n.__dict__.has_key('ownerElement'): | |
566 | n.__dict__['ownerElement'] = None | |
567 | return n | |
568 | else: | |
569 | raise xml.dom.NotFoundErr() | |
570 | ||
571 | def removeNamedItemNS(self, namespaceURI, localName): | |
572 | n = self.getNamedItemNS(namespaceURI, localName) | |
573 | if n is not None: | |
574 | _clear_id_cache(self._ownerElement) | |
575 | del self._attrsNS[(n.namespaceURI, n.localName)] | |
576 | del self._attrs[n.nodeName] | |
577 | if n.__dict__.has_key('ownerElement'): | |
578 | n.__dict__['ownerElement'] = None | |
579 | return n | |
580 | else: | |
581 | raise xml.dom.NotFoundErr() | |
582 | ||
583 | def setNamedItem(self, node): | |
584 | if not isinstance(node, Attr): | |
585 | raise xml.dom.HierarchyRequestErr( | |
586 | "%s cannot be child of %s" % (repr(node), repr(self))) | |
587 | old = self._attrs.get(node.name) | |
588 | if old: | |
589 | old.unlink() | |
590 | self._attrs[node.name] = node | |
591 | self._attrsNS[(node.namespaceURI, node.localName)] = node | |
592 | node.ownerElement = self._ownerElement | |
593 | _clear_id_cache(node.ownerElement) | |
594 | return old | |
595 | ||
596 | def setNamedItemNS(self, node): | |
597 | return self.setNamedItem(node) | |
598 | ||
599 | def __delitem__(self, attname_or_tuple): | |
600 | node = self[attname_or_tuple] | |
601 | _clear_id_cache(node.ownerElement) | |
602 | node.unlink() | |
603 | ||
604 | def __getstate__(self): | |
605 | return self._attrs, self._attrsNS, self._ownerElement | |
606 | ||
607 | def __setstate__(self, state): | |
608 | self._attrs, self._attrsNS, self._ownerElement = state | |
609 | ||
610 | defproperty(NamedNodeMap, "length", | |
611 | doc="Number of nodes in the NamedNodeMap.") | |
612 | ||
613 | AttributeList = NamedNodeMap | |
614 | ||
615 | ||
616 | class TypeInfo(NewStyle): | |
617 | __slots__ = 'namespace', 'name' | |
618 | ||
619 | def __init__(self, namespace, name): | |
620 | self.namespace = namespace | |
621 | self.name = name | |
622 | ||
623 | def __repr__(self): | |
624 | if self.namespace: | |
625 | return "<TypeInfo %r (from %r)>" % (self.name, self.namespace) | |
626 | else: | |
627 | return "<TypeInfo %r>" % self.name | |
628 | ||
629 | def _get_name(self): | |
630 | return self.name | |
631 | ||
632 | def _get_namespace(self): | |
633 | return self.namespace | |
634 | ||
635 | _no_type = TypeInfo(None, None) | |
636 | ||
637 | class Element(Node): | |
638 | nodeType = Node.ELEMENT_NODE | |
639 | nodeValue = None | |
640 | schemaType = _no_type | |
641 | ||
642 | _magic_id_nodes = 0 | |
643 | ||
644 | _child_node_types = (Node.ELEMENT_NODE, | |
645 | Node.PROCESSING_INSTRUCTION_NODE, | |
646 | Node.COMMENT_NODE, | |
647 | Node.TEXT_NODE, | |
648 | Node.CDATA_SECTION_NODE, | |
649 | Node.ENTITY_REFERENCE_NODE) | |
650 | ||
651 | def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None, | |
652 | localName=None): | |
653 | self.tagName = self.nodeName = tagName | |
654 | self.prefix = prefix | |
655 | self.namespaceURI = namespaceURI | |
656 | self.childNodes = NodeList() | |
657 | ||
658 | self._attrs = {} # attributes are double-indexed: | |
659 | self._attrsNS = {} # tagName -> Attribute | |
660 | # URI,localName -> Attribute | |
661 | # in the future: consider lazy generation | |
662 | # of attribute objects this is too tricky | |
663 | # for now because of headaches with | |
664 | # namespaces. | |
665 | ||
666 | def _get_localName(self): | |
667 | return self.tagName.split(":", 1)[-1] | |
668 | ||
669 | def _get_tagName(self): | |
670 | return self.tagName | |
671 | ||
672 | def unlink(self): | |
673 | for attr in self._attrs.values(): | |
674 | attr.unlink() | |
675 | self._attrs = None | |
676 | self._attrsNS = None | |
677 | Node.unlink(self) | |
678 | ||
679 | def getAttribute(self, attname): | |
680 | try: | |
681 | return self._attrs[attname].value | |
682 | except KeyError: | |
683 | return "" | |
684 | ||
685 | def getAttributeNS(self, namespaceURI, localName): | |
686 | try: | |
687 | return self._attrsNS[(namespaceURI, localName)].value | |
688 | except KeyError: | |
689 | return "" | |
690 | ||
691 | def setAttribute(self, attname, value): | |
692 | attr = self.getAttributeNode(attname) | |
693 | if attr is None: | |
694 | attr = Attr(attname) | |
695 | # for performance | |
696 | d = attr.__dict__ | |
697 | d["value"] = d["nodeValue"] = value | |
698 | d["ownerDocument"] = self.ownerDocument | |
699 | self.setAttributeNode(attr) | |
700 | elif value != attr.value: | |
701 | d = attr.__dict__ | |
702 | d["value"] = d["nodeValue"] = value | |
703 | if attr.isId: | |
704 | _clear_id_cache(self) | |
705 | ||
706 | def setAttributeNS(self, namespaceURI, qualifiedName, value): | |
707 | prefix, localname = _nssplit(qualifiedName) | |
708 | attr = self.getAttributeNodeNS(namespaceURI, localname) | |
709 | if attr is None: | |
710 | # for performance | |
711 | attr = Attr(qualifiedName, namespaceURI, localname, prefix) | |
712 | d = attr.__dict__ | |
713 | d["prefix"] = prefix | |
714 | d["nodeName"] = qualifiedName | |
715 | d["value"] = d["nodeValue"] = value | |
716 | d["ownerDocument"] = self.ownerDocument | |
717 | self.setAttributeNode(attr) | |
718 | else: | |
719 | d = attr.__dict__ | |
720 | if value != attr.value: | |
721 | d["value"] = d["nodeValue"] = value | |
722 | if attr.isId: | |
723 | _clear_id_cache(self) | |
724 | if attr.prefix != prefix: | |
725 | d["prefix"] = prefix | |
726 | d["nodeName"] = qualifiedName | |
727 | ||
728 | def getAttributeNode(self, attrname): | |
729 | return self._attrs.get(attrname) | |
730 | ||
731 | def getAttributeNodeNS(self, namespaceURI, localName): | |
732 | return self._attrsNS.get((namespaceURI, localName)) | |
733 | ||
734 | def setAttributeNode(self, attr): | |
735 | if attr.ownerElement not in (None, self): | |
736 | raise xml.dom.InuseAttributeErr("attribute node already owned") | |
737 | old1 = self._attrs.get(attr.name, None) | |
738 | if old1 is not None: | |
739 | self.removeAttributeNode(old1) | |
740 | old2 = self._attrsNS.get((attr.namespaceURI, attr.localName), None) | |
741 | if old2 is not None and old2 is not old1: | |
742 | self.removeAttributeNode(old2) | |
743 | _set_attribute_node(self, attr) | |
744 | ||
745 | if old1 is not attr: | |
746 | # It might have already been part of this node, in which case | |
747 | # it doesn't represent a change, and should not be returned. | |
748 | return old1 | |
749 | if old2 is not attr: | |
750 | return old2 | |
751 | ||
752 | setAttributeNodeNS = setAttributeNode | |
753 | ||
754 | def removeAttribute(self, name): | |
755 | try: | |
756 | attr = self._attrs[name] | |
757 | except KeyError: | |
758 | raise xml.dom.NotFoundErr() | |
759 | self.removeAttributeNode(attr) | |
760 | ||
761 | def removeAttributeNS(self, namespaceURI, localName): | |
762 | try: | |
763 | attr = self._attrsNS[(namespaceURI, localName)] | |
764 | except KeyError: | |
765 | raise xml.dom.NotFoundErr() | |
766 | self.removeAttributeNode(attr) | |
767 | ||
768 | def removeAttributeNode(self, node): | |
769 | if node is None: | |
770 | raise xml.dom.NotFoundErr() | |
771 | try: | |
772 | self._attrs[node.name] | |
773 | except KeyError: | |
774 | raise xml.dom.NotFoundErr() | |
775 | _clear_id_cache(self) | |
776 | node.unlink() | |
777 | # Restore this since the node is still useful and otherwise | |
778 | # unlinked | |
779 | node.ownerDocument = self.ownerDocument | |
780 | ||
781 | removeAttributeNodeNS = removeAttributeNode | |
782 | ||
783 | def hasAttribute(self, name): | |
784 | return self._attrs.has_key(name) | |
785 | ||
786 | def hasAttributeNS(self, namespaceURI, localName): | |
787 | return self._attrsNS.has_key((namespaceURI, localName)) | |
788 | ||
789 | def getElementsByTagName(self, name): | |
790 | return _get_elements_by_tagName_helper(self, name, NodeList()) | |
791 | ||
792 | def getElementsByTagNameNS(self, namespaceURI, localName): | |
793 | return _get_elements_by_tagName_ns_helper( | |
794 | self, namespaceURI, localName, NodeList()) | |
795 | ||
796 | def __repr__(self): | |
797 | return "<DOM Element: %s at %#x>" % (self.tagName, id(self)) | |
798 | ||
799 | def writexml(self, writer, indent="", addindent="", newl=""): | |
800 | # indent = current indentation | |
801 | # addindent = indentation to add to higher levels | |
802 | # newl = newline string | |
803 | writer.write(indent+"<" + self.tagName) | |
804 | ||
805 | attrs = self._get_attributes() | |
806 | a_names = attrs.keys() | |
807 | a_names.sort() | |
808 | ||
809 | for a_name in a_names: | |
810 | writer.write(" %s=\"" % a_name) | |
811 | _write_data(writer, attrs[a_name].value) | |
812 | writer.write("\"") | |
813 | if self.childNodes: | |
814 | writer.write(">%s"%(newl)) | |
815 | for node in self.childNodes: | |
816 | node.writexml(writer,indent+addindent,addindent,newl) | |
817 | writer.write("%s</%s>%s" % (indent,self.tagName,newl)) | |
818 | else: | |
819 | writer.write("/>%s"%(newl)) | |
820 | ||
821 | def _get_attributes(self): | |
822 | return NamedNodeMap(self._attrs, self._attrsNS, self) | |
823 | ||
824 | def hasAttributes(self): | |
825 | if self._attrs: | |
826 | return True | |
827 | else: | |
828 | return False | |
829 | ||
830 | # DOM Level 3 attributes, based on the 22 Oct 2002 draft | |
831 | ||
832 | def setIdAttribute(self, name): | |
833 | idAttr = self.getAttributeNode(name) | |
834 | self.setIdAttributeNode(idAttr) | |
835 | ||
836 | def setIdAttributeNS(self, namespaceURI, localName): | |
837 | idAttr = self.getAttributeNodeNS(namespaceURI, localName) | |
838 | self.setIdAttributeNode(idAttr) | |
839 | ||
840 | def setIdAttributeNode(self, idAttr): | |
841 | if idAttr is None or not self.isSameNode(idAttr.ownerElement): | |
842 | raise xml.dom.NotFoundErr() | |
843 | if _get_containing_entref(self) is not None: | |
844 | raise xml.dom.NoModificationAllowedErr() | |
845 | if not idAttr._is_id: | |
846 | idAttr.__dict__['_is_id'] = True | |
847 | self._magic_id_nodes += 1 | |
848 | self.ownerDocument._magic_id_count += 1 | |
849 | _clear_id_cache(self) | |
850 | ||
851 | defproperty(Element, "attributes", | |
852 | doc="NamedNodeMap of attributes on the element.") | |
853 | defproperty(Element, "localName", | |
854 | doc="Namespace-local name of this element.") | |
855 | ||
856 | ||
857 | def _set_attribute_node(element, attr): | |
858 | _clear_id_cache(element) | |
859 | element._attrs[attr.name] = attr | |
860 | element._attrsNS[(attr.namespaceURI, attr.localName)] = attr | |
861 | ||
862 | # This creates a circular reference, but Element.unlink() | |
863 | # breaks the cycle since the references to the attribute | |
864 | # dictionaries are tossed. | |
865 | attr.__dict__['ownerElement'] = element | |
866 | ||
867 | ||
868 | class Childless: | |
869 | """Mixin that makes childless-ness easy to implement and avoids | |
870 | the complexity of the Node methods that deal with children. | |
871 | """ | |
872 | ||
873 | attributes = None | |
874 | childNodes = EmptyNodeList() | |
875 | firstChild = None | |
876 | lastChild = None | |
877 | ||
878 | def _get_firstChild(self): | |
879 | return None | |
880 | ||
881 | def _get_lastChild(self): | |
882 | return None | |
883 | ||
884 | def appendChild(self, node): | |
885 | raise xml.dom.HierarchyRequestErr( | |
886 | self.nodeName + " nodes cannot have children") | |
887 | ||
888 | def hasChildNodes(self): | |
889 | return False | |
890 | ||
891 | def insertBefore(self, newChild, refChild): | |
892 | raise xml.dom.HierarchyRequestErr( | |
893 | self.nodeName + " nodes do not have children") | |
894 | ||
895 | def removeChild(self, oldChild): | |
896 | raise xml.dom.NotFoundErr( | |
897 | self.nodeName + " nodes do not have children") | |
898 | ||
899 | def replaceChild(self, newChild, oldChild): | |
900 | raise xml.dom.HierarchyRequestErr( | |
901 | self.nodeName + " nodes do not have children") | |
902 | ||
903 | ||
904 | class ProcessingInstruction(Childless, Node): | |
905 | nodeType = Node.PROCESSING_INSTRUCTION_NODE | |
906 | ||
907 | def __init__(self, target, data): | |
908 | self.target = self.nodeName = target | |
909 | self.data = self.nodeValue = data | |
910 | ||
911 | def _get_data(self): | |
912 | return self.data | |
913 | def _set_data(self, value): | |
914 | d = self.__dict__ | |
915 | d['data'] = d['nodeValue'] = value | |
916 | ||
917 | def _get_target(self): | |
918 | return self.target | |
919 | def _set_target(self, value): | |
920 | d = self.__dict__ | |
921 | d['target'] = d['nodeName'] = value | |
922 | ||
923 | def __setattr__(self, name, value): | |
924 | if name == "data" or name == "nodeValue": | |
925 | self.__dict__['data'] = self.__dict__['nodeValue'] = value | |
926 | elif name == "target" or name == "nodeName": | |
927 | self.__dict__['target'] = self.__dict__['nodeName'] = value | |
928 | else: | |
929 | self.__dict__[name] = value | |
930 | ||
931 | def writexml(self, writer, indent="", addindent="", newl=""): | |
932 | writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl)) | |
933 | ||
934 | ||
935 | class CharacterData(Childless, Node): | |
936 | def _get_length(self): | |
937 | return len(self.data) | |
938 | __len__ = _get_length | |
939 | ||
940 | def _get_data(self): | |
941 | return self.__dict__['data'] | |
942 | def _set_data(self, data): | |
943 | d = self.__dict__ | |
944 | d['data'] = d['nodeValue'] = data | |
945 | ||
946 | _get_nodeValue = _get_data | |
947 | _set_nodeValue = _set_data | |
948 | ||
949 | def __setattr__(self, name, value): | |
950 | if name == "data" or name == "nodeValue": | |
951 | self.__dict__['data'] = self.__dict__['nodeValue'] = value | |
952 | else: | |
953 | self.__dict__[name] = value | |
954 | ||
955 | def __repr__(self): | |
956 | data = self.data | |
957 | if len(data) > 10: | |
958 | dotdotdot = "..." | |
959 | else: | |
960 | dotdotdot = "" | |
961 | return "<DOM %s node \"%s%s\">" % ( | |
962 | self.__class__.__name__, data[0:10], dotdotdot) | |
963 | ||
964 | def substringData(self, offset, count): | |
965 | if offset < 0: | |
966 | raise xml.dom.IndexSizeErr("offset cannot be negative") | |
967 | if offset >= len(self.data): | |
968 | raise xml.dom.IndexSizeErr("offset cannot be beyond end of data") | |
969 | if count < 0: | |
970 | raise xml.dom.IndexSizeErr("count cannot be negative") | |
971 | return self.data[offset:offset+count] | |
972 | ||
973 | def appendData(self, arg): | |
974 | self.data = self.data + arg | |
975 | ||
976 | def insertData(self, offset, arg): | |
977 | if offset < 0: | |
978 | raise xml.dom.IndexSizeErr("offset cannot be negative") | |
979 | if offset >= len(self.data): | |
980 | raise xml.dom.IndexSizeErr("offset cannot be beyond end of data") | |
981 | if arg: | |
982 | self.data = "%s%s%s" % ( | |
983 | self.data[:offset], arg, self.data[offset:]) | |
984 | ||
985 | def deleteData(self, offset, count): | |
986 | if offset < 0: | |
987 | raise xml.dom.IndexSizeErr("offset cannot be negative") | |
988 | if offset >= len(self.data): | |
989 | raise xml.dom.IndexSizeErr("offset cannot be beyond end of data") | |
990 | if count < 0: | |
991 | raise xml.dom.IndexSizeErr("count cannot be negative") | |
992 | if count: | |
993 | self.data = self.data[:offset] + self.data[offset+count:] | |
994 | ||
995 | def replaceData(self, offset, count, arg): | |
996 | if offset < 0: | |
997 | raise xml.dom.IndexSizeErr("offset cannot be negative") | |
998 | if offset >= len(self.data): | |
999 | raise xml.dom.IndexSizeErr("offset cannot be beyond end of data") | |
1000 | if count < 0: | |
1001 | raise xml.dom.IndexSizeErr("count cannot be negative") | |
1002 | if count: | |
1003 | self.data = "%s%s%s" % ( | |
1004 | self.data[:offset], arg, self.data[offset+count:]) | |
1005 | ||
1006 | defproperty(CharacterData, "length", doc="Length of the string data.") | |
1007 | ||
1008 | ||
1009 | class Text(CharacterData): | |
1010 | # Make sure we don't add an instance __dict__ if we don't already | |
1011 | # have one, at least when that's possible: | |
1012 | # XXX this does not work, CharacterData is an old-style class | |
1013 | # __slots__ = () | |
1014 | ||
1015 | nodeType = Node.TEXT_NODE | |
1016 | nodeName = "#text" | |
1017 | attributes = None | |
1018 | ||
1019 | def splitText(self, offset): | |
1020 | if offset < 0 or offset > len(self.data): | |
1021 | raise xml.dom.IndexSizeErr("illegal offset value") | |
1022 | newText = self.__class__() | |
1023 | newText.data = self.data[offset:] | |
1024 | newText.ownerDocument = self.ownerDocument | |
1025 | next = self.nextSibling | |
1026 | if self.parentNode and self in self.parentNode.childNodes: | |
1027 | if next is None: | |
1028 | self.parentNode.appendChild(newText) | |
1029 | else: | |
1030 | self.parentNode.insertBefore(newText, next) | |
1031 | self.data = self.data[:offset] | |
1032 | return newText | |
1033 | ||
1034 | def writexml(self, writer, indent="", addindent="", newl=""): | |
1035 | _write_data(writer, "%s%s%s"%(indent, self.data, newl)) | |
1036 | ||
1037 | # DOM Level 3 (WD 9 April 2002) | |
1038 | ||
1039 | def _get_wholeText(self): | |
1040 | L = [self.data] | |
1041 | n = self.previousSibling | |
1042 | while n is not None: | |
1043 | if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): | |
1044 | L.insert(0, n.data) | |
1045 | n = n.previousSibling | |
1046 | else: | |
1047 | break | |
1048 | n = self.nextSibling | |
1049 | while n is not None: | |
1050 | if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): | |
1051 | L.append(n.data) | |
1052 | n = n.nextSibling | |
1053 | else: | |
1054 | break | |
1055 | return ''.join(L) | |
1056 | ||
1057 | def replaceWholeText(self, content): | |
1058 | # XXX This needs to be seriously changed if minidom ever | |
1059 | # supports EntityReference nodes. | |
1060 | parent = self.parentNode | |
1061 | n = self.previousSibling | |
1062 | while n is not None: | |
1063 | if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): | |
1064 | next = n.previousSibling | |
1065 | parent.removeChild(n) | |
1066 | n = next | |
1067 | else: | |
1068 | break | |
1069 | n = self.nextSibling | |
1070 | if not content: | |
1071 | parent.removeChild(self) | |
1072 | while n is not None: | |
1073 | if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): | |
1074 | next = n.nextSibling | |
1075 | parent.removeChild(n) | |
1076 | n = next | |
1077 | else: | |
1078 | break | |
1079 | if content: | |
1080 | d = self.__dict__ | |
1081 | d['data'] = content | |
1082 | d['nodeValue'] = content | |
1083 | return self | |
1084 | else: | |
1085 | return None | |
1086 | ||
1087 | def _get_isWhitespaceInElementContent(self): | |
1088 | if self.data.strip(): | |
1089 | return False | |
1090 | elem = _get_containing_element(self) | |
1091 | if elem is None: | |
1092 | return False | |
1093 | info = self.ownerDocument._get_elem_info(elem) | |
1094 | if info is None: | |
1095 | return False | |
1096 | else: | |
1097 | return info.isElementContent() | |
1098 | ||
1099 | defproperty(Text, "isWhitespaceInElementContent", | |
1100 | doc="True iff this text node contains only whitespace" | |
1101 | " and is in element content.") | |
1102 | defproperty(Text, "wholeText", | |
1103 | doc="The text of all logically-adjacent text nodes.") | |
1104 | ||
1105 | ||
1106 | def _get_containing_element(node): | |
1107 | c = node.parentNode | |
1108 | while c is not None: | |
1109 | if c.nodeType == Node.ELEMENT_NODE: | |
1110 | return c | |
1111 | c = c.parentNode | |
1112 | return None | |
1113 | ||
1114 | def _get_containing_entref(node): | |
1115 | c = node.parentNode | |
1116 | while c is not None: | |
1117 | if c.nodeType == Node.ENTITY_REFERENCE_NODE: | |
1118 | return c | |
1119 | c = c.parentNode | |
1120 | return None | |
1121 | ||
1122 | ||
1123 | class Comment(Childless, CharacterData): | |
1124 | nodeType = Node.COMMENT_NODE | |
1125 | nodeName = "#comment" | |
1126 | ||
1127 | def __init__(self, data): | |
1128 | self.data = self.nodeValue = data | |
1129 | ||
1130 | def writexml(self, writer, indent="", addindent="", newl=""): | |
1131 | writer.write("%s<!--%s-->%s" % (indent, self.data, newl)) | |
1132 | ||
1133 | ||
1134 | class CDATASection(Text): | |
1135 | # Make sure we don't add an instance __dict__ if we don't already | |
1136 | # have one, at least when that's possible: | |
1137 | # XXX this does not work, Text is an old-style class | |
1138 | # __slots__ = () | |
1139 | ||
1140 | nodeType = Node.CDATA_SECTION_NODE | |
1141 | nodeName = "#cdata-section" | |
1142 | ||
1143 | def writexml(self, writer, indent="", addindent="", newl=""): | |
1144 | if self.data.find("]]>") >= 0: | |
1145 | raise ValueError("']]>' not allowed in a CDATA section") | |
1146 | writer.write("<![CDATA[%s]]>" % self.data) | |
1147 | ||
1148 | ||
1149 | class ReadOnlySequentialNamedNodeMap(NewStyle, GetattrMagic): | |
1150 | __slots__ = '_seq', | |
1151 | ||
1152 | def __init__(self, seq=()): | |
1153 | # seq should be a list or tuple | |
1154 | self._seq = seq | |
1155 | ||
1156 | def __len__(self): | |
1157 | return len(self._seq) | |
1158 | ||
1159 | def _get_length(self): | |
1160 | return len(self._seq) | |
1161 | ||
1162 | def getNamedItem(self, name): | |
1163 | for n in self._seq: | |
1164 | if n.nodeName == name: | |
1165 | return n | |
1166 | ||
1167 | def getNamedItemNS(self, namespaceURI, localName): | |
1168 | for n in self._seq: | |
1169 | if n.namespaceURI == namespaceURI and n.localName == localName: | |
1170 | return n | |
1171 | ||
1172 | def __getitem__(self, name_or_tuple): | |
1173 | if isinstance(name_or_tuple, _TupleType): | |
1174 | node = self.getNamedItemNS(*name_or_tuple) | |
1175 | else: | |
1176 | node = self.getNamedItem(name_or_tuple) | |
1177 | if node is None: | |
1178 | raise KeyError, name_or_tuple | |
1179 | return node | |
1180 | ||
1181 | def item(self, index): | |
1182 | if index < 0: | |
1183 | return None | |
1184 | try: | |
1185 | return self._seq[index] | |
1186 | except IndexError: | |
1187 | return None | |
1188 | ||
1189 | def removeNamedItem(self, name): | |
1190 | raise xml.dom.NoModificationAllowedErr( | |
1191 | "NamedNodeMap instance is read-only") | |
1192 | ||
1193 | def removeNamedItemNS(self, namespaceURI, localName): | |
1194 | raise xml.dom.NoModificationAllowedErr( | |
1195 | "NamedNodeMap instance is read-only") | |
1196 | ||
1197 | def setNamedItem(self, node): | |
1198 | raise xml.dom.NoModificationAllowedErr( | |
1199 | "NamedNodeMap instance is read-only") | |
1200 | ||
1201 | def setNamedItemNS(self, node): | |
1202 | raise xml.dom.NoModificationAllowedErr( | |
1203 | "NamedNodeMap instance is read-only") | |
1204 | ||
1205 | def __getstate__(self): | |
1206 | return [self._seq] | |
1207 | ||
1208 | def __setstate__(self, state): | |
1209 | self._seq = state[0] | |
1210 | ||
1211 | defproperty(ReadOnlySequentialNamedNodeMap, "length", | |
1212 | doc="Number of entries in the NamedNodeMap.") | |
1213 | ||
1214 | ||
1215 | class Identified: | |
1216 | """Mix-in class that supports the publicId and systemId attributes.""" | |
1217 | ||
1218 | # XXX this does not work, this is an old-style class | |
1219 | # __slots__ = 'publicId', 'systemId' | |
1220 | ||
1221 | def _identified_mixin_init(self, publicId, systemId): | |
1222 | self.publicId = publicId | |
1223 | self.systemId = systemId | |
1224 | ||
1225 | def _get_publicId(self): | |
1226 | return self.publicId | |
1227 | ||
1228 | def _get_systemId(self): | |
1229 | return self.systemId | |
1230 | ||
1231 | class DocumentType(Identified, Childless, Node): | |
1232 | nodeType = Node.DOCUMENT_TYPE_NODE | |
1233 | nodeValue = None | |
1234 | name = None | |
1235 | publicId = None | |
1236 | systemId = None | |
1237 | internalSubset = None | |
1238 | ||
1239 | def __init__(self, qualifiedName): | |
1240 | self.entities = ReadOnlySequentialNamedNodeMap() | |
1241 | self.notations = ReadOnlySequentialNamedNodeMap() | |
1242 | if qualifiedName: | |
1243 | prefix, localname = _nssplit(qualifiedName) | |
1244 | self.name = localname | |
1245 | self.nodeName = self.name | |
1246 | ||
1247 | def _get_internalSubset(self): | |
1248 | return self.internalSubset | |
1249 | ||
1250 | def cloneNode(self, deep): | |
1251 | if self.ownerDocument is None: | |
1252 | # it's ok | |
1253 | clone = DocumentType(None) | |
1254 | clone.name = self.name | |
1255 | clone.nodeName = self.name | |
1256 | operation = xml.dom.UserDataHandler.NODE_CLONED | |
1257 | if deep: | |
1258 | clone.entities._seq = [] | |
1259 | clone.notations._seq = [] | |
1260 | for n in self.notations._seq: | |
1261 | notation = Notation(n.nodeName, n.publicId, n.systemId) | |
1262 | clone.notations._seq.append(notation) | |
1263 | n._call_user_data_handler(operation, n, notation) | |
1264 | for e in self.entities._seq: | |
1265 | entity = Entity(e.nodeName, e.publicId, e.systemId, | |
1266 | e.notationName) | |
1267 | entity.actualEncoding = e.actualEncoding | |
1268 | entity.encoding = e.encoding | |
1269 | entity.version = e.version | |
1270 | clone.entities._seq.append(entity) | |
1271 | e._call_user_data_handler(operation, n, entity) | |
1272 | self._call_user_data_handler(operation, self, clone) | |
1273 | return clone | |
1274 | else: | |
1275 | return None | |
1276 | ||
1277 | def writexml(self, writer, indent="", addindent="", newl=""): | |
1278 | writer.write("<!DOCTYPE ") | |
1279 | writer.write(self.name) | |
1280 | if self.publicId: | |
1281 | writer.write("%s PUBLIC '%s'%s '%s'" | |
1282 | % (newl, self.publicId, newl, self.systemId)) | |
1283 | elif self.systemId: | |
1284 | writer.write("%s SYSTEM '%s'" % (newl, self.systemId)) | |
1285 | if self.internalSubset is not None: | |
1286 | writer.write(" [") | |
1287 | writer.write(self.internalSubset) | |
1288 | writer.write("]") | |
1289 | writer.write(">"+newl) | |
1290 | ||
1291 | class Entity(Identified, Node): | |
1292 | attributes = None | |
1293 | nodeType = Node.ENTITY_NODE | |
1294 | nodeValue = None | |
1295 | ||
1296 | actualEncoding = None | |
1297 | encoding = None | |
1298 | version = None | |
1299 | ||
1300 | def __init__(self, name, publicId, systemId, notation): | |
1301 | self.nodeName = name | |
1302 | self.notationName = notation | |
1303 | self.childNodes = NodeList() | |
1304 | self._identified_mixin_init(publicId, systemId) | |
1305 | ||
1306 | def _get_actualEncoding(self): | |
1307 | return self.actualEncoding | |
1308 | ||
1309 | def _get_encoding(self): | |
1310 | return self.encoding | |
1311 | ||
1312 | def _get_version(self): | |
1313 | return self.version | |
1314 | ||
1315 | def appendChild(self, newChild): | |
1316 | raise xml.dom.HierarchyRequestErr( | |
1317 | "cannot append children to an entity node") | |
1318 | ||
1319 | def insertBefore(self, newChild, refChild): | |
1320 | raise xml.dom.HierarchyRequestErr( | |
1321 | "cannot insert children below an entity node") | |
1322 | ||
1323 | def removeChild(self, oldChild): | |
1324 | raise xml.dom.HierarchyRequestErr( | |
1325 | "cannot remove children from an entity node") | |
1326 | ||
1327 | def replaceChild(self, newChild, oldChild): | |
1328 | raise xml.dom.HierarchyRequestErr( | |
1329 | "cannot replace children of an entity node") | |
1330 | ||
1331 | class Notation(Identified, Childless, Node): | |
1332 | nodeType = Node.NOTATION_NODE | |
1333 | nodeValue = None | |
1334 | ||
1335 | def __init__(self, name, publicId, systemId): | |
1336 | self.nodeName = name | |
1337 | self._identified_mixin_init(publicId, systemId) | |
1338 | ||
1339 | ||
1340 | class DOMImplementation(DOMImplementationLS): | |
1341 | _features = [("core", "1.0"), | |
1342 | ("core", "2.0"), | |
1343 | ("core", "3.0"), | |
1344 | ("core", None), | |
1345 | ("xml", "1.0"), | |
1346 | ("xml", "2.0"), | |
1347 | ("xml", "3.0"), | |
1348 | ("xml", None), | |
1349 | ("ls-load", "3.0"), | |
1350 | ("ls-load", None), | |
1351 | ] | |
1352 | ||
1353 | def hasFeature(self, feature, version): | |
1354 | if version == "": | |
1355 | version = None | |
1356 | return (feature.lower(), version) in self._features | |
1357 | ||
1358 | def createDocument(self, namespaceURI, qualifiedName, doctype): | |
1359 | if doctype and doctype.parentNode is not None: | |
1360 | raise xml.dom.WrongDocumentErr( | |
1361 | "doctype object owned by another DOM tree") | |
1362 | doc = self._create_document() | |
1363 | ||
1364 | add_root_element = not (namespaceURI is None | |
1365 | and qualifiedName is None | |
1366 | and doctype is None) | |
1367 | ||
1368 | if not qualifiedName and add_root_element: | |
1369 | # The spec is unclear what to raise here; SyntaxErr | |
1370 | # would be the other obvious candidate. Since Xerces raises | |
1371 | # InvalidCharacterErr, and since SyntaxErr is not listed | |
1372 | # for createDocument, that seems to be the better choice. | |
1373 | # XXX: need to check for illegal characters here and in | |
1374 | # createElement. | |
1375 | ||
1376 | # DOM Level III clears this up when talking about the return value | |
1377 | # of this function. If namespaceURI, qName and DocType are | |
1378 | # Null the document is returned without a document element | |
1379 | # Otherwise if doctype or namespaceURI are not None | |
1380 | # Then we go back to the above problem | |
1381 | raise xml.dom.InvalidCharacterErr("Element with no name") | |
1382 | ||
1383 | if add_root_element: | |
1384 | prefix, localname = _nssplit(qualifiedName) | |
1385 | if prefix == "xml" \ | |
1386 | and namespaceURI != "http://www.w3.org/XML/1998/namespace": | |
1387 | raise xml.dom.NamespaceErr("illegal use of 'xml' prefix") | |
1388 | if prefix and not namespaceURI: | |
1389 | raise xml.dom.NamespaceErr( | |
1390 | "illegal use of prefix without namespaces") | |
1391 | element = doc.createElementNS(namespaceURI, qualifiedName) | |
1392 | if doctype: | |
1393 | doc.appendChild(doctype) | |
1394 | doc.appendChild(element) | |
1395 | ||
1396 | if doctype: | |
1397 | doctype.parentNode = doctype.ownerDocument = doc | |
1398 | ||
1399 | doc.doctype = doctype | |
1400 | doc.implementation = self | |
1401 | return doc | |
1402 | ||
1403 | def createDocumentType(self, qualifiedName, publicId, systemId): | |
1404 | doctype = DocumentType(qualifiedName) | |
1405 | doctype.publicId = publicId | |
1406 | doctype.systemId = systemId | |
1407 | return doctype | |
1408 | ||
1409 | # DOM Level 3 (WD 9 April 2002) | |
1410 | ||
1411 | def getInterface(self, feature): | |
1412 | if self.hasFeature(feature, None): | |
1413 | return self | |
1414 | else: | |
1415 | return None | |
1416 | ||
1417 | # internal | |
1418 | def _create_document(self): | |
1419 | return Document() | |
1420 | ||
1421 | class ElementInfo(NewStyle): | |
1422 | """Object that represents content-model information for an element. | |
1423 | ||
1424 | This implementation is not expected to be used in practice; DOM | |
1425 | builders should provide implementations which do the right thing | |
1426 | using information available to it. | |
1427 | ||
1428 | """ | |
1429 | ||
1430 | __slots__ = 'tagName', | |
1431 | ||
1432 | def __init__(self, name): | |
1433 | self.tagName = name | |
1434 | ||
1435 | def getAttributeType(self, aname): | |
1436 | return _no_type | |
1437 | ||
1438 | def getAttributeTypeNS(self, namespaceURI, localName): | |
1439 | return _no_type | |
1440 | ||
1441 | def isElementContent(self): | |
1442 | return False | |
1443 | ||
1444 | def isEmpty(self): | |
1445 | """Returns true iff this element is declared to have an EMPTY | |
1446 | content model.""" | |
1447 | return False | |
1448 | ||
1449 | def isId(self, aname): | |
1450 | """Returns true iff the named attribte is a DTD-style ID.""" | |
1451 | return False | |
1452 | ||
1453 | def isIdNS(self, namespaceURI, localName): | |
1454 | """Returns true iff the identified attribute is a DTD-style ID.""" | |
1455 | return False | |
1456 | ||
1457 | def __getstate__(self): | |
1458 | return self.tagName | |
1459 | ||
1460 | def __setstate__(self, state): | |
1461 | self.tagName = state | |
1462 | ||
1463 | def _clear_id_cache(node): | |
1464 | if node.nodeType == Node.DOCUMENT_NODE: | |
1465 | node._id_cache.clear() | |
1466 | node._id_search_stack = None | |
1467 | elif _in_document(node): | |
1468 | node.ownerDocument._id_cache.clear() | |
1469 | node.ownerDocument._id_search_stack= None | |
1470 | ||
1471 | class Document(Node, DocumentLS): | |
1472 | _child_node_types = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE, | |
1473 | Node.COMMENT_NODE, Node.DOCUMENT_TYPE_NODE) | |
1474 | ||
1475 | nodeType = Node.DOCUMENT_NODE | |
1476 | nodeName = "#document" | |
1477 | nodeValue = None | |
1478 | attributes = None | |
1479 | doctype = None | |
1480 | parentNode = None | |
1481 | previousSibling = nextSibling = None | |
1482 | ||
1483 | implementation = DOMImplementation() | |
1484 | ||
1485 | # Document attributes from Level 3 (WD 9 April 2002) | |
1486 | ||
1487 | actualEncoding = None | |
1488 | encoding = None | |
1489 | standalone = None | |
1490 | version = None | |
1491 | strictErrorChecking = False | |
1492 | errorHandler = None | |
1493 | documentURI = None | |
1494 | ||
1495 | _magic_id_count = 0 | |
1496 | ||
1497 | def __init__(self): | |
1498 | self.childNodes = NodeList() | |
1499 | # mapping of (namespaceURI, localName) -> ElementInfo | |
1500 | # and tagName -> ElementInfo | |
1501 | self._elem_info = {} | |
1502 | self._id_cache = {} | |
1503 | self._id_search_stack = None | |
1504 | ||
1505 | def _get_elem_info(self, element): | |
1506 | if element.namespaceURI: | |
1507 | key = element.namespaceURI, element.localName | |
1508 | else: | |
1509 | key = element.tagName | |
1510 | return self._elem_info.get(key) | |
1511 | ||
1512 | def _get_actualEncoding(self): | |
1513 | return self.actualEncoding | |
1514 | ||
1515 | def _get_doctype(self): | |
1516 | return self.doctype | |
1517 | ||
1518 | def _get_documentURI(self): | |
1519 | return self.documentURI | |
1520 | ||
1521 | def _get_encoding(self): | |
1522 | return self.encoding | |
1523 | ||
1524 | def _get_errorHandler(self): | |
1525 | return self.errorHandler | |
1526 | ||
1527 | def _get_standalone(self): | |
1528 | return self.standalone | |
1529 | ||
1530 | def _get_strictErrorChecking(self): | |
1531 | return self.strictErrorChecking | |
1532 | ||
1533 | def _get_version(self): | |
1534 | return self.version | |
1535 | ||
1536 | def appendChild(self, node): | |
1537 | if node.nodeType not in self._child_node_types: | |
1538 | raise xml.dom.HierarchyRequestErr( | |
1539 | "%s cannot be child of %s" % (repr(node), repr(self))) | |
1540 | if node.parentNode is not None: | |
1541 | # This needs to be done before the next test since this | |
1542 | # may *be* the document element, in which case it should | |
1543 | # end up re-ordered to the end. | |
1544 | node.parentNode.removeChild(node) | |
1545 | ||
1546 | if node.nodeType == Node.ELEMENT_NODE \ | |
1547 | and self._get_documentElement(): | |
1548 | raise xml.dom.HierarchyRequestErr( | |
1549 | "two document elements disallowed") | |
1550 | return Node.appendChild(self, node) | |
1551 | ||
1552 | def removeChild(self, oldChild): | |
1553 | try: | |
1554 | self.childNodes.remove(oldChild) | |
1555 | except ValueError: | |
1556 | raise xml.dom.NotFoundErr() | |
1557 | oldChild.nextSibling = oldChild.previousSibling = None | |
1558 | oldChild.parentNode = None | |
1559 | if self.documentElement is oldChild: | |
1560 | self.documentElement = None | |
1561 | ||
1562 | return oldChild | |
1563 | ||
1564 | def _get_documentElement(self): | |
1565 | for node in self.childNodes: | |
1566 | if node.nodeType == Node.ELEMENT_NODE: | |
1567 | return node | |
1568 | ||
1569 | def unlink(self): | |
1570 | if self.doctype is not None: | |
1571 | self.doctype.unlink() | |
1572 | self.doctype = None | |
1573 | Node.unlink(self) | |
1574 | ||
1575 | def cloneNode(self, deep): | |
1576 | if not deep: | |
1577 | return None | |
1578 | clone = self.implementation.createDocument(None, None, None) | |
1579 | clone.encoding = self.encoding | |
1580 | clone.standalone = self.standalone | |
1581 | clone.version = self.version | |
1582 | for n in self.childNodes: | |
1583 | childclone = _clone_node(n, deep, clone) | |
1584 | assert childclone.ownerDocument.isSameNode(clone) | |
1585 | clone.childNodes.append(childclone) | |
1586 | if childclone.nodeType == Node.DOCUMENT_NODE: | |
1587 | assert clone.documentElement is None | |
1588 | elif childclone.nodeType == Node.DOCUMENT_TYPE_NODE: | |
1589 | assert clone.doctype is None | |
1590 | clone.doctype = childclone | |
1591 | childclone.parentNode = clone | |
1592 | self._call_user_data_handler(xml.dom.UserDataHandler.NODE_CLONED, | |
1593 | self, clone) | |
1594 | return clone | |
1595 | ||
1596 | def createDocumentFragment(self): | |
1597 | d = DocumentFragment() | |
1598 | d.ownerDocument = self | |
1599 | return d | |
1600 | ||
1601 | def createElement(self, tagName): | |
1602 | e = Element(tagName) | |
1603 | e.ownerDocument = self | |
1604 | return e | |
1605 | ||
1606 | def createTextNode(self, data): | |
1607 | if not isinstance(data, StringTypes): | |
1608 | raise TypeError, "node contents must be a string" | |
1609 | t = Text() | |
1610 | t.data = data | |
1611 | t.ownerDocument = self | |
1612 | return t | |
1613 | ||
1614 | def createCDATASection(self, data): | |
1615 | if not isinstance(data, StringTypes): | |
1616 | raise TypeError, "node contents must be a string" | |
1617 | c = CDATASection() | |
1618 | c.data = data | |
1619 | c.ownerDocument = self | |
1620 | return c | |
1621 | ||
1622 | def createComment(self, data): | |
1623 | c = Comment(data) | |
1624 | c.ownerDocument = self | |
1625 | return c | |
1626 | ||
1627 | def createProcessingInstruction(self, target, data): | |
1628 | p = ProcessingInstruction(target, data) | |
1629 | p.ownerDocument = self | |
1630 | return p | |
1631 | ||
1632 | def createAttribute(self, qName): | |
1633 | a = Attr(qName) | |
1634 | a.ownerDocument = self | |
1635 | a.value = "" | |
1636 | return a | |
1637 | ||
1638 | def createElementNS(self, namespaceURI, qualifiedName): | |
1639 | prefix, localName = _nssplit(qualifiedName) | |
1640 | e = Element(qualifiedName, namespaceURI, prefix) | |
1641 | e.ownerDocument = self | |
1642 | return e | |
1643 | ||
1644 | def createAttributeNS(self, namespaceURI, qualifiedName): | |
1645 | prefix, localName = _nssplit(qualifiedName) | |
1646 | a = Attr(qualifiedName, namespaceURI, localName, prefix) | |
1647 | a.ownerDocument = self | |
1648 | a.value = "" | |
1649 | return a | |
1650 | ||
1651 | # A couple of implementation-specific helpers to create node types | |
1652 | # not supported by the W3C DOM specs: | |
1653 | ||
1654 | def _create_entity(self, name, publicId, systemId, notationName): | |
1655 | e = Entity(name, publicId, systemId, notationName) | |
1656 | e.ownerDocument = self | |
1657 | return e | |
1658 | ||
1659 | def _create_notation(self, name, publicId, systemId): | |
1660 | n = Notation(name, publicId, systemId) | |
1661 | n.ownerDocument = self | |
1662 | return n | |
1663 | ||
1664 | def getElementById(self, id): | |
1665 | if self._id_cache.has_key(id): | |
1666 | return self._id_cache[id] | |
1667 | if not (self._elem_info or self._magic_id_count): | |
1668 | return None | |
1669 | ||
1670 | stack = self._id_search_stack | |
1671 | if stack is None: | |
1672 | # we never searched before, or the cache has been cleared | |
1673 | stack = [self.documentElement] | |
1674 | self._id_search_stack = stack | |
1675 | elif not stack: | |
1676 | # Previous search was completed and cache is still valid; | |
1677 | # no matching node. | |
1678 | return None | |
1679 | ||
1680 | result = None | |
1681 | while stack: | |
1682 | node = stack.pop() | |
1683 | # add child elements to stack for continued searching | |
1684 | stack.extend([child for child in node.childNodes | |
1685 | if child.nodeType in _nodeTypes_with_children]) | |
1686 | # check this node | |
1687 | info = self._get_elem_info(node) | |
1688 | if info: | |
1689 | # We have to process all ID attributes before | |
1690 | # returning in order to get all the attributes set to | |
1691 | # be IDs using Element.setIdAttribute*(). | |
1692 | for attr in node.attributes.values(): | |
1693 | if attr.namespaceURI: | |
1694 | if info.isIdNS(attr.namespaceURI, attr.localName): | |
1695 | self._id_cache[attr.value] = node | |
1696 | if attr.value == id: | |
1697 | result = node | |
1698 | elif not node._magic_id_nodes: | |
1699 | break | |
1700 | elif info.isId(attr.name): | |
1701 | self._id_cache[attr.value] = node | |
1702 | if attr.value == id: | |
1703 | result = node | |
1704 | elif not node._magic_id_nodes: | |
1705 | break | |
1706 | elif attr._is_id: | |
1707 | self._id_cache[attr.value] = node | |
1708 | if attr.value == id: | |
1709 | result = node | |
1710 | elif node._magic_id_nodes == 1: | |
1711 | break | |
1712 | elif node._magic_id_nodes: | |
1713 | for attr in node.attributes.values(): | |
1714 | if attr._is_id: | |
1715 | self._id_cache[attr.value] = node | |
1716 | if attr.value == id: | |
1717 | result = node | |
1718 | if result is not None: | |
1719 | break | |
1720 | return result | |
1721 | ||
1722 | def getElementsByTagName(self, name): | |
1723 | return _get_elements_by_tagName_helper(self, name, NodeList()) | |
1724 | ||
1725 | def getElementsByTagNameNS(self, namespaceURI, localName): | |
1726 | return _get_elements_by_tagName_ns_helper( | |
1727 | self, namespaceURI, localName, NodeList()) | |
1728 | ||
1729 | def isSupported(self, feature, version): | |
1730 | return self.implementation.hasFeature(feature, version) | |
1731 | ||
1732 | def importNode(self, node, deep): | |
1733 | if node.nodeType == Node.DOCUMENT_NODE: | |
1734 | raise xml.dom.NotSupportedErr("cannot import document nodes") | |
1735 | elif node.nodeType == Node.DOCUMENT_TYPE_NODE: | |
1736 | raise xml.dom.NotSupportedErr("cannot import document type nodes") | |
1737 | return _clone_node(node, deep, self) | |
1738 | ||
1739 | def writexml(self, writer, indent="", addindent="", newl="", | |
1740 | encoding = None): | |
1741 | if encoding is None: | |
1742 | writer.write('<?xml version="1.0" ?>'+newl) | |
1743 | else: | |
1744 | writer.write('<?xml version="1.0" encoding="%s"?>%s' % (encoding, newl)) | |
1745 | for node in self.childNodes: | |
1746 | node.writexml(writer, indent, addindent, newl) | |
1747 | ||
1748 | # DOM Level 3 (WD 9 April 2002) | |
1749 | ||
1750 | def renameNode(self, n, namespaceURI, name): | |
1751 | if n.ownerDocument is not self: | |
1752 | raise xml.dom.WrongDocumentErr( | |
1753 | "cannot rename nodes from other documents;\n" | |
1754 | "expected %s,\nfound %s" % (self, n.ownerDocument)) | |
1755 | if n.nodeType not in (Node.ELEMENT_NODE, Node.ATTRIBUTE_NODE): | |
1756 | raise xml.dom.NotSupportedErr( | |
1757 | "renameNode() only applies to element and attribute nodes") | |
1758 | if namespaceURI != EMPTY_NAMESPACE: | |
1759 | if ':' in name: | |
1760 | prefix, localName = name.split(':', 1) | |
1761 | if ( prefix == "xmlns" | |
1762 | and namespaceURI != xml.dom.XMLNS_NAMESPACE): | |
1763 | raise xml.dom.NamespaceErr( | |
1764 | "illegal use of 'xmlns' prefix") | |
1765 | else: | |
1766 | if ( name == "xmlns" | |
1767 | and namespaceURI != xml.dom.XMLNS_NAMESPACE | |
1768 | and n.nodeType == Node.ATTRIBUTE_NODE): | |
1769 | raise xml.dom.NamespaceErr( | |
1770 | "illegal use of the 'xmlns' attribute") | |
1771 | prefix = None | |
1772 | localName = name | |
1773 | else: | |
1774 | prefix = None | |
1775 | localName = None | |
1776 | if n.nodeType == Node.ATTRIBUTE_NODE: | |
1777 | element = n.ownerElement | |
1778 | if element is not None: | |
1779 | is_id = n._is_id | |
1780 | element.removeAttributeNode(n) | |
1781 | else: | |
1782 | element = None | |
1783 | # avoid __setattr__ | |
1784 | d = n.__dict__ | |
1785 | d['prefix'] = prefix | |
1786 | d['localName'] = localName | |
1787 | d['namespaceURI'] = namespaceURI | |
1788 | d['nodeName'] = name | |
1789 | if n.nodeType == Node.ELEMENT_NODE: | |
1790 | d['tagName'] = name | |
1791 | else: | |
1792 | # attribute node | |
1793 | d['name'] = name | |
1794 | if element is not None: | |
1795 | element.setAttributeNode(n) | |
1796 | if is_id: | |
1797 | element.setIdAttributeNode(n) | |
1798 | # It's not clear from a semantic perspective whether we should | |
1799 | # call the user data handlers for the NODE_RENAMED event since | |
1800 | # we're re-using the existing node. The draft spec has been | |
1801 | # interpreted as meaning "no, don't call the handler unless a | |
1802 | # new node is created." | |
1803 | return n | |
1804 | ||
1805 | defproperty(Document, "documentElement", | |
1806 | doc="Top-level element of this document.") | |
1807 | ||
1808 | ||
1809 | def _clone_node(node, deep, newOwnerDocument): | |
1810 | """ | |
1811 | Clone a node and give it the new owner document. | |
1812 | Called by Node.cloneNode and Document.importNode | |
1813 | """ | |
1814 | if node.ownerDocument.isSameNode(newOwnerDocument): | |
1815 | operation = xml.dom.UserDataHandler.NODE_CLONED | |
1816 | else: | |
1817 | operation = xml.dom.UserDataHandler.NODE_IMPORTED | |
1818 | if node.nodeType == Node.ELEMENT_NODE: | |
1819 | clone = newOwnerDocument.createElementNS(node.namespaceURI, | |
1820 | node.nodeName) | |
1821 | for attr in node.attributes.values(): | |
1822 | clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value) | |
1823 | a = clone.getAttributeNodeNS(attr.namespaceURI, attr.localName) | |
1824 | a.specified = attr.specified | |
1825 | ||
1826 | if deep: | |
1827 | for child in node.childNodes: | |
1828 | c = _clone_node(child, deep, newOwnerDocument) | |
1829 | clone.appendChild(c) | |
1830 | ||
1831 | elif node.nodeType == Node.DOCUMENT_FRAGMENT_NODE: | |
1832 | clone = newOwnerDocument.createDocumentFragment() | |
1833 | if deep: | |
1834 | for child in node.childNodes: | |
1835 | c = _clone_node(child, deep, newOwnerDocument) | |
1836 | clone.appendChild(c) | |
1837 | ||
1838 | elif node.nodeType == Node.TEXT_NODE: | |
1839 | clone = newOwnerDocument.createTextNode(node.data) | |
1840 | elif node.nodeType == Node.CDATA_SECTION_NODE: | |
1841 | clone = newOwnerDocument.createCDATASection(node.data) | |
1842 | elif node.nodeType == Node.PROCESSING_INSTRUCTION_NODE: | |
1843 | clone = newOwnerDocument.createProcessingInstruction(node.target, | |
1844 | node.data) | |
1845 | elif node.nodeType == Node.COMMENT_NODE: | |
1846 | clone = newOwnerDocument.createComment(node.data) | |
1847 | elif node.nodeType == Node.ATTRIBUTE_NODE: | |
1848 | clone = newOwnerDocument.createAttributeNS(node.namespaceURI, | |
1849 | node.nodeName) | |
1850 | clone.specified = True | |
1851 | clone.value = node.value | |
1852 | elif node.nodeType == Node.DOCUMENT_TYPE_NODE: | |
1853 | assert node.ownerDocument is not newOwnerDocument | |
1854 | operation = xml.dom.UserDataHandler.NODE_IMPORTED | |
1855 | clone = newOwnerDocument.implementation.createDocumentType( | |
1856 | node.name, node.publicId, node.systemId) | |
1857 | clone.ownerDocument = newOwnerDocument | |
1858 | if deep: | |
1859 | clone.entities._seq = [] | |
1860 | clone.notations._seq = [] | |
1861 | for n in node.notations._seq: | |
1862 | notation = Notation(n.nodeName, n.publicId, n.systemId) | |
1863 | notation.ownerDocument = newOwnerDocument | |
1864 | clone.notations._seq.append(notation) | |
1865 | if hasattr(n, '_call_user_data_handler'): | |
1866 | n._call_user_data_handler(operation, n, notation) | |
1867 | for e in node.entities._seq: | |
1868 | entity = Entity(e.nodeName, e.publicId, e.systemId, | |
1869 | e.notationName) | |
1870 | entity.actualEncoding = e.actualEncoding | |
1871 | entity.encoding = e.encoding | |
1872 | entity.version = e.version | |
1873 | entity.ownerDocument = newOwnerDocument | |
1874 | clone.entities._seq.append(entity) | |
1875 | if hasattr(e, '_call_user_data_handler'): | |
1876 | e._call_user_data_handler(operation, n, entity) | |
1877 | else: | |
1878 | # Note the cloning of Document and DocumentType nodes is | |
1879 | # implemenetation specific. minidom handles those cases | |
1880 | # directly in the cloneNode() methods. | |
1881 | raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node)) | |
1882 | ||
1883 | # Check for _call_user_data_handler() since this could conceivably | |
1884 | # used with other DOM implementations (one of the FourThought | |
1885 | # DOMs, perhaps?). | |
1886 | if hasattr(node, '_call_user_data_handler'): | |
1887 | node._call_user_data_handler(operation, node, clone) | |
1888 | return clone | |
1889 | ||
1890 | ||
1891 | def _nssplit(qualifiedName): | |
1892 | fields = qualifiedName.split(':', 1) | |
1893 | if len(fields) == 2: | |
1894 | return fields | |
1895 | else: | |
1896 | return (None, fields[0]) | |
1897 | ||
1898 | ||
1899 | def _get_StringIO(): | |
1900 | # we can't use cStringIO since it doesn't support Unicode strings | |
1901 | from StringIO import StringIO | |
1902 | return StringIO() | |
1903 | ||
1904 | def _do_pulldom_parse(func, args, kwargs): | |
1905 | events = func(*args, **kwargs) | |
1906 | toktype, rootNode = events.getEvent() | |
1907 | events.expandNode(rootNode) | |
1908 | events.clear() | |
1909 | return rootNode | |
1910 | ||
1911 | def parse(file, parser=None, bufsize=None): | |
1912 | """Parse a file into a DOM by filename or file object.""" | |
1913 | if parser is None and not bufsize: | |
1914 | from xml.dom import expatbuilder | |
1915 | return expatbuilder.parse(file) | |
1916 | else: | |
1917 | from xml.dom import pulldom | |
1918 | return _do_pulldom_parse(pulldom.parse, (file,), | |
1919 | {'parser': parser, 'bufsize': bufsize}) | |
1920 | ||
1921 | def parseString(string, parser=None): | |
1922 | """Parse a file into a DOM from a string.""" | |
1923 | if parser is None: | |
1924 | from xml.dom import expatbuilder | |
1925 | return expatbuilder.parseString(string) | |
1926 | else: | |
1927 | from xml.dom import pulldom | |
1928 | return _do_pulldom_parse(pulldom.parseString, (string,), | |
1929 | {'parser': parser}) | |
1930 | ||
1931 | def getDOMImplementation(features=None): | |
1932 | if features: | |
1933 | if isinstance(features, StringTypes): | |
1934 | features = domreg._parse_feature_string(features) | |
1935 | for f, v in features: | |
1936 | if not Document.implementation.hasFeature(f, v): | |
1937 | return None | |
1938 | return Document.implementation |