1 from xml
.dom
import Node
, minidom
3 XMLNS_INTERFACE
= "http://zero-install.sourceforge.net/2004/injector/interface"
6 """Return all the text directly inside this DOM Node."""
7 return ''.join([text
.nodeValue
for text
in node
.childNodes
8 if text
.nodeType
== Node
.TEXT_NODE
])
10 def set_data(elem
, value
):
11 """Replace all children of 'elem' with a single text node containing 'value'"""
12 for node
in elem
.childNodes
:
13 elem
.removeChild(node
)
15 text
= elem
.ownerDocument
.createTextNode(value
)
16 elem
.appendChild(text
)
19 """If x's previous sibling is whitespace, return its length. Otherwise, return 0."""
20 indent
= x
.previousSibling
21 if indent
and indent
.nodeType
== Node
.TEXT_NODE
:
22 spaces
= indent
.nodeValue
.split('\n')[-1]
23 if spaces
.strip() == '':
27 def insert_before(new
, next
):
28 indent_depth
= indent_of(next
)
29 new_parent
= next
.parentNode
31 prev
= next
.previousSibling
32 if prev
and prev
.nodeType
== Node
.TEXT_NODE
:
35 new_parent
.insertBefore(new
, next
)
37 text
= new_parent
.ownerDocument
.createTextNode('\n' + (' ' * indent_depth
))
38 new_parent
.insertBefore(text
, new
)
40 def insert_element(new
, parent
, before
= []):
41 indent
= indent_of(parent
) + 2 # Default indent
43 for x
in parent
.childNodes
:
44 if x
.nodeType
== Node
.ELEMENT_NODE
:
45 indent
= indent
or indent_of(x
)
46 if x
.localName
in before
:
47 parent
.insertBefore(new
, last_element
.nextSibling
)
52 parent
.insertBefore(new
, last_element
.nextSibling
)
54 had_children
= bool(list(child_elements(parent
)))
55 parent
.appendChild(new
)
57 final_indent
= '\n' + (' ' * indent_of(parent
))
58 parent
.appendChild(parent
.ownerDocument
.createTextNode(final_indent
))
60 indent_text
= parent
.ownerDocument
.createTextNode('\n' + (' ' * indent
))
61 parent
.insertBefore(indent_text
, new
)
63 def create_element(parent
, name
, uri
= XMLNS_INTERFACE
, before
= []):
64 """Create a new child element with the given name.
65 Add it as far down the list of children as possible, but before
66 any element in the 'before' set. Indent it sensibly."""
67 new
= parent
.ownerDocument
.createElementNS(uri
, name
)
68 insert_element(new
, parent
, before
)
71 def remove_element(elem
):
72 """Remove 'elem' and any whitespace before it."""
73 parent
= elem
.parentNode
74 prev
= elem
.previousSibling
75 if prev
and prev
.nodeType
== Node
.TEXT_NODE
:
76 if prev
.nodeValue
.strip() == '':
77 parent
.removeChild(prev
)
78 parent
.removeChild(elem
)
81 for x
in parent
.childNodes
:
82 if x
.nodeType
!= Node
.TEXT_NODE
: return
83 if x
.nodeValue
.strip(): return
86 # Nothing but white-space left
90 def format_para(para
):
91 """Turn new-lines into spaces, removing any blank lines."""
92 lines
= [l
.strip() for l
in para
.split('\n')]
93 return ' '.join(filter(None, lines
))
95 def attrs_match(elem
, attrs
):
97 if not elem
.hasAttribute(x
): return False
98 if elem
.getAttribute(x
) != attrs
[x
]: return False
101 def child_elements(parent
):
102 """Yield all direct child elements."""
103 for x
in parent
.childNodes
:
104 if x
.nodeType
== Node
.ELEMENT_NODE
:
107 def children(parent
, localName
, uri
= XMLNS_INTERFACE
, attrs
= {}):
108 """Yield all direct child elements with this name and attributes."""
109 for x
in parent
.childNodes
:
110 if x
.nodeType
== Node
.ELEMENT_NODE
:
111 if x
.nodeName
== localName
and x
.namespaceURI
== uri
and attrs_match(x
, attrs
):
114 def singleton_text(parent
, localName
, uri
= XMLNS_INTERFACE
):
115 """Return the text of the first child element with this name, or None
116 if there aren't any."""
117 elements
= list(children(parent
, localName
, uri
))
119 return data(elements
[0])
121 def set_or_remove(element
, attr_name
, value
):
123 element
.setAttribute(attr_name
, value
)
124 elif element
.hasAttribute(attr_name
):
125 element
.removeAttribute(attr_name
)