rubytree 0.6.2 → 0.7.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/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
|