rubytree 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (8) hide show
  1. data/{LICENSE → COPYING} +1 -1
  2. data/ChangeLog +24 -1
  3. data/README +15 -11
  4. data/Rakefile +101 -89
  5. data/lib/tree.rb +380 -244
  6. data/test/person.rb +54 -47
  7. data/test/testtree.rb +355 -219
  8. metadata +4 -4
@@ -1,7 +1,7 @@
1
1
  RUBYTREE - http://rubytree.rubyforge.org
2
2
  ========================================
3
3
 
4
- Copyright (c) 2007, Anupam Sengupta
4
+ Copyright (c) 2006, 2007 Anupam Sengupta
5
5
 
6
6
  All rights reserved.
7
7
 
data/ChangeLog CHANGED
@@ -1,5 +1,29 @@
1
+ 2007-07-16 Anupam Sengupta <anupamsg@gmail.com>
2
+
3
+ * lib/tree.rb (Tree::TreeNode): Added navigation methods for
4
+ siblings and children. Also added some convenience methods.
5
+
6
+ 2007-07-08 Anupam Sengupta <anupamsg@gmail.com>
7
+
8
+ * Rakefile: Added a developer target for generating rdoc for the
9
+ website.
10
+
11
+ 2007-06-24 Anupam Sengupta <anupamsg@gmail.com>
12
+
13
+ * test/testtree.rb, lib/tree.rb: Added the each_leaf traversal method.
14
+ v
15
+ * README: Replaced all occurrances of LICENSE with COPYING
16
+ and lowercased all instances of the word 'RubyTree'.
17
+
18
+ * Rakefile: Replaced all occurrances of LICENSE with COPYING
19
+
1
20
  2007-06-23 Anupam Sengupta <anupamsg@gmail.com>
2
21
 
22
+ * lib/tree.rb (Tree::TreeNode::isLeaf): Added a isLeaf? method.
23
+
24
+ * test/testtree.rb (TC_TreeTest::test_removeFromParent): Added
25
+ test for isLeaf? method
26
+
3
27
  * Rakefile: Added the LICENSE and ChangeLog to the extra RDoc files.
4
28
 
5
29
  * lib/tree.rb: Minor updates to the comments.
@@ -18,4 +42,3 @@
18
42
  * LICENSE: Added the BSD LICENSE file.
19
43
 
20
44
  * Changelog: Added the Changelog file.
21
-
data/README CHANGED
@@ -1,29 +1,29 @@
1
- = RubyTree
1
+ = Rubytree 0.3.0
2
2
 
3
3
  (c) 2006, 2007 Anupam Sengupta
4
4
  http://rubytree.rubyforge.org
5
5
 
6
- Document Revision: $Revision: 1.4 $
6
+ Document Revision: $Revision: 1.6 $ by $Author: anupamsg $
7
7
 
8
8
  == License
9
9
 
10
- RubyTree has been released under the BSD License. See the file LICENSE for
10
+ Rubytree has been released under the BSD License. See the file COPYING for
11
11
  details.
12
12
 
13
13
  == Introduction
14
14
 
15
- RubyTree is a simple implementation of the generic Tree data structure. This
15
+ Rubytree is a simple implementation of the generic Tree data structure. This
16
16
  implementation is node-centric, where the individual nodes on the tree are the
17
17
  primary objects and drive the structure.
18
18
 
19
- == Getting RubyTree
19
+ == Getting Rubytree
20
20
 
21
- RubyTree is an open source project and is hosted at
21
+ Rubytree is an open source project and is hosted at
22
22
  http://rubyforge.org/projects/rubytree
23
23
 
24
24
  The project home page is: http://rubytree.rubyforge.org
25
25
 
26
- RubyTree can be downloaded as a Ruby Gem or as a tar/zip file from:
26
+ Rubytree can be downloaded as a Ruby Gem or as a tar/zip file from:
27
27
 
28
28
  http://rubyforge.org/frs/?group_id=1215&release_id=8817
29
29
 
