annotate vendor/gems/rubytree-0.5.2/lib/tree.rb @ 1478:5ca1f4a47171 bibplugin_db_migrations

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