diff vendor/gems/rubytree-0.5.2/lib/tree.rb @ 0:513646585e45

* Import Redmine trunk SVN rev 3859
author Chris Cannam
date Fri, 23 Jul 2010 15:52:44 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/gems/rubytree-0.5.2/lib/tree.rb	Fri Jul 23 15:52:44 2010 +0100
@@ -0,0 +1,539 @@
+# tree.rb
+#
+# $Revision: 1.29 $ by $Author: anupamsg $
+# $Name:  $
+#
+# = tree.rb - Generic Multi-way Tree implementation
+#
+# Provides a generic tree data structure with ability to
+# store keyed node elements in the tree. The implementation
+# mixes in the Enumerable module.
+#
+# Author:: Anupam Sengupta (anupamsg@gmail.com)
+#
+
+# Copyright (c) 2006, 2007 Anupam Sengupta
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# - Redistributions of source code must retain the above copyright notice, this
+#   list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright notice, this
+#   list of conditions and the following disclaimer in the documentation and/or
+#   other materials provided with the distribution.
+#
+# - Neither the name of the organization nor the names of its contributors may
+#   be used to endorse or promote products derived from this software without
+#   specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# This module provides a TreeNode class which is the primary class for all
+# nodes represented in the Tree.
+# This module mixes in the Enumerable module.
+module Tree
+
+  # Rubytree Package Version
+  VERSION = '0.5.2'
+
+  # == TreeNode Class Description
+  #
+  # The node class for the tree representation. the nodes are named and have a
+  # place-holder for the node data (i.e., the `content' of the node). The node
+  # names are expected to be unique.  In addition, the node provides navigation
+  # methods to traverse the tree.
+  #
+  # The nodes can have any number of child nodes attached to it. Note that while
+  # this implementation does not support directed graphs, the class itself makes
+  # no restrictions on associating a node's CONTENT with multiple parent nodes.
+  #
+  #
+  # == Example
+  #
+  #  The following code-snippet implements this tree structure:
+  #
+  #                    +------------+
+  #                    |    ROOT    |
+  #                    +-----+------+
+  #            +-------------+------------+
+  #            |                          |
+  #    +-------+-------+          +-------+-------+
+  #    |  CHILD 1      |          |  CHILD 2      |
+  #    +-------+-------+          +---------------+
+  #            |
+  #            |
+  #    +-------+-------+
+  #    | GRANDCHILD 1  |
+  #    +---------------+
+  #
+  # require 'tree'
+  #
+  # myTreeRoot = Tree::TreeNode.new("ROOT", "Root Content")
+  #
+  # myTreeRoot << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
+  #
+  # myTreeRoot << Tree::TreeNode.new("CHILD2", "Child2 Content")
+  #
+  # myTreeRoot.printTree
+  #
+  # child1 = myTreeRoot["CHILD1"]
+  #
+  # grandChild1 = myTreeRoot["CHILD1"]["GRANDCHILD1"]
+  #
+  # siblingsOfChild1Array = child1.siblings
+  #
+  # immediateChildrenArray = myTreeRoot.children
+  #
+  # # Process all nodes
+  #
+  # myTreeRoot.each { |node| node.content.reverse }
+  #
+  # myTreeRoot.remove!(child1) # Remove the child
+  class TreeNode
+    include Enumerable
+
+    attr_reader :content, :name, :parent
+    attr_writer :content
+
+    # Constructor which expects the name of the node
+    #
+    # Name of the node is expected to be unique across the
+    # tree.
+    #
+    # The content can be of any type, and is defaulted to _nil_.
+    def initialize(name, content = nil)
+      raise "Node name HAS to be provided" if name == nil
+      @name = name
+      @content = content
+      self.setAsRoot!
+
+      @childrenHash = Hash.new
+      @children = []
+    end
+
+    # Returns a copy of this node, with the parent and children links removed.
+    def detached_copy
+      Tree::TreeNode.new(@name, @content ? @content.clone : nil)
+    end
+
+    # Print the string representation of this node.
+    def to_s
+      "Node Name: #{@name}" +
+        " Content: " + (@content || "<Empty>") +
+        " Parent: " + (isRoot?()  ? "<None>" : @parent.name) +
+        " Children: #{@children.length}" +
+        " Total Nodes: #{size()}"
+    end
+
+    # Returns an array of ancestors in reversed order (the first element is the
+    # immediate parent). Returns nil if this is a root node.
+    def parentage
+      return nil if isRoot?
+
+      parentageArray = []
+      prevParent = self.parent
+      while (prevParent)
+        parentageArray << prevParent
+        prevParent = prevParent.parent
+      end
+
+      parentageArray
+    end
+
+    # Protected method to set the parent node.
+    # This method should NOT be invoked by client code.
+    def parent=(parent)
+      @parent = parent
+    end
+
+    # Convenience synonym for TreeNode#add method.  This method allows a convenient
+    # method to add children hierarchies in the tree.
+    #
+    # E.g. root << child << grand_child
+    def <<(child)
+      add(child)
+    end
+
+    # Adds the specified child node to the receiver node.  The child node's
+    # parent is set to be the receiver.  The child is added as the last child in
+    # the current list of children for the receiver node.
+    def add(child)
+      raise "Child already added" if @childrenHash.has_key?(child.name)
+
+      @childrenHash[child.name]  = child
+      @children << child
+      child.parent = self
+      return child
+
+    end
+
+    # Removes the specified child node from the receiver node.  The removed
+    # children nodes are orphaned but available if an alternate reference
+    # exists.
+    #
+    # Returns the child node.
+    def remove!(child)
+      @childrenHash.delete(child.name)
+      @children.delete(child)
+      child.setAsRoot! unless child == nil
+      return child
+    end
+
+    # Removes this node from its parent. If this is the root node, then does
+    # nothing.
+    def removeFromParent!
+      @parent.remove!(self) unless isRoot?
+    end
+
+    # Removes all children from the receiver node.
+    def removeAll!
+      for child in @children
+        child.setAsRoot!
+      end
+      @childrenHash.clear
+      @children.clear
+      self
+    end
+
+    # Indicates whether this node has any associated content.
+    def hasContent?
+      @content != nil
+    end
+
+    # Protected method which sets this node as a root node.
+    def setAsRoot!
+      @parent = nil
+    end
+
+    # Indicates whether this node is a root node. Note that
+    # orphaned children will also be reported as root nodes.
+    def isRoot?
+      @parent == nil
+    end
+
+    # Indicates whether this node has any immediate child nodes.
+    def hasChildren?
+      @children.length != 0
+    end
+
+    # Indicates whether this node is a 'leaf' - i.e., one without
+    # any children
+    def isLeaf?
+      !hasChildren?
+    end
+
+    # Returns an array of all the immediate children.  If a block is given,
+    # yields each child node to the block.
+    def children
+      if block_given?
+        @children.each {|child| yield child}
+      else
+        @children
+      end
+    end
+
+    # Returns the first child of this node. Will return nil if no children are
+    # present.
+    def firstChild
+      children.first
+    end
+
+    # Returns the last child of this node. Will return nil if no children are
+    # present.
+    def lastChild
+      children.last
+    end
+
+    # Returns every node (including the receiver node) from the tree to the
+    # specified block. The traversal is depth first and from left to right in
+    # pre-ordered sequence.
+    def each &block
+      yield self
+      children { |child| child.each(&block) }
+    end
+
+    # Traverses the tree in a pre-ordered sequence. This is equivalent to
+    # TreeNode#each
+    def preordered_each &block
+      each(&block)
+    end
+
+    # Performs breadth first traversal of the tree rooted at this node. The
+    # traversal in a given level is from left to right.
+    def breadth_each &block
+      node_queue = [self]       # Create a queue with self as the initial entry
+
+      # Use a queue to do breadth traversal
+      until node_queue.empty?
+        node_to_traverse = node_queue.shift
+        yield node_to_traverse
+        # Enqueue the children from left to right.
+        node_to_traverse.children { |child| node_queue.push child }
+      end
+    end
+
+    # Yields all leaf nodes from this node to the specified block. May yield
+    # this node as well if this is a leaf node.  Leaf traversal depth first and
+    # left to right.
+    def each_leaf &block
+      self.each { |node| yield(node) if node.isLeaf? }
+    end
+
+    # Returns the requested node from the set of immediate children.
+    #
+    # If the parameter is _numeric_, then the in-sequence array of children is
+    # accessed (see Tree#children).  If the parameter is not _numeric_, then it
+    # is assumed to be the *name* of the child node to be returned.
+    def [](name_or_index)
+      raise "Name_or_index needs to be provided" if name_or_index == nil
+
+      if name_or_index.kind_of?(Integer)
+        @children[name_or_index]
+      else
+        @childrenHash[name_or_index]
+      end
+    end
+
+    # Returns the total number of nodes in this tree, rooted at the receiver
+    # node.
+    def size
+      @children.inject(1) {|sum, node| sum + node.size}
+    end
+
+    # Convenience synonym for Tree#size
+    def length
+      size()
+    end
+
+    # Pretty prints the tree starting with the receiver node.
+    def printTree(level = 0)
+
+      if isRoot?
+        print "*"
+      else
+        print "|" unless parent.isLastSibling?
+        print(' ' * (level - 1) * 4)
+        print(isLastSibling? ? "+" : "|")
+        print "---"
+        print(hasChildren? ? "+" : ">")
+      end
+
+      puts " #{name}"
+
+      children { |child| child.printTree(level + 1)}
+    end
+
+    # Returns the root for this tree. Root's root is itself.
+    def root
+      root = self
+      root = root.parent while !root.isRoot?
+      root
+    end
+
+    # Returns the first sibling for this node. If this is the root node, returns
+    # itself.
+    def firstSibling
+      if isRoot?
+        self
+      else
+        parent.children.first
+      end
+    end
+
+    # Returns true if this node is the first sibling.
+    def isFirstSibling?
+      firstSibling == self
+    end
+
+    # Returns the last sibling for this node.  If this node is the root, returns
+    # itself.
+    def lastSibling
+      if isRoot?
+        self
+      else
+        parent.children.last
+      end
+    end
+
+    # Returns true if his node is the last sibling
+    def isLastSibling?
+      lastSibling == self
+    end
+
+    # Returns an array of siblings for this node.
+    # If a block is provided, yields each of the sibling
+    # nodes to the block. The root always has nil siblings.
+    def siblings
+      return nil if isRoot?
+      if block_given?
+        for sibling in parent.children
+          yield sibling if sibling != self
+        end
+      else
+        siblings = []
+        parent.children {|sibling| siblings << sibling if sibling != self}
+        siblings
+      end
+    end
+
+    # Returns true if this node is the only child of its parent
+    def isOnlyChild?
+      parent.children.size == 1
+    end
+
+    # Returns the next sibling for this node. Will return nil if no subsequent
+    # node is present.
+    def nextSibling
+      if myidx = parent.children.index(self)
+        parent.children.at(myidx + 1)
+      end
+    end
+
+    # Returns the previous sibling for this node. Will return nil if no
+    # subsequent node is present.
+    def previousSibling
+      if myidx = parent.children.index(self)
+        parent.children.at(myidx - 1) if myidx > 0
+      end
+    end
+
+    # Provides a comparision operation for the nodes. Comparision
+    # is based on the natural character-set ordering for the
+    # node names.
+    def <=>(other)
+      return +1 if other == nil
+      self.name <=> other.name
+    end
+
+    # Freezes all nodes in the tree
+    def freezeTree!
+      each {|node| node.freeze}
+    end
+
+    # Creates the marshal-dump represention of the tree rooted at this node.
+    def marshal_dump
+      self.collect { |node| node.createDumpRep }
+    end
+
+    # Creates a dump representation and returns the same as a hash.
+    def createDumpRep
+      { :name => @name, :parent => (isRoot? ? nil : @parent.name),  :content => Marshal.dump(@content)}
+    end
+
+    # Loads a marshalled dump of the tree and returns the root node of the
+    # reconstructed tree. See the Marshal class for additional details.
+    def marshal_load(dumped_tree_array)
+      nodes = { }
+      for node_hash in dumped_tree_array do
+        name        = node_hash[:name]
+        parent_name = node_hash[:parent]
+        content     = Marshal.load(node_hash[:content])
+
+        if parent_name then
+          nodes[name] = current_node = Tree::TreeNode.new(name, content)
+          nodes[parent_name].add current_node
+        else
+          # This is the root node, hence initialize self.
+          initialize(name, content)
+
+          nodes[name] = self    # Add self to the list of nodes
+         end
+      end
+    end
+
+    # Returns depth of the tree from this node. A single leaf node has a
+    # depth of 1.
+    def depth
+      return 1 if isLeaf?
+      1 + @children.collect { |child| child.depth }.max
+    end
+
+    # Returns breadth of the tree at this node level. A single node has a
+    # breadth of 1.
+    def breadth
+      return 1 if isRoot?
+      parent.children.size
+    end
+
+    protected :parent=, :setAsRoot!, :createDumpRep
+
+  end
+end
+
+# $Log: tree.rb,v $
+# Revision 1.29  2007/12/22 00:28:59  anupamsg
+# Added more test cases, and enabled ZenTest compatibility.
+#
+# Revision 1.28  2007/12/20 03:19:33  anupamsg
+# * README (Module): Modified the install instructions from source.
+# (Module): Updated the minor version number.
+#
+# Revision 1.27  2007/12/20 03:00:03  anupamsg
+# Minor code changes. Removed self_initialize from the protected methods' list.
+#
+# Revision 1.26  2007/12/20 02:50:04  anupamsg
+# (Tree::TreeNode): Removed the spurious self_initialize from the protected list.
+#
+# Revision 1.25  2007/12/19 20:28:05  anupamsg
+# Removed the unnecesary self_initialize method.
+#
+# Revision 1.24  2007/12/19 06:39:17  anupamsg
+# Removed the unnecessary field and record separator constants.  Also updated the
+# history.txt file.
+#
+# Revision 1.23  2007/12/19 06:25:00  anupamsg
+# (Tree::TreeNode): Minor fix to the comments.  Also fixed the private/protected
+# scope issue with the createDumpRep method.
+#
+# Revision 1.22  2007/12/19 06:22:03  anupamsg
+# Updated the marshalling logic to correctly handle non-string content. This
+# should fix the bug # 15614 ("When dumping with an Object as the content, you get
+# a delimiter collision")
+#
+# Revision 1.21  2007/12/19 02:24:17  anupamsg
+# Updated the marshalling logic to handle non-string contents on the nodes.
+#
+# Revision 1.20  2007/10/10 08:42:57  anupamsg
+# Release 0.4.3
+#
+# Revision 1.19  2007/08/31 01:16:27  anupamsg
+# Added breadth and pre-order traversals for the tree. Also added a method
+# to return the detached copy of a node from the tree.
+#
+# Revision 1.18  2007/07/21 05:14:44  anupamsg
+# Added a VERSION constant to the Tree module,
+# and using the same in the Rakefile.
+#
+# Revision 1.17  2007/07/21 03:24:25  anupamsg
+# Minor edits to parameter names. User visible functionality does not change.
+#
+# Revision 1.16  2007/07/18 23:38:55  anupamsg
+# Minor updates to tree.rb
+#
+# Revision 1.15  2007/07/18 22:11:50  anupamsg
+# Added depth and breadth methods for the TreeNode.
+#
+# Revision 1.14  2007/07/18 19:33:27  anupamsg
+# Added a new binary tree implementation.
+#
+# Revision 1.13  2007/07/18 07:17:34  anupamsg
+# Fixed a  issue where TreeNode.ancestors was shadowing Module.ancestors. This method
+# has been renamed to TreeNode.parentage.
+#
+# Revision 1.12  2007/07/17 03:39:28  anupamsg
+# Moved the CVS Log keyword to end of the files.
+#