]> git.sesse.net Git - xml-template/blob - python/xmltemplate.py
Add Python 3 support.
[xml-template] / python / xmltemplate.py
1 #! /usr/bin/python
2 import re
3 import sys
4 import xml.dom.minidom
5
6 if sys.version_info[0] > 2:
7     string = str
8 else:
9     string = basestring
10
11
12 def process_file(filename, obj, clean = True):
13         doc = xml.dom.minidom.parse(filename)
14         process(doc, obj, clean)
15         return doc
16
17 def process(node, obj, clean = True):
18         if isinstance(obj, string):                     # overwrite
19                 while not node.firstChild is None:
20                         node.removeChild(node.firstChild)
21                 doc = _get_document_element(node)
22                 node.appendChild(doc.createTextNode(obj))
23         elif isinstance(obj, xml.dom.Node):             # overwrite
24                 while not node.firstChild is None:
25                         node.removeChild(node.firstChild)
26
27                 if isinstance(obj, xml.dom.minidom.Document):
28                         obj = obj.documentElement
29
30                 newobj = obj.cloneNode(True)
31                 node.appendChild(newobj)
32
33                 process(newobj, {}, clean)
34         elif isinstance(obj, dict):                     # substitute
35                 for child in node.childNodes:
36                         processed = False
37
38                         if child.nodeType == xml.dom.Node.ELEMENT_NODE:
39                                 id = None
40
41                                 attrs = child.attributes
42                                 attrs_to_remove = []
43                                 if not attrs is None:
44                                         for i in range(attrs.length):
45                                                 attr = attrs.item(i)
46                                                 if attr.namespaceURI == "http://template.sesse.net/" and attr.localName == "id":
47                                                         id = attr.value
48                                                         if clean:
49                                                                 attrs_to_remove.append(attr.name)
50                                                 if attr.name.startswith("xmlns:") and attr.value == "http://template.sesse.net/" and clean:
51                                                         attrs_to_remove.append(attr.name)
52
53                                         for a in attrs_to_remove:
54                                                 if child.hasAttribute(a):
55                                                         child.removeAttribute(a)
56
57
58                                 # check all substitutions to see if we found anything
59                                 # appropriate
60                                 for key in obj.keys():
61                                         if key.startswith(child.tagName + "/"):
62                                                 child.setAttribute(key.split("/")[1], obj[key])
63                                         elif (not id is None) and key.startswith("#" + id + "/"):
64                                                 child.setAttribute(key.split("/")[1], obj[key])
65
66                                         if not processed:
67                                                 if key == child.localName or ((not id is None) and key == "#" + id):
68                                                         process(child, obj[key], clean)
69                                                         processed = True
70
71                         if not processed:
72                                 process(child, obj, clean)
73         elif hasattr(obj, '__iter__'):                  # repeat
74                 doc = _get_document_element(node)
75                 frag = doc.createElement("temporary-fragment")     # ugh
76
77                 while not node.firstChild is None:
78                         child = node.firstChild
79                         node.removeChild(child)
80                         frag.appendChild(child)
81
82                 for instance in obj:
83                         if instance is not None:
84                                 newnode = frag.cloneNode(True)
85                                 node.appendChild(newnode)
86                                 process(newnode, instance, clean)
87                                 if clean:
88                                         _clean(newnode)
89
90                 # remove all the <fragment> tags
91
92                 children_to_remove = []
93                 for child in node.childNodes:
94                         if isinstance(child, xml.dom.minidom.Element) and child.tagName == 'temporary-fragment':
95                                 while not child.firstChild is None:
96                                         child2 = child.firstChild
97                                         child.removeChild(child2)
98                                         node.appendChild(child2)
99                                 children_to_remove.append(child)
100
101                 for child in children_to_remove:
102                         node.removeChild(child)
103
104         if clean:
105                 _clean(node)
106
107 def alternate(tag, array, *elems):
108         i = 0
109         for ref in array:
110                 if ref is not None:
111                         ref[tag] = elems[i % len(elems)]
112                         i = i + 1
113                 
114         return array
115
116 def _clean(node):
117         if node.nodeType == xml.dom.Node.ELEMENT_NODE and node.namespaceURI == "http://template.sesse.net/":
118                 # as this is a dummy node, we want to remove it and move everything further up
119                 # after we've done any required replacements
120                 doc = _get_document_element(node)
121                 parent = node.parentNode
122
123                 while not node.firstChild is None:
124                         child = node.firstChild
125                         node.removeChild(child)
126                         parent.insertBefore(child, node)
127
128                 parent.removeChild(node)
129
130 # ugh
131 def _get_document_element(node):
132         if node.parentNode is None:
133                 return node
134         else:
135                 return _get_document_element(node.parentNode)