Mercurial > hg > soundsoftware-site
comparison vendor/gems/rubytree-0.5.2/lib/.svn/text-base/tree.rb.svn-base @ 0:513646585e45
* Import Redmine trunk SVN rev 3859
author | Chris Cannam |
---|---|
date | Fri, 23 Jul 2010 15:52:44 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:513646585e45 |
---|---|
1 # tree.rb | |
2 # | |
3 # $Revision: 1.29 $ by $Author: anupamsg $ | |
4 # $Name: $ | |
5 # | |
6 # = tree.rb - Generic Multi-way Tree implementation | |
7 # | |
8 # Provides a generic tree data structure with ability to | |
9 # store keyed node elements in the tree. The implementation | |
10 # mixes in the Enumerable module. | |
11 # | |
12 # Author:: Anupam Sengupta (anupamsg@gmail.com) | |
13 # | |
14 | |
15 # Copyright (c) 2006, 2007 Anupam Sengupta | |
16 # | |
17 # All rights reserved. | |
18 # | |
19 # Redistribution and use in source and binary forms, with or without modification, | |
20 # are permitted provided that the following conditions are met: | |
21 # | |
22 # - Redistributions of source code must retain the above copyright notice, this | |
23 # list of conditions and the following disclaimer. | |
24 # | |
25 # - Redistributions in binary form must reproduce the above copyright notice, this | |
26 # list of conditions and the following disclaimer in the documentation and/or | |
27 # other materials provided with the distribution. | |
28 # | |
29 # - Neither the name of the organization nor the names of its contributors may | |
30 # be used to endorse or promote products derived from this software without | |
31 # specific prior written permission. | |
32 # | |
33 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
34 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
35 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
36 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
37 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
38 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
39 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
40 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
41 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
42 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
43 # | |
44 | |
45 # This module provides a TreeNode class which is the primary class for all | |
46 # nodes represented in the Tree. | |
47 # This module mixes in the Enumerable module. | |
48 module Tree | |
49 | |
50 # Rubytree Package Version | |
51 VERSION = '0.5.2' | |
52 | |
53 # == TreeNode Class Description | |
54 # | |
55 # The node class for the tree representation. the nodes are named and have a | |
56 # place-holder for the node data (i.e., the `content' of the node). The node | |
57 # names are expected to be unique. In addition, the node provides navigation | |
58 # methods to traverse the tree. | |
59 # | |
60 # The nodes can have any number of child nodes attached to it. Note that while | |
61 # this implementation does not support directed graphs, the class itself makes | |
62 # no restrictions on associating a node's CONTENT with multiple parent nodes. | |
63 # | |
64 # | |
65 # == Example | |
66 # | |
67 # The following code-snippet implements this tree structure: | |
68 # | |
69 # +------------+ | |
70 # | ROOT | | |
71 # +-----+------+ | |
72 # +-------------+------------+ | |
73 # | | | |
74 # +-------+-------+ +-------+-------+ | |
75 # | CHILD 1 | | CHILD 2 | | |
76 # +-------+-------+ +---------------+ | |
77 # | | |
78 # | | |
79 # +-------+-------+ | |
80 # | GRANDCHILD 1 | | |
81 # +---------------+ | |
82 # | |
83 # require 'tree' | |
84 # | |
85 # myTreeRoot = Tree::TreeNode.new("ROOT", "Root Content") | |
86 # | |
87 # myTreeRoot << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content") | |
88 # | |
89 # myTreeRoot << Tree::TreeNode.new("CHILD2", "Child2 Content") | |
90 # | |
91 # myTreeRoot.printTree | |
92 # | |
93 # child1 = myTreeRoot["CHILD1"] | |
94 # | |
95 # grandChild1 = myTreeRoot["CHILD1"]["GRANDCHILD1"] | |
96 # | |
97 # siblingsOfChild1Array = child1.siblings | |
98 # | |
99 # immediateChildrenArray = myTreeRoot.children | |
100 # | |
101 # # Process all nodes | |
102 # | |
103 # myTreeRoot.each { |node| node.content.reverse } | |
104 # | |
105 # myTreeRoot.remove!(child1) # Remove the child | |
106 class TreeNode | |
107 include Enumerable | |
108 | |
109 attr_reader :content, :name, :parent | |
110 attr_writer :content | |
111 | |
112 # Constructor which expects the name of the node | |
113 # | |
114 # Name of the node is expected to be unique across the | |
115 # tree. | |
116 # | |
117 # The content can be of any type, and is defaulted to _nil_. | |
118 def initialize(name, content = nil) | |
119 raise "Node name HAS to be provided" if name == nil | |
120 @name = name | |
121 @content = content | |
122 self.setAsRoot! | |
123 | |
124 @childrenHash = Hash.new | |
125 @children = [] | |
126 end | |
127 | |
128 # Returns a copy of this node, with the parent and children links removed. | |
129 def detached_copy | |
130 Tree::TreeNode.new(@name, @content ? @content.clone : nil) | |
131 end | |
132 | |
133 # Print the string representation of this node. | |
134 def to_s | |
135 "Node Name: #{@name}" + | |
136 " Content: " + (@content || "<Empty>") + | |
137 " Parent: " + (isRoot?() ? "<None>" : @parent.name) + | |
138 " Children: #{@children.length}" + | |
139 " Total Nodes: #{size()}" | |
140 end | |
141 | |
142 # Returns an array of ancestors in reversed order (the first element is the | |
143 # immediate parent). Returns nil if this is a root node. | |
144 def parentage | |
145 return nil if isRoot? | |
146 | |
147 parentageArray = [] | |
148 prevParent = self.parent | |
149 while (prevParent) | |
150 parentageArray << prevParent | |
151 prevParent = prevParent.parent | |
152 end | |
153 | |
154 parentageArray | |
155 end | |
156 | |
157 # Protected method to set the parent node. | |
158 # This method should NOT be invoked by client code. | |
159 def parent=(parent) | |
160 @parent = parent | |
161 end | |
162 | |
163 # Convenience synonym for TreeNode#add method. This method allows a convenient | |
164 # method to add children hierarchies in the tree. | |
165 # | |
166 # E.g. root << child << grand_child | |
167 def <<(child) | |
168 add(child) | |
169 end | |
170 | |
171 # Adds the specified child node to the receiver node. The child node's | |
172 # parent is set to be the receiver. The child is added as the last child in | |
173 # the current list of children for the receiver node. | |
174 def add(child) | |
175 raise "Child already added" if @childrenHash.has_key?(child.name) | |
176 | |
177 @childrenHash[child.name] = child | |
178 @children << child | |
179 child.parent = self | |
180 return child | |
181 | |
182 end | |
183 | |
184 # Removes the specified child node from the receiver node. The removed | |
185 # children nodes are orphaned but available if an alternate reference | |
186 # exists. | |
187 # | |
188 # Returns the child node. | |
189 def remove!(child) | |
190 @childrenHash.delete(child.name) | |
191 @children.delete(child) | |
192 child.setAsRoot! unless child == nil | |
193 return child | |
194 end | |
195 | |
196 # Removes this node from its parent. If this is the root node, then does | |
197 # nothing. | |
198 def removeFromParent! | |
199 @parent.remove!(self) unless isRoot? | |
200 end | |
201 | |
202 # Removes all children from the receiver node. | |
203 def removeAll! | |
204 for child in @children | |
205 child.setAsRoot! | |
206 end | |
207 @childrenHash.clear | |
208 @children.clear | |
209 self | |
210 end | |
211 | |
212 # Indicates whether this node has any associated content. | |
213 def hasContent? | |
214 @content != nil | |
215 end | |
216 | |
217 # Protected method which sets this node as a root node. | |
218 def setAsRoot! | |
219 @parent = nil | |
220 end | |
221 | |
222 # Indicates whether this node is a root node. Note that | |
223 # orphaned children will also be reported as root nodes. | |
224 def isRoot? | |
225 @parent == nil | |
226 end | |
227 | |
228 # Indicates whether this node has any immediate child nodes. | |
229 def hasChildren? | |
230 @children.length != 0 | |
231 end | |
232 | |
233 # Indicates whether this node is a 'leaf' - i.e., one without | |
234 # any children | |
235 def isLeaf? | |
236 !hasChildren? | |
237 end | |
238 | |
239 # Returns an array of all the immediate children. If a block is given, | |
240 # yields each child node to the block. | |
241 def children | |
242 if block_given? | |
243 @children.each {|child| yield child} | |
244 else | |
245 @children | |
246 end | |
247 end | |
248 | |
249 # Returns the first child of this node. Will return nil if no children are | |
250 # present. | |
251 def firstChild | |
252 children.first | |
253 end | |
254 | |
255 # Returns the last child of this node. Will return nil if no children are | |
256 # present. | |
257 def lastChild | |
258 children.last | |
259 end | |
260 | |
261 # Returns every node (including the receiver node) from the tree to the | |
262 # specified block. The traversal is depth first and from left to right in | |
263 # pre-ordered sequence. | |
264 def each &block | |
265 yield self | |
266 children { |child| child.each(&block) } | |
267 end | |
268 | |
269 # Traverses the tree in a pre-ordered sequence. This is equivalent to | |
270 # TreeNode#each | |
271 def preordered_each &block | |
272 each(&block) | |
273 end | |
274 | |
275 # Performs breadth first traversal of the tree rooted at this node. The | |
276 # traversal in a given level is from left to right. | |
277 def breadth_each &block | |
278 node_queue = [self] # Create a queue with self as the initial entry | |
279 | |
280 # Use a queue to do breadth traversal | |
281 until node_queue.empty? | |
282 node_to_traverse = node_queue.shift | |
283 yield node_to_traverse | |
284 # Enqueue the children from left to right. | |
285 node_to_traverse.children { |child| node_queue.push child } | |
286 end | |
287 end | |
288 | |
289 # Yields all leaf nodes from this node to the specified block. May yield | |
290 # this node as well if this is a leaf node. Leaf traversal depth first and | |
291 # left to right. | |
292 def each_leaf &block | |
293 self.each { |node| yield(node) if node.isLeaf? } | |
294 end | |
295 | |
296 # Returns the requested node from the set of immediate children. | |
297 # | |
298 # If the parameter is _numeric_, then the in-sequence array of children is | |
299 # accessed (see Tree#children). If the parameter is not _numeric_, then it | |
300 # is assumed to be the *name* of the child node to be returned. | |
301 def [](name_or_index) | |
302 raise "Name_or_index needs to be provided" if name_or_index == nil | |
303 | |
304 if name_or_index.kind_of?(Integer) | |
305 @children[name_or_index] | |
306 else | |
307 @childrenHash[name_or_index] | |
308 end | |
309 end | |
310 | |
311 # Returns the total number of nodes in this tree, rooted at the receiver | |
312 # node. | |
313 def size | |
314 @children.inject(1) {|sum, node| sum + node.size} | |
315 end | |
316 | |
317 # Convenience synonym for Tree#size | |
318 def length | |
319 size() | |
320 end | |
321 | |
322 # Pretty prints the tree starting with the receiver node. | |
323 def printTree(level = 0) | |
324 | |
325 if isRoot? | |
326 print "*" | |
327 else | |
328 print "|" unless parent.isLastSibling? | |
329 print(' ' * (level - 1) * 4) | |
330 print(isLastSibling? ? "+" : "|") | |
331 print "---" | |
332 print(hasChildren? ? "+" : ">") | |
333 end | |
334 | |
335 puts " #{name}" | |
336 | |
337 children { |child| child.printTree(level + 1)} | |
338 end | |
339 | |
340 # Returns the root for this tree. Root's root is itself. | |
341 def root | |
342 root = self | |
343 root = root.parent while !root.isRoot? | |
344 root | |
345 end | |
346 | |
347 # Returns the first sibling for this node. If this is the root node, returns | |
348 # itself. | |
349 def firstSibling | |
350 if isRoot? | |
351 self | |
352 else | |
353 parent.children.first | |
354 end | |
355 end | |
356 | |
357 # Returns true if this node is the first sibling. | |
358 def isFirstSibling? | |
359 firstSibling == self | |
360 end | |
361 | |
362 # Returns the last sibling for this node. If this node is the root, returns | |
363 # itself. | |
364 def lastSibling | |
365 if isRoot? | |
366 self | |
367 else | |
368 parent.children.last | |
369 end | |
370 end | |
371 | |
372 # Returns true if his node is the last sibling | |
373 def isLastSibling? | |
374 lastSibling == self | |
375 end | |
376 | |
377 # Returns an array of siblings for this node. | |
378 # If a block is provided, yields each of the sibling | |
379 # nodes to the block. The root always has nil siblings. | |
380 def siblings | |
381 return nil if isRoot? | |
382 if block_given? | |
383 for sibling in parent.children | |
384 yield sibling if sibling != self | |
385 end | |
386 else | |
387 siblings = [] | |
388 parent.children {|sibling| siblings << sibling if sibling != self} | |
389 siblings | |
390 end | |
391 end | |
392 | |
393 # Returns true if this node is the only child of its parent | |
394 def isOnlyChild? | |
395 parent.children.size == 1 | |
396 end | |
397 | |
398 # Returns the next sibling for this node. Will return nil if no subsequent | |
399 # node is present. | |
400 def nextSibling | |
401 if myidx = parent.children.index(self) | |
402 parent.children.at(myidx + 1) | |
403 end | |
404 end | |
405 | |
406 # Returns the previous sibling for this node. Will return nil if no | |
407 # subsequent node is present. | |
408 def previousSibling | |
409 if myidx = parent.children.index(self) | |
410 parent.children.at(myidx - 1) if myidx > 0 | |
411 end | |
412 end | |
413 | |
414 # Provides a comparision operation for the nodes. Comparision | |
415 # is based on the natural character-set ordering for the | |
416 # node names. | |
417 def <=>(other) | |
418 return +1 if other == nil | |
419 self.name <=> other.name | |
420 end | |
421 | |
422 # Freezes all nodes in the tree | |
423 def freezeTree! | |
424 each {|node| node.freeze} | |
425 end | |
426 | |
427 # Creates the marshal-dump represention of the tree rooted at this node. | |
428 def marshal_dump | |
429 self.collect { |node| node.createDumpRep } | |
430 end | |
431 | |
432 # Creates a dump representation and returns the same as a hash. | |
433 def createDumpRep | |
434 { :name => @name, :parent => (isRoot? ? nil : @parent.name), :content => Marshal.dump(@content)} | |
435 end | |
436 | |
437 # Loads a marshalled dump of the tree and returns the root node of the | |
438 # reconstructed tree. See the Marshal class for additional details. | |
439 def marshal_load(dumped_tree_array) | |
440 nodes = { } | |
441 for node_hash in dumped_tree_array do | |
442 name = node_hash[:name] | |
443 parent_name = node_hash[:parent] | |
444 content = Marshal.load(node_hash[:content]) | |
445 | |
446 if parent_name then | |
447 nodes[name] = current_node = Tree::TreeNode.new(name, content) | |
448 nodes[parent_name].add current_node | |
449 else | |
450 # This is the root node, hence initialize self. | |
451 initialize(name, content) | |
452 | |
453 nodes[name] = self # Add self to the list of nodes | |
454 end | |
455 end | |
456 end | |
457 | |
458 # Returns depth of the tree from this node. A single leaf node has a | |
459 # depth of 1. | |
460 def depth | |
461 return 1 if isLeaf? | |
462 1 + @children.collect { |child| child.depth }.max | |
463 end | |
464 | |
465 # Returns breadth of the tree at this node level. A single node has a | |
466 # breadth of 1. | |
467 def breadth | |
468 return 1 if isRoot? | |
469 parent.children.size | |
470 end | |
471 | |
472 protected :parent=, :setAsRoot!, :createDumpRep | |
473 | |
474 end | |
475 end | |
476 | |
477 # $Log: tree.rb,v $ | |
478 # Revision 1.29 2007/12/22 00:28:59 anupamsg | |
479 # Added more test cases, and enabled ZenTest compatibility. | |
480 # | |
481 # Revision 1.28 2007/12/20 03:19:33 anupamsg | |
482 # * README (Module): Modified the install instructions from source. | |
483 # (Module): Updated the minor version number. | |
484 # | |
485 # Revision 1.27 2007/12/20 03:00:03 anupamsg | |
486 # Minor code changes. Removed self_initialize from the protected methods' list. | |
487 # | |
488 # Revision 1.26 2007/12/20 02:50:04 anupamsg | |
489 # (Tree::TreeNode): Removed the spurious self_initialize from the protected list. | |
490 # | |
491 # Revision 1.25 2007/12/19 20:28:05 anupamsg | |
492 # Removed the unnecesary self_initialize method. | |
493 # | |
494 # Revision 1.24 2007/12/19 06:39:17 anupamsg | |
495 # Removed the unnecessary field and record separator constants. Also updated the | |
496 # history.txt file. | |
497 # | |
498 # Revision 1.23 2007/12/19 06:25:00 anupamsg | |
499 # (Tree::TreeNode): Minor fix to the comments. Also fixed the private/protected | |
500 # scope issue with the createDumpRep method. | |
501 # | |
502 # Revision 1.22 2007/12/19 06:22:03 anupamsg | |
503 # Updated the marshalling logic to correctly handle non-string content. This | |
504 # should fix the bug # 15614 ("When dumping with an Object as the content, you get | |
505 # a delimiter collision") | |
506 # | |
507 # Revision 1.21 2007/12/19 02:24:17 anupamsg | |
508 # Updated the marshalling logic to handle non-string contents on the nodes. | |
509 # | |
510 # Revision 1.20 2007/10/10 08:42:57 anupamsg | |
511 # Release 0.4.3 | |
512 # | |
513 # Revision 1.19 2007/08/31 01:16:27 anupamsg | |
514 # Added breadth and pre-order traversals for the tree. Also added a method | |
515 # to return the detached copy of a node from the tree. | |
516 # | |
517 # Revision 1.18 2007/07/21 05:14:44 anupamsg | |
518 # Added a VERSION constant to the Tree module, | |
519 # and using the same in the Rakefile. | |
520 # | |
521 # Revision 1.17 2007/07/21 03:24:25 anupamsg | |
522 # Minor edits to parameter names. User visible functionality does not change. | |
523 # | |
524 # Revision 1.16 2007/07/18 23:38:55 anupamsg | |
525 # Minor updates to tree.rb | |
526 # | |
527 # Revision 1.15 2007/07/18 22:11:50 anupamsg | |
528 # Added depth and breadth methods for the TreeNode. | |
529 # | |
530 # Revision 1.14 2007/07/18 19:33:27 anupamsg | |
531 # Added a new binary tree implementation. | |
532 # | |
533 # Revision 1.13 2007/07/18 07:17:34 anupamsg | |
534 # Fixed a issue where TreeNode.ancestors was shadowing Module.ancestors. This method | |
535 # has been renamed to TreeNode.parentage. | |
536 # | |
537 # Revision 1.12 2007/07/17 03:39:28 anupamsg | |
538 # Moved the CVS Log keyword to end of the files. | |
539 # |