1 # Bugs Everywhere, a distributed bugtracker
2 # Copyright (C) 2008-2011 Chris Ball <cjb@laptop.org>
3 # Gianluca Montecchi <gian@grys.it>
4 # W. Trevor King <wking@drexel.edu>
6 # This file is part of Bugs Everywhere.
8 # Bugs Everywhere is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by the
10 # Free Software Foundation, either version 2 of the License, or (at your
11 # option) any later version.
13 # Bugs Everywhere is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Bugs Everywhere. If not, see <http://www.gnu.org/licenses/>.
21 """Define :class:`Tree`, a traversable tree structure.
25 if libbe.TESTING == True:
29 """A traversable tree structure.
42 >>> i = Tree(); i.n = "i"
43 >>> h = Tree([i]); h.n = "h"
44 >>> f = Tree([h]); f.n = "f"
45 >>> e = Tree(); e.n = "e"
46 >>> c = Tree([f,e]); c.n = "c"
47 >>> g = Tree(); g.n = "g"
48 >>> d = Tree([g]); d.n = "d"
49 >>> b = Tree([d]); b.n = "b"
50 >>> a = Tree(); a.n = "a"
54 Get the longest branch length with
59 Sort the tree recursively. Here we sort longest branch length
62 >>> a.sort(key=lambda node : -node.branch_len())
63 >>> "".join([node.n for node in a.traverse()])
66 And here we sort shortest branch length first.
68 >>> a.sort(key=lambda node : node.branch_len())
69 >>> "".join([node.n for node in a.traverse()])
72 We can also do breadth-first traverses.
74 >>> "".join([node.n for node in a.traverse(depth_first=False)])
77 Serialize the tree with depth marking branches.
79 >>> for depth,node in a.thread():
80 ... print "%*s" % (2*depth+1, node.n)
91 Flattening the thread disables depth increases except at
94 >>> for depth,node in a.thread(flatten=True):
95 ... print "%*s" % (2*depth+1, node.n)
106 We can also check if a node is contained in a tree.
108 >>> a.has_descendant(g)
110 >>> c.has_descendant(g)
112 >>> a.has_descendant(a)
114 >>> a.has_descendant(a, match_self=True)
117 def __cmp__(self, other):
118 return cmp(id(self), id(other))
120 def __eq__(self, other):
121 return self.__cmp__(other) == 0
123 def __ne__(self, other):
124 return self.__cmp__(other) != 0
126 def branch_len(self):
127 """Return the largest number of nodes from root to leaf (inclusive).
135 this method returns 5.
139 Exhaustive search every time == *slow*.
141 Use only on small trees, or reimplement by overriding
142 child-addition methods to allow accurate caching.
147 return 1 + max([child.branch_len() for child in self])
149 def sort(self, *args, **kwargs):
150 """Sort the tree recursively.
152 This method extends :meth:`list.sort` to Trees.
156 This method can be slow, e.g. on a :meth:`branch_len` sort,
157 since a node at depth `N` from the root has it's
158 :meth:`branch_len` method called `N` times.
160 list.sort(self, *args, **kwargs)
162 child.sort(*args, **kwargs)
164 def traverse(self, depth_first=True):
165 """Generate all the nodes in a tree, starting with the root node.
170 Depth first by default, but you can set `depth_first` to
171 `False` for breadth first ordering. Siblings are returned
172 in the order they are stored, so you might want to
173 :meth:`sort` your tree first.
175 if depth_first == True:
178 for descendant in child.traverse():
180 else: # breadth first, Wikipedia algorithm
181 # http://en.wikipedia.org/wiki/Breadth-first_search
183 while len(queue) > 0:
188 def thread(self, flatten=False):
189 """Generate a (depth, node) tuple for every node in the tree.
191 When `flatten` is `False`, the depth of any node is one
192 greater than the depth of its parent. That way the
193 inheritance is explicit, but you can end up with highly
196 When `flatten` is `True`, the depth of any node is only
197 greater than the depth of its parent when there is a branch,
198 and the node is not the last child. This can lead to ancestry
199 ambiguity, but keeps the total indentation down. For example::
205 would both produce (after sorting by :meth:`branch_len`)::
215 stack = [] # ancestry of the current node
219 for node in self.traverse(depth_first=True):
220 while len(stack) > 0 \
221 and id(node) not in [id(c) for c in stack[-1]]:
230 depth = depthDict[id(parent)]
231 if len(parent) > 1 and node != parent[-1]:
233 depthDict[id(node)] = depth
237 def has_descendant(self, descendant, depth_first=True, match_self=False):
238 """Check if a node is contained in a tree.
243 The potential descendant.
245 The search order. Set this if you feel depth/breadth would
250 x.has_descendant(x, match_self=True) -> True
252 if descendant == self:
254 for d in self.traverse(depth_first):
259 if libbe.TESTING == True:
260 suite = doctest.DocTestSuite()