Editing requires's URI and version fields works.
[0publish-gui.git] / xmltools.py
blobc5aed9d1f29728938b2c8a5c4e072420d29714b6
1 from xml.dom import Node, minidom
3 XMLNS_INTERFACE = "http://zero-install.sourceforge.net/2004/injector/interface"
5 def data(node):
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)
14 if value:
15 text = elem.ownerDocument.createTextNode(value)
16 elem.appendChild(text)
18 def indent_of(x):
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() == '':
24 return len(spaces)
25 return 0
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:
33 next = prev
35 new_parent.insertBefore(new, next)
36 if indent_depth:
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
42 last_element = None
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)
48 break
49 last_element = x
50 else:
51 if last_element:
52 parent.insertBefore(new, last_element.nextSibling)
53 else:
54 had_children = bool(list(child_elements(parent)))
55 parent.appendChild(new)
56 if not had_children:
57 final_indent = '\n' + (' ' * indent_of(parent))
58 parent.appendChild(parent.ownerDocument.createTextNode(final_indent))
59 if 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)
69 return new
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):
86 for x in attrs:
87 if not elem.hasAttribute(x): return False
88 if elem.getAttribute(x) != attrs[x]: return False
89 return True
91 def child_elements(parent):
92 """Yield all direct child elements."""
93 for x in parent.childNodes:
94 if x.nodeType == Node.ELEMENT_NODE:
95 yield x
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):
102 yield x
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))
108 if elements:
109 return data(elements[0])
111 def set_or_remove(element, attr_name, value):
112 if value:
113 element.setAttribute(attr_name, value)
114 elif element.hasAttribute(attr_name):
115 element.removeAttribute(attr_name)