Updated for hfsplus module, new gusi libs.
[python/dscho.git] / Lib / xml / dom / minidom.py
blob75ff3c31eb099c06e88bbb4ae4e6763554ef8431
1 """\
2 minidom.py -- a lightweight DOM implementation.
4 parse("foo.xml")
6 parseString("<foo><bar/></foo>")
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 """
17 import string
18 _string = string
19 del string
21 from xml.dom import HierarchyRequestErr, EMPTY_NAMESPACE
23 # localize the types, and allow support for Unicode values if available:
24 import types
25 _TupleType = types.TupleType
26 try:
27 _StringTypes = (types.StringType, types.UnicodeType)
28 except AttributeError:
29 _StringTypes = (types.StringType,)
30 del types
32 import xml.dom
35 if list is type([]):
36 class NodeList(list):
37 def item(self, index):
38 if 0 <= index < len(self):
39 return self[index]
41 length = property(lambda self: len(self),
42 doc="The number of nodes in the NodeList.")
44 else:
45 def NodeList():
46 return []
49 class Node(xml.dom.Node):
50 allnodes = {}
51 _debug = 0
52 _makeParentNodes = 1
53 debug = None
54 childNodeTypes = ()
55 namespaceURI = None # this is non-null only for elements and attributes
56 parentNode = None
57 ownerDocument = None
59 def __init__(self):
60 self.childNodes = NodeList()
61 if Node._debug:
62 index = repr(id(self)) + repr(self.__class__)
63 Node.allnodes[index] = repr(self.__dict__)
64 if Node.debug is None:
65 Node.debug = _get_StringIO()
66 #open("debug4.out", "w")
67 Node.debug.write("create %s\n" % index)
69 def __nonzero__(self):
70 return 1
72 def toxml(self):
73 writer = _get_StringIO()
74 self.writexml(writer)
75 return writer.getvalue()
77 def toprettyxml(self, indent="\t", newl="\n"):
78 # indent = the indentation string to prepend, per level
79 # newl = the newline string to append
80 writer = _get_StringIO()
81 self.writexml(writer, "", indent, newl)
82 return writer.getvalue()
84 def hasChildNodes(self):
85 if self.childNodes:
86 return 1
87 else:
88 return 0
90 def _get_firstChild(self):
91 if self.childNodes:
92 return self.childNodes[0]
94 def _get_lastChild(self):
95 if self.childNodes:
96 return self.childNodes[-1]
98 try:
99 property
100 except NameError:
101 def __getattr__(self, key):
102 if key[0:2] == "__":
103 raise AttributeError, key
104 # getattr should never call getattr!
105 if self.__dict__.has_key("inGetAttr"):
106 del self.inGetAttr
107 raise AttributeError, key
109 prefix, attrname = key[:5], key[5:]
110 if prefix == "_get_":
111 self.inGetAttr = 1
112 if hasattr(self, attrname):
113 del self.inGetAttr
114 return (lambda self=self, attrname=attrname:
115 getattr(self, attrname))
116 else:
117 del self.inGetAttr
118 raise AttributeError, key
119 else:
120 self.inGetAttr = 1
121 try:
122 func = getattr(self, "_get_" + key)
123 except AttributeError:
124 raise AttributeError, key
125 del self.inGetAttr
126 return func()
127 else:
128 firstChild = property(_get_firstChild,
129 doc="First child node, or None.")
130 lastChild = property(_get_lastChild,
131 doc="Last child node, or None.")
133 def insertBefore(self, newChild, refChild):
134 if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
135 for c in tuple(newChild.childNodes):
136 self.insertBefore(c, refChild)
137 ### The DOM does not clearly specify what to return in this case
138 return newChild
139 if newChild.nodeType not in self.childNodeTypes:
140 raise HierarchyRequestErr, \
141 "%s cannot be child of %s" % (repr(newChild), repr(self))
142 if newChild.parentNode is not None:
143 newChild.parentNode.removeChild(newChild)
144 if refChild is None:
145 self.appendChild(newChild)
146 else:
147 index = self.childNodes.index(refChild)
148 self.childNodes.insert(index, newChild)
149 newChild.nextSibling = refChild
150 refChild.previousSibling = newChild
151 if index:
152 node = self.childNodes[index-1]
153 node.nextSibling = newChild
154 newChild.previousSibling = node
155 else:
156 newChild.previousSibling = None
157 if self._makeParentNodes:
158 newChild.parentNode = self
159 return newChild
161 def appendChild(self, node):
162 if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
163 for c in tuple(node.childNodes):
164 self.appendChild(c)
165 ### The DOM does not clearly specify what to return in this case
166 return node
167 if node.nodeType not in self.childNodeTypes:
168 raise HierarchyRequestErr, \
169 "%s cannot be child of %s" % (repr(node), repr(self))
170 if node.parentNode is not None:
171 node.parentNode.removeChild(node)
172 if self.childNodes:
173 last = self.lastChild
174 node.previousSibling = last
175 last.nextSibling = node
176 else:
177 node.previousSibling = None
178 node.nextSibling = None
179 self.childNodes.append(node)
180 if self._makeParentNodes:
181 node.parentNode = self
182 return node
184 def replaceChild(self, newChild, oldChild):
185 if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
186 refChild = oldChild.nextSibling
187 self.removeChild(oldChild)
188 return self.insertBefore(newChild, refChild)
189 if newChild.nodeType not in self.childNodeTypes:
190 raise HierarchyRequestErr, \
191 "%s cannot be child of %s" % (repr(newChild), repr(self))
192 if newChild.parentNode is not None:
193 newChild.parentNode.removeChild(newChild)
194 if newChild is oldChild:
195 return
196 index = self.childNodes.index(oldChild)
197 self.childNodes[index] = newChild
198 if self._makeParentNodes:
199 newChild.parentNode = self
200 oldChild.parentNode = None
201 newChild.nextSibling = oldChild.nextSibling
202 newChild.previousSibling = oldChild.previousSibling
203 oldChild.nextSibling = None
204 oldChild.previousSibling = None
205 if newChild.previousSibling:
206 newChild.previousSibling.nextSibling = newChild
207 if newChild.nextSibling:
208 newChild.nextSibling.previousSibling = newChild
209 return oldChild
211 def removeChild(self, oldChild):
212 self.childNodes.remove(oldChild)
213 if oldChild.nextSibling is not None:
214 oldChild.nextSibling.previousSibling = oldChild.previousSibling
215 if oldChild.previousSibling is not None:
216 oldChild.previousSibling.nextSibling = oldChild.nextSibling
217 oldChild.nextSibling = oldChild.previousSibling = None
219 if self._makeParentNodes:
220 oldChild.parentNode = None
221 return oldChild
223 def normalize(self):
224 L = []
225 for child in self.childNodes:
226 if child.nodeType == Node.TEXT_NODE:
227 data = child.data
228 if data and L and L[-1].nodeType == child.nodeType:
229 # collapse text node
230 node = L[-1]
231 node.data = node.nodeValue = node.data + child.data
232 node.nextSibling = child.nextSibling
233 child.unlink()
234 elif data:
235 if L:
236 L[-1].nextSibling = child
237 child.previousSibling = L[-1]
238 else:
239 child.previousSibling = None
240 L.append(child)
241 else:
242 # empty text node; discard
243 child.unlink()
244 else:
245 if L:
246 L[-1].nextSibling = child
247 child.previousSibling = L[-1]
248 else:
249 child.previousSibling = None
250 L.append(child)
251 if child.nodeType == Node.ELEMENT_NODE:
252 child.normalize()
253 self.childNodes[:] = L
255 def cloneNode(self, deep):
256 import new
257 clone = new.instance(self.__class__, self.__dict__.copy())
258 if self._makeParentNodes:
259 clone.parentNode = None
260 clone.childNodes = NodeList()
261 if deep:
262 for child in self.childNodes:
263 clone.appendChild(child.cloneNode(1))
264 return clone
266 # DOM Level 3 (Working Draft 2001-Jan-26)
268 def isSameNode(self, other):
269 return self is other
271 # minidom-specific API:
273 def unlink(self):
274 self.parentNode = self.ownerDocument = None
275 for child in self.childNodes:
276 child.unlink()
277 self.childNodes = None
278 self.previousSibling = None
279 self.nextSibling = None
280 if Node._debug:
281 index = repr(id(self)) + repr(self.__class__)
282 self.debug.write("Deleting: %s\n" % index)
283 del Node.allnodes[index]
285 def _write_data(writer, data):
286 "Writes datachars to writer."
287 replace = _string.replace
288 data = replace(data, "&", "&amp;")
289 data = replace(data, "<", "&lt;")
290 data = replace(data, "\"", "&quot;")
291 data = replace(data, ">", "&gt;")
292 writer.write(data)
294 def _getElementsByTagNameHelper(parent, name, rc):
295 for node in parent.childNodes:
296 if node.nodeType == Node.ELEMENT_NODE and \
297 (name == "*" or node.tagName == name):
298 rc.append(node)
299 _getElementsByTagNameHelper(node, name, rc)
300 return rc
302 def _getElementsByTagNameNSHelper(parent, nsURI, localName, rc):
303 for node in parent.childNodes:
304 if node.nodeType == Node.ELEMENT_NODE:
305 if ((localName == "*" or node.localName == localName) and
306 (nsURI == "*" or node.namespaceURI == nsURI)):
307 rc.append(node)
308 _getElementsByTagNameNSHelper(node, nsURI, localName, rc)
309 return rc
311 class DocumentFragment(Node):
312 nodeType = Node.DOCUMENT_FRAGMENT_NODE
313 nodeName = "#document-fragment"
314 nodeValue = None
315 attributes = None
316 parentNode = None
317 childNodeTypes = (Node.ELEMENT_NODE,
318 Node.TEXT_NODE,
319 Node.CDATA_SECTION_NODE,
320 Node.ENTITY_REFERENCE_NODE,
321 Node.PROCESSING_INSTRUCTION_NODE,
322 Node.COMMENT_NODE,
323 Node.NOTATION_NODE)
326 class Attr(Node):
327 nodeType = Node.ATTRIBUTE_NODE
328 attributes = None
329 ownerElement = None
330 childNodeTypes = (Node.TEXT_NODE, Node.ENTITY_REFERENCE_NODE)
332 def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None, prefix=None):
333 # skip setattr for performance
334 d = self.__dict__
335 d["localName"] = localName or qName
336 d["nodeName"] = d["name"] = qName
337 d["namespaceURI"] = namespaceURI
338 d["prefix"] = prefix
339 Node.__init__(self)
340 # nodeValue and value are set elsewhere
342 def __setattr__(self, name, value):
343 d = self.__dict__
344 if name in ("value", "nodeValue"):
345 d["value"] = d["nodeValue"] = value
346 elif name in ("name", "nodeName"):
347 d["name"] = d["nodeName"] = value
348 else:
349 d[name] = value
351 def cloneNode(self, deep):
352 clone = Node.cloneNode(self, deep)
353 if clone.__dict__.has_key("ownerElement"):
354 del clone.ownerElement
355 return clone
358 class NamedNodeMap:
359 """The attribute list is a transient interface to the underlying
360 dictionaries. Mutations here will change the underlying element's
361 dictionary.
363 Ordering is imposed artificially and does not reflect the order of
364 attributes as found in an input document.
367 def __init__(self, attrs, attrsNS, ownerElement):
368 self._attrs = attrs
369 self._attrsNS = attrsNS
370 self._ownerElement = ownerElement
372 try:
373 property
374 except NameError:
375 def __getattr__(self, name):
376 if name == "length":
377 return len(self._attrs)
378 raise AttributeError, name
379 else:
380 length = property(lambda self: len(self._attrs),
381 doc="Number of nodes in the NamedNodeMap.")
383 def item(self, index):
384 try:
385 return self[self._attrs.keys()[index]]
386 except IndexError:
387 return None
389 def items(self):
390 L = []
391 for node in self._attrs.values():
392 L.append((node.nodeName, node.value))
393 return L
395 def itemsNS(self):
396 L = []
397 for node in self._attrs.values():
398 L.append(((node.namespaceURI, node.localName), node.value))
399 return L
401 def keys(self):
402 return self._attrs.keys()
404 def keysNS(self):
405 return self._attrsNS.keys()
407 def values(self):
408 return self._attrs.values()
410 def get(self, name, value = None):
411 return self._attrs.get(name, value)
413 def __len__(self):
414 return self.length
416 def __cmp__(self, other):
417 if self._attrs is getattr(other, "_attrs", None):
418 return 0
419 else:
420 return cmp(id(self), id(other))
422 #FIXME: is it appropriate to return .value?
423 def __getitem__(self, attname_or_tuple):
424 if type(attname_or_tuple) is _TupleType:
425 return self._attrsNS[attname_or_tuple]
426 else:
427 return self._attrs[attname_or_tuple]
429 # same as set
430 def __setitem__(self, attname, value):
431 if type(value) in _StringTypes:
432 node = Attr(attname)
433 node.value = value
434 node.ownerDocument = self._ownerElement.ownerDocument
435 else:
436 if not isinstance(value, Attr):
437 raise TypeError, "value must be a string or Attr object"
438 node = value
439 self.setNamedItem(node)
441 def setNamedItem(self, node):
442 if not isinstance(node, Attr):
443 raise HierarchyRequestErr, \
444 "%s cannot be child of %s" % (repr(node), repr(self))
445 old = self._attrs.get(node.name)
446 if old:
447 old.unlink()
448 self._attrs[node.name] = node
449 self._attrsNS[(node.namespaceURI, node.localName)] = node
450 node.ownerElement = self._ownerElement
451 return old
453 def setNamedItemNS(self, node):
454 return self.setNamedItem(node)
456 def __delitem__(self, attname_or_tuple):
457 node = self[attname_or_tuple]
458 node.unlink()
459 del self._attrs[node.name]
460 del self._attrsNS[(node.namespaceURI, node.localName)]
461 self.length = len(self._attrs)
463 AttributeList = NamedNodeMap
466 class Element(Node):
467 nodeType = Node.ELEMENT_NODE
468 nextSibling = None
469 previousSibling = None
470 childNodeTypes = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
471 Node.COMMENT_NODE, Node.TEXT_NODE,
472 Node.CDATA_SECTION_NODE, Node.ENTITY_REFERENCE_NODE)
474 def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
475 localName=None):
476 Node.__init__(self)
477 self.tagName = self.nodeName = tagName
478 self.localName = localName or tagName
479 self.prefix = prefix
480 self.namespaceURI = namespaceURI
481 self.nodeValue = None
483 self._attrs = {} # attributes are double-indexed:
484 self._attrsNS = {} # tagName -> Attribute
485 # URI,localName -> Attribute
486 # in the future: consider lazy generation
487 # of attribute objects this is too tricky
488 # for now because of headaches with
489 # namespaces.
491 def cloneNode(self, deep):
492 clone = Node.cloneNode(self, deep)
493 clone._attrs = {}
494 clone._attrsNS = {}
495 for attr in self._attrs.values():
496 node = attr.cloneNode(1)
497 clone._attrs[node.name] = node
498 clone._attrsNS[(node.namespaceURI, node.localName)] = node
499 node.ownerElement = clone
500 return clone
502 def unlink(self):
503 for attr in self._attrs.values():
504 attr.unlink()
505 self._attrs = None
506 self._attrsNS = None
507 Node.unlink(self)
509 def getAttribute(self, attname):
510 try:
511 return self._attrs[attname].value
512 except KeyError:
513 return ""
515 def getAttributeNS(self, namespaceURI, localName):
516 try:
517 return self._attrsNS[(namespaceURI, localName)].value
518 except KeyError:
519 return ""
521 def setAttribute(self, attname, value):
522 attr = Attr(attname)
523 # for performance
524 d = attr.__dict__
525 d["value"] = d["nodeValue"] = value
526 d["ownerDocument"] = self.ownerDocument
527 self.setAttributeNode(attr)
529 def setAttributeNS(self, namespaceURI, qualifiedName, value):
530 prefix, localname = _nssplit(qualifiedName)
531 # for performance
532 attr = Attr(qualifiedName, namespaceURI, localname, prefix)
533 d = attr.__dict__
534 d["value"] = d["nodeValue"] = value
535 d["ownerDocument"] = self.ownerDocument
536 self.setAttributeNode(attr)
538 def getAttributeNode(self, attrname):
539 return self._attrs.get(attrname)
541 def getAttributeNodeNS(self, namespaceURI, localName):
542 return self._attrsNS.get((namespaceURI, localName))
544 def setAttributeNode(self, attr):
545 if attr.ownerElement not in (None, self):
546 raise xml.dom.InuseAttributeErr("attribute node already owned")
547 old = self._attrs.get(attr.name, None)
548 if old:
549 old.unlink()
550 self._attrs[attr.name] = attr
551 self._attrsNS[(attr.namespaceURI, attr.localName)] = attr
553 # This creates a circular reference, but Element.unlink()
554 # breaks the cycle since the references to the attribute
555 # dictionaries are tossed.
556 attr.ownerElement = self
558 if old is not attr:
559 # It might have already been part of this node, in which case
560 # it doesn't represent a change, and should not be returned.
561 return old
563 setAttributeNodeNS = setAttributeNode
565 def removeAttribute(self, name):
566 attr = self._attrs[name]
567 self.removeAttributeNode(attr)
569 def removeAttributeNS(self, namespaceURI, localName):
570 attr = self._attrsNS[(namespaceURI, localName)]
571 self.removeAttributeNode(attr)
573 def removeAttributeNode(self, node):
574 node.unlink()
575 del self._attrs[node.name]
576 del self._attrsNS[(node.namespaceURI, node.localName)]
578 removeAttributeNodeNS = removeAttributeNode
580 def hasAttribute(self, name):
581 return self._attrs.has_key(name)
583 def hasAttributeNS(self, namespaceURI, localName):
584 return self._attrsNS.has_key((namespaceURI, localName))
586 def getElementsByTagName(self, name):
587 return _getElementsByTagNameHelper(self, name, [])
589 def getElementsByTagNameNS(self, namespaceURI, localName):
590 return _getElementsByTagNameNSHelper(self, namespaceURI, localName, [])
592 def __repr__(self):
593 return "<DOM Element: %s at %s>" % (self.tagName, id(self))
595 def writexml(self, writer, indent="", addindent="", newl=""):
596 # indent = current indentation
597 # addindent = indentation to add to higher levels
598 # newl = newline string
599 writer.write(indent+"<" + self.tagName)
601 attrs = self._get_attributes()
602 a_names = attrs.keys()
603 a_names.sort()
605 for a_name in a_names:
606 writer.write(" %s=\"" % a_name)
607 _write_data(writer, attrs[a_name].value)
608 writer.write("\"")
609 if self.childNodes:
610 writer.write(">%s"%(newl))
611 for node in self.childNodes:
612 node.writexml(writer,indent+addindent,addindent,newl)
613 writer.write("%s</%s>%s" % (indent,self.tagName,newl))
614 else:
615 writer.write("/>%s"%(newl))
617 def _get_attributes(self):
618 return NamedNodeMap(self._attrs, self._attrsNS, self)
620 try:
621 property
622 except NameError:
623 pass
624 else:
625 attributes = property(_get_attributes,
626 doc="NamedNodeMap of attributes on the element.")
628 def hasAttributes(self):
629 if self._attrs or self._attrsNS:
630 return 1
631 else:
632 return 0
634 class Comment(Node):
635 nodeType = Node.COMMENT_NODE
636 nodeName = "#comment"
637 attributes = None
638 childNodeTypes = ()
640 def __init__(self, data):
641 Node.__init__(self)
642 self.data = self.nodeValue = data
644 def writexml(self, writer, indent="", addindent="", newl=""):
645 writer.write("%s<!--%s-->%s" % (indent,self.data,newl))
647 class ProcessingInstruction(Node):
648 nodeType = Node.PROCESSING_INSTRUCTION_NODE
649 attributes = None
650 childNodeTypes = ()
652 def __init__(self, target, data):
653 Node.__init__(self)
654 self.target = self.nodeName = target
655 self.data = self.nodeValue = data
657 def writexml(self, writer, indent="", addindent="", newl=""):
658 writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl))
660 class CharacterData(Node):
661 def __init__(self, data):
662 if type(data) not in _StringTypes:
663 raise TypeError, "node contents must be a string"
664 Node.__init__(self)
665 self.data = self.nodeValue = data
666 self.length = len(data)
668 def __repr__(self):
669 if len(self.data) > 10:
670 dotdotdot = "..."
671 else:
672 dotdotdot = ""
673 return "<DOM %s node \"%s%s\">" % (
674 self.__class__.__name__, self.data[0:10], dotdotdot)
676 def substringData(self, offset, count):
677 if offset < 0:
678 raise xml.dom.IndexSizeErr("offset cannot be negative")
679 if offset >= len(self.data):
680 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
681 if count < 0:
682 raise xml.dom.IndexSizeErr("count cannot be negative")
683 return self.data[offset:offset+count]
685 def appendData(self, arg):
686 self.data = self.data + arg
687 self.nodeValue = self.data
688 self.length = len(self.data)
690 def insertData(self, offset, arg):
691 if offset < 0:
692 raise xml.dom.IndexSizeErr("offset cannot be negative")
693 if offset >= len(self.data):
694 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
695 if arg:
696 self.data = "%s%s%s" % (
697 self.data[:offset], arg, self.data[offset:])
698 self.nodeValue = self.data
699 self.length = len(self.data)
701 def deleteData(self, offset, count):
702 if offset < 0:
703 raise xml.dom.IndexSizeErr("offset cannot be negative")
704 if offset >= len(self.data):
705 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
706 if count < 0:
707 raise xml.dom.IndexSizeErr("count cannot be negative")
708 if count:
709 self.data = self.data[:offset] + self.data[offset+count:]
710 self.nodeValue = self.data
711 self.length = len(self.data)
713 def replaceData(self, offset, count, arg):
714 if offset < 0:
715 raise xml.dom.IndexSizeErr("offset cannot be negative")
716 if offset >= len(self.data):
717 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
718 if count < 0:
719 raise xml.dom.IndexSizeErr("count cannot be negative")
720 if count:
721 self.data = "%s%s%s" % (
722 self.data[:offset], arg, self.data[offset+count:])
723 self.nodeValue = self.data
724 self.length = len(self.data)
726 class Text(CharacterData):
727 nodeType = Node.TEXT_NODE
728 nodeName = "#text"
729 attributes = None
730 childNodeTypes = ()
732 def splitText(self, offset):
733 if offset < 0 or offset > len(self.data):
734 raise xml.dom.IndexSizeErr("illegal offset value")
735 newText = Text(self.data[offset:])
736 next = self.nextSibling
737 if self.parentNode and self in self.parentNode.childNodes:
738 if next is None:
739 self.parentNode.appendChild(newText)
740 else:
741 self.parentNode.insertBefore(newText, next)
742 self.data = self.data[:offset]
743 self.nodeValue = self.data
744 self.length = len(self.data)
745 return newText
747 def writexml(self, writer, indent="", addindent="", newl=""):
748 _write_data(writer, "%s%s%s"%(indent, self.data, newl))
751 class CDATASection(Text):
752 nodeType = Node.CDATA_SECTION_NODE
753 nodeName = "#cdata-section"
755 def writexml(self, writer, indent="", addindent="", newl=""):
756 writer.write("<![CDATA[%s]]>" % self.data)
759 def _nssplit(qualifiedName):
760 fields = _string.split(qualifiedName, ':', 1)
761 if len(fields) == 2:
762 return fields
763 elif len(fields) == 1:
764 return (None, fields[0])
767 class DocumentType(Node):
768 nodeType = Node.DOCUMENT_TYPE_NODE
769 nodeValue = None
770 attributes = None
771 name = None
772 publicId = None
773 systemId = None
774 internalSubset = None
775 entities = None
776 notations = None
778 def __init__(self, qualifiedName):
779 Node.__init__(self)
780 if qualifiedName:
781 prefix, localname = _nssplit(qualifiedName)
782 self.name = localname
785 class DOMImplementation:
786 def hasFeature(self, feature, version):
787 if version not in ("1.0", "2.0"):
788 return 0
789 feature = _string.lower(feature)
790 return feature == "core"
792 def createDocument(self, namespaceURI, qualifiedName, doctype):
793 if doctype and doctype.parentNode is not None:
794 raise xml.dom.WrongDocumentErr(
795 "doctype object owned by another DOM tree")
796 doc = self._createDocument()
797 if doctype is None:
798 doctype = self.createDocumentType(qualifiedName, None, None)
799 if not qualifiedName:
800 # The spec is unclear what to raise here; SyntaxErr
801 # would be the other obvious candidate. Since Xerces raises
802 # InvalidCharacterErr, and since SyntaxErr is not listed
803 # for createDocument, that seems to be the better choice.
804 # XXX: need to check for illegal characters here and in
805 # createElement.
806 raise xml.dom.InvalidCharacterErr("Element with no name")
807 prefix, localname = _nssplit(qualifiedName)
808 if prefix == "xml" \
809 and namespaceURI != "http://www.w3.org/XML/1998/namespace":
810 raise xml.dom.NamespaceErr("illegal use of 'xml' prefix")
811 if prefix and not namespaceURI:
812 raise xml.dom.NamespaceErr(
813 "illegal use of prefix without namespaces")
814 element = doc.createElementNS(namespaceURI, qualifiedName)
815 doc.appendChild(element)
816 doctype.parentNode = doctype.ownerDocument = doc
817 doc.doctype = doctype
818 doc.implementation = self
819 return doc
821 def createDocumentType(self, qualifiedName, publicId, systemId):
822 doctype = DocumentType(qualifiedName)
823 doctype.publicId = publicId
824 doctype.systemId = systemId
825 return doctype
827 # internal
828 def _createDocument(self):
829 return Document()
831 class Document(Node):
832 nodeType = Node.DOCUMENT_NODE
833 nodeName = "#document"
834 nodeValue = None
835 attributes = None
836 doctype = None
837 parentNode = None
838 previousSibling = nextSibling = None
840 implementation = DOMImplementation()
841 childNodeTypes = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
842 Node.COMMENT_NODE, Node.DOCUMENT_TYPE_NODE)
844 def appendChild(self, node):
845 if node.nodeType not in self.childNodeTypes:
846 raise HierarchyRequestErr, \
847 "%s cannot be child of %s" % (repr(node), repr(self))
848 if node.parentNode is not None:
849 node.parentNode.removeChild(node)
851 if node.nodeType == Node.ELEMENT_NODE \
852 and self._get_documentElement():
853 raise xml.dom.HierarchyRequestErr(
854 "two document elements disallowed")
855 return Node.appendChild(self, node)
857 def removeChild(self, oldChild):
858 self.childNodes.remove(oldChild)
859 oldChild.nextSibling = oldChild.previousSibling = None
860 oldChild.parentNode = None
861 if self.documentElement is oldChild:
862 self.documentElement = None
864 return oldChild
866 def _get_documentElement(self):
867 for node in self.childNodes:
868 if node.nodeType == Node.ELEMENT_NODE:
869 return node
871 try:
872 property
873 except NameError:
874 pass
875 else:
876 documentElement = property(_get_documentElement,
877 doc="Top-level element of this document.")
879 def unlink(self):
880 if self.doctype is not None:
881 self.doctype.unlink()
882 self.doctype = None
883 Node.unlink(self)
885 def createDocumentFragment(self):
886 d = DocumentFragment()
887 d.ownerDoc = self
888 return d
890 def createElement(self, tagName):
891 e = Element(tagName)
892 e.ownerDocument = self
893 return e
895 def createTextNode(self, data):
896 t = Text(data)
897 t.ownerDocument = self
898 return t
900 def createCDATASection(self, data):
901 c = CDATASection(data)
902 c.ownerDocument = self
903 return c
905 def createComment(self, data):
906 c = Comment(data)
907 c.ownerDocument = self
908 return c
910 def createProcessingInstruction(self, target, data):
911 p = ProcessingInstruction(target, data)
912 p.ownerDocument = self
913 return p
915 def createAttribute(self, qName):
916 a = Attr(qName)
917 a.ownerDocument = self
918 a.value = ""
919 return a
921 def createElementNS(self, namespaceURI, qualifiedName):
922 prefix, localName = _nssplit(qualifiedName)
923 e = Element(qualifiedName, namespaceURI, prefix, localName)
924 e.ownerDocument = self
925 return e
927 def createAttributeNS(self, namespaceURI, qualifiedName):
928 prefix, localName = _nssplit(qualifiedName)
929 a = Attr(qualifiedName, namespaceURI, localName, prefix)
930 a.ownerDocument = self
931 a.value = ""
932 return a
934 def getElementsByTagName(self, name):
935 return _getElementsByTagNameHelper(self, name, [])
937 def getElementsByTagNameNS(self, namespaceURI, localName):
938 return _getElementsByTagNameNSHelper(self, namespaceURI, localName, [])
940 def writexml(self, writer, indent="", addindent="", newl=""):
941 writer.write('<?xml version="1.0" ?>\n')
942 for node in self.childNodes:
943 node.writexml(writer, indent, addindent, newl)
945 def _get_StringIO():
946 # we can't use cStringIO since it doesn't support Unicode strings
947 from StringIO import StringIO
948 return StringIO()
950 def _doparse(func, args, kwargs):
951 events = apply(func, args, kwargs)
952 toktype, rootNode = events.getEvent()
953 events.expandNode(rootNode)
954 events.clear()
955 return rootNode
957 def parse(*args, **kwargs):
958 """Parse a file into a DOM by filename or file object."""
959 from xml.dom import pulldom
960 return _doparse(pulldom.parse, args, kwargs)
962 def parseString(*args, **kwargs):
963 """Parse a file into a DOM from a string."""
964 from xml.dom import pulldom
965 return _doparse(pulldom.parseString, args, kwargs)
967 def getDOMImplementation():
968 return Document.implementation