emmental/curds/tree.py

140 lines
4.0 KiB
Python

# Copyright 2019 (c) Anna Schumaker.
from . import notify
from . import sort
import threading
class ETree:
def __init__(self, name="", icon=""):
self.icon = icon
self.name = name
self.children = [ ]
self.child_ids = dict()
self.tree_lock = threading.Lock()
self.parent = None
self.sibling = None
self.__new_track__()
def __getstate__(self):
with self.tree_lock:
state = self.__dict__.copy()
del state["child_ids"]
del state["tree_lock"]
return state
def __insert__(self, index, child):
self.children.insert(index, child)
child.sibling = self.__nth_child__(index + 1)
child.parent = self
if (prev := self.__nth_child__(index - 1)) != None:
with prev.tree_lock:
prev.sibling = child
self.__map_child__(child)
if len(self.children) == 1:
notify.notify("first-child", self, self.get_path())
notify.notify("child-inserted", child, self.get_path() + [ index ])
return child
def __new_track__(self):
if self.new_track != ETree.new_track:
notify.register("new-track", self.new_track)
def __nth_child__(self, n):
return self.children[n] if -1 < n < len(self.children) else None
def __map_child__(self, child):
self.child_ids[id(child)] = child
self.child_ids.update(child.child_ids)
if self.parent != None:
with self.parent.tree_lock:
self.parent.__map_child__(child)
def __setstate__(self, state):
self.parent = None
self.child_ids = dict()
self.tree_lock = threading.Lock()
self.icon = state["icon"]
self.name = state["name"]
self.children = state["children"]
for child in self.children:
self.__map_child__(child)
self.parent = state["parent"]
self.sibling = state["sibling"]
self.__new_track__()
def alloc_child(self, name):
return ETree(name)
def child_index(self, child):
with self.tree_lock:
if child.parent == self:
return self.children.index(child)
return None
def get_markup(self):
return f"<big>{self.name}</big>"
def get_path(self):
if self.parent == None:
return [ ]
return self.parent.get_path() + [ self.parent.child_index(self) ]
def insert_child(self, child):
with self.tree_lock:
(index, node) = sort.bisect(self.children, sort.key(child.name))
if node == child:
return child
return self.__insert__(index, child)
def lookup(self, name):
with self.tree_lock:
(index, child) = sort.bisect(self.children, sort.key(name))
if child != None:
return child
return self.__insert__(index, self.alloc_child(name))
def lookup_byid(self, id):
return self.child_ids.get(id, None)
def lookup_path(self, path):
if path == None:
return None
if len(path) == 0:
return self
if (child := self.nth_child(path[0])) and len(path) > 1:
child = child.lookup_path(path[1:])
return child
def n_children(self):
with self.tree_lock:
return len(self.children)
def new_track(self, track):
pass
def next_child(self):
with self.tree_lock:
return self.sibling
def nth_child(self, n):
with self.tree_lock:
return self.__nth_child__(n)
def reset(self):
with self.tree_lock:
self.children.clear()
self.__new_track__()
def sort_key(self, name=None):
return sort.key(self.name)
def walk(self):
with self.tree_lock:
if self.parent != None:
yield self
for child in self.children:
yield from child.walk()