aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/util/tree.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/util/tree.rs')
-rw-r--r--src/components/util/tree.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/components/util/tree.rs b/src/components/util/tree.rs
new file mode 100644
index 00000000000..23b8ca80d77
--- /dev/null
+++ b/src/components/util/tree.rs
@@ -0,0 +1,173 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Helper functions for garbage collected doubly-linked trees.
+
+/// The basic trait. This function is meant to encapsulate a clonable reference to a tree node.
+pub trait TreeNodeRef<N> : Clone {
+ /// Borrows this node as immutable.
+ fn with_base<R>(&self, callback: &fn(&N) -> R) -> R;
+
+ /// Borrows this node as mutable.
+ fn with_mut_base<R>(&self, callback: &fn(&mut N) -> R) -> R;
+}
+
+/// The contents of a tree node.
+pub trait TreeNode<NR> {
+ /// Returns the parent of this node.
+ fn parent_node(&self) -> Option<NR>;
+
+ /// Returns the first child of this node.
+ fn first_child(&self) -> Option<NR>;
+
+ /// Returns the last child of this node.
+ fn last_child(&self) -> Option<NR>;
+
+ /// Returns the previous sibling of this node.
+ fn prev_sibling(&self) -> Option<NR>;
+
+ /// Returns the next sibling of this node.
+ fn next_sibling(&self) -> Option<NR>;
+
+ /// Sets the parent of this node.
+ fn set_parent_node(&mut self, new_parent: Option<NR>);
+
+ /// Sets the first child of this node.
+ fn set_first_child(&mut self, new_first_child: Option<NR>);
+
+ /// Sets the last child of this node.
+ fn set_last_child(&mut self, new_last_child: Option<NR>);
+
+ /// Sets the previous sibling of this node.
+ fn set_prev_sibling(&mut self, new_prev_sibling: Option<NR>);
+
+ /// Sets the next sibling of this node.
+ fn set_next_sibling(&mut self, new_next_sibling: Option<NR>);
+}
+
+/// A set of helper functions useful for operating on trees.
+pub trait TreeUtils {
+ /// Returns true if this node is disconnected from the tree or has no children.
+ fn is_leaf(&self) -> bool;
+
+ /// Adds a new child to the end of this node's list of children.
+ ///
+ /// Fails unless `new_child` is disconnected from the tree.
+ fn add_child(&self, new_child: Self);
+
+ /// Removes the given child from this node's list of children.
+ ///
+ /// Fails unless `child` is a child of this node. (FIXME: This is not yet checked.)
+ fn remove_child(&self, child: Self);
+
+ /// Iterates over all children of this node.
+ fn each_child(&self, callback: &fn(Self) -> bool) -> bool;
+
+ /// Iterates over this node and all its descendants, in preorder.
+ fn traverse_preorder(&self, callback: &fn(Self) -> bool) -> bool;
+
+ /// Iterates over this node and all its descendants, in postorder.
+ fn traverse_postorder(&self, callback: &fn(Self) -> bool) -> bool;
+}
+
+impl<NR:TreeNodeRef<N>,N:TreeNode<NR>> TreeUtils for NR {
+ fn is_leaf(&self) -> bool {
+ do self.with_base |this_node| {
+ this_node.first_child().is_none()
+ }
+ }
+
+ fn add_child(&self, new_child: NR) {
+ do self.with_mut_base |this_node| {
+ do new_child.with_mut_base |new_child_node| {
+ assert!(new_child_node.parent_node().is_none());
+ assert!(new_child_node.prev_sibling().is_none());
+ assert!(new_child_node.next_sibling().is_none());
+
+ match this_node.last_child() {
+ None => this_node.set_first_child(Some(new_child.clone())),
+ Some(last_child) => {
+ do last_child.with_mut_base |last_child_node| {
+ assert!(last_child_node.next_sibling().is_none());
+ last_child_node.set_next_sibling(Some(new_child.clone()));
+ new_child_node.set_prev_sibling(Some(last_child.clone()));
+ }
+ }
+ }
+
+ this_node.set_last_child(Some(new_child.clone()));
+ new_child_node.set_parent_node(Some((*self).clone()));
+ }
+ }
+ }
+
+ fn remove_child(&self, child: NR) {
+ do self.with_mut_base |this_node| {
+ do child.with_mut_base |child_node| {
+ assert!(child_node.parent_node().is_some());
+
+ match child_node.prev_sibling() {
+ None => this_node.set_first_child(child_node.next_sibling()),
+ Some(prev_sibling) => {
+ do prev_sibling.with_mut_base |prev_sibling_node| {
+ prev_sibling_node.set_next_sibling(child_node.next_sibling());
+ }
+ }
+ }
+
+ match child_node.next_sibling() {
+ None => this_node.set_last_child(child_node.prev_sibling()),
+ Some(next_sibling) => {
+ do next_sibling.with_mut_base |next_sibling_node| {
+ next_sibling_node.set_prev_sibling(child_node.prev_sibling());
+ }
+ }
+ }
+
+ child_node.set_prev_sibling(None);
+ child_node.set_next_sibling(None);
+ child_node.set_parent_node(None);
+ }
+ }
+ }
+
+ fn each_child(&self, callback: &fn(NR) -> bool) -> bool {
+ let mut maybe_current = self.with_base(|n| n.first_child());
+ while !maybe_current.is_none() {
+ let current = maybe_current.get_ref().clone();
+ if !callback(current.clone()) {
+ break;
+ }
+
+ maybe_current = current.with_base(|n| n.next_sibling());
+ }
+
+ true
+ }
+
+ fn traverse_preorder(&self, callback: &fn(NR) -> bool) -> bool {
+ if !callback((*self).clone()) {
+ return false;
+ }
+
+ for self.each_child |kid| {
+ if !kid.traverse_preorder(callback) {
+ return false;
+ }
+ }
+
+ true
+ }
+
+ fn traverse_postorder(&self, callback: &fn(NR) -> bool) -> bool {
+ for self.each_child |kid| {
+ if !kid.traverse_postorder(callback) {
+ return false;
+ }
+ }
+
+ callback((*self).clone())
+ }
+}
+