rubytree 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/API-CHANGES +42 -0
- data/COPYING +14 -0
- data/History.txt +21 -0
- data/Manifest.txt +1 -1
- data/README +63 -8
- data/Rakefile +12 -6
- data/TODO +45 -17
- data/lib/tree.rb +376 -115
- data/lib/tree/binarytree.rb +41 -8
- data/test/test_binarytree.rb +13 -2
- data/test/test_tree.rb +204 -50
- metadata +52 -30
- data/ChangeLog +0 -235
data/API-CHANGES
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
= API Changes in RubyTree
|
2
|
+
|
3
|
+
This file documents various API level changes that have been made to the RubyTree package.
|
4
|
+
|
5
|
+
Note: API level changes are expected to reduce dramatically after the 1.x release. In most cases, an alternative will
|
6
|
+
be provided to ensure relatively smooth transition to the new APIs.
|
7
|
+
|
8
|
+
== Release 0.7.0 Changes
|
9
|
+
|
10
|
+
- Converted all exceptions thrown on invalid method arguments to from 'RuntimeError' to 'ArgumentError'. This impacts the
|
11
|
+
following methods:
|
12
|
+
|
13
|
+
- {Tree::TreeNode#initialize}
|
14
|
+
- {Tree::TreeNode#add}
|
15
|
+
- {Tree::TreeNode#[]}
|
16
|
+
- {Tree::BinaryTreeNode#add}
|
17
|
+
|
18
|
+
- Added {Tree::TreeNode#level} as an alias for {Tree::TreeNode#nodeDepth}
|
19
|
+
|
20
|
+
- Added new methods {Tree::TreeNode#in_degree} and {Tree::TreeNode#out_degree} to report the node's degree stats
|
21
|
+
|
22
|
+
- {Tree::TreeNode#isOnlyChild?} now returns +true+ for a root node.
|
23
|
+
|
24
|
+
- {Tree::TreeNode#nextSibling} and {Tree::TreeNode#previousSibling} now return +nil+ for a root node.
|
25
|
+
|
26
|
+
- {Tree::TreeNode#add} and {Tree::TreeNode#<<} now throw an ArgumentError exception if a +nil+ node is passed as an argument.
|
27
|
+
|
28
|
+
- Added new methods {Tree::TreeNode#to_json} and {Tree::TreeNode::json_create} to convert to/from the JSON format.
|
29
|
+
Thanks to Dirk[http://github.com/railsbros-dirk] for this change.
|
30
|
+
|
31
|
+
== Release 0.6.1 Changes
|
32
|
+
|
33
|
+
- Deprecated the {Tree::TreeNode#depth} method as it was returning an incorrect depth value. Have introduced a new replacement
|
34
|
+
method {Tree::TreeNode#nodeDepth} which returns the correct result.
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
# Local Variables:
|
40
|
+
# mode: text
|
41
|
+
# coding: utf-8-unix
|
42
|
+
# End:
|
data/COPYING
CHANGED
@@ -27,3 +27,17 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
|
|
27
27
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
|
29
29
|
$Id$
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
# Local Variables:
|
41
|
+
# mode: text
|
42
|
+
# coding: utf-8-unix
|
43
|
+
# End:
|
data/History.txt
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
=== 0.7.0 / 2010-05-03
|
2
|
+
|
3
|
+
* Added new methods to report the degree statistics of a node.
|
4
|
+
|
5
|
+
* Added a convenience method alias 'level' to 'nodeDepth'.
|
6
|
+
|
7
|
+
* Converted the exceptions thrown on invalid arguments to 'ArgumentError' instead of 'RuntimeError'.
|
8
|
+
|
9
|
+
* Converted the documentation to Yard format.
|
10
|
+
|
11
|
+
* Added new methods for converting from/to JSON formats. Thanks to Dirk Breuer[http://github.com/railsbros-dirk] for
|
12
|
+
this fork[http://github.com/galaxycats/].
|
13
|
+
|
14
|
+
* Added a separate API-CHANGES documentation file.
|
15
|
+
|
16
|
+
* Added fixes for root related edge conditions to 'isOnlyChild?', 'nextSibling', 'previousSibling' and 'remove' methods.
|
17
|
+
|
18
|
+
* Removed the 'ChangeLog' file as this can now be generated from the git logs.
|
19
|
+
|
20
|
+
* Other minor code cleanup.
|
21
|
+
|
1
22
|
=== 0.6.2 / 2010-01-30
|
2
23
|
|
3
24
|
* Updated the documentation.
|
data/Manifest.txt
CHANGED
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
=
|
1
|
+
= RubyTree
|
2
2
|
__ _ _
|
3
3
|
/__\_ _| |__ _ _| |_ _ __ ___ ___
|
4
4
|
/ \// | | | '_ \| | | | __| '__/ _ \/ _ \
|
@@ -6,8 +6,9 @@
|
|
6
6
|
\/ \_/\__,_|_.__/ \__, |\__|_| \___|\___|
|
7
7
|
|___/
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
Copyright (c) 2006, 2007, 2008, 2009, 2010 Anupam Sengupta (anupamsg at gmail dot com)
|
10
|
+
|
11
|
+
http://rubytree.rubyforge.org
|
11
12
|
|
12
13
|
== DESCRIPTION:
|
13
14
|
|
@@ -52,7 +53,7 @@ As an example, the following code-snippet implements this tree structure:
|
|
52
53
|
child1 = root_node["CHILD1"]
|
53
54
|
grand_child1 = root_node["CHILD1"]["GRANDCHILD1"]
|
54
55
|
|
55
|
-
# .....
|
56
|
+
# ..... Lets retrieve siblings of the current node as an array.
|
56
57
|
siblings_of_child1 = child1.siblings
|
57
58
|
|
58
59
|
# ..... Lets retrieve immediate children of the root node as an array.
|
@@ -68,10 +69,16 @@ As an example, the following code-snippet implements this tree structure:
|
|
68
69
|
== REQUIREMENTS:
|
69
70
|
|
70
71
|
* Ruby 1.8+ (http://www.ruby-lang.org)
|
71
|
-
* Hoe (http://seattlerb.rubyforge.org/hoe/Hoe.html) Rubygem
|
72
72
|
|
73
73
|
* Optional but recommended:
|
74
74
|
* structured_warnings (http://github.com/schmidt/structured_warnings) Rubygem
|
75
|
+
* Yard (http://yardoc.org) Rubygem for the documentation
|
76
|
+
* JSON (http://flori.github.com/json) Rubygem for converting to/from the JSON format
|
77
|
+
|
78
|
+
* Development dependencies (not required for installing the gem):
|
79
|
+
* Hoe (http://seattlerb.rubyforge.org/hoe/Hoe.html) Rubygem
|
80
|
+
* gemcutter (http://gemcutter.org/gems/gemcutter) Rubygem
|
81
|
+
* Rubyforge (http://codeforpeople.rubyforge.org/rubyforge) Rubygem
|
75
82
|
|
76
83
|
== INSTALL:
|
77
84
|
|
@@ -119,20 +126,63 @@ From a command line/terminal prompt, you can issue the following command to view
|
|
119
126
|
|
120
127
|
ri Tree::TreeNode
|
121
128
|
|
122
|
-
Documentation
|
129
|
+
Documentation for the latest released version is available at:
|
123
130
|
|
124
131
|
http://rubytree.rubyforge.org/rdoc
|
125
132
|
|
133
|
+
Documentation for the latest git HEAD is available at:
|
134
|
+
|
135
|
+
http://rdoc.info/projects/evolve75/RubyTree
|
136
|
+
|
137
|
+
Note that the documentation is formatted for Yard (http://yardoc.org).
|
138
|
+
|
126
139
|
== DEVELOPERS:
|
127
140
|
|
141
|
+
You can download the latest released source code using the tar or zip version as mentioned above in the installation
|
142
|
+
section.
|
143
|
+
|
144
|
+
Alternatively, you can checkout the latest commit/revision from the version control system. Note that RubyTree's
|
145
|
+
primary SCM[http://en.wikipedia.org/wiki/Source_Code_Management] is on git[http://git-scm.com] and is
|
146
|
+
also mirrored on github[http://www.github.com].
|
147
|
+
|
148
|
+
=== Using the Git repository
|
149
|
+
|
150
|
+
For checking out from the primary Git repository, use the following command:
|
151
|
+
|
152
|
+
$ git clone git://rubyforge.org/rubytree.git
|
153
|
+
|
154
|
+
The Git repository is available for browsing on the web at: http://fisheye2.atlassian.com/browse/rubytree
|
155
|
+
|
156
|
+
=== Using the Git repository on http://github.com
|
157
|
+
|
158
|
+
For cloning the git repository, use one of the following commands:
|
159
|
+
|
160
|
+
$ git clone git://github.com/evolve75/RubyTree.git
|
161
|
+
|
162
|
+
or
|
163
|
+
$ git clone http://github.com/evolve75/RubyTree.git
|
164
|
+
|
165
|
+
The git repository is available on the web at: http://github.com/evolve75/RubyTree
|
166
|
+
|
167
|
+
=== Setting up the development environment
|
168
|
+
|
128
169
|
After checking out the source, run:
|
129
170
|
|
130
171
|
$ rake newb
|
131
172
|
|
132
173
|
This task will install any missing dependencies, run the tests/specs, and generate the RDoc.
|
133
174
|
|
134
|
-
Note that you need to have the Rubygem
|
135
|
-
tasks.
|
175
|
+
Note that you need to have the Rubygem Hoe[http://seattlerb.rubyforge.org/hoe/Hoe.html] to successfully run the rake
|
176
|
+
tasks. Installing Hoe may also install additional pre-requisite gems. See the REQUIREMENTS section in this document for
|
177
|
+
details.
|
178
|
+
|
179
|
+
For generating the documentation, it is strongly suggested that the Yard[http://yardoc.org] gem be installed.
|
180
|
+
|
181
|
+
== ACKNOWLEDGMENTS:
|
182
|
+
|
183
|
+
I would like to acknowledge the following contributors for helping improve RubyTree:
|
184
|
+
|
185
|
+
1. Dirk Breuer (http://github.com/railsbros-dirk) for contributing the JSON conversion code.
|
136
186
|
|
137
187
|
== LICENSE:
|
138
188
|
|
@@ -163,3 +213,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
163
213
|
|
164
214
|
|
165
215
|
(Document Revision: $Revision$ by $Author$)
|
216
|
+
|
217
|
+
# Local Variables:
|
218
|
+
# mode: text
|
219
|
+
# coding: utf-8-unix
|
220
|
+
# End:
|
data/Rakefile
CHANGED
@@ -43,16 +43,20 @@ task :default => :gem
|
|
43
43
|
# Use Hoe to define the rake tasks.
|
44
44
|
begin
|
45
45
|
require 'hoe'
|
46
|
+
Hoe.plugin :yard
|
47
|
+
|
46
48
|
Hoe.spec PKG_NAME do
|
47
49
|
# The GemSpec settings
|
48
50
|
self.rubyforge_name = PKG_NAME
|
49
51
|
developer "Anupam Sengupta", "anupamsg@gmail.com"
|
50
|
-
|
52
|
+
|
51
53
|
self.url = "http://rubytree.rubyforge.org"
|
52
54
|
self.readme_file = 'README'
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
|
56
|
+
# Set the Yard Options
|
57
|
+
extra_docs = ["COPYING", "API-CHANGES"]
|
58
|
+
extra_docs.each { |file| self.yard_files << file }
|
59
|
+
self.yard_options = ["--files", extra_docs.join(",") ]
|
56
60
|
|
57
61
|
# Now the publishing settings
|
58
62
|
self.remote_rdoc_dir = 'rdoc'
|
@@ -65,13 +69,15 @@ begin
|
|
65
69
|
self.post_install_message = <<MSGEND
|
66
70
|
========================================================================
|
67
71
|
|
68
|
-
Thank you for installing #{PKG_NAME
|
72
|
+
Thank you for installing #{PKG_NAME}.
|
69
73
|
|
70
|
-
Please note that a few APIs have been deprecated since Version 0.6.1
|
74
|
+
Please note that a few APIs have been deprecated since Version 0.6.1.
|
71
75
|
|
72
76
|
Specifically, the 'Tree::TreeNode#depth' method is now deprecated, and
|
73
77
|
a new nodeDepth() method has been introduced.
|
74
78
|
|
79
|
+
Details of the API changes are documented in the API-CHANGES file.
|
80
|
+
|
75
81
|
========================================================================
|
76
82
|
MSGEND
|
77
83
|
|
data/TODO
CHANGED
@@ -1,24 +1,52 @@
|
|
1
1
|
# -*- mode: org; coding: utf-8-unix; -*-
|
2
2
|
|
3
|
-
* TODO Convert all method names to the canonical /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ pattern
|
4
|
-
See Roodi report at http://getcaliper.com/caliper/tool?tool=roodi&repo=git://github.com/evolve75/RubyTree.git
|
5
|
-
* TODO Fix the TreeNode#root method to return nil for root's root.
|
6
|
-
* TODO Fix the marshal_load method. This probably needs to be a class method.
|
7
|
-
* TODO Fix the semantic inconsistency between the TreeNode#first|lastSibling method and the siblings method w.r.t. the root
|
8
|
-
* TODO Create the basic UML diagrams and upload to the Site
|
9
|
-
DEADLINE: <2010-01-04 Mon>
|
10
3
|
|
11
|
-
*
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
4
|
+
* R0.7.0
|
5
|
+
*** TODO Start using signed tags from R0.7.0
|
6
|
+
*** DONE Add a check in the Tree::TreeNode.add method to prevent addition of nil child nodes
|
7
|
+
CLOSED: [2010-02-23 Tue 23:07]
|
8
|
+
*** DONE Fix the edge condition for Tree::TreeNode.isOnlyChild? when the root node is the receiver.
|
9
|
+
CLOSED: [2010-02-23 Tue 22:03]
|
10
|
+
There really is no good default to this situation. We will return 'true' simply because there is no other sibling
|
11
|
+
to a root. However, a good case can be made that a root node does not have any parent either.
|
12
|
+
*** DONE Add a convenience 'level' method to the TreeNode class (will be an alias to nodeDepth)
|
13
|
+
CLOSED: [2010-02-21 Sun 01:02]
|
14
|
+
*** DONE Add a API-CHANGES file to document the various API changes made till date
|
15
|
+
CLOSED: [2010-01-31 Sun 00:52]
|
16
|
+
*** DONE Add new methods to return the degree counts of the receiver node (in-degree and out-degree)
|
17
|
+
CLOSED: [2010-01-30 Sat 23:56]
|
18
|
+
|
19
|
+
* R0.8.0
|
20
|
+
*** TODO Convert all method names to the canonical /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ pattern
|
21
|
+
See Roodi report at http://getcaliper.com/caliper/tool?tool=roodi&repo=git://github.com/evolve75/RubyTree.git
|
22
|
+
*** TODO Fix the inconsistency of returning root as its first sibling, and returning a nil instead. Ditto for last sibling.
|
23
|
+
*** TODO fix the inconsistency of returning nil for the root, and an empty array for nodes which have no siblings.
|
24
|
+
*** TODO We should perhaps return nil as root's root.
|
25
|
+
*** TODO The semantic of length is probably unclear. Should return the node_depth instead (or remove the method)
|
26
|
+
The current equivalence of length to size should also be removed.
|
27
|
+
|
28
|
+
|
29
|
+
* Unplanned / Not assigned to any release
|
30
|
+
*** TODO Fix the TreeNode#root method to return nil for root's root.
|
31
|
+
*** TODO Fix the marshal_load method. This probably needs to be a class method.
|
32
|
+
*** TODO Fix the semantic inconsistency between the TreeNode#first|lastSibling method and the siblings method w.r.t. the root
|
33
|
+
*** TODO Create the basic UML diagrams and upload to the Site
|
34
|
+
DEADLINE: <2010-01-04 Mon>
|
35
|
+
|
36
|
+
*** DONE Fix bug # [[http://rubyforge.org/tracker/index.php%3Ffunc%3Ddetail&aid%3D22535&group_id%3D1215&atid%3D4793][22535]]: The method Tree::TreeNode#depth is a misnomer. The current definition actually provides the height function.
|
37
|
+
DEADLINE: <2010-01-09 Sat> CLOSED: [2010-01-03 Sun 22:15]
|
38
|
+
|
39
|
+
*** TODO Add a YAML export method to the TreeNode class.
|
40
|
+
|
41
|
+
*** DONE Get the version control moved from CVS to Subversion (request submitted to RubyForge)
|
42
|
+
CLOSED: [2010-01-02 Sat 17:58]
|
43
|
+
|
44
|
+
*** DONE Add logic in Rakefile to read the file list from Manifest.txt file.
|
20
45
|
CLOSED: [2009-12-31 Thu 23:37]
|
46
|
+
*** TODO Fix the inconsistency of returning root as its last sibling, and returning a nil array for siblings of the node
|
47
|
+
*** TODO marshal_load method probably should be a class method. It currently clobbers self.
|
48
|
+
*** TODO Fix the inconsistency of returning root as its first/last sibling, and returning a nil array for siblings of the node
|
21
49
|
|
50
|
+
$Id$
|
22
51
|
|
23
52
|
|
24
|
-
$Id$
|
data/lib/tree.rb
CHANGED
@@ -44,39 +44,39 @@
|
|
44
44
|
# This module provides a TreeNode class which is the primary class for representing
|
45
45
|
# nodes in the tree.
|
46
46
|
#
|
47
|
-
# This module
|
48
|
-
# classes in RubyTree.
|
47
|
+
# This module also acts as the namespace for all classes in the RubyTree package.
|
49
48
|
module Tree
|
50
49
|
|
51
50
|
# Rubytree Package Version
|
52
|
-
VERSION = '0.
|
51
|
+
VERSION = '0.7.0'
|
53
52
|
|
54
53
|
# == TreeNode Class Description
|
55
54
|
#
|
56
|
-
# This class models the nodes for an N-ary tree data structue. The nodes are
|
57
|
-
# and have a place-holder for the node data (i.e.,
|
55
|
+
# This class models the nodes for an *N-ary* tree data structue. The nodes are *named*
|
56
|
+
# and have a place-holder for the node data (i.e., _content_ of the node). The node
|
58
57
|
# names are required to be *unique* within the tree.
|
59
58
|
#
|
60
|
-
# The node
|
59
|
+
# The node's _content_ is *not* required to be unique across different nodes in the tree, and
|
61
60
|
# can be +nil+ as well.
|
62
61
|
#
|
63
|
-
# The class provides various
|
64
|
-
#
|
65
|
-
# and
|
62
|
+
# The class provides various methods to navigate the tree, traverse the structure,
|
63
|
+
# modify contents of the node, change position of the node in the tree,
|
64
|
+
# and to make structural changes to the tree.
|
66
65
|
#
|
67
|
-
# A node can have any number of child nodes attached to it and hence can be used to create N-ary trees.
|
66
|
+
# A node can have any number of *child* nodes attached to it and hence can be used to create N-ary trees.
|
68
67
|
# Access to the child nodes can be made in order (with the conventional left to right access), or
|
69
68
|
# randomly.
|
70
69
|
#
|
71
|
-
# The node also provides direct access to its parent
|
72
|
-
# root of the tree. In addition, a node can also access its sibling nodes, if present.
|
73
|
-
#
|
74
|
-
#
|
70
|
+
# The node also provides direct access to its *parent* node as well as other superior parents in the path to
|
71
|
+
# root of the tree. In addition, a node can also access its *sibling* nodes, if present.
|
72
|
+
#
|
73
|
+
# Note that while this implementation does not _explicitly_ support directed graphs, the class itself makes
|
74
|
+
# no restrictions on associating a node's *content* with multiple nodes in the tree.
|
75
75
|
#
|
76
76
|
#
|
77
77
|
# == Example
|
78
78
|
#
|
79
|
-
#
|
79
|
+
# The following example implements this tree structure:
|
80
80
|
#
|
81
81
|
# +------------+
|
82
82
|
# | ROOT |
|
@@ -92,30 +92,36 @@ module Tree
|
|
92
92
|
# | GRANDCHILD 1 |
|
93
93
|
# +---------------+
|
94
94
|
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
# root_node = Tree::TreeNode.new("ROOT", "Root Content")
|
98
|
-
#
|
99
|
-
# root_node << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
|
100
|
-
#
|
101
|
-
# root_node << Tree::TreeNode.new("CHILD2", "Child2 Content")
|
95
|
+
# # ..... Example starts.
|
96
|
+
# require 'tree' # Load the library
|
102
97
|
#
|
103
|
-
#
|
98
|
+
# # ..... Create the root node first. Note that every node has a name and an optional content payload.
|
99
|
+
# root_node = Tree::TreeNode.new("ROOT", "Root Content")
|
104
100
|
#
|
105
|
-
#
|
101
|
+
# # ..... Now insert the child nodes. Note that you can "chain" the child insertions for a given path to any depth.
|
102
|
+
# root_node << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
|
103
|
+
# root_node << Tree::TreeNode.new("CHILD2", "Child2 Content")
|
106
104
|
#
|
107
|
-
#
|
105
|
+
# # ..... Lets print the representation to stdout. This is primarily used for debugging purposes.
|
106
|
+
# root_node.printTree
|
108
107
|
#
|
109
|
-
#
|
108
|
+
# # ..... Lets directly access children and grandchildren of the root. The can be "chained" for a given path to any depth.
|
109
|
+
# child1 = root_node["CHILD1"]
|
110
|
+
# grand_child1 = root_node["CHILD1"]["GRANDCHILD1"]
|
110
111
|
#
|
111
|
-
#
|
112
|
+
# # ..... Now lets retrieve siblings of the current node as an array.
|
113
|
+
# siblings_of_child1 = child1.siblings
|
112
114
|
#
|
113
|
-
#
|
115
|
+
# # ..... Lets retrieve immediate children of the root node as an array.
|
116
|
+
# children_of_root = root_node.children
|
114
117
|
#
|
115
|
-
#
|
118
|
+
# # ..... This is a depth-first and L-to-R pre-ordered traversal.
|
119
|
+
# root_node.each { |node| node.content.reverse }
|
116
120
|
#
|
117
|
-
#
|
121
|
+
# # ..... Lets remove a child node from the root node.
|
122
|
+
# root_node.remove!(child1)
|
118
123
|
#
|
124
|
+
# @author Anupam Sengupta
|
119
125
|
class TreeNode
|
120
126
|
include Enumerable
|
121
127
|
|
@@ -125,31 +131,40 @@ module Tree
|
|
125
131
|
# Content of this node. Can be +nil+.
|
126
132
|
attr_accessor :content
|
127
133
|
|
128
|
-
# Parent of this node. Will be +nil+ for root
|
134
|
+
# Parent of this node. Will be +nil+ for a root node.
|
129
135
|
attr_reader :parent
|
130
136
|
|
131
137
|
|
132
|
-
#
|
133
|
-
#
|
138
|
+
# Creates a new node with a name and optional content.
|
139
|
+
# The node name is expected to be unique within the tree.
|
134
140
|
#
|
135
|
-
# The content can be of any type, defaults to +nil+.
|
141
|
+
# The content can be of any type, and defaults to +nil+.
|
142
|
+
#
|
143
|
+
# @param [Object] name Name of the node. Usual usage is to pass a String.
|
144
|
+
# @param [Object] content Content of the node.
|
145
|
+
#
|
146
|
+
# @raise [ArgumentError] Raised if the node name is empty.
|
136
147
|
def initialize(name, content = nil)
|
137
|
-
raise "Node name HAS to be provided" if name == nil
|
138
|
-
@name = name
|
139
|
-
@content = content
|
140
|
-
self.setAsRoot!
|
148
|
+
raise ArgumentError, "Node name HAS to be provided!" if name == nil
|
149
|
+
@name, @content = name, content
|
141
150
|
|
151
|
+
self.setAsRoot!
|
142
152
|
@childrenHash = Hash.new
|
143
153
|
@children = []
|
144
154
|
end
|
145
155
|
|
146
156
|
# Returns a copy of the receiver node, with its parent and children links removed.
|
147
157
|
# The original node remains attached to its tree.
|
158
|
+
#
|
159
|
+
# @return [Tree::TreeNode] A copy of the receiver node.
|
148
160
|
def detached_copy
|
149
161
|
Tree::TreeNode.new(@name, @content ? @content.clone : nil)
|
150
162
|
end
|
151
163
|
|
152
|
-
#
|
164
|
+
# Returns string representation of the receiver node.
|
165
|
+
# This method is primarily meant for debugging purposes.
|
166
|
+
#
|
167
|
+
# @return [String] A string representation of the node.
|
153
168
|
def to_s
|
154
169
|
"Node Name: #{@name}" +
|
155
170
|
" Content: " + (@content || "<Empty>") +
|
@@ -162,6 +177,8 @@ module Tree
|
|
162
177
|
# (the first element is the immediate parent of the receiver).
|
163
178
|
#
|
164
179
|
# Returns +nil+ if the receiver is a root node.
|
180
|
+
#
|
181
|
+
# @return [Array, nil] An array of ancestors of the receiver node, or +nil+ if this is a root node.
|
165
182
|
def parentage
|
166
183
|
return nil if isRoot?
|
167
184
|
|
@@ -178,19 +195,26 @@ module Tree
|
|
178
195
|
# Protected method to set the parent node for the receiver node.
|
179
196
|
# This method should *NOT* be invoked by client code.
|
180
197
|
#
|
181
|
-
#
|
198
|
+
# @param [Tree::TreeNode] parent The parent node.
|
199
|
+
#
|
200
|
+
# @return [Tree::TreeNode] The parent node.
|
182
201
|
def parent=(parent) # :nodoc:
|
183
202
|
@parent = parent
|
184
203
|
end
|
185
204
|
|
186
|
-
# Convenience synonym for TreeNode#add method.
|
205
|
+
# Convenience synonym for {Tree::TreeNode#add} method.
|
187
206
|
#
|
188
|
-
# This method allows an easy
|
207
|
+
# This method allows an easy mechanism to add node hierarchies to the tree
|
189
208
|
# on a given path via chaining the method calls to successive child nodes.
|
190
209
|
#
|
191
|
-
#
|
210
|
+
# @example Add a child and grand-child to the root
|
211
|
+
# root << child << grand_child
|
192
212
|
#
|
193
|
-
#
|
213
|
+
# @param [Tree::TreeNode] child the child node to add.
|
214
|
+
#
|
215
|
+
# @return [Tree::TreeNode] The added child node.
|
216
|
+
#
|
217
|
+
# @see Tree::TreeNode#add
|
194
218
|
def <<(child)
|
195
219
|
add(child)
|
196
220
|
end
|
@@ -204,11 +228,18 @@ module Tree
|
|
204
228
|
# the child is added as the last child ("right most") in the current set of
|
205
229
|
# children of the receiver node.
|
206
230
|
#
|
207
|
-
#
|
231
|
+
# @param [Tree::TreeNode] child The child node to add.
|
232
|
+
#
|
233
|
+
# @return [Tree::TreeNode] The added child node.
|
208
234
|
#
|
209
|
-
#
|
235
|
+
# @raise [RuntimeError] This exception is raised if another child node with the same
|
236
|
+
# name exists.
|
237
|
+
# @raise [ArgumentError] This exception is raised if a +nil+ node is passed as the argument.
|
238
|
+
#
|
239
|
+
# @see #<<
|
210
240
|
def add(child)
|
211
|
-
raise "
|
241
|
+
raise ArgumentError, "Attempting to add a nil node" unless child
|
242
|
+
raise "Child #{child.name} already added!" if @childrenHash.has_key?(child.name)
|
212
243
|
|
213
244
|
@childrenHash[child.name] = child
|
214
245
|
@children << child
|
@@ -218,32 +249,45 @@ module Tree
|
|
218
249
|
|
219
250
|
# Removes the specified child node from the receiver node.
|
220
251
|
#
|
221
|
-
# This method can also be used for *pruning* a
|
222
|
-
# the root of the
|
252
|
+
# This method can also be used for *pruning* a sub-tree, in cases where the removed child node is
|
253
|
+
# the root of the sub-tree to be pruned.
|
254
|
+
#
|
255
|
+
# The removed child node is orphaned but accessible if an alternate reference exists. If accessible via
|
256
|
+
# an alternate reference, the removed child will report itself as a root node for its sub-tree.
|
223
257
|
#
|
224
|
-
#
|
225
|
-
# an alternate reference, the removed child will report itself as a root node for its subtree.
|
258
|
+
# @param [Tree::TreeNode] child The child node to remove.
|
226
259
|
#
|
227
|
-
#
|
260
|
+
# @return [Tree::TreeNode] The removed child node, or +nil+ if a +nil+ was passed in as argument.
|
261
|
+
#
|
262
|
+
# @see #removeFromParent!
|
263
|
+
# @see #removeAll!
|
228
264
|
def remove!(child)
|
265
|
+
return nil unless child
|
266
|
+
|
229
267
|
@childrenHash.delete(child.name)
|
230
268
|
@children.delete(child)
|
231
|
-
child.setAsRoot!
|
232
|
-
|
269
|
+
child.setAsRoot!
|
270
|
+
child
|
233
271
|
end
|
234
272
|
|
235
273
|
# Removes the receiver node from its parent. The reciever node becomes the new root for its subtree.
|
236
274
|
#
|
237
275
|
# If this is the root node, then does nothing.
|
238
276
|
#
|
239
|
-
#
|
277
|
+
# @return [Tree:TreeNode] +self+ (the removed receiver node) if the operation is successful, +nil+ otherwise.
|
278
|
+
#
|
279
|
+
# @see #removeAll!
|
240
280
|
def removeFromParent!
|
241
281
|
@parent.remove!(self) unless isRoot?
|
242
282
|
end
|
243
283
|
|
244
|
-
# Removes all children from the receiver node.
|
284
|
+
# Removes all children from the receiver node. If an indepedent reference exists to the child
|
285
|
+
# nodes, then these child nodes report themselves as roots after this operation.
|
245
286
|
#
|
246
|
-
#
|
287
|
+
# @return [Tree::TreeNode] The receiver node (+self+)
|
288
|
+
#
|
289
|
+
# @see #remove!
|
290
|
+
# @see #removeFromParent!
|
247
291
|
def removeAll!
|
248
292
|
for child in @children
|
249
293
|
child.setAsRoot!
|
@@ -253,38 +297,56 @@ module Tree
|
|
253
297
|
self
|
254
298
|
end
|
255
299
|
|
256
|
-
# Returns +true+ if the receiver node has
|
300
|
+
# Returns +true+ if the receiver node has content.
|
301
|
+
#
|
302
|
+
# @return [Boolean] +true+ if the node has content.
|
257
303
|
def hasContent?
|
258
304
|
@content != nil
|
259
305
|
end
|
260
306
|
|
261
307
|
# Protected method which sets the receiver node as a root node.
|
262
308
|
#
|
263
|
-
#
|
309
|
+
# @return +nil+.
|
264
310
|
def setAsRoot! # :nodoc:
|
265
311
|
@parent = nil
|
266
312
|
end
|
267
313
|
|
268
314
|
# Returns +true+ if the receiver is a root node. Note that
|
269
315
|
# orphaned children will also be reported as root nodes.
|
316
|
+
#
|
317
|
+
# @return [Boolean] +true+ if this is a root node.
|
270
318
|
def isRoot?
|
271
319
|
@parent == nil
|
272
320
|
end
|
273
321
|
|
274
|
-
# Returns +true+ if the receiver node has any
|
322
|
+
# Returns +true+ if the receiver node has any child node.
|
323
|
+
#
|
324
|
+
# @return [Boolean] +true+ if child nodes exist.
|
325
|
+
#
|
326
|
+
# @see #isLeaf?
|
275
327
|
def hasChildren?
|
276
328
|
@children.length != 0
|
277
329
|
end
|
278
330
|
|
279
331
|
# Returns +true+ if the receiver node is a 'leaf' - i.e., one without
|
280
332
|
# any children.
|
333
|
+
#
|
334
|
+
# @return [Boolean] +true+ if this is a leaf node.
|
335
|
+
#
|
336
|
+
# @see #hasChildren?
|
281
337
|
def isLeaf?
|
282
338
|
!hasChildren?
|
283
339
|
end
|
284
340
|
|
285
|
-
# Returns an array of all the immediate children of the receiver node.
|
341
|
+
# Returns an array of all the immediate children of the receiver node. The child nodes are ordered
|
342
|
+
# "left-to-right" in the returned array.
|
286
343
|
#
|
287
344
|
# If a block is given, yields each child node to the block traversing from left to right.
|
345
|
+
#
|
346
|
+
# @yield [child] Each child is passed to the block, if given
|
347
|
+
# @yieldparam [Tree::TreeNode] child Each child node.
|
348
|
+
#
|
349
|
+
# @return [Array<Tree::TreeNode>] An array of the child nodes, if no block is given.
|
288
350
|
def children
|
289
351
|
if block_given?
|
290
352
|
@children.each {|child| yield child}
|
@@ -296,6 +358,8 @@ module Tree
|
|
296
358
|
# Returns the first child of the receiver node.
|
297
359
|
#
|
298
360
|
# Will return +nil+ if no children are present.
|
361
|
+
#
|
362
|
+
# @return [Tree::TreeNode] The first child, or +nil+ if none is present.
|
299
363
|
def firstChild
|
300
364
|
children.first
|
301
365
|
end
|
@@ -303,28 +367,49 @@ module Tree
|
|
303
367
|
# Returns the last child of the receiver node.
|
304
368
|
#
|
305
369
|
# Will return +nil+ if no children are present.
|
370
|
+
#
|
371
|
+
# @return [Tree::TreeNode] The last child, or +nil+ if none is present.
|
306
372
|
def lastChild
|
307
373
|
children.last
|
308
374
|
end
|
309
375
|
|
310
|
-
# Traverses
|
311
|
-
# by yielding the
|
376
|
+
# Traverses each node (including the receiver node) of the (sub)tree rooted at this node
|
377
|
+
# by yielding the nodes to the specified block.
|
378
|
+
#
|
379
|
+
# The traversal is *depth-first* and from *left-to-right* in pre-ordered sequence.
|
380
|
+
#
|
381
|
+
# @yield [child] Each node is passed to the block.
|
382
|
+
# @yieldparam [Tree::TreeNode] child Each node.
|
312
383
|
#
|
313
|
-
#
|
314
|
-
|
384
|
+
# @see #preordered_each
|
385
|
+
# @see #breadth_each
|
386
|
+
def each(&block) # :yields: node
|
315
387
|
yield self
|
316
388
|
children { |child| child.each(&block) }
|
317
389
|
end
|
318
390
|
|
319
|
-
# Traverses the tree in
|
320
|
-
# TreeNode#each
|
321
|
-
|
391
|
+
# Traverses the (sub)tree rooted at the receiver node in pre-ordered sequence.
|
392
|
+
# This is a synonym of {Tree::TreeNode#each}.
|
393
|
+
#
|
394
|
+
# @yield [child] Each child is passed to the block.
|
395
|
+
# @yieldparam [Tree::TreeNode] node Each node.
|
396
|
+
#
|
397
|
+
# @see #each
|
398
|
+
# @see #breadth_each
|
399
|
+
def preordered_each(&block) # :yields: node
|
322
400
|
each(&block)
|
323
401
|
end
|
324
402
|
|
325
|
-
# Performs breadth
|
326
|
-
# traversal at a given level is from left
|
327
|
-
|
403
|
+
# Performs breadth-first traversal of the (sub)tree rooted at the receiver node. The
|
404
|
+
# traversal at a given level is from *left-to-right*. The receiver node itself is the first
|
405
|
+
# node to be traversed.
|
406
|
+
#
|
407
|
+
# @yield [child] Each node is passed to the block.
|
408
|
+
# @yieldparam [Tree::TreeNode] node Each node.
|
409
|
+
#
|
410
|
+
# @see #preordered_each
|
411
|
+
# @see #breadth_each
|
412
|
+
def breadth_each(&block)
|
328
413
|
node_queue = [self] # Create a queue with self as the initial entry
|
329
414
|
|
330
415
|
# Use a queue to do breadth traversal
|
@@ -336,23 +421,39 @@ module Tree
|
|
336
421
|
end
|
337
422
|
end
|
338
423
|
|
339
|
-
# Yields
|
424
|
+
# Yields every leaf node of the (sub)tree rooted at the receiver node to the specified block.
|
340
425
|
#
|
341
426
|
# May yield this node as well if this is a leaf node.
|
342
|
-
# Leaf traversal is depth-first and left
|
427
|
+
# Leaf traversal is *depth-first* and *left-to-right*.
|
428
|
+
#
|
429
|
+
# @yield [node] Each leaf node is passed to the block.
|
430
|
+
# @yieldparam [Tree::TreeNode] node Each leaf node.
|
431
|
+
#
|
432
|
+
# @see #each
|
433
|
+
# @see #breadth_each
|
343
434
|
def each_leaf &block
|
344
435
|
self.each { |node| yield(node) if node.isLeaf? }
|
345
436
|
end
|
346
437
|
|
347
438
|
# Returns the requested node from the set of immediate children.
|
348
439
|
#
|
349
|
-
# If the argument is _numeric_, then the in-sequence array of children is
|
350
|
-
#
|
351
|
-
#
|
440
|
+
# If the argument is _numeric_, then the in-sequence array of children is accessed using
|
441
|
+
# the argument as the *index* (zero-based).
|
442
|
+
#
|
443
|
+
# If the argument is *NOT* _numeric_, then it is taken to be the *name* of the child node to be returned.
|
444
|
+
#
|
445
|
+
# An ArgumentError exception is raised if neither name nor an index is provided.
|
446
|
+
#
|
447
|
+
# @param [String|Number] name_or_index Name of the child, or its positional index in the array of child nodes.
|
448
|
+
#
|
449
|
+
# @return [Tree::TreeNode] the requested child node. If the index in not in range, or the name is not
|
450
|
+
# present, then a +nil+ is returned.
|
451
|
+
#
|
452
|
+
# @raise [ArgumentError] Raised if neither name nor index is provided.
|
352
453
|
#
|
353
|
-
#
|
454
|
+
# @see #add
|
354
455
|
def [](name_or_index)
|
355
|
-
raise "Name_or_index needs to be provided" if name_or_index == nil
|
456
|
+
raise ArgumentError, "Name_or_index needs to be provided!" if name_or_index == nil
|
356
457
|
|
357
458
|
if name_or_index.kind_of?(Integer)
|
358
459
|
@children[name_or_index]
|
@@ -366,16 +467,28 @@ module Tree
|
|
366
467
|
# Size of the tree is defined as:
|
367
468
|
#
|
368
469
|
# Size:: Total number nodes in the subtree including the receiver node.
|
470
|
+
#
|
471
|
+
# @return [Number] Total number of nodes in this (sub)tree.
|
369
472
|
def size
|
370
473
|
@children.inject(1) {|sum, node| sum + node.size}
|
371
474
|
end
|
372
475
|
|
373
|
-
# Convenience synonym for Tree#size
|
476
|
+
# Convenience synonym for {Tree::TreeNode#size}.
|
477
|
+
#
|
478
|
+
# @todo The semantic of length is probably unclear. Should return the node depth instead
|
479
|
+
# to reflect the path length.
|
480
|
+
#
|
481
|
+
# @deprecated This method name is ambiguous and may be removed. Use TreeNode#size instead.
|
482
|
+
#
|
483
|
+
# @return [Number] The total number of nodes in this (sub)tree.
|
484
|
+
# @see #size
|
374
485
|
def length
|
375
486
|
size()
|
376
487
|
end
|
377
488
|
|
378
|
-
# Pretty prints the tree
|
489
|
+
# Pretty prints the (sub)tree rooted at the receiver node.
|
490
|
+
#
|
491
|
+
# @param [Number] level The indentation level (4 spaces) to start with.
|
379
492
|
def printTree(level = 0)
|
380
493
|
|
381
494
|
if isRoot?
|
@@ -396,47 +509,66 @@ module Tree
|
|
396
509
|
# Returns root node for the (sub)tree to which the receiver node belongs.
|
397
510
|
#
|
398
511
|
# Note that a root node's root is itself (*beware* of any loop construct that may become infinite!)
|
399
|
-
|
400
|
-
#
|
401
|
-
|
512
|
+
#
|
513
|
+
# @todo We should perhaps return nil as root's root.
|
514
|
+
#
|
515
|
+
# @return [Tree::TreeNode] Root of the (sub)tree.
|
402
516
|
def root
|
403
517
|
root = self
|
404
518
|
root = root.parent while !root.isRoot?
|
405
519
|
root
|
406
520
|
end
|
407
521
|
|
408
|
-
# Returns the first sibling
|
522
|
+
# Returns the first sibling of the receiver node. If this is the root node, then returns
|
409
523
|
# itself.
|
410
524
|
#
|
411
525
|
# 'First' sibling is defined as follows:
|
412
526
|
# First sibling:: The left-most child of the receiver's parent, which may be the receiver itself
|
413
|
-
|
414
|
-
#
|
415
|
-
# a nil array for siblings
|
416
|
-
|
527
|
+
#
|
528
|
+
# @todo Fix the inconsistency of returning root as its first sibling, and returning
|
529
|
+
# a +nil+ array for siblings of the node.
|
530
|
+
#
|
531
|
+
# @return [Tree::TreeNode] The first sibling node.
|
532
|
+
#
|
533
|
+
# @see #isFirstSibling?
|
534
|
+
# @see #lastSibling
|
417
535
|
def firstSibling
|
418
536
|
isRoot? ? self : parent.children.first
|
419
537
|
end
|
420
538
|
|
421
|
-
# Returns true if the receiver node is the first sibling.
|
539
|
+
# Returns +true+ if the receiver node is the first sibling at its level.
|
540
|
+
#
|
541
|
+
# @return [Boolean] +true+ if this is the first sibling.
|
542
|
+
#
|
543
|
+
# @see #isLastSibling?
|
544
|
+
# @see #firstSibling
|
422
545
|
def isFirstSibling?
|
423
546
|
firstSibling == self
|
424
547
|
end
|
425
548
|
|
426
|
-
# Returns the last sibling
|
549
|
+
# Returns the last sibling of the receiver node. If this is the root node, then returns
|
427
550
|
# itself.
|
428
551
|
#
|
429
552
|
# 'Last' sibling is defined as follows:
|
430
553
|
# Last sibling:: The right-most child of the receiver's parent, which may be the receiver itself
|
431
|
-
|
432
|
-
#
|
433
|
-
# a nil array for siblings
|
434
|
-
|
554
|
+
#
|
555
|
+
# @todo Fix the inconsistency of returning root as its last sibling, and returning
|
556
|
+
# a +nil+ array for siblings of the node.
|
557
|
+
#
|
558
|
+
# @return [Tree::TreeNode] The last sibling node.
|
559
|
+
#
|
560
|
+
# @see #isLastSibling?
|
561
|
+
# @see #firstSibling
|
435
562
|
def lastSibling
|
436
563
|
isRoot? ? self : parent.children.last
|
437
564
|
end
|
438
565
|
|
439
|
-
# Returns true if the
|
566
|
+
# Returns +true+ if the receiver node is the last sibling at its level.
|
567
|
+
#
|
568
|
+
# @return [Boolean] +true+ if this is the last sibling.
|
569
|
+
#
|
570
|
+
# @see #isFirstSibling?
|
571
|
+
# @see #lastSibling
|
440
572
|
def isLastSibling?
|
441
573
|
lastSibling == self
|
442
574
|
end
|
@@ -445,10 +577,19 @@ module Tree
|
|
445
577
|
#
|
446
578
|
# If a block is provided, yields each of the sibling nodes to the block.
|
447
579
|
# The root always has +nil+ siblings.
|
448
|
-
|
449
|
-
#
|
450
|
-
# a nil array for siblings
|
451
|
-
|
580
|
+
#
|
581
|
+
# @todo Fix the inconsistency of returning root as its own first/last sibling, and returning
|
582
|
+
# a +nil+ array for siblings of the same root node.
|
583
|
+
# @todo Also fix the inconsistency of returning +nil+ for a root node, and an empty array for nodes
|
584
|
+
# which have no siblings.
|
585
|
+
#
|
586
|
+
# @yield [sibling] Each sibling is passed to the block.
|
587
|
+
# @yieldparam [Tree::TreeNode] sibling Each sibling node.
|
588
|
+
#
|
589
|
+
# @return [Array<Tree::TreeNode>] Array of siblings of this node.
|
590
|
+
#
|
591
|
+
# @see #firstSibling
|
592
|
+
# @see #lastSibling
|
452
593
|
def siblings
|
453
594
|
return nil if isRoot?
|
454
595
|
|
@@ -463,26 +604,44 @@ module Tree
|
|
463
604
|
end
|
464
605
|
end
|
465
606
|
|
466
|
-
# Returns true if the receiver node is the only child of its parent.
|
607
|
+
# Returns +true+ if the receiver node is the only child of its parent.
|
608
|
+
#
|
609
|
+
# As a special case, a root node will always return +true+.
|
610
|
+
#
|
611
|
+
# @return [Boolean] +true+ if this is the only child of its parent.
|
612
|
+
#
|
613
|
+
# @see #siblings
|
467
614
|
def isOnlyChild?
|
468
|
-
parent.children.size == 1
|
615
|
+
isRoot? ? true : parent.children.size == 1
|
469
616
|
end
|
470
617
|
|
471
618
|
# Returns the next sibling for the receiver node.
|
472
619
|
# The 'next' node is defined as the node to right of the receiver node.
|
473
620
|
#
|
474
|
-
# Will return +nil+ if no subsequent node is present.
|
621
|
+
# Will return +nil+ if no subsequent node is present, or if the receiver is a root node.
|
622
|
+
#
|
623
|
+
# @return [Tree::treeNode] the next sibling node, if present.
|
624
|
+
#
|
625
|
+
# @see #previousSibling
|
626
|
+
# @see #siblings
|
475
627
|
def nextSibling
|
628
|
+
return nil if isRoot?
|
476
629
|
if myidx = parent.children.index(self)
|
477
630
|
parent.children.at(myidx + 1)
|
478
631
|
end
|
479
632
|
end
|
480
633
|
|
481
|
-
# Returns the previous sibling
|
482
|
-
#
|
634
|
+
# Returns the previous sibling of the receiver node.
|
635
|
+
# 'Previous' node is defined to be the node to left of the receiver node.
|
636
|
+
#
|
637
|
+
# Will return +nil+ if no predecessor node is present, or if the receiver is a root node.
|
638
|
+
#
|
639
|
+
# @return [Tree::treeNode] the previous sibling node, if present.
|
483
640
|
#
|
484
|
-
#
|
641
|
+
# @see #nextSibling
|
642
|
+
# @see #siblings
|
485
643
|
def previousSibling
|
644
|
+
return nil if isRoot?
|
486
645
|
if myidx = parent.children.index(self)
|
487
646
|
parent.children.at(myidx - 1) if myidx > 0
|
488
647
|
end
|
@@ -490,13 +649,20 @@ module Tree
|
|
490
649
|
|
491
650
|
# Provides a comparision operation for the nodes.
|
492
651
|
#
|
493
|
-
# Comparision is based on the natural character-set ordering of the
|
652
|
+
# Comparision is based on the natural character-set ordering of the node name.
|
653
|
+
#
|
654
|
+
# @param [Tree::TreeNode] other The other node to compare against.
|
655
|
+
#
|
656
|
+
# @return [Number] +1 if this node is a 'successor', 0 if equal and -1 if this node is a 'predecessor'.
|
494
657
|
def <=>(other)
|
495
658
|
return +1 if other == nil
|
496
659
|
self.name <=> other.name
|
497
660
|
end
|
498
661
|
|
499
662
|
# Freezes all nodes in the (sub)tree rooted at the receiver node.
|
663
|
+
#
|
664
|
+
# The nodes become immutable after this operation. In effect, the entire tree's
|
665
|
+
# structure and contents become _read-only_ and cannot be changed.
|
500
666
|
def freezeTree!
|
501
667
|
each {|node| node.freeze}
|
502
668
|
end
|
@@ -514,10 +680,10 @@ module Tree
|
|
514
680
|
# Loads a marshalled dump of a tree and returns the root node of the
|
515
681
|
# reconstructed tree. See the Marshal class for additional details.
|
516
682
|
#
|
517
|
-
|
518
|
-
#
|
683
|
+
#
|
684
|
+
# @todo This method probably should be a class method. It currently clobbers self
|
519
685
|
# and makes itself the root.
|
520
|
-
|
686
|
+
#
|
521
687
|
def marshal_load(dumped_tree_array)
|
522
688
|
nodes = { }
|
523
689
|
for node_hash in dumped_tree_array do
|
@@ -537,12 +703,73 @@ module Tree
|
|
537
703
|
end
|
538
704
|
end
|
539
705
|
|
706
|
+
# Creates a JSON representation of this node including all it's children. This requires the JSON gem to be
|
707
|
+
# available, or else the operation fails with a warning message.
|
708
|
+
#
|
709
|
+
# @author Dirk Breuer (http://github.com/railsbros-dirk)
|
710
|
+
# @since 0.7.0
|
711
|
+
#
|
712
|
+
# @return The JSON representation of this subtree.
|
713
|
+
#
|
714
|
+
# @see Tree::TreeNode.json_create
|
715
|
+
# @see http://flori.github.com/json
|
716
|
+
def to_json(*a)
|
717
|
+
begin
|
718
|
+
require 'json'
|
719
|
+
|
720
|
+
json_hash = {
|
721
|
+
"name" => name,
|
722
|
+
"content" => content,
|
723
|
+
JSON.create_id => self.class.name
|
724
|
+
}
|
725
|
+
|
726
|
+
if hasChildren?
|
727
|
+
json_hash["children"] = children
|
728
|
+
end
|
729
|
+
|
730
|
+
return json_hash.to_json
|
731
|
+
|
732
|
+
rescue LoadError => e
|
733
|
+
warn "The JSON gem couldn't be loaded. Due to this we cannot serialize the tree to a JSON representation"
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
# Creates a Tree::TreeNode object instance from a given JSON Hash representation. This requires the JSON gem to be
|
738
|
+
# available, or else the operation fails with a warning message.
|
739
|
+
#
|
740
|
+
# @author Dirk Breuer (http://github.com/railsbros-dirk)
|
741
|
+
# @since 0.7.0
|
742
|
+
#
|
743
|
+
# @param [Hash] json_hash The JSON hash to convert from.
|
744
|
+
#
|
745
|
+
# @return [Tree::TreeNode] The created tree.
|
746
|
+
#
|
747
|
+
# @see #to_json
|
748
|
+
# @see http://flori.github.com/json
|
749
|
+
def self.json_create(json_hash)
|
750
|
+
begin
|
751
|
+
require 'json'
|
752
|
+
|
753
|
+
node = new(json_hash["name"], json_hash["content"])
|
754
|
+
|
755
|
+
json_hash["children"].each do |child|
|
756
|
+
node << child
|
757
|
+
end if json_hash["children"]
|
758
|
+
|
759
|
+
return node
|
760
|
+
rescue LoadError => e
|
761
|
+
warn "The JSON gem couldn't be loaded. Due to this we cannot serialize the tree to a JSON representation."
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
540
765
|
# Returns height of the (sub)tree from the receiver node. Height of a node is defined as:
|
541
766
|
#
|
542
767
|
# Height:: Length of the longest downward path to a leaf from the node.
|
543
768
|
#
|
544
769
|
# - Height from a root node is height of the entire tree.
|
545
770
|
# - The height of a leaf node is zero.
|
771
|
+
#
|
772
|
+
# @return [Number] Height of the node.
|
546
773
|
def nodeHeight
|
547
774
|
return 0 if isLeaf?
|
548
775
|
1 + @children.collect { |child| child.nodeHeight }.max
|
@@ -552,13 +779,16 @@ module Tree
|
|
552
779
|
#
|
553
780
|
# Depth:: Length of the node's path to its root. Depth of a root node is zero.
|
554
781
|
#
|
555
|
-
#
|
556
|
-
#
|
782
|
+
# 'level' is an alias for this method.
|
783
|
+
#
|
784
|
+
# @return [Number] Depth of this node.
|
557
785
|
def nodeDepth
|
558
786
|
return 0 if isRoot?
|
559
787
|
1 + parent.nodeDepth
|
560
788
|
end
|
561
789
|
|
790
|
+
alias level nodeDepth # Aliased level() method to the nodeDepth().
|
791
|
+
|
562
792
|
# Returns depth of the tree from the receiver node. A single leaf node has a depth of 1.
|
563
793
|
#
|
564
794
|
# This method is *DEPRECATED* and may be removed in the subsequent releases.
|
@@ -566,8 +796,13 @@ module Tree
|
|
566
796
|
#
|
567
797
|
# _height_ + 1 of the node, *NOT* the _depth_.
|
568
798
|
#
|
569
|
-
# For correct and conventional behavior, please use Tree::TreeNode#nodeDepth and
|
570
|
-
# Tree::TreeNode#nodeHeight methods instead.
|
799
|
+
# For correct and conventional behavior, please use {Tree::TreeNode#nodeDepth} and
|
800
|
+
# {Tree::TreeNode#nodeHeight} methods instead.
|
801
|
+
#
|
802
|
+
# @return [Number] depth of the node.
|
803
|
+
# @deprecated This method returns an incorrect value. Use the 'nodeDepth' method instead.
|
804
|
+
#
|
805
|
+
# @see #nodeDepth
|
571
806
|
def depth
|
572
807
|
begin
|
573
808
|
require 'structured_warnings' # To enable a nice way of deprecating of the depth method.
|
@@ -581,15 +816,41 @@ module Tree
|
|
581
816
|
1 + @children.collect { |child| child.depth }.max
|
582
817
|
end
|
583
818
|
|
584
|
-
# Returns breadth of the tree at the receiver node's
|
819
|
+
# Returns breadth of the tree at the receiver node's level.
|
585
820
|
# A single node without siblings has a breadth of 1.
|
586
821
|
#
|
587
822
|
# Breadth is defined to be:
|
588
|
-
# Breadth:: Number of sibling nodes to this node + 1 (this node itself),
|
823
|
+
# Breadth:: Number of sibling nodes to this node + 1 (this node itself),
|
824
|
+
# i.e., the number of children the parent of this node has.
|
825
|
+
#
|
826
|
+
# @return [Number] breadth of the node's level.
|
589
827
|
def breadth
|
590
828
|
isRoot? ? 1 : parent.children.size
|
591
829
|
end
|
592
830
|
|
831
|
+
# Returns the incoming edge-count of the receiver node.
|
832
|
+
#
|
833
|
+
# In-degree is defined as:
|
834
|
+
# In-degree:: The number of edges arriving at the node (0 for root, 1 for all other nodes)
|
835
|
+
#
|
836
|
+
# - In-degree = 0 for a root or orphaned node
|
837
|
+
# - In-degree = 1 for a node which has a parent
|
838
|
+
#
|
839
|
+
# @return [Number] The in-degree of this node.
|
840
|
+
def in_degree
|
841
|
+
isRoot? ? 0 : 1
|
842
|
+
end
|
843
|
+
|
844
|
+
# Returns the outgoing edge-count of the receiver node.
|
845
|
+
#
|
846
|
+
# Out-degree is defined as:
|
847
|
+
# Out-degree:: The number of edges leaving the node (zero for leafs)
|
848
|
+
#
|
849
|
+
# @return [Number] The out-degree of this node.
|
850
|
+
def out_degree
|
851
|
+
isLeaf? ? 0 : children.size
|
852
|
+
end
|
853
|
+
|
593
854
|
protected :parent=, :setAsRoot!, :createDumpRep
|
594
855
|
|
595
856
|
end
|