@@ -35,9 +35,9 @@ The file name is one of:
35
35
 
36
36
  Download the appropriate file-type.
37
37
 
38
- == Installing RubyTree
38
+ == Installing Rubytree
39
39
 
40
- It is recommended to install RubyTree as a Ruby Gem, as this is an easy way to
40
+ It is recommended to install Rubytree as a Ruby Gem, as this is an easy way to
41
41
  keep the version updated, and keep multiple versions of the library available on
42
42
  your system.
43
43
 
@@ -46,7 +46,7 @@ To Install the Gem, from a Terminal/CLI command prompt, issue the command:
46
46
 
47
47
  gem install rubytree
48
48
 
49
- This should install the gem file for RubyTree. Note that you may need to be a
49
+ This should install the gem file for Rubytree. Note that you may need to be a
50
50
  super-user (root) to successfully install the gem.
51
51
 
52
52
  === Installing from the tgz/zip file
@@ -64,6 +64,10 @@ the text mode ri documentation:
64
64
 
65
65
  ri Tree::TreeNode
66
66
 
67
+ Documentation on the web is available at:
68
+
69
+ http://rubytree.rubyforge.org/rdoc/
70
+
67
71
  == Example
68
72
 
69
73
  The following code-snippet implements this tree structure:
@@ -108,7 +112,7 @@ the text mode ri documentation:
108
112
 
109
113
  == LICENSE
110
114
 
111
- RubyTree is licensed under BSD license.
115
+ Rubytree is licensed under BSD license.
112
116
 
113
117
  Copyright (c) 2007, Anupam Sengupta
114
118
 
