Change element.Element.text to be list, rather than str with per-element tail
authorNick Downing <nick@ndcode.org>
Tue, 29 Jan 2019 02:37:38 +0000 (13:37 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 29 Jan 2019 02:37:38 +0000 (13:37 +1100)
skel/element.py

index 6863164..1711d7d 100644 (file)
@@ -160,13 +160,13 @@ class Element:
     self.tag = tag
     self.tail = None
     self.visited = None # (element, ref, seen)
-    set_text(self, 0, text)
+    self.text = [text]
     self.children = children.copy()
+    self.text.extend(['' for i in range(len(children))]) # bogus
   def serialize(self, ref_list):
     element = self.visited[0]
-    text = get_text(self, 0)
-    if len(text):
-      element.text = text
+    if len(self.text[0]):
+      element.text = self.text[0]
     for i in range(len(self)):
       child = self[i]
       if child.visited is None:
@@ -177,9 +177,8 @@ class Element:
         child_element, child_ref, child_seen = child.visited
         assert not child_seen
         child.visited = (child_element, child_ref, True)
-      tail = get_text(self, i + 1)
-      if len(tail):
-        child_element.tail = tail
+      if len(self.text[i + 1]):
+        child_element.tail = self.text[i + 1]
       element.append(child_element)
     return element
   def deserialize(self, element, ref_list):
@@ -187,7 +186,6 @@ class Element:
   def copy(self, factory = None):
     result = (Element if factory is None else factory)(self.tag, self.attrib)
     result.text = self.text
-    result.tail = self.tail
     result[:] = [i.copy() for i in self]
     return result
 
@@ -251,47 +249,42 @@ def deserialize(fin, factory = Element, encoding = 'unicode'):
       ref_list[ref] = node
     children = [factory(i.tag) for i in element]
     node[:] = children
-    set_text(node, 0, '' if element.text is None else element.text)
-    for j in range(len(element)):
-      set_text(node, j + 1, '' if element[j].tail is None else element[j].tail)
+    node.text = (
+      ['' if element.text is None else element.text] +
+      ['' if j.tail is None else j.tail for j in element]
+    )
     todo.extend(zip(element, children))
     i += 1
   for element, node in todo:
     node.deserialize(element, ref_list)
   return ref_list[0]
 
-# compatibility scheme to access arbitrary xml.etree.ElementTree.Element-like
-# objects (not just Element defined above) using a more consistent interface:
 def get_text(root, i):
-  if i < 0:
-    i += len(root) + 1
-  text = root.text if i == 0 else root[i - 1].tail
-  return '' if text is None else text
+  assert len(root.text) == len(root) + 1
+  return root.text[i]
 
 def set_text(root, i, text):
-  if i < 0:
-    i += len(root) + 1
-  if len(text) == 0:
-    text = None
-  if i == 0:
-    root.text = text
-  else:
-    root[i - 1].tail = text
+  assert len(root.text) == len(root) + 1
+  root.text[i] = text
 
 def to_text(root):
+  assert len(root.text) == len(root) + 1
   return ''.join(
     [
       j
       for i in range(len(root))
-      for j in [get_text(root, i), to_text(root[i])]
+      for j in [root.text[i], to_text(root[i])]
     ] +
-    [get_text(root, len(root))]
+    [root.text[-1]]
   )
 
 def concatenate(children, factory = Element, *args, **kwargs):
   root = factory(*args, **kwargs)
   for child in children:
-    i = len(root)
-    set_text(root, i, get_text(root, i) + get_text(child, 0))
-    root[i:] = child[:]
+    assert len(root.text) == len(root) + 1
+    assert len(child.text) == len(child) + 1
+    root.text[-1] += child.text[0]
+    root.extend(child[:])
+    root.text.extend(child.text[1:])
+  assert len(root.text) == len(root) + 1
   return root