rubytree 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ #