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