1 # Copyright (C) 2018 Nick Downing <nick@ndcode.org>
2 # SPDX-License-Identifier: GPL-2.0-only
4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; version 2.
8 # This program is distributed in the hope that it will be useful, but WITHOUT
9 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc., 51
15 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 import xml.etree.ElementTree
19 class Element(xml.etree.ElementTree._Element_Py):
20 def __init__(self, tag = 'Element', attrib = {}, text = '', children = []):
21 xml.etree.ElementTree._Element_Py.__init__(self, tag, attrib)
24 set_text(self, 0, text)
26 def serialize(self, ref_list):
28 # parented, enforce that child can only be parented at most once
29 # (although there can be unlimited numbers of numeric refs to it)
34 def deserialize(self, ref_list):
36 i.deserialize(ref_list)
37 def copy(self, factory = None):
38 result = (Element if factory is None else factory)(self.tag, self.attrib)
39 result.text = self.text
40 result.tail = self.tail
41 result[:] = [i.copy() for i in self]
43 def repr_serialize(self, params):
46 'children = [{0:s}]'.format(
47 ', '.join([repr(i) for i in self])
52 self.repr_serialize(params)
53 return 'element.Element({0:s})'.format(', '.join(params))
55 bool_to_str = ['false', 'true']
56 def serialize_bool(value):
57 return bool_to_str[int(value)]
59 str_to_bool = {'false': False, 'true': True}
60 def deserialize_bool(text):
61 return str_to_bool[text]
63 def serialize_int(value):
66 def deserialize_int(text):
69 def serialize_ref(value, ref_list):
76 ref_list.append(value)
78 value.set('ref', str(ref))
79 # this doesn't set the seen flag, so it will be parented by the
80 # root, unless it is already parented or gets parented later on
82 value.serialize(ref_list)
85 def deserialize_ref(text, ref_list):
87 return None if ref < 0 else ref_list[ref]
89 def serialize_str(value):
92 def deserialize_str(text):
95 def serialize(value, fout, encoding = 'unicode'):
97 serialize_ref(value, ref_list)
98 parents = [i for i in ref_list if not i.seen]
99 root = Element('root', children = parents)
100 for i in range(len(root)):
101 set_text(root, i, '\n ')
102 set_text(root, len(root), '\n')
104 xml.etree.ElementTree.ElementTree(root).write(fout, encoding)
111 while i < len(parents):
117 def deserialize(fin, factory = Element, encoding = 'unicode'):
118 root = xml.etree.ElementTree.parse(
120 xml.etree.ElementTree.XMLParser(
121 target = xml.etree.ElementTree.TreeBuilder(factory),
125 assert root.tag == 'root'
131 while i < len(parents):
133 if 'ref' in j.attrib:
134 ref = int(j.attrib['ref'])
136 if len(ref_list) < ref + 1:
137 ref_list.extend([None] * (ref + 1 - len(ref_list)))
142 i.deserialize(ref_list)
145 # compatibility scheme to access arbitrary xml.etree.ElementTree.Element-like
146 # objects (not just Element defined above) using a more consistent interface:
147 def get_text(root, i):
150 text = root.text if i == 0 else root[i - 1].tail
151 return '' if text is None else text
153 def set_text(root, i, text):
161 root[i - 1].tail = text
167 for i in range(len(root))
168 for j in [get_text(root, i), to_text(root[i])]
170 [get_text(root, len(root))]
173 def concatenate(children, factory = Element, *args, **kwargs):
174 root = factory(*args, **kwargs)
175 for child in children:
177 set_text(root, i, get_text(root, i) + get_text(child, 0))