snail/tree.py

196 lines
5.4 KiB
Python

# encoding: utf-8
"""
https://dom.spec.whatwg.org/
Other URL:
- https://fr.wikipedia.org/wiki/Document_Object_Model
"""
class Node:
"""
An object that participates in a tree has a parent, which is either null or an
object, and has children, which is an ordered set of objects. An object A whose
parent is object B is a child of B.
"""
def __init__(self, parent=None):
self.parent = parent
self.children = []
self.order = 0
def add_child(self, child):
"""
Adding a child to Node after lasts known child.
"""
# Compute new order for modified tree
if len(self.children) == 0:
order = self.order + 1
else:
for node in self.nodes:
order = node.order + 1
# Add child to node
self.children.append(child)
child.parent = self
# Reorder tree
self.root.reorder(order)
child.order = order
@property
def root(self):
"""
The root of an object is itself, if its parent is null, or else it is the root
of its parent. The root of a tree is any object participating in that tree whose
parent is null.
"""
if self.parent is None:
return self
else:
return self.parent.root
@property
def first_child(self):
"""
The first child of an object is its first child or null if it has no children.
"""
try:
return self.children[0]
except IndexError:
return None
@property
def last_child(self):
"""
The last child of an object is its last child or null if it has no children.
"""
try:
return self.children[-1]
except IndexError:
return None
@property
def index(self):
"""
The index of an object is its number of preceding siblings, or 0 if it has none.
"""
try:
return self.parent.children.index(self)
except AttributeError:
return None
@property
def previous_sibling(self):
"""
The previous sibling of an object is its first preceding sibling or null if it
has no preceding sibling.
"""
if self.index == 0:
return None
return self.parent.children[self.index - 1]
@property
def next_sibling(self):
"""
The next sibling of an object is its first following sibling or null if it has
no following sibling.
"""
if self.parent.last_child == self:
return None
return self.parent.children[self.index + 1]
@property
def nodes(self):
"""
List Node and all of its nodes through depth-first traversal
See: https://en.wikipedia.org/wiki/Depth-first_search
"""
yield self
for child in self.children:
yield from child.nodes
def reorder(self, value):
"""
Increment all nodes with order equal or greater than value
"""
for node in self.root.nodes:
if node.order >= value:
node.order += 1
def is_descendant(self, node):
"""
An object A is called a descendant of an object B, if either A is a child of B
or A is a child of an object C that is a descendant of B.
"""
if (
self.parent == node
or self.parent is not None and self.parent.is_descendant(node)
):
return True
return False
def is_inclusive_descendant(self, node):
"""
An inclusive descendant is an object or one of its descendants.
"""
if node is self:
return True
return self.is_descendant(node)
def is_ancestor(self, node):
"""
An object A is called an ancestor of an object B if and only if B is a
descendant of A.
"""
return node.is_descendant(self)
def is_inclusive_ancestor(self, node):
"""
An inclusive ancestor is an object or one of its ancestors.
"""
if node is self:
return True
return self.is_ancestor(node)
def is_sibling(self, node):
"""
An object A is called a sibling of an object B, if and only if B and A share the
same non-null parent.
"""
if self.parent is not None and self.parent == node.parent:
return True
return False
def is_inclusive_sibling(self, node):
"""
An inclusive sibling is an object or one of its siblings.
"""
if node is self:
return True
return self.is_sibling(node)
def is_preceding(self, node):
"""
An object A is preceding an object B if A and B are in the same tree and A comes
before B in tree order.
"""
if self.root == node.root:
return True
return False
def is_following(self, node):
"""
An object A is following an object B if A and B are in the same tree and A comes
after B in tree order.
"""
if self.root == node.root:
return True
return False
class Tree:
"""
A tree is a finite hierarchical tree structure. In tree order is preorder,
depth-first traversal of a tree.
"""
def __init__(self):
self.root = Node()