rubytree 0.8.3 → 0.9.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.
- checksums.yaml +7 -0
- data/API-CHANGES.rdoc +51 -20
- data/Gemfile +5 -1
- data/Gemfile.lock +14 -13
- data/History.rdoc +110 -42
- data/LICENSE.md +37 -0
- data/README.md +276 -0
- data/Rakefile +53 -28
- data/TAGS +218 -0
- data/TODO.org +114 -42
- data/examples/example_basic.rb +53 -0
- data/lib/tree.rb +357 -529
- data/lib/tree/binarytree.rb +76 -36
- data/lib/tree/utils/camel_case_method_handler.rb +78 -0
- data/lib/tree/utils/json_converter.rb +125 -0
- data/lib/tree/utils/metrics_methods.rb +172 -0
- data/lib/tree/utils/tree_merge_handler.rb +118 -0
- data/lib/tree/utils/utils.rb +41 -0
- data/lib/tree/version.rb +3 -3
- data/test/test_binarytree.rb +46 -1
- data/test/test_subclassed_node.rb +70 -0
- data/test/test_thread_and_fiber.rb +88 -0
- data/test/test_tree.rb +209 -35
- metadata +85 -82
- data/.dir-locals.el +0 -5
- data/COPYING.rdoc +0 -32
- data/README.rdoc +0 -219
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# example_basic.rb:: Basic usage of the tree library.
|
4
|
+
#
|
5
|
+
# Author: Anupam Sengupta
|
6
|
+
# Time-stamp: <2013-12-28 12:14:20 anupam>
|
7
|
+
# Copyright (C) 2013 Anupam Sengupta <anupamsg@gmail.com>
|
8
|
+
#
|
9
|
+
# The following example implements this tree structure:
|
10
|
+
#
|
11
|
+
# +------------+
|
12
|
+
# | ROOT |
|
13
|
+
# +-----+------+
|
14
|
+
# +-------------+------------+
|
15
|
+
# | |
|
16
|
+
# +-------+-------+ +-------+-------+
|
17
|
+
# | CHILD 1 | | CHILD 2 |
|
18
|
+
# +-------+-------+ +---------------+
|
19
|
+
# |
|
20
|
+
# |
|
21
|
+
# +-------+-------+
|
22
|
+
# | GRANDCHILD 1 |
|
23
|
+
# +---------------+
|
24
|
+
|
25
|
+
# ..... Example starts.
|
26
|
+
require 'tree' # Load the library
|
27
|
+
|
28
|
+
# ..... Create the root node first. Note that every node has a name and an optional content payload.
|
29
|
+
root_node = Tree::TreeNode.new("ROOT", "Root Content")
|
30
|
+
root_node.print_tree
|
31
|
+
|
32
|
+
# ..... Now insert the child nodes. Note that you can "chain" the child insertions for a given path to any depth.
|
33
|
+
root_node << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
|
34
|
+
root_node << Tree::TreeNode.new("CHILD2", "Child2 Content")
|
35
|
+
|
36
|
+
# ..... Lets print the representation to stdout. This is primarily used for debugging purposes.
|
37
|
+
root_node.print_tree
|
38
|
+
|
39
|
+
# ..... Lets directly access children and grandchildren of the root. The can be "chained" for a given path to any depth.
|
40
|
+
child1 = root_node["CHILD1"]
|
41
|
+
grand_child1 = root_node["CHILD1"]["GRANDCHILD1"]
|
42
|
+
|
43
|
+
# ..... Now lets retrieve siblings of the current node as an array.
|
44
|
+
siblings_of_child1 = child1.siblings
|
45
|
+
|
46
|
+
# ..... Lets retrieve immediate children of the root node as an array.
|
47
|
+
children_of_root = root_node.children
|
48
|
+
|
49
|
+
# ..... This is a depth-first and L-to-R pre-ordered traversal.
|
50
|
+
root_node.each { |node| node.content.reverse }
|
51
|
+
|
52
|
+
# ..... Lets remove a child node from the root node.
|
53
|
+
root_node.remove!(child1)
|
data/lib/tree.rb
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
# Author:: Anupam Sengupta (anupamsg@gmail.com)
|
10
10
|
#
|
11
11
|
|
12
|
-
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Anupam Sengupta
|
12
|
+
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Anupam Sengupta
|
13
13
|
#
|
14
14
|
# All rights reserved.
|
15
15
|
#
|
@@ -41,17 +41,21 @@
|
|
41
41
|
|
42
42
|
require 'tree/tree_deps'
|
43
43
|
require 'tree/version'
|
44
|
+
require 'tree/utils/metrics_methods'
|
45
|
+
require 'tree/utils/camel_case_method_handler'
|
46
|
+
require 'tree/utils/json_converter'
|
47
|
+
require 'tree/utils/tree_merge_handler'
|
44
48
|
|
45
|
-
# This module provides a TreeNode class
|
46
|
-
# nodes in the tree.
|
49
|
+
# This module provides a *TreeNode* class whose instances are the primary objects
|
50
|
+
# for representing nodes in the tree.
|
47
51
|
#
|
48
|
-
# This module also acts as the namespace for all classes in the RubyTree package.
|
52
|
+
# This module also acts as the namespace for all classes in the *RubyTree* package.
|
49
53
|
module Tree
|
50
54
|
|
51
55
|
# == TreeNode Class Description
|
52
56
|
#
|
53
|
-
# This class models the nodes for an *N-ary* tree data
|
54
|
-
# nodes are *named
|
57
|
+
# This class models the nodes for an *N-ary* tree data structure. The
|
58
|
+
# nodes are *named*, and have a place-holder for the node data (i.e.,
|
55
59
|
# _content_ of the node). The node names are required to be *unique*
|
56
60
|
# within the tree (as the name is implicitly used as an _ID_ within
|
57
61
|
# the data structure).
|
@@ -78,58 +82,20 @@ module Tree
|
|
78
82
|
# However, having duplicate nodes within the structure is likely to
|
79
83
|
# cause unpredictable behavior.
|
80
84
|
#
|
81
|
-
#
|
82
85
|
# == Example
|
83
86
|
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# +------------+
|
87
|
-
# | ROOT |
|
88
|
-
# +-----+------+
|
89
|
-
# +-------------+------------+
|
90
|
-
# | |
|
91
|
-
# +-------+-------+ +-------+-------+
|
92
|
-
# | CHILD 1 | | CHILD 2 |
|
93
|
-
# +-------+-------+ +---------------+
|
94
|
-
# |
|
95
|
-
# |
|
96
|
-
# +-------+-------+
|
97
|
-
# | GRANDCHILD 1 |
|
98
|
-
# +---------------+
|
99
|
-
#
|
100
|
-
# # ..... Example starts.
|
101
|
-
# require 'tree' # Load the library
|
102
|
-
#
|
103
|
-
# # ..... Create the root node first. Note that every node has a name and an optional content payload.
|
104
|
-
# root_node = Tree::TreeNode.new("ROOT", "Root Content")
|
105
|
-
# root_node.print_tree
|
106
|
-
#
|
107
|
-
# # ..... Now insert the child nodes. Note that you can "chain" the child insertions for a given path to any depth.
|
108
|
-
# root_node << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
|
109
|
-
# root_node << Tree::TreeNode.new("CHILD2", "Child2 Content")
|
110
|
-
#
|
111
|
-
# # ..... Lets print the representation to stdout. This is primarily used for debugging purposes.
|
112
|
-
# root_node.print_tree
|
113
|
-
#
|
114
|
-
# # ..... Lets directly access children and grandchildren of the root. The can be "chained" for a given path to any depth.
|
115
|
-
# child1 = root_node["CHILD1"]
|
116
|
-
# grand_child1 = root_node["CHILD1"]["GRANDCHILD1"]
|
117
|
-
#
|
118
|
-
# # ..... Now lets retrieve siblings of the current node as an array.
|
119
|
-
# siblings_of_child1 = child1.siblings
|
120
|
-
#
|
121
|
-
# # ..... Lets retrieve immediate children of the root node as an array.
|
122
|
-
# children_of_root = root_node.children
|
123
|
-
#
|
124
|
-
# # ..... This is a depth-first and L-to-R pre-ordered traversal.
|
125
|
-
# root_node.each { |node| node.content.reverse }
|
126
|
-
#
|
127
|
-
# # ..... Lets remove a child node from the root node.
|
128
|
-
# root_node.remove!(child1)
|
87
|
+
# {include:file:examples/example_basic.rb}
|
129
88
|
#
|
130
89
|
# @author Anupam Sengupta
|
131
90
|
class TreeNode
|
132
91
|
include Enumerable
|
92
|
+
include Comparable
|
93
|
+
include Tree::Utils::TreeMetricsHandler
|
94
|
+
include Tree::Utils::CamelCaseMethodHandler
|
95
|
+
include Tree::Utils::JSONConverter
|
96
|
+
include Tree::Utils::TreeMergeHandler
|
97
|
+
|
98
|
+
# @!group Core Attributes
|
133
99
|
|
134
100
|
# @!attribute [r] name
|
135
101
|
#
|
@@ -147,7 +113,6 @@ module Tree
|
|
147
113
|
attr_reader :name
|
148
114
|
|
149
115
|
# @!attribute [rw] content
|
150
|
-
#
|
151
116
|
# Content of this node. Can be +nil+. Note that there is no
|
152
117
|
# uniqueness constraint related to this attribute.
|
153
118
|
#
|
@@ -155,10 +120,78 @@ module Tree
|
|
155
120
|
attr_accessor :content
|
156
121
|
|
157
122
|
# @!attribute [r] parent
|
158
|
-
#
|
159
123
|
# Parent of this node. Will be +nil+ for a root node.
|
160
124
|
attr_reader :parent
|
161
125
|
|
126
|
+
# @!attribute [r] root
|
127
|
+
# Root node for the (sub)tree to which this node belongs.
|
128
|
+
# A root node's root is itself.
|
129
|
+
#
|
130
|
+
# @return [Tree::TreeNode] Root of the (sub)tree.
|
131
|
+
def root
|
132
|
+
root = self
|
133
|
+
root = root.parent while !root.is_root?
|
134
|
+
root
|
135
|
+
end
|
136
|
+
|
137
|
+
# @!attribute [r] is_root?
|
138
|
+
# Returns +true+ if this is a root node. Note that
|
139
|
+
# orphaned children will also be reported as root nodes.
|
140
|
+
#
|
141
|
+
# @return [Boolean] +true+ if this is a root node.
|
142
|
+
def is_root?
|
143
|
+
@parent == nil
|
144
|
+
end
|
145
|
+
|
146
|
+
# @!attribute [r] has_content?
|
147
|
+
# +true+ if this node has content.
|
148
|
+
#
|
149
|
+
# @return [Boolean] +true+ if the node has content.
|
150
|
+
def has_content?
|
151
|
+
@content != nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# @!attribute [r] is_leaf?
|
155
|
+
# +true+ if this node is a _leaf_ - i.e., one without
|
156
|
+
# any children.
|
157
|
+
#
|
158
|
+
# @return [Boolean] +true+ if this is a leaf node.
|
159
|
+
#
|
160
|
+
# @see #has_children?
|
161
|
+
def is_leaf?
|
162
|
+
!has_children?
|
163
|
+
end
|
164
|
+
|
165
|
+
# @!attribute [r] parentage
|
166
|
+
# An array of ancestors of this node in reversed order
|
167
|
+
# (the first element is the immediate parent of this node).
|
168
|
+
#
|
169
|
+
# Returns +nil+ if this is a root node.
|
170
|
+
#
|
171
|
+
# @return [Array<Tree::TreeNode>] An array of ancestors of this node
|
172
|
+
# @return [nil] if this is a root node.
|
173
|
+
def parentage
|
174
|
+
return nil if is_root?
|
175
|
+
|
176
|
+
parentage_array = []
|
177
|
+
prev_parent = self.parent
|
178
|
+
while (prev_parent)
|
179
|
+
parentage_array << prev_parent
|
180
|
+
prev_parent = prev_parent.parent
|
181
|
+
end
|
182
|
+
parentage_array
|
183
|
+
end
|
184
|
+
|
185
|
+
# @!attribute [r] has_children?
|
186
|
+
# +true+ if the this node has any child node.
|
187
|
+
#
|
188
|
+
# @return [Boolean] +true+ if child nodes exist.
|
189
|
+
#
|
190
|
+
# @see #is_leaf?
|
191
|
+
def has_children?
|
192
|
+
@children.length != 0
|
193
|
+
end
|
194
|
+
|
162
195
|
# @!group Node Creation
|
163
196
|
|
164
197
|
# Creates a new node with a name and optional content.
|
@@ -172,10 +205,10 @@ module Tree
|
|
172
205
|
#
|
173
206
|
# @raise [ArgumentError] Raised if the node name is empty.
|
174
207
|
#
|
175
|
-
# @note If the name is an +Integer+, then the semantics of
|
176
|
-
# be surprising, as an +Integer+ parameter to that method
|
177
|
-
# as an index to the
|
178
|
-
#
|
208
|
+
# @note If the name is an +Integer+, then the semantics of {#[]} access
|
209
|
+
# method can be surprising, as an +Integer+ parameter to that method
|
210
|
+
# normally acts as an index to the children array, and follows the
|
211
|
+
# _zero-based_ indexing convention.
|
179
212
|
#
|
180
213
|
# @see #[]
|
181
214
|
def initialize(name, content = nil)
|
@@ -192,20 +225,20 @@ module Tree
|
|
192
225
|
@children = []
|
193
226
|
end
|
194
227
|
|
195
|
-
# Returns a copy of
|
228
|
+
# Returns a copy of this node, with its parent and children links removed.
|
196
229
|
# The original node remains attached to its tree.
|
197
230
|
#
|
198
|
-
# @return [Tree::TreeNode] A copy of
|
231
|
+
# @return [Tree::TreeNode] A copy of this node.
|
199
232
|
def detached_copy
|
200
233
|
Tree::TreeNode.new(@name, @content ? @content.clone : nil)
|
201
234
|
end
|
202
235
|
|
203
|
-
# Returns a copy of entire (sub-)tree from
|
236
|
+
# Returns a copy of entire (sub-)tree from this node.
|
204
237
|
#
|
205
238
|
# @author Vincenzo Farruggia
|
206
239
|
# @since 0.8.0
|
207
240
|
#
|
208
|
-
# @return [Tree::TreeNode] A copy of (sub-)tree from
|
241
|
+
# @return [Tree::TreeNode] A copy of (sub-)tree from this node.
|
209
242
|
def detached_subtree_copy
|
210
243
|
new_node = detached_copy
|
211
244
|
children { |child| new_node << child.detached_subtree_copy }
|
@@ -217,9 +250,52 @@ module Tree
|
|
217
250
|
# @see Tree::TreeNode#detached_subtree_copy
|
218
251
|
alias :dup :detached_subtree_copy
|
219
252
|
|
253
|
+
# Returns a {marshal-dump}[http://ruby-doc.org/core-1.8.7/Marshal.html]
|
254
|
+
# represention of the (sub)tree rooted at this node.
|
255
|
+
#
|
256
|
+
def marshal_dump
|
257
|
+
self.collect { |node| node.create_dump_rep }
|
258
|
+
end
|
259
|
+
|
260
|
+
# Creates a dump representation of this node and returns the same as
|
261
|
+
# a hash.
|
262
|
+
def create_dump_rep # :nodoc:
|
263
|
+
{ :name => @name, :parent => (is_root? ? nil : @parent.name), :content => Marshal.dump(@content)}
|
264
|
+
end
|
265
|
+
|
266
|
+
protected :create_dump_rep
|
267
|
+
|
268
|
+
# Loads a marshalled dump of a tree and returns the root node of the
|
269
|
+
# reconstructed tree. See the
|
270
|
+
# {Marshal}[http://ruby-doc.org/core-1.8.7/Marshal.html] class for
|
271
|
+
# additional details.
|
272
|
+
#
|
273
|
+
#
|
274
|
+
# @todo This method probably should be a class method. It currently clobbers self
|
275
|
+
# and makes itself the root.
|
276
|
+
#
|
277
|
+
def marshal_load(dumped_tree_array)
|
278
|
+
nodes = { }
|
279
|
+
dumped_tree_array.each do |node_hash|
|
280
|
+
name = node_hash[:name]
|
281
|
+
parent_name = node_hash[:parent]
|
282
|
+
content = Marshal.load(node_hash[:content])
|
283
|
+
|
284
|
+
if parent_name then
|
285
|
+
nodes[name] = current_node = Tree::TreeNode.new(name, content)
|
286
|
+
nodes[parent_name].add current_node
|
287
|
+
else
|
288
|
+
# This is the root node, hence initialize self.
|
289
|
+
initialize(name, content)
|
290
|
+
|
291
|
+
nodes[name] = self # Add self to the list of nodes
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
220
296
|
# @!endgroup
|
221
297
|
|
222
|
-
# Returns string representation of
|
298
|
+
# Returns string representation of this node.
|
223
299
|
# This method is primarily meant for debugging purposes.
|
224
300
|
#
|
225
301
|
# @return [String] A string representation of the node.
|
@@ -231,36 +307,6 @@ module Tree
|
|
231
307
|
" Total Nodes: #{size()}"
|
232
308
|
end
|
233
309
|
|
234
|
-
# @!attribute [r] parentage
|
235
|
-
# An array of ancestors of the receiver node in reversed order
|
236
|
-
# (the first element is the immediate parent of the receiver).
|
237
|
-
#
|
238
|
-
# Returns +nil+ if the receiver is a root node.
|
239
|
-
#
|
240
|
-
# @return [Array, nil] An array of ancestors of the receiver node, or +nil+ if this is a root node.
|
241
|
-
def parentage
|
242
|
-
return nil if is_root?
|
243
|
-
|
244
|
-
parentage_array = []
|
245
|
-
prev_parent = self.parent
|
246
|
-
while (prev_parent)
|
247
|
-
parentage_array << prev_parent
|
248
|
-
prev_parent = prev_parent.parent
|
249
|
-
end
|
250
|
-
|
251
|
-
parentage_array
|
252
|
-
end
|
253
|
-
|
254
|
-
# Protected method to set the parent node for the receiver node.
|
255
|
-
# This method should *NOT* be invoked by client code.
|
256
|
-
#
|
257
|
-
# @param [Tree::TreeNode] parent The parent node.
|
258
|
-
#
|
259
|
-
# @return [Tree::TreeNode] The parent node.
|
260
|
-
def parent=(parent) # :nodoc:
|
261
|
-
@parent = parent
|
262
|
-
end
|
263
|
-
|
264
310
|
# @!group Structure Modification
|
265
311
|
|
266
312
|
# Convenience synonym for {Tree::TreeNode#add} method.
|
@@ -280,14 +326,15 @@ module Tree
|
|
280
326
|
add(child)
|
281
327
|
end
|
282
328
|
|
283
|
-
# Adds the specified child node to
|
329
|
+
# Adds the specified child node to this node.
|
284
330
|
#
|
285
|
-
# This method can also be used for *grafting* a subtree into
|
286
|
-
# is the root of a subtree (i.e.,
|
331
|
+
# This method can also be used for *grafting* a subtree into this
|
332
|
+
# node's tree, if the specified child node is the root of a subtree (i.e.,
|
333
|
+
# has child nodes under it).
|
287
334
|
#
|
288
|
-
#
|
335
|
+
# this node becomes parent of the node passed in as the argument, and
|
289
336
|
# the child is added as the last child ("right most") in the current set of
|
290
|
-
# children of
|
337
|
+
# children of this node.
|
291
338
|
#
|
292
339
|
# Additionally you can specify a insert position. The new node will be inserted
|
293
340
|
# BEFORE that position. If you don't specify any position the node will be
|
@@ -313,7 +360,9 @@ module Tree
|
|
313
360
|
def add(child, at_index = -1)
|
314
361
|
raise ArgumentError, "Attempting to add a nil node" unless child # Only handles the immediate child scenario
|
315
362
|
raise ArgumentError, "Attempting add node to itself" if self == child
|
316
|
-
|
363
|
+
|
364
|
+
# Lazy mans unique test, won't test if children of child are unique in this tree too.
|
365
|
+
self.root.each { |node| raise "Child #{child.name} already added!" if node.name == child.name }
|
317
366
|
|
318
367
|
if insertion_range.include?(at_index)
|
319
368
|
@children.insert(at_index, child)
|
@@ -326,7 +375,16 @@ module Tree
|
|
326
375
|
return child
|
327
376
|
end
|
328
377
|
|
329
|
-
#
|
378
|
+
# Return a range of valid insertion positions. Used in the #add method.
|
379
|
+
def insertion_range
|
380
|
+
max = @children.size
|
381
|
+
min = -(max+1)
|
382
|
+
min..max
|
383
|
+
end
|
384
|
+
|
385
|
+
private :insertion_range
|
386
|
+
|
387
|
+
# Removes the specified child node from this node.
|
330
388
|
#
|
331
389
|
# This method can also be used for *pruning* a sub-tree, in cases where the removed child node is
|
332
390
|
# the root of the sub-tree to be pruned.
|
@@ -349,21 +407,33 @@ module Tree
|
|
349
407
|
child
|
350
408
|
end
|
351
409
|
|
352
|
-
#
|
410
|
+
# Protected method to set the parent node for this node.
|
411
|
+
# This method should *NOT* be invoked by client code.
|
412
|
+
#
|
413
|
+
# @param [Tree::TreeNode] parent The parent node.
|
414
|
+
#
|
415
|
+
# @return [Tree::TreeNode] The parent node.
|
416
|
+
def parent=(parent) # :nodoc:
|
417
|
+
@parent = parent
|
418
|
+
end
|
419
|
+
|
420
|
+
protected :parent=
|
421
|
+
|
422
|
+
# Removes this node from its parent. This node becomes the new root for its subtree.
|
353
423
|
#
|
354
424
|
# If this is the root node, then does nothing.
|
355
425
|
#
|
356
|
-
# @return [Tree:TreeNode] +self+ (the removed
|
426
|
+
# @return [Tree:TreeNode] +self+ (the removed node) if the operation is successful, +nil+ otherwise.
|
357
427
|
#
|
358
428
|
# @see #remove_all!
|
359
429
|
def remove_from_parent!
|
360
430
|
@parent.remove!(self) unless is_root?
|
361
431
|
end
|
362
432
|
|
363
|
-
# Removes all children from
|
433
|
+
# Removes all children from this node. If an independent reference exists to the child
|
364
434
|
# nodes, then these child nodes report themselves as roots after this operation.
|
365
435
|
#
|
366
|
-
# @return [Tree::TreeNode]
|
436
|
+
# @return [Tree::TreeNode] this node (+self+)
|
367
437
|
#
|
368
438
|
# @see #remove!
|
369
439
|
# @see #remove_from_parent!
|
@@ -375,126 +445,163 @@ module Tree
|
|
375
445
|
self
|
376
446
|
end
|
377
447
|
|
378
|
-
#
|
379
|
-
#
|
380
|
-
# @return [Boolean] +true+ if the node has content.
|
381
|
-
def has_content?
|
382
|
-
@content != nil
|
383
|
-
end
|
384
|
-
|
385
|
-
# Protected method which sets the receiver node as a root node.
|
448
|
+
# Protected method which sets this node as a root node.
|
386
449
|
#
|
387
450
|
# @return +nil+.
|
388
451
|
def set_as_root! # :nodoc:
|
389
452
|
@parent = nil
|
390
453
|
end
|
391
454
|
|
392
|
-
|
455
|
+
protected :set_as_root!
|
393
456
|
|
394
|
-
#
|
395
|
-
# orphaned children will also be reported as root nodes.
|
457
|
+
# Freezes all nodes in the (sub)tree rooted at this node.
|
396
458
|
#
|
397
|
-
#
|
398
|
-
|
399
|
-
|
459
|
+
# The nodes become immutable after this operation. In effect, the entire tree's
|
460
|
+
# structure and contents become _read-only_ and cannot be changed.
|
461
|
+
def freeze_tree!
|
462
|
+
each {|node| node.freeze}
|
400
463
|
end
|
401
464
|
|
402
|
-
#
|
465
|
+
# @!endgroup
|
466
|
+
|
467
|
+
# @!group Tree Traversal
|
468
|
+
|
469
|
+
# Returns the requested node from the set of immediate children.
|
403
470
|
#
|
404
|
-
#
|
471
|
+
# - If the +name+ argument is an _Integer_, then the in-sequence
|
472
|
+
# array of children is accessed using the argument as the
|
473
|
+
# *index* (zero-based). However, if the second _optional_
|
474
|
+
# +num_as_name+ argument is +true+, then the +name+ is used
|
475
|
+
# literally as a name, and *NOT* as an *index*
|
405
476
|
#
|
406
|
-
#
|
407
|
-
|
408
|
-
@children.length != 0
|
409
|
-
end
|
410
|
-
|
411
|
-
# Returns +true+ if the receiver node is a 'leaf' - i.e., one without
|
412
|
-
# any children.
|
477
|
+
# - If the +name+ argument is *NOT* an _Integer_, then it is taken to
|
478
|
+
# be the *name* of the child node to be returned.
|
413
479
|
#
|
414
|
-
#
|
480
|
+
# If a non-+Integer+ +name+ is passed, and the +num_as_name+
|
481
|
+
# parameter is also +true+, then a warning is thrown (as this is a
|
482
|
+
# redundant use of the +num_as_name+ flag.)
|
415
483
|
#
|
416
|
-
# @
|
417
|
-
|
418
|
-
!has_children?
|
419
|
-
end
|
420
|
-
|
421
|
-
# @!attribute [rw] children
|
422
|
-
# An array of all the immediate children of the receiver
|
423
|
-
# node. The child nodes are ordered "left-to-right" in the
|
424
|
-
# returned array.
|
484
|
+
# @param [String|Number] name_or_index Name of the child, or its
|
485
|
+
# positional index in the array of child nodes.
|
425
486
|
#
|
426
|
-
#
|
427
|
-
#
|
487
|
+
# @param [Boolean] num_as_name Whether to treat the +Integer+
|
488
|
+
# +name+ argument as an actual name, and *NOT* as an _index_ to
|
489
|
+
# the children array.
|
428
490
|
#
|
429
|
-
# @
|
430
|
-
#
|
491
|
+
# @return [Tree::TreeNode] the requested child node. If the index
|
492
|
+
# in not in range, or the name is not present, then a +nil+
|
493
|
+
# is returned.
|
431
494
|
#
|
432
|
-
# @
|
433
|
-
def children
|
434
|
-
if block_given?
|
435
|
-
@children.each {|child| yield child}
|
436
|
-
else
|
437
|
-
@children
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
# @!attribute [rw] first_child
|
442
|
-
# First child of the receiver node.
|
443
|
-
# Will be +nil+ if no children are present.
|
495
|
+
# @note The use of +Integer+ names is allowed by using the optional +num_as_name+ flag.
|
444
496
|
#
|
445
|
-
# @
|
446
|
-
def first_child
|
447
|
-
children.first
|
448
|
-
end
|
449
|
-
|
450
|
-
# @!attribute [rw] last_child
|
451
|
-
# Last child of the receiver node.
|
452
|
-
# Will be +nil+ if no children are present.
|
497
|
+
# @raise [ArgumentError] Raised if the +name_or_index+ argument is +nil+.
|
453
498
|
#
|
454
|
-
# @
|
455
|
-
|
456
|
-
|
457
|
-
|
499
|
+
# @see #add
|
500
|
+
# @see #initialize
|
501
|
+
def [](name_or_index, num_as_name=false)
|
502
|
+
raise ArgumentError, "Name_or_index needs to be provided!" if name_or_index == nil
|
458
503
|
|
459
|
-
|
504
|
+
if name_or_index.kind_of?(Integer) and not num_as_name
|
505
|
+
@children[name_or_index]
|
506
|
+
else
|
507
|
+
if num_as_name and not name_or_index.kind_of?(Integer)
|
508
|
+
warn StandardWarning, "Redundant use of the `num_as_name` flag for non-integer node name"
|
509
|
+
end
|
510
|
+
@children_hash[name_or_index]
|
511
|
+
end
|
512
|
+
end
|
460
513
|
|
461
|
-
# Traverses each node (including
|
514
|
+
# Traverses each node (including this node) of the (sub)tree rooted at this node
|
462
515
|
# by yielding the nodes to the specified block.
|
463
516
|
#
|
464
517
|
# The traversal is *depth-first* and from *left-to-right* in pre-ordered sequence.
|
465
518
|
#
|
466
|
-
# @
|
467
|
-
# @yieldparam [Tree::TreeNode] child Each node.
|
519
|
+
# @yieldparam node [Tree::TreeNode] Each node.
|
468
520
|
#
|
469
521
|
# @see #preordered_each
|
470
522
|
# @see #breadth_each
|
523
|
+
#
|
524
|
+
# @return [Tree::TreeNode] this node, if a block if given
|
525
|
+
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
471
526
|
def each(&block) # :yields: node
|
472
|
-
|
473
|
-
|
527
|
+
|
528
|
+
return self.to_enum unless block_given?
|
529
|
+
|
530
|
+
node_stack = [self] # Start with this node
|
531
|
+
|
532
|
+
until node_stack.empty?
|
533
|
+
current = node_stack.shift # Pop the top-most node
|
534
|
+
if current # The node might be 'nil' (esp. for binary trees)
|
535
|
+
yield current # and process it
|
536
|
+
# Stack children of the current node at top of the stack
|
537
|
+
node_stack = current.children.clone.concat(node_stack)
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
return self if block_given?
|
474
542
|
end
|
475
543
|
|
476
|
-
# Traverses the (sub)tree rooted at
|
544
|
+
# Traverses the (sub)tree rooted at this node in pre-ordered sequence.
|
477
545
|
# This is a synonym of {Tree::TreeNode#each}.
|
478
546
|
#
|
479
|
-
# @
|
480
|
-
# @yieldparam [Tree::TreeNode] node Each node.
|
547
|
+
# @yieldparam node [Tree::TreeNode] Each node.
|
481
548
|
#
|
482
549
|
# @see #each
|
483
550
|
# @see #breadth_each
|
551
|
+
#
|
552
|
+
# @return [Tree::TreeNode] this node, if a block if given
|
553
|
+
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
484
554
|
def preordered_each(&block) # :yields: node
|
485
555
|
each(&block)
|
486
556
|
end
|
487
557
|
|
488
|
-
#
|
489
|
-
#
|
558
|
+
# Traverses the (sub)tree rooted at this node in post-ordered sequence.
|
559
|
+
#
|
560
|
+
# @yieldparam node [Tree::TreeNode] Each node.
|
561
|
+
#
|
562
|
+
# @see #preordered_each
|
563
|
+
# @see #breadth_each
|
564
|
+
# @return [Tree::TreeNode] this node, if a block if given
|
565
|
+
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
566
|
+
def postordered_each(&block)
|
567
|
+
return self.to_enum unless block_given?
|
568
|
+
|
569
|
+
# Using a marked node in order to skip adding the children of nodes that
|
570
|
+
# have already been visited. This allows the stack depth to be controlled,
|
571
|
+
# and also allows stateful backtracking.
|
572
|
+
markednode = Struct.new(:node, :visited)
|
573
|
+
node_stack = [markednode.new(self, false)] # Start with self
|
574
|
+
|
575
|
+
until node_stack.empty?
|
576
|
+
peek_node = node_stack[0]
|
577
|
+
if peek_node.node.has_children? and not peek_node.visited
|
578
|
+
peek_node.visited = true
|
579
|
+
# Add the children to the stack. Use the marking structure.
|
580
|
+
marked_children = peek_node.node.children.map {|node| markednode.new(node, false)}
|
581
|
+
node_stack = marked_children.concat(node_stack)
|
582
|
+
next
|
583
|
+
else
|
584
|
+
yield node_stack.shift.node # Pop and yield the current node
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
return self if block_given?
|
589
|
+
end
|
590
|
+
|
591
|
+
# Performs breadth-first traversal of the (sub)tree rooted at this node. The
|
592
|
+
# traversal at a given level is from *left-to-right*. this node itself is the first
|
490
593
|
# node to be traversed.
|
491
594
|
#
|
492
|
-
# @
|
493
|
-
# @yieldparam [Tree::TreeNode] node Each node.
|
595
|
+
# @yieldparam node [Tree::TreeNode] Each node.
|
494
596
|
#
|
495
597
|
# @see #preordered_each
|
496
598
|
# @see #breadth_each
|
599
|
+
#
|
600
|
+
# @return [Tree::TreeNode] this node, if a block if given
|
601
|
+
# @return [Enumerator] an enumerator on this tree, if a block is *not* given
|
497
602
|
def breadth_each(&block)
|
603
|
+
return self.to_enum unless block_given?
|
604
|
+
|
498
605
|
node_queue = [self] # Create a queue with self as the initial entry
|
499
606
|
|
500
607
|
# Use a queue to do breadth traversal
|
@@ -504,131 +611,77 @@ module Tree
|
|
504
611
|
# Enqueue the children from left to right.
|
505
612
|
node_to_traverse.children { |child| node_queue.push child }
|
506
613
|
end
|
614
|
+
|
615
|
+
return self if block_given?
|
507
616
|
end
|
508
617
|
|
509
|
-
#
|
618
|
+
# An array of all the immediate children of this node. The child
|
619
|
+
# nodes are ordered "left-to-right" in the returned array.
|
510
620
|
#
|
511
|
-
#
|
512
|
-
#
|
621
|
+
# If a block is given, yields each child node to the block
|
622
|
+
# traversing from left to right.
|
513
623
|
#
|
514
|
-
# @
|
515
|
-
# @yieldparam [Tree::TreeNode] node Each leaf node.
|
624
|
+
# @yieldparam child [Tree::TreeNode] Each child node.
|
516
625
|
#
|
517
|
-
# @
|
518
|
-
# @
|
519
|
-
def
|
626
|
+
# @return [Tree::TreeNode] This node, if a block is given
|
627
|
+
# @return [Array<Tree::TreeNode>] An array of the child nodes, if no block is given.
|
628
|
+
def children
|
520
629
|
if block_given?
|
521
|
-
|
630
|
+
@children.each {|child| yield child}
|
631
|
+
return self
|
522
632
|
else
|
523
|
-
|
633
|
+
return @children.clone
|
524
634
|
end
|
525
635
|
end
|
526
636
|
|
527
|
-
#
|
637
|
+
# Yields every leaf node of the (sub)tree rooted at this node to the specified block.
|
528
638
|
#
|
529
|
-
#
|
530
|
-
#
|
531
|
-
# *index* (zero-based). However, if the second _optional_
|
532
|
-
# +num_as_name+ argument is +true+, then the +name+ is used
|
533
|
-
# literally as a name, and *NOT* as an *index*
|
534
|
-
#
|
535
|
-
# - If the +name+ argument is *NOT* an _Integer_, then it is taken to
|
536
|
-
# be the *name* of the child node to be returned.
|
537
|
-
#
|
538
|
-
# If a non-+Integer+ +name+ is passed, and the +num_as_name+
|
539
|
-
# parameter is also +true+, then a warning is thrown (as this is a
|
540
|
-
# redundant use of the +num_as_name+ flag.)
|
541
|
-
#
|
542
|
-
# @param [String|Number] name_or_index Name of the child, or its
|
543
|
-
# positional index in the array of child nodes.
|
544
|
-
#
|
545
|
-
# @param [Boolean] num_as_name Whether to treat the +Integer+
|
546
|
-
# +name+ argument as an actual name, and *NOT* as an _index_ to
|
547
|
-
# the children array.
|
548
|
-
#
|
549
|
-
# @return [Tree::TreeNode] the requested child node. If the index
|
550
|
-
# in not in range, or the name is not present, then a +nil+
|
551
|
-
# is returned.
|
639
|
+
# May yield this node as well if this is a leaf node.
|
640
|
+
# Leaf traversal is *depth-first* and *left-to-right*.
|
552
641
|
#
|
553
|
-
# @
|
642
|
+
# @yieldparam node [Tree::TreeNode] Each leaf node.
|
554
643
|
#
|
555
|
-
# @
|
644
|
+
# @see #each
|
645
|
+
# @see #breadth_each
|
556
646
|
#
|
557
|
-
# @
|
558
|
-
# @
|
559
|
-
def
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
@children[name_or_index]
|
647
|
+
# @return [Tree::TreeNode] this node, if a block if given
|
648
|
+
# @return [Array<Tree::TreeNode>] An array of the leaf nodes
|
649
|
+
def each_leaf &block
|
650
|
+
if block_given?
|
651
|
+
self.each { |node| yield(node) if node.is_leaf? }
|
652
|
+
return self
|
564
653
|
else
|
565
|
-
|
566
|
-
warn StandardWarning, "Redundant use of the `num_as_name` flag for non-integer node name"
|
567
|
-
end
|
568
|
-
@children_hash[name_or_index]
|
654
|
+
self.select { |node| node.is_leaf?}
|
569
655
|
end
|
570
656
|
end
|
571
657
|
|
572
658
|
# @!endgroup
|
573
659
|
|
574
|
-
# @!
|
575
|
-
# Total number of nodes in this (sub)tree, including the receiver node.
|
576
|
-
#
|
577
|
-
# Size of the tree is defined as:
|
578
|
-
#
|
579
|
-
# Size:: Total number nodes in the subtree including the receiver node.
|
580
|
-
#
|
581
|
-
# @return [Integer] Total number of nodes in this (sub)tree.
|
582
|
-
def size
|
583
|
-
@children.inject(1) {|sum, node| sum + node.size}
|
584
|
-
end
|
660
|
+
# @!group Navigating the Child Nodes
|
585
661
|
|
586
|
-
#
|
587
|
-
#
|
588
|
-
# @deprecated This method name is ambiguous and may be removed. Use TreeNode#size instead.
|
662
|
+
# First child of this node.
|
663
|
+
# Will be +nil+ if no children are present.
|
589
664
|
#
|
590
|
-
# @return [
|
591
|
-
|
592
|
-
|
593
|
-
size()
|
665
|
+
# @return [Tree::TreeNode] The first child, or +nil+ if none is present.
|
666
|
+
def first_child
|
667
|
+
children.first
|
594
668
|
end
|
595
669
|
|
596
|
-
#
|
670
|
+
# Last child of this node.
|
671
|
+
# Will be +nil+ if no children are present.
|
597
672
|
#
|
598
|
-
# @
|
599
|
-
def
|
600
|
-
|
601
|
-
print "*"
|
602
|
-
else
|
603
|
-
print "|" unless parent.is_last_sibling?
|
604
|
-
print(' ' * (level - 1) * 4)
|
605
|
-
print(is_last_sibling? ? "+" : "|")
|
606
|
-
print "---"
|
607
|
-
print(has_children? ? "+" : ">")
|
608
|
-
end
|
609
|
-
|
610
|
-
puts " #{name}"
|
611
|
-
|
612
|
-
children { |child| child.print_tree(level + 1)}
|
673
|
+
# @return [Tree::TreeNode] The last child, or +nil+ if none is present.
|
674
|
+
def last_child
|
675
|
+
children.last
|
613
676
|
end
|
614
677
|
|
615
|
-
# @!
|
616
|
-
# root node for the (sub)tree to which the receiver node belongs.
|
617
|
-
# A root node's root is itself.
|
618
|
-
#
|
619
|
-
# @return [Tree::TreeNode] Root of the (sub)tree.
|
620
|
-
def root
|
621
|
-
root = self
|
622
|
-
root = root.parent while !root.is_root?
|
623
|
-
root
|
624
|
-
end
|
678
|
+
# @!group Navigating the Sibling Nodes
|
625
679
|
|
626
|
-
#
|
627
|
-
# First sibling of the receiver node. If this is the root node, then returns
|
680
|
+
# First sibling of this node. If this is the root node, then returns
|
628
681
|
# itself.
|
629
682
|
#
|
630
683
|
# 'First' sibling is defined as follows:
|
631
|
-
# First sibling:: The left-most child of
|
684
|
+
# First sibling:: The left-most child of this node's parent, which may be this node itself
|
632
685
|
#
|
633
686
|
# @return [Tree::TreeNode] The first sibling node.
|
634
687
|
#
|
@@ -638,7 +691,7 @@ module Tree
|
|
638
691
|
is_root? ? self : parent.children.first
|
639
692
|
end
|
640
693
|
|
641
|
-
# Returns +true+ if
|
694
|
+
# Returns +true+ if this node is the first sibling at its level.
|
642
695
|
#
|
643
696
|
# @return [Boolean] +true+ if this is the first sibling.
|
644
697
|
#
|
@@ -648,12 +701,11 @@ module Tree
|
|
648
701
|
first_sibling == self
|
649
702
|
end
|
650
703
|
|
651
|
-
#
|
652
|
-
# Last sibling of the receiver node. If this is the root node, then returns
|
704
|
+
# Last sibling of this node. If this is the root node, then returns
|
653
705
|
# itself.
|
654
706
|
#
|
655
707
|
# 'Last' sibling is defined as follows:
|
656
|
-
# Last sibling:: The right-most child of
|
708
|
+
# Last sibling:: The right-most child of this node's parent, which may be this node itself
|
657
709
|
#
|
658
710
|
# @return [Tree::TreeNode] The last sibling node.
|
659
711
|
#
|
@@ -663,7 +715,7 @@ module Tree
|
|
663
715
|
is_root? ? self : parent.children.last
|
664
716
|
end
|
665
717
|
|
666
|
-
# Returns +true+ if
|
718
|
+
# Returns +true+ if this node is the last sibling at its level.
|
667
719
|
#
|
668
720
|
# @return [Boolean] +true+ if this is the last sibling.
|
669
721
|
#
|
@@ -673,32 +725,31 @@ module Tree
|
|
673
725
|
last_sibling == self
|
674
726
|
end
|
675
727
|
|
676
|
-
#
|
677
|
-
# An array of siblings for the receiver node. The receiver node is excluded.
|
728
|
+
# An array of siblings for this node. This node is excluded.
|
678
729
|
#
|
679
730
|
# If a block is provided, yields each of the sibling nodes to the block.
|
680
731
|
# The root always has +nil+ siblings.
|
681
732
|
#
|
682
|
-
# @
|
683
|
-
# @yieldparam [Tree::TreeNode] sibling Each sibling node.
|
733
|
+
# @yieldparam sibling [Tree::TreeNode] Each sibling node.
|
684
734
|
#
|
685
|
-
# @return [Array<Tree::TreeNode>] Array of siblings of this node.
|
735
|
+
# @return [Array<Tree::TreeNode>] Array of siblings of this node. Will return an empty array for *root*
|
736
|
+
# @return [Tree::TreeNode] This node, if no block is given
|
686
737
|
#
|
687
738
|
# @see #first_sibling
|
688
739
|
# @see #last_sibling
|
689
740
|
def siblings
|
690
|
-
return [] if is_root?
|
691
|
-
|
692
741
|
if block_given?
|
693
742
|
parent.children.each { |sibling| yield sibling if sibling != self }
|
743
|
+
return self
|
694
744
|
else
|
745
|
+
return [] if is_root?
|
695
746
|
siblings = []
|
696
747
|
parent.children {|my_sibling| siblings << my_sibling if my_sibling != self}
|
697
748
|
siblings
|
698
749
|
end
|
699
750
|
end
|
700
751
|
|
701
|
-
#
|
752
|
+
# +true+ if this node is the only child of its parent.
|
702
753
|
#
|
703
754
|
# As a special case, a root node will always return +true+.
|
704
755
|
#
|
@@ -709,11 +760,10 @@ module Tree
|
|
709
760
|
is_root? ? true : parent.children.size == 1
|
710
761
|
end
|
711
762
|
|
712
|
-
#
|
713
|
-
#
|
714
|
-
# The 'next' node is defined as the node to right of the receiver node.
|
763
|
+
# Next sibling for this node.
|
764
|
+
# The _next_ node is defined as the node to right of this node.
|
715
765
|
#
|
716
|
-
# Will return +nil+ if no subsequent node is present, or if
|
766
|
+
# Will return +nil+ if no subsequent node is present, or if this is a root node.
|
717
767
|
#
|
718
768
|
# @return [Tree::treeNode] the next sibling node, if present.
|
719
769
|
#
|
@@ -726,11 +776,10 @@ module Tree
|
|
726
776
|
parent.children.at(myidx + 1) if myidx
|
727
777
|
end
|
728
778
|
|
729
|
-
#
|
730
|
-
#
|
731
|
-
# 'Previous' node is defined to be the node to left of the receiver node.
|
779
|
+
# Previous sibling of this node.
|
780
|
+
# _Previous_ node is defined to be the node to left of this node.
|
732
781
|
#
|
733
|
-
# Will return +nil+ if no predecessor node is present, or if
|
782
|
+
# Will return +nil+ if no predecessor node is present, or if this is a root node.
|
734
783
|
#
|
735
784
|
# @return [Tree::treeNode] the previous sibling node, if present.
|
736
785
|
#
|
@@ -743,9 +792,11 @@ module Tree
|
|
743
792
|
parent.children.at(myidx - 1) if myidx && myidx > 0
|
744
793
|
end
|
745
794
|
|
795
|
+
# @!endgroup
|
796
|
+
|
746
797
|
# Provides a comparision operation for the nodes.
|
747
798
|
#
|
748
|
-
# Comparision is based on the natural
|
799
|
+
# Comparision is based on the natural ordering of the node name objects.
|
749
800
|
#
|
750
801
|
# @param [Tree::TreeNode] other The other node to compare against.
|
751
802
|
#
|
@@ -755,246 +806,23 @@ module Tree
|
|
755
806
|
self.name <=> other.name
|
756
807
|
end
|
757
808
|
|
758
|
-
#
|
809
|
+
# Pretty prints the (sub)tree rooted at this node.
|
759
810
|
#
|
760
|
-
#
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
end
|
765
|
-
|
766
|
-
# Returns a marshal-dump represention of the (sub)tree rooted at the receiver node.
|
767
|
-
def marshal_dump
|
768
|
-
self.collect { |node| node.create_dump_rep }
|
769
|
-
end
|
770
|
-
|
771
|
-
# Creates a dump representation of the reciever node and returns the same as a hash.
|
772
|
-
def create_dump_rep # :nodoc:
|
773
|
-
{ :name => @name, :parent => (is_root? ? nil : @parent.name), :content => Marshal.dump(@content)}
|
774
|
-
end
|
775
|
-
|
776
|
-
# Loads a marshalled dump of a tree and returns the root node of the
|
777
|
-
# reconstructed tree. See the Marshal class for additional details.
|
778
|
-
#
|
779
|
-
#
|
780
|
-
# @todo This method probably should be a class method. It currently clobbers self
|
781
|
-
# and makes itself the root.
|
782
|
-
#
|
783
|
-
def marshal_load(dumped_tree_array)
|
784
|
-
nodes = { }
|
785
|
-
dumped_tree_array.each do |node_hash|
|
786
|
-
name = node_hash[:name]
|
787
|
-
parent_name = node_hash[:parent]
|
788
|
-
content = Marshal.load(node_hash[:content])
|
789
|
-
|
790
|
-
if parent_name then
|
791
|
-
nodes[name] = current_node = Tree::TreeNode.new(name, content)
|
792
|
-
nodes[parent_name].add current_node
|
793
|
-
else
|
794
|
-
# This is the root node, hence initialize self.
|
795
|
-
initialize(name, content)
|
796
|
-
|
797
|
-
nodes[name] = self # Add self to the list of nodes
|
798
|
-
end
|
799
|
-
end
|
800
|
-
end
|
801
|
-
|
802
|
-
# Creates a JSON ready Hash for the #to_json method.
|
803
|
-
#
|
804
|
-
# @author Eric Cline (https://github.com/escline)
|
805
|
-
# @since 0.8.3
|
806
|
-
#
|
807
|
-
# @return A hash based representation of the JSON
|
808
|
-
#
|
809
|
-
# Rails uses JSON in ActiveSupport, and all Rails JSON encoding goes through as_json
|
810
|
-
#
|
811
|
-
# @see Tree::TreeNode.to_json
|
812
|
-
# @see http://stackoverflow.com/a/6880638/273808
|
813
|
-
def as_json(options = {})
|
814
|
-
|
815
|
-
json_hash = {
|
816
|
-
"name" => name,
|
817
|
-
"content" => content,
|
818
|
-
JSON.create_id => self.class.name
|
819
|
-
}
|
820
|
-
|
821
|
-
if has_children?
|
822
|
-
json_hash["children"] = children
|
823
|
-
end
|
824
|
-
|
825
|
-
return json_hash
|
826
|
-
|
827
|
-
end
|
828
|
-
|
829
|
-
# Creates a JSON representation of this node including all it's children. This requires the JSON gem to be
|
830
|
-
# available, or else the operation fails with a warning message.
|
831
|
-
# Uses the Hash output of as_json method, defined above.
|
832
|
-
#
|
833
|
-
# @author Dirk Breuer (http://github.com/railsbros-dirk)
|
834
|
-
# @since 0.7.0
|
835
|
-
#
|
836
|
-
# @return The JSON representation of this subtree.
|
837
|
-
#
|
838
|
-
# @see Tree::TreeNode.json_create
|
839
|
-
# @see Tree::TreeNode.as_json
|
840
|
-
# @see http://flori.github.com/json
|
841
|
-
def to_json(*a)
|
842
|
-
as_json.to_json(*a)
|
843
|
-
end
|
844
|
-
|
845
|
-
# Helper method to create a Tree::TreeNode instance from the JSON hash representation. Note that this method should
|
846
|
-
# *NOT* be called directly. Instead, to convert the JSON hash back to a tree, do:
|
847
|
-
#
|
848
|
-
# tree = JSON.parse (the_json_hash)
|
849
|
-
#
|
850
|
-
# This operation requires the JSON gem to be available, or else the operation fails with a warning message.
|
851
|
-
#
|
852
|
-
# @author Dirk Breuer (http://github.com/railsbros-dirk)
|
853
|
-
# @since 0.7.0
|
854
|
-
#
|
855
|
-
# @param [Hash] json_hash The JSON hash to convert from.
|
856
|
-
#
|
857
|
-
# @return [Tree::TreeNode] The created tree.
|
858
|
-
#
|
859
|
-
# @see #to_json
|
860
|
-
# @see http://flori.github.com/json
|
861
|
-
def self.json_create(json_hash)
|
862
|
-
|
863
|
-
node = new(json_hash["name"], json_hash["content"])
|
864
|
-
|
865
|
-
json_hash["children"].each do |child|
|
866
|
-
node << child
|
867
|
-
end if json_hash["children"]
|
868
|
-
|
869
|
-
return node
|
870
|
-
|
871
|
-
end
|
872
|
-
|
873
|
-
# @!attribute [r] node_height
|
874
|
-
# Height of the (sub)tree from the receiver node. Height of a node is defined as:
|
875
|
-
#
|
876
|
-
# Height:: Length of the longest downward path to a leaf from the node.
|
877
|
-
#
|
878
|
-
# - Height from a root node is height of the entire tree.
|
879
|
-
# - The height of a leaf node is zero.
|
880
|
-
#
|
881
|
-
# @return [Integer] Height of the node.
|
882
|
-
def node_height
|
883
|
-
return 0 if is_leaf?
|
884
|
-
1 + @children.collect { |child| child.node_height }.max
|
885
|
-
end
|
886
|
-
|
887
|
-
# @!attribute [r] node_depth
|
888
|
-
# Depth of the receiver node in its tree. Depth of a node is defined as:
|
889
|
-
#
|
890
|
-
# Depth:: Length of the node's path to its root. Depth of a root node is zero.
|
891
|
-
#
|
892
|
-
# *Note* that the deprecated method Tree::TreeNode#depth was incorrectly computing this value.
|
893
|
-
# Please replace all calls to the old method with Tree::TreeNode#node_depth instead.
|
894
|
-
#
|
895
|
-
# 'level' is an alias for this method.
|
896
|
-
#
|
897
|
-
# @return [Integer] Depth of this node.
|
898
|
-
def node_depth
|
899
|
-
return 0 if is_root?
|
900
|
-
1 + parent.node_depth
|
901
|
-
end
|
902
|
-
|
903
|
-
alias level node_depth # Aliased level() method to the node_depth().
|
904
|
-
|
905
|
-
# Returns depth of the tree from the receiver node. A single leaf node has a depth of 1.
|
906
|
-
#
|
907
|
-
# This method is *DEPRECATED* and may be removed in the subsequent releases.
|
908
|
-
# Note that the value returned by this method is actually the:
|
909
|
-
#
|
910
|
-
# _height_ + 1 of the node, *NOT* the _depth_.
|
911
|
-
#
|
912
|
-
# For correct and conventional behavior, please use {Tree::TreeNode#node_depth} and
|
913
|
-
# {Tree::TreeNode#node_height} methods instead.
|
914
|
-
#
|
915
|
-
# @return [Integer] depth of the node.
|
916
|
-
# @deprecated This method returns an incorrect value. Use the 'node_depth' method instead.
|
917
|
-
#
|
918
|
-
# @see #node_depth
|
919
|
-
def depth
|
920
|
-
warn DeprecatedMethodWarning, 'This method is deprecated. Please use node_depth() or node_height() instead (bug # 22535)'
|
921
|
-
|
922
|
-
return 1 if is_leaf?
|
923
|
-
1 + @children.collect { |child| child.depth }.max
|
924
|
-
end
|
925
|
-
|
926
|
-
# Allow the deprecated CamelCase method names. Display a warning.
|
927
|
-
# :nodoc:
|
928
|
-
def method_missing(meth, *args, &blk)
|
929
|
-
if self.respond_to?(new_method_name = underscore(meth))
|
930
|
-
warn DeprecatedMethodWarning, "The camelCased methods are deprecated. Please use #{new_method_name} instead of #{meth}"
|
931
|
-
return send(new_method_name, *args, &blk)
|
811
|
+
# @param [Integer] level The indentation level (4 spaces) to start with.
|
812
|
+
def print_tree(level = 0)
|
813
|
+
if is_root?
|
814
|
+
print "*"
|
932
815
|
else
|
933
|
-
|
816
|
+
print "|" unless parent.is_last_sibling?
|
817
|
+
print(' ' * (level - 1) * 4)
|
818
|
+
print(is_last_sibling? ? "+" : "|")
|
819
|
+
print "---"
|
820
|
+
print(has_children? ? "+" : ">")
|
934
821
|
end
|
935
|
-
end
|
936
|
-
|
937
|
-
# @!attribute [r] breadth
|
938
|
-
# Breadth of the tree at the receiver node's level.
|
939
|
-
# A single node without siblings has a breadth of 1.
|
940
|
-
#
|
941
|
-
# Breadth is defined to be:
|
942
|
-
# Breadth:: Number of sibling nodes to this node + 1 (this node itself),
|
943
|
-
# i.e., the number of children the parent of this node has.
|
944
|
-
#
|
945
|
-
# @return [Integer] breadth of the node's level.
|
946
|
-
def breadth
|
947
|
-
is_root? ? 1 : parent.children.size
|
948
|
-
end
|
949
|
-
|
950
|
-
# @!attribute [r] in_degree
|
951
|
-
# The incoming edge-count of the receiver node.
|
952
|
-
#
|
953
|
-
# In-degree is defined as:
|
954
|
-
# In-degree:: Number of edges arriving at the node (0 for root, 1 for all other nodes)
|
955
|
-
#
|
956
|
-
# - In-degree = 0 for a root or orphaned node
|
957
|
-
# - In-degree = 1 for a node which has a parent
|
958
|
-
#
|
959
|
-
# @return [Integer] The in-degree of this node.
|
960
|
-
def in_degree
|
961
|
-
is_root? ? 0 : 1
|
962
|
-
end
|
963
822
|
|
964
|
-
|
965
|
-
# The outgoing edge-count of the receiver node.
|
966
|
-
#
|
967
|
-
# Out-degree is defined as:
|
968
|
-
# Out-degree:: Number of edges leaving the node (zero for leafs)
|
969
|
-
#
|
970
|
-
# @return [Integer] The out-degree of this node.
|
971
|
-
def out_degree
|
972
|
-
is_leaf? ? 0 : children.size
|
973
|
-
end
|
974
|
-
|
975
|
-
protected :parent=, :set_as_root!, :create_dump_rep
|
976
|
-
|
977
|
-
private
|
978
|
-
|
979
|
-
# Convert a CamelCasedWord to a underscore separated camel_cased_word.
|
980
|
-
#
|
981
|
-
# Just copied from ActiveSupport::Inflector because it is only needed
|
982
|
-
# aliasing deprecated methods
|
983
|
-
def underscore(camel_cased_word)
|
984
|
-
word = camel_cased_word.to_s.dup
|
985
|
-
word.gsub!(/::/, '/')
|
986
|
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
987
|
-
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
988
|
-
word.tr!("-", "_")
|
989
|
-
word.downcase!
|
990
|
-
word
|
991
|
-
end
|
823
|
+
puts " #{name}"
|
992
824
|
|
993
|
-
|
994
|
-
def insertion_range
|
995
|
-
max = @children.size
|
996
|
-
min = -(max+1)
|
997
|
-
min..max
|
825
|
+
children { |child| child.print_tree(level + 1) if child } # Child might be 'nil'
|
998
826
|
end
|
999
827
|
|
1000
828
|
end
|