data/Rakefile CHANGED
@@ -1,89 +1,101 @@
1
- # Rakefile
2
- #
3
- # Revision: $Revision: 1.8 $
4
- #
5
- # Copyright (c) 2006, 2007 Anupam Sengupta
6
- #
7
- # All rights reserved.
8
- #
9
- # Redistribution and use in source and binary forms, with or without modification,
10
- # are permitted provided that the following conditions are met:
11
- #
12
- # - Redistributions of source code must retain the above copyright notice, this
13
- # list of conditions and the following disclaimer.
14
- #
15
- # - Redistributions in binary form must reproduce the above copyright notice, this
16
- # list of conditions and the following disclaimer in the documentation and/or
17
- # other materials provided with the distribution.
18
- #
19
- # - Neither the name of the organization nor the names of its contributors may
20
- # be used to endorse or promote products derived from this software without
21
- # specific prior written permission.
22
- #
23
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
- # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
27
- # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30
- # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
-
34
- require 'rubygems'
35
- require 'rake/clean'
36
- require 'rake/gempackagetask'
37
- require 'rake/testtask'
38
- require 'rake/rdoctask'
39
-
40
- desc "Default Task"
41
- task :default => :gem
42
-
43
- PKG_VERSION = '0.2.4'
44
- PKG_FILES = FileList[
45
- '[A-Z]*',
46
- 'lib/**/*.rb',
47
- 'test/**/*.rb'
48
- ]
49
-
50
- spec = Gem::Specification.new do |s|
51
- s.name = "rubytree"
52
- s.version = PKG_VERSION
53
- s.platform = Gem::Platform::RUBY
54
- s.author = "Anupam Sengupta"
55
- s.email = "anupamsg@gmail.com"
56
- s.summary = "Ruby implementation of the Tree data structure."
57
-
58
- s.description = <<-END
59
- Provides a generic tree data-structure with ability to
60
- store keyed node-elements in the tree. The implementation
61
- mixes in the Enumerable module.
62
-
63
- Website: http://rubytree.rubyforge.org/
64
- END
65
-
66
- s.has_rdoc = true
67
- s.extra_rdoc_files = ['README', 'LICENSE', 'ChangeLog']
68
- s.autorequire = "tree"
69
- s.files = PKG_FILES.to_a
70
- s.test_files = Dir.glob('test/test*.rb')
71
- end
72
-
73
- Rake::GemPackageTask.new(spec) do |pkg|
74
- pkg.need_zip = true
75
- pkg.need_tar = true
76
- end
77
-
78
- Rake::TestTask.new do |t|
79
- t.libs << "test"
80
- t.test_files = FileList['test/test*.rb']
81
- t.verbose = true
82
- end
83
-
84
- Rake::RDocTask.new do |rd|
85
- rd.rdoc_files.include("README", "LICENSE", "ChangeLog", "lib/**/*.rb")
86
- end
87
-
88
-
89
-
1
+ # Rakefile
2
+ #
3
+ # $Revision: 1.14 $ by $Author: anupamsg $
4
+ # $Name: $
5
+ #
6
+ # Copyright (c) 2006, 2007 Anupam Sengupta
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without modification,
11
+ # are permitted provided that the following conditions are met:
12
+ #
13
+ # - Redistributions of source code must retain the above copyright notice, this
14
+ # list of conditions and the following disclaimer.
15
+ #
16
+ # - Redistributions in binary form must reproduce the above copyright notice, this
17
+ # list of conditions and the following disclaimer in the documentation and/or
18
+ # other materials provided with the distribution.
19
+ #
20
+ # - Neither the name of the organization nor the names of its contributors may
21
+ # be used to endorse or promote products derived from this software without
22
+ # specific prior written permission.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
28
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
31
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ #
35
+
36
+ require 'rubygems'
37
+ require 'rake/clean'
38
+ require 'rake/gempackagetask'
39
+ require 'rake/testtask'
40
+ require 'rake/rdoctask'
41
+
42
+ desc "Default Task"
43
+ task :default => :gem
44
+
45
+ PKG_VERSION = '0.3.0'
46
+ PKG_FILES = FileList[
47
+ '[A-Z]*',
48
+ 'lib/**/*.rb',
49
+ 'test/**/*.rb'
50
+ ]
51
+
52
+ spec = Gem::Specification.new do |s|
53
+ s.name = "rubytree"
54
+ s.version = PKG_VERSION
55
+ s.platform = Gem::Platform::RUBY
56
+ s.author = "Anupam Sengupta"
57
+ s.email = "anupamsg@gmail.com"
58
+ s.summary = "Ruby implementation of the Tree data structure."
59
+
60
+ s.description = <<-END
61
+ Provides a generic tree data-structure with ability to
62
+ store keyed node-elements in the tree. The implementation
63
+ mixes in the Enumerable module.
64
+
65
+ Website: http://rubytree.rubyforge.org/
66
+ END
67
+
68
+ s.has_rdoc = true
69
+ s.extra_rdoc_files = ['README', 'COPYING', 'ChangeLog']
70
+ s.autorequire = "tree"
71
+ s.files = PKG_FILES.to_a
72
+ s.test_files = Dir.glob('test/test*.rb')
73
+ end
74
+
75
+ Rake::GemPackageTask.new(spec) do |pkg|
76
+ pkg.need_zip = true
77
+ pkg.need_tar = true
78
+ end
79
+
80
+ Rake::TestTask.new do |t|
81
+ t.libs << "test"
82
+ t.test_files = FileList['test/test*.rb']
83
+ t.verbose = true
84
+ end
85
+
86
+ Rake::RDocTask.new do |rd|
87
+ rd.rdoc_files.include("README", "COPYING", "ChangeLog", "lib/**/*.rb")
88
+ rd.title = "Rubytree Documentation"
89
+ end
90
+
91
+ Rake::RDocTask.new(:rdoc_www) do |rd|
92
+ rd.rdoc_files.include("README", "COPYING", "ChangeLog", "lib/**/*.rb")
93
+ rd.title = "Rubytree Documentation"
94
+ rd.template = "rubytree-template.rb"
95
+ end
96
+
97
+
98
+ # $Log: Rakefile,v $
99
+ # Revision 1.14 2007/07/17 03:39:28 anupamsg
100
+ # Moved the CVS Log keyword to end of the files.
101
+ #
@@ -1,6 +1,7 @@
1
1
  # tree.rb
2
2
  #
3
- # Revision: $Revision: 1.6 $
3
+ # $Revision: 1.12 $ by $Author: anupamsg $
4
+ # $Name: $
4
5
  #
5
6
  # = tree.rb - Generic Tree implementation
6
7
  #
@@ -39,292 +40,427 @@
39
40
  # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40
41
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41
42
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
42
44
 
43
-
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.
44
48
  module Tree
45
49
 
46
- # The Tree node class implementation. Mixes in the Enumerable
47
- # module.
48
- #
49
- # == Example
50
- #
51
- # The following code-snippet implements this tree structure:
52
- #
53
- # +------------+
54
- # | ROOT |
55
- # +-----+------+
56
- # +-------------+------------+
57
- # | |
58
- # +-------+-------+ +-------+-------+
59
- # | CHILD 1 | | CHILD 2 |
60
- # +-------+-------+ +---------------+
61
- # |
62
- # |
63
- # +-------+-------+
64
- # | GRANDCHILD 1 |
65
- # +---------------+
66
- #
67
- # require 'tree'
68
- #
69
- # myTreeRoot = Tree::TreeNode.new("ROOT", "Root Content")
70
- #
71
- # myTreeRoot << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
72
- #
73
- # myTreeRoot << Tree::TreeNode.new("CHILD2", "Child2 Content")
74
- #
75
- # myTreeRoot.printTree
76
- #
77
- # child1 = myTreeRoot["CHILD1"]
78
- #
79
- # grandChild1 = myTreeRoot["CHILD1"]["GRANDCHILD1"]
80
- #
81
- # siblingsOfChild1Array = child1.siblings
50
+ # == TreeNode Class Description
51
+ #
52
+ # The node class for the tree representation. the nodes are named and have a
53
+ # place-holder for the node data (i.e., the `content' of the node). The node
54
+ # names are expected to be unique. In addition, the node provides navigation
55
+ # methods to traverse the tree.
56
+ #
57
+ # The nodes can have any number of child nodes attached to it. Note that while
58
+ # this implementation does not support directed graphs, the class itself makes
59
+ # no restrictions on associating a node's CONTENT with multiple parent nodes.
60
+ #
61
+ #
62
+ # == Example
63
+ #
64
+ # The following code-snippet implements this tree structure:
65
+ #
66
+ # +------------+
67
+ # | ROOT |
68
+ # +-----+------+
69
+ # +-------------+------------+
70
+ # | |
71
+ # +-------+-------+ +-------+-------+
72
+ # | CHILD 1 | | CHILD 2 |
73
+ # +-------+-------+ +---------------+
74
+ # |
75
+ # |
76
+ # +-------+-------+
77
+ # | GRANDCHILD 1 |
78
+ # +---------------+
79
+ #
80
+ # require 'tree'
81
+ #
82
+ # myTreeRoot = Tree::TreeNode.new("ROOT", "Root Content")
83
+ #
84
+ # myTreeRoot << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
85
+ #
86
+ # myTreeRoot << Tree::TreeNode.new("CHILD2", "Child2 Content")
87
+ #
88
+ # myTreeRoot.printTree
89
+ #
90
+ # child1 = myTreeRoot["CHILD1"]
91
+ #
92
+ # grandChild1 = myTreeRoot["CHILD1"]["GRANDCHILD1"]
93
+ #
94
+ # siblingsOfChild1Array = child1.siblings
95
+ #
96
+ # immediateChildrenArray = myTreeRoot.children
97
+ #
98
+ # # Process all nodes
99
+ #
100
+ # myTreeRoot.each { |node| node.content.reverse }
101
+ #
102
+ # myTreeRoot.remove!(child1) # Remove the child
103
+ class TreeNode
104
+ include Enumerable
105
+
106
+ attr_reader :content, :name, :parent
107
+ attr_writer :content
108
+
109
+ @@fieldSep = '|'
110
+ @@recordSep = "\n"
111
+
112
+ # Constructor which expects the name of the node
82
113
  #
83
- # immediateChildrenArray = myTreeRoot.children
114
+ # Name of the node is expected to be unique across the
115
+ # tree.
84
116
  #
85
- # # Process all nodes
117
+ # The content can be of any type, and is defaulted to _nil_.
118
+ def initialize(name, content = nil)
119
+
120
+ raise "Node name HAS to be provided" if name == nil
121
+
122
+ @name = name
123
+ @content = content
124
+
125
+ self.setAsRoot!
126
+
127
+ @childrenHash = Hash.new
128
+ @children = []
129
+ end
130
+
131
+ # Print the string representation of this node.
132
+ def to_s
133
+
134
+ "Node Name: #{@name}" +
135
+ " Content: " + (@content || "<Empty>") +
136
+ " Parent: " + (isRoot?() ? "<None>" : @parent.name) +
137
+ " Children: #{@children.length}" +
138
+ " Total Nodes: #{size()}"
139
+
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 ancestors
145
+ return nil if isRoot?
146
+
147
+ ancestorsArray = []
148
+ prevParent = self.parent
149
+ while (prevParent)
150
+ ancestorsArray << prevParent
151
+ prevParent = prevParent.parent
152
+ end
153
+
154
+ ancestorsArray
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 Tree#add method. This method allows a convenient
164
+ # method to add children hierarchies in the tree.
86
165
  #
87
- # myTreeRoot.each { |node| node.content.reverse }
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.
88
187
  #
89
- # myTreeRoot.remove!(child1) # Remove the child
90
- class TreeNode
91
- include Enumerable
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
92
195
 
93
- attr_reader :content, :name, :parent
94
- attr_writer :content
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
95
201
 
96
- @@fieldSep = '|'
97
- @@recordSep = "\n"
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
98
211
 
99
- # Constructor which expects the name of the node
100
- #
101
- # Name of the node is expected to be unique across the
102
- # tree.
103
- #
104
- # The content can be of any type, and is defaulted to _nil_.
105
- def initialize(name, content = nil)
212
+ # Indicates whether this node has any associated content.
213
+ def hasContent?
214
+ @content != nil
215
+ end
106
216
 
107
- raise "Node name HAS to be provided" if name == nil
217
+ # Private method which sets this node as a root node.
218
+ def setAsRoot!
219
+ @parent = nil
220
+ end
108
221
 
109
- @name = name
110
- @content = content
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
111
227
 
112
- self.setAsRoot!
228
+ # Indicates whether this node has any immediate child nodes.
229
+ def hasChildren?
230
+ @children.length != 0
231
+ end
113
232
 
114
- @childrenHash = Hash.new
115
- @children = []
116
- end
233
+ # Indicates whether this node is a 'leaf' - i.e., one without
234
+ # any children
235
+ def isLeaf?
236
+ !hasChildren?
237
+ end
117
238
 
118
- # Print the string representation of this node.
119
- def to_s
120
- s = size()
121
- "Node ID: #{@name} Content: #{@content} Parent: " +
122
- (isRoot?() ? "ROOT" : "#{@parent.name}") +
123
- " Children: #{@children.length}" +
124
- " Total Nodes: #{s}"
125
- end
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
126
248
 
127
- # Protected method to set the parent node.
128
- # This method should NOT be invoked by client code.
129
- def parent=(parent)
130
- @parent = parent
131
- end
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
132
254
 
133
- # Convenience synonym for Tree#add method.
134
- # This method allows a convenient method to add
135
- # children hierarchies in the tree.
136
- # E.g. root << child << grand_child
137
- def <<(child)
138
- add(child)
139
- end
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
140
260
 
141
- # Adds the specified child node to the receiver node.
142
- # The child node's parent is set to be the receiver.
143
- # The child is added as the last child in the current
144
- # list of children for the receiver node.
145
- def add(child)
146
- raise "Child already added" if @childrenHash.has_key?(child.name)
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.
263
+ def each &block
264
+ yield self
265
+ children { |child| child.each(&block) }
266
+ end
147
267
 
148
- @childrenHash[child.name] = child
149
- @children << child
150
- child.parent = self
151
- return child
268
+ # Yields all leaf nodes from this node to the specified block. May yield this
269
+ # node as well if this is a leaf node. Leaf traversal is left to right.
270
+ def each_leaf &block
271
+ self.each { |node| yield(node) if node.isLeaf? }
272
+ end
152
273
 
153
- end
274
+ # Returns the requested node from the set of immediate children.
275
+ #
276
+ # If the key is _numeric_, then the in-sequence array of children is
277
+ # accessed (see Tree#children). If the key is not _numeric_, then it is
278
+ # assumed to be the *name* of the child node to be returned.
279
+ def [](key)
280
+ raise "Key needs to be provided" if key == nil
281
+
282
+ if key.kind_of?(Integer)
283
+ @children[key]
284
+ else
285
+ @childrenHash[key]
286
+ end
287
+ end
154
288
 
155
- # Removes the specified child node from the receiver node.
156
- # The removed children nodes are orphaned but available
157
- # if an alternate reference exists.
158
- # Returns the child node.
159
- def remove!(child)
160
- @childrenHash.delete(child.name)
161
- @children.delete(child)
162
- child.setAsRoot! unless child == nil
163
- return child
164
- end
289
+ # Returns the total number of nodes in this tree, rooted at the receiver
290
+ # node.
291
+ def size
292
+ @children.inject(1) {|sum, node| sum + node.size}
293
+ end
165
294
 
166
- # Removes this node from its parent. If this is the root node,
167
- # then does nothing.
168
- def removeFromParent!
169
- @parent.remove!(self) unless isRoot?
170
- end
295
+ # Convenience synonym for Tree#size
296
+ def length
297
+ size()
298
+ end
171
299
 
172
- # Removes all children from the receiver node.
173
- def removeAll!
174
- for child in @children
175
- child.setAsRoot!
176
- end
177
- @childrenHash.clear
178
- @children.clear
179
- self
180
- end
300
+ # Pretty prints the tree starting with the receiver node.
301
+ def printTree(level = 0)
181
302
 
182
- # Indicates whether this node has any associated content.
183
- def hasContent?
184
- @content != nil
185
- end
303
+ if isRoot?
304
+ print "*"
305
+ else
306
+ print "|" unless parent.isLastSibling?
307
+ print(' ' * (level - 1) * 4)
308
+ print(isLastSibling? ? "+" : "|")
309
+ print "---"
310
+ print(hasChildren? ? "+" : ">")
186
311
 
187
- # Private method which sets this node as a root node.
188
- def setAsRoot!
189
- @parent = nil
190
- end
312
+ # print((' ' * level) + "|--" + (hasChildren? ? "+" : ">"))
313
+ end
191
314
 
192
- # Indicates whether this node is a root node. Note that
193
- # orphaned children will also be reported as root nodes.
194
- def isRoot?
195
- @parent == nil
196
- end
315
+ puts " #{name}"
197
316
 
198
- # Indicates whether this node has any immediate child nodes.
199
- def hasChildren?
200
- @children.length != 0
201
- end
317
+ children { |child| child.printTree(level + 1)}
318
+ end
202
319
 
203
- # Returns an array of all the immediate children.
204
- # If a block is given, yields each child node to the block.
205
- def children
206
- if block_given?
207
- @children.each {|child| yield child}
208
- else
209
- @children
210
- end
211
- end
320
+ # Returns the root for this tree. Root's root is itself.
321
+ def root
322
+ root = self
323
+ root = root.parent while !root.isRoot?
324
+ root
325
+ end
212
326
 
213
- # Returns every node (including the receiver node) from the
214
- # tree to the specified block.
215
- def each &block
216
- yield self
217
- children { |child| child.each(&block) }
218
- end
327
+ # Returns the first sibling for this node. If this is the root node, returns
328
+ # itself.
329
+ def firstSibling
330
+ if isRoot?
331
+ self
332
+ else
333
+ parent.children.first
334
+ end
335
+ end
219
336
 
220
- # Returns the requested node from the set of immediate
221
- # children.
222
- #
223
- # If the key is _numeric_, then the in-sequence array of
224
- # children is accessed (see Tree#children).
225
- # If the key is not _numeric_, then it is assumed to be
226
- # the *name* of the child node to be returned.
227
- def [](key)
228
- raise "Key needs to be provided" if key == nil
229
-
230
- if key.kind_of?(Integer)
231
- @children[key]
232
- else
233
- @childrenHash[key]
234
- end
235
- end
337
+ # Returns true if this node is the first sibling.
338
+ def isFirstSibling?
339
+ firstSibling == self
340
+ end
236
341
 
237
- # Returns the total number of nodes in this tree, rooted
238
- # at the receiver node.
239
- def size
240
- @children.inject(1) {|sum, node| sum + node.size}
241
- end
342
+ # Returns the last sibling for this node. If this node is the root, returns
343
+ # itself.
344
+ def lastSibling
345
+ if isRoot?
346
+ self
347
+ else
348
+ parent.children.last
349
+ end
350
+ end
242
351
 
243
- # Convenience synonym for Tree#size
244
- def length
245
- size()
246
- end
352
+ # Returns true if his node is the last sibling
353
+ def isLastSibling?
354
+ lastSibling == self
355
+ end
247
356
 
248
- # Pretty prints the tree starting with the receiver node.
249
- def printTree(tab = 0)
250
- puts((' ' * tab) + self.to_s)
251
- children {|child| child.printTree(tab + 4)}
357
+ # Returns an array of siblings for this node.
358
+ # If a block is provided, yields each of the sibling
359
+ # nodes to the block. The root always has nil siblings.
360
+ def siblings
361
+ return nil if isRoot?
362
+ if block_given?
363
+ for sibling in parent.children
364
+ yield sibling if sibling != self
252
365
  end
366
+ else
367
+ siblings = []
368
+ parent.children {|sibling| siblings << sibling if sibling != self}
369
+ siblings
370
+ end
371
+ end
253
372
 
254
- # Returns the root for this tree.
255
- def root
256
- root = self
257
- root = root.parent while !root.isRoot?
258
- root
259
- end
373
+ # Returns true if this node is the only child of its parent
374
+ def isOnlyChild?
375
+ parent.children.size == 1
376
+ end
260
377
 
261
- # Returns an array of siblings for this node.
262
- # If a block is provided, yields each of the sibling
263
- # nodes to the block. The root always has nil siblings.
264
- def siblings
265
- return nil if isRoot?
266
- if block_given?
267
- for sibling in parent.children
268
- yield sibling if sibling != self
269
- end
270
- else
271
- siblings = []
272
- parent.children {|sibling| siblings << sibling if sibling != self}
273
- siblings
274
- end
275
- end
378
+ # Returns the next sibling for this node. Will return nil if no subsequent
379
+ # node is present.
380
+ def nextSibling
381
+ if myidx = parent.children.index(self)
382
+ parent.children.at(myidx + 1)
383
+ end
384
+ end
276
385
 
277
- # Provides a comparision operation for the nodes. Comparision
278
- # is based on the natural character-set ordering for the
279
- # node names.
280
- def <=>(other)
281
- return +1 if other == nil
282
- self.name <=> other.name
283
- end
386
+ # Returns the previous sibling for this node. Will return nil if no
387
+ # subsequent node is present.
388
+ def previousSibling
389
+ if myidx = parent.children.index(self)
390
+ parent.children.at(myidx - 1) if myidx > 0
391
+ end
392
+ end
284
393
 
285
- # Freezes all nodes in the tree
286
- def freezeTree!
287
- each {|node| node.freeze}
288
- end
394
+ # Provides a comparision operation for the nodes. Comparision
395
+ # is based on the natural character-set ordering for the
396
+ # node names.
397
+ def <=>(other)
398
+ return +1 if other == nil
399
+ self.name <=> other.name
400
+ end
289
401
 
290
- # Creates a dump representation and returns the same as a string
291
- def createDumpRep
292
- strRep = String.new
293
- strRep << @name << @@fieldSep << (isRoot? ? @name : @parent.name)
294
- strRep << @@fieldSep << Marshal.dump(@content) << @@recordSep
295
- end
402
+ # Freezes all nodes in the tree
403
+ def freezeTree!
404
+ each {|node| node.freeze}
405
+ end
296
406
 
297
- def _dump(depth)
298
- strRep = String.new
299
- each {|node| strRep << node.createDumpRep}
300
- strRep
301
- end
407
+ # Creates a dump representation and returns the same as a string
408
+ def createDumpRep
409
+ strRep = String.new
410
+ strRep << @name << @@fieldSep << (isRoot? ? @name : @parent.name)
411
+ strRep << @@fieldSep << Marshal.dump(@content) << @@recordSep
412
+ end
302
413
 
303
- # Loads a dump representation of the tree from the specified string
304
- def TreeNode.loadDumpRep(str)
305
- nodeHash = Hash.new
306
- rootNode = nil
307
- str.split(@@recordSep).each do |line|
308
- name, parent, contentStr = line.split(@@fieldSep)
309
- content = Marshal.load(contentStr)
310
- currentNode = Tree::TreeNode.new(name, content)
311
- nodeHash[name] = currentNode
312
- if name != parent # Do for a child node
313
- nodeHash[parent].add(currentNode)
314
- else
315
- rootNode = currentNode
316
- end
317
- end
318
- rootNode
319
- end
414
+ def _dump(depth)
415
+ strRep = String.new
416
+ each {|node| strRep << node.createDumpRep}
417
+ strRep
418
+ end
320
419
 
321
- # Loads a dump representation of the tree from the specified string.
322
- def TreeNode._load(str)
323
- loadDumpRep(str)
420
+ # Loads a dump representation of the tree from the specified string
421
+ def TreeNode.loadDumpRep(str)
422
+ nodeHash = Hash.new
423
+ rootNode = nil
424
+ str.split(@@recordSep).each do |line|
425
+ name, parent, contentStr = line.split(@@fieldSep)
426
+ content = Marshal.load(contentStr)
427
+ currentNode = Tree::TreeNode.new(name, content)
428
+ nodeHash[name] = currentNode
429
+ if name != parent # Do for a child node
430
+ nodeHash[parent].add(currentNode)
431
+ else
432
+ rootNode = currentNode
324
433
  end
434
+ end
435
+ rootNode
436
+ end
325
437
 
326
- protected :parent=, :setAsRoot!
327
- private_class_method :loadDumpRep
328
-
438
+ # Loads a dump representation of the tree from the specified string.
439
+ def TreeNode._load(str)
440
+ loadDumpRep(str)
329
441
  end
442
+
443
+ protected :parent=, :setAsRoot!
444
+ private_class_method :loadDumpRep
445
+
446
+ end
447
+ end
448
+
449
+ if __FILE__ == $0
450
+ root = Tree::TreeNode.new("ROOT")
451
+ child1 = Tree::TreeNode.new("CHILD1")
452
+ child2 = Tree::TreeNode.new("CHILD2")
453
+ child3 = Tree::TreeNode.new("CHILD3")
454
+ grandchild1 = Tree::TreeNode.new("GRANDCHILD1")
455
+ root << child1 << grandchild1
456
+ root << child2
457
+ root << child3
458
+
459
+ puts child1.nextSibling
460
+ root.printTree
330
461
  end
462
+
463
+ # $Log: tree.rb,v $
464
+ # Revision 1.12 2007/07/17 03:39:28 anupamsg
465
+ # Moved the CVS Log keyword to end of the files.
466
+ #