To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / vendor / gems / rubytree-0.5.2 / lib / tree.rb @ 442:753f1380d6bc

History | View | Annotate | Download (16.1 KB)

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
#