Load feed URL correctly.
[0publish-gui.git] / xmltools.py
blob72248964a4f776dc2c7cc4c0ba7f07d5c62577ea
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 whitespace = []
81 for x in parent.childNodes:
82 if x.nodeType != Node.TEXT_NODE: return
83 if x.nodeValue.strip(): return
84 whitespace.append(x)
86 # Nothing but white-space left
87 for w in whitespace:
88 parent.removeChild(w)
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):
96 for x in attrs:
97 if not elem.hasAttribute(x): return False
98 if elem.getAttribute(x) != attrs[x]: return False
99 return True
101 def child_elements(parent):
102 """Yield all direct child elements."""
103 for x in parent.childNodes:
104 if x.nodeType == Node.ELEMENT_NODE:
105 yield x
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):
112 yield x
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))
118 if elements:
119 return data(elements[0])
121 def set_or_remove(element, attr_name, value):
122 if value:
123 element.setAttribute(attr_name, value)
124 elif element.hasAttribute(attr_name):
125 element.removeAttribute(attr_name)