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
)
80 def format_para(para
):
81 """Turn new-lines into spaces, removing any blank lines."""
82 lines
= [l
.strip() for l
in para
.split('\n')]
83 return ' '.join(filter(None, lines
))
85 def attrs_match(elem
, attrs
):
87 if not elem
.hasAttribute(x
): return False
88 if elem
.getAttribute(x
) != attrs
[x
]: return False
91 def child_elements(parent
):
92 """Yield all direct child elements."""
93 for x
in parent
.childNodes
:
94 if x
.nodeType
== Node
.ELEMENT_NODE
:
97 def children(parent
, localName
, uri
= XMLNS_INTERFACE
, attrs
= {}):
98 """Yield all direct child elements with this name and attributes."""
99 for x
in parent
.childNodes
:
100 if x
.nodeType
== Node
.ELEMENT_NODE
:
101 if x
.nodeName
== localName
and x
.namespaceURI
== uri
and attrs_match(x
, attrs
):
104 def singleton_text(parent
, localName
, uri
= XMLNS_INTERFACE
):
105 """Return the text of the first child element with this name, or None
106 if there aren't any."""
107 elements
= list(children(parent
, localName
, uri
))
109 return data(elements
[0])
111 def set_or_remove(element
, attr_name
, value
):
113 element
.setAttribute(attr_name
, value
)
114 elif element
.hasAttribute(attr_name
):
115 element
.removeAttribute(attr_name
)