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.
- data/{LICENSE → COPYING} +1 -1
- data/ChangeLog +24 -1
- data/README +15 -11
- data/Rakefile +101 -89
- data/lib/tree.rb +380 -244
- data/test/person.rb +54 -47
- data/test/testtree.rb +355 -219
- metadata +4 -4
data/{LICENSE → COPYING}
RENAMED
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
|
-
=
|
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.
|
6
|
+
Document Revision: $Revision: 1.6 $ by $Author: anupamsg $
|
7
7
|
|
8
8
|
== License
|
9
9
|
|
10
|
-
|
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
|
-
|
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
|
19
|
+
== Getting Rubytree
|
20
20
|
|
21
|
-
|
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
|
-
|
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
|
38
|
+
== Installing Rubytree
|
39
39
|
|
40
|
-
It is recommended to install
|
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
|
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
|
-
|
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
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
require '
|
37
|
-
require 'rake/
|
38
|
-
require 'rake/
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
'
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
s.
|
54
|
-
s.
|
55
|
-
s.
|
56
|
-
s.
|
57
|
-
|
58
|
-
s.
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
s.
|
69
|
-
s.
|
70
|
-
s.
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
t.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
+
#
|
data/lib/tree.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# tree.rb
|
2
2
|
#
|
3
|
-
#
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
#
|
114
|
+
# Name of the node is expected to be unique across the
|
115
|
+
# tree.
|
84
116
|
#
|
85
|
-
#
|
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
|
-
#
|
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
|
-
#
|
90
|
-
|
91
|
-
|
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
|
-
|
94
|
-
|
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
|
-
|
97
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
217
|
+
# Private method which sets this node as a root node.
|
218
|
+
def setAsRoot!
|
219
|
+
@parent = nil
|
220
|
+
end
|
108
221
|
|
109
|
-
|
110
|
-
|
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
|
-
|
228
|
+
# Indicates whether this node has any immediate child nodes.
|
229
|
+
def hasChildren?
|
230
|
+
@children.length != 0
|
231
|
+
end
|
113
232
|
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
295
|
+
# Convenience synonym for Tree#size
|
296
|
+
def length
|
297
|
+
size()
|
298
|
+
end
|
171
299
|
|
172
|
-
|
173
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
188
|
-
|
189
|
-
@parent = nil
|
190
|
-
end
|
312
|
+
# print((' ' * level) + "|--" + (hasChildren? ? "+" : ">"))
|
313
|
+
end
|
191
314
|
|
192
|
-
|
193
|
-
# orphaned children will also be reported as root nodes.
|
194
|
-
def isRoot?
|
195
|
-
@parent == nil
|
196
|
-
end
|
315
|
+
puts " #{name}"
|
197
316
|
|
198
|
-
|
199
|
-
|
200
|
-
@children.length != 0
|
201
|
-
end
|
317
|
+
children { |child| child.printTree(level + 1)}
|
318
|
+
end
|
202
319
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
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
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
352
|
+
# Returns true if his node is the last sibling
|
353
|
+
def isLastSibling?
|
354
|
+
lastSibling == self
|
355
|
+
end
|
247
356
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
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
|
-
|
322
|
-
|
323
|
-
|
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
|
-
|
327
|
-
|
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
|
+
#
|