1 from xml
.dom
import Node
, XMLNS_NAMESPACE
3 XMLNS_INTERFACE
= "http://zero-install.sourceforge.net/2004/injector/interface"
4 XMLNS_COMPILE
= "http://zero-install.sourceforge.net/2006/namespaces/0compile"
7 """Return all the text directly inside this DOM Node."""
8 return ''.join([text
.nodeValue
for text
in node
.childNodes
9 if text
.nodeType
== Node
.TEXT_NODE
])
11 def set_data(elem
, value
):
12 """Replace all children of 'elem' with a single text node containing 'value'"""
13 for node
in elem
.childNodes
:
14 elem
.removeChild(node
)
16 text
= elem
.ownerDocument
.createTextNode(value
)
17 elem
.appendChild(text
)
20 """If x's previous sibling is whitespace, return its length. Otherwise, return 0."""
21 indent
= x
.previousSibling
22 if indent
and indent
.nodeType
== Node
.TEXT_NODE
:
23 spaces
= indent
.nodeValue
.split('\n')[-1]
24 if spaces
.strip() == '':
28 def insert_before(new
, next
):
29 indent_depth
= indent_of(next
)
30 new_parent
= next
.parentNode
32 prev
= next
.previousSibling
33 if prev
and prev
.nodeType
== Node
.TEXT_NODE
:
36 new_parent
.insertBefore(new
, next
)
38 text
= new_parent
.ownerDocument
.createTextNode('\n' + (' ' * indent_depth
))
39 new_parent
.insertBefore(text
, new
)
41 def insert_after(new
, prev
):
42 indent_depth
= indent_of(prev
)
43 new_parent
= prev
.parentNode
46 new_parent
.insertBefore(new
, prev
.nextSibling
)
48 new_parent
.appendChild(new
, new_parent
)
51 text
= new_parent
.ownerDocument
.createTextNode('\n' + (' ' * indent_depth
))
52 new_parent
.insertBefore(text
, new
)
54 def insert_element(new
, parent
, before
= []):
55 indent
= indent_of(parent
) + 2 # Default indent
57 for x
in parent
.childNodes
:
58 if x
.nodeType
== Node
.ELEMENT_NODE
:
59 indent
= indent
or indent_of(x
)
60 if x
.localName
in before
:
61 parent
.insertBefore(new
, last_element
.nextSibling
)
66 parent
.insertBefore(new
, last_element
.nextSibling
)
68 had_children
= bool(list(child_elements(parent
)))
69 parent
.appendChild(new
)
71 final_indent
= '\n' + (' ' * indent_of(parent
))
72 parent
.appendChild(parent
.ownerDocument
.createTextNode(final_indent
))
74 indent_text
= parent
.ownerDocument
.createTextNode('\n' + (' ' * indent
))
75 parent
.insertBefore(indent_text
, new
)
77 def create_element(parent
, name
, uri
= XMLNS_INTERFACE
, before
= []):
78 """Create a new child element with the given name.
79 Add it as far down the list of children as possible, but before
80 any element in the 'before' set. Indent it sensibly."""
81 new
= parent
.ownerDocument
.createElementNS(uri
, name
)
82 insert_element(new
, parent
, before
)
85 def remove_element(elem
):
86 """Remove 'elem' and any whitespace before it."""
87 parent
= elem
.parentNode
88 prev
= elem
.previousSibling
89 if prev
and prev
.nodeType
== Node
.TEXT_NODE
:
90 if prev
.nodeValue
.strip() == '':
91 parent
.removeChild(prev
)
92 parent
.removeChild(elem
)
95 for x
in parent
.childNodes
:
96 if x
.nodeType
!= Node
.TEXT_NODE
: return
97 if x
.nodeValue
.strip(): return
100 # Nothing but white-space left
102 parent
.removeChild(w
)
104 def format_para(para
):
105 """Turn new-lines into spaces, removing any blank lines."""
106 lines
= [l
.strip() for l
in para
.split('\n')]
107 return ' '.join(filter(None, lines
))
109 def attrs_match(elem
, attrs
):
111 if not elem
.hasAttribute(x
): return False
112 if elem
.getAttribute(x
) != attrs
[x
]: return False
115 def child_elements(parent
):
116 """Yield all direct child elements."""
117 for x
in parent
.childNodes
:
118 if x
.nodeType
== Node
.ELEMENT_NODE
:
121 def children(parent
, localName
, uri
= XMLNS_INTERFACE
, attrs
= {}):
122 """Yield all direct child elements with this name and attributes."""
123 for x
in parent
.childNodes
:
124 if x
.nodeType
== Node
.ELEMENT_NODE
:
125 if x
.nodeName
== localName
and x
.namespaceURI
== uri
and attrs_match(x
, attrs
):
128 def singleton_text(parent
, localName
, uri
= XMLNS_INTERFACE
):
129 """Return the text of the first child element with this name, or None
130 if there aren't any."""
131 elements
= list(children(parent
, localName
, uri
))
133 return data(elements
[0])
135 def set_or_remove(element
, attr_name
, value
):
137 element
.setAttribute(attr_name
, value
)
138 elif element
.hasAttribute(attr_name
):
139 element
.removeAttribute(attr_name
)
141 namespace_prefixes
= {} # Namespace -> prefix
143 def register_namespace(namespace
, prefix
= None):
144 """Return the prefix to use for a namespace.
145 If none is registered, create a new one based on the suggested prefix.
146 @param namespace: namespace to register / query
147 @param prefix: suggested prefix
148 @return: the actual prefix
150 if namespace
== XMLNS_INTERFACE
:
152 existing_prefix
= namespace_prefixes
.get(namespace
, None)
154 return existing_prefix
159 # Find a variation on 'prefix' that isn't used yet, if necessary
162 while prefix
in namespace_prefixes
.values():
163 #print "Prefix %s already in %s, not %s" % (prefix, namespace_prefixes, namespace)
165 prefix
= orig_prefix
+ str(n
)
166 namespace_prefixes
[namespace
] = prefix
170 def add_attribute_ns(element
, uri
, name
, value
):
171 """Set an attribute, giving it the correct prefix or namespace declarations needed."""
173 element
.setAttributeNS(None, name
, value
)
175 prefix
= register_namespace(uri
)
176 element
.setAttributeNS(uri
, '%s:%s' % (prefix
, name
), value
)
177 element
.ownerDocument
.documentElement
.setAttributeNS(XMLNS_NAMESPACE
, 'xmlns:' + prefix
, uri
)
179 def import_node(target_doc
, source_node
):
180 """Import a node for a new document, fixing up namespace prefixes as we go."""
181 target_root
= target_doc
.documentElement
183 new_node
= target_doc
.importNode(source_node
, True)
185 elem
.prefix
= register_namespace(elem
.namespaceURI
, elem
.prefix
)
187 elem
.tagName
= elem
.nodeName
= '%s:%s' % (elem
.prefix
, elem
.localName
)
188 target_root
.setAttributeNS(XMLNS_NAMESPACE
, 'xmlns:' + elem
.prefix
, elem
.namespaceURI
)
190 for (uri
, name
), value
in list(elem
.attributes
.itemsNS()):
191 if uri
== XMLNS_NAMESPACE
:
192 elem
.removeAttributeNS(uri
, name
)
194 new_prefix
= register_namespace(uri
)
195 target_root
.setAttributeNS(XMLNS_NAMESPACE
, 'xmlns:' + new_prefix
, uri
)
196 elem
.removeAttributeNS(uri
, name
)
197 elem
.setAttributeNS(uri
, '%s:%s' % (new_prefix
, name
), value
)
199 for child
in elem
.childNodes
:
200 if child
.nodeType
== Node
.ELEMENT_NODE
: