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