tree.rb 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/LICENSE.txt +20 -0
- data/README.md +160 -0
- data/Rakefile +12 -0
- data/bin/tree.rb +9 -0
- data/examples/directory_walker/directory_without_subdirectory.rb +37 -0
- data/examples/directory_walker/find_files.rb +18 -0
- data/examples/directory_walker/print_files.rb +12 -0
- data/examples/protovis/directory_to_json_visitor.rb +15 -0
- data/examples/protovis/index.html +87 -0
- data/examples/protovis/protovis-r3.2.js +277 -0
- data/examples/protovis/treevisitor.js +33 -0
- data/examples/protovis/treevisitor.png +0 -0
- data/lib/tree_visitor.rb +2 -0
- data/lib/treevisitor/abs_node.rb +144 -0
- data/lib/treevisitor/basic_tree_node_visitor.rb +44 -0
- data/lib/treevisitor/cli/cli_tree.rb +121 -0
- data/lib/treevisitor/directory_walker.rb +300 -0
- data/lib/treevisitor/leaf_node.rb +33 -0
- data/lib/treevisitor/tree_node.rb +296 -0
- data/lib/treevisitor/tree_node_visitor.rb +120 -0
- data/lib/treevisitor/util/dir_processor.rb +46 -0
- data/lib/treevisitor/version.rb +4 -0
- data/lib/treevisitor/visitors/block_tree_node_visitor.rb +21 -0
- data/lib/treevisitor/visitors/build_dir_tree_visitor.rb +57 -0
- data/lib/treevisitor/visitors/callback_tree_node_visitor2.rb +48 -0
- data/lib/treevisitor/visitors/clone_tree_node_visitor.rb +39 -0
- data/lib/treevisitor/visitors/depth_tree_node_visitor.rb +27 -0
- data/lib/treevisitor/visitors/directory_to_hash_visitor.rb +33 -0
- data/lib/treevisitor/visitors/flat_print_tree_node_visitors.rb +20 -0
- data/lib/treevisitor/visitors/print_dir_tree_visitor.rb +21 -0
- data/lib/treevisitor/visitors/print_tree_node_visitor.rb +40 -0
- data/lib/treevisitor.rb +40 -0
- data/lib/treevisitor_cli.rb +7 -0
- data/spec/fixtures/test_dir_1/.dir_with_dot/dummy.txt +0 -0
- data/spec/fixtures/test_dir_1/dir.1/dir.1.2/file.1.2.1 +0 -0
- data/spec/fixtures/test_dir_1/dir.1/file.1.1 +0 -0
- data/spec/fixtures/test_dir_1/dir.2/file.2.1 +0 -0
- data/spec/fixtures/test_dir_2/[Dsube]/sub/.gitkeep +0 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/treevisitor/cli/cli_tree_spec.rb +69 -0
- data/spec/treevisitor/directory_walker_spec.rb +99 -0
- data/spec/treevisitor/tree_dsl_spec.rb +57 -0
- data/spec/treevisitor/tree_dsl_with_derived_class1_spec.rb +53 -0
- data/spec/treevisitor/tree_dsl_with_derived_class_spec.rb +51 -0
- data/spec/treevisitor/tree_node_paths_spec.rb +70 -0
- data/spec/treevisitor/tree_node_spec.rb +135 -0
- data/spec/treevisitor/tree_node_visitor_delegate_spec.rb +35 -0
- data/spec/treevisitor/tree_node_visitor_dsl_spec.rb +66 -0
- data/spec/treevisitor/util/dir_processor_spec.rb +13 -0
- data/spec/treevisitor/visitors/block_tree_node_visitor_spec.rb +25 -0
- data/spec/treevisitor/visitors/callback_tree_node_visitor2_spec.rb +38 -0
- data/spec/treevisitor/visitors/depth_tree_node_visitor_spec.rb +28 -0
- data/spec/treevisitor/visitors/tree_node_visitors_spec.rb +27 -0
- data/tasks/rspec.rake +34 -0
- data/tasks/yard.rake +36 -0
- data/tree.rb.gemspec +70 -0
- metadata +231 -0
@@ -0,0 +1,296 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# TreeNode can contains other TreeNode (children)
|
5
|
+
# and can contains LeafNode (leaves)
|
6
|
+
#
|
7
|
+
# TreeNode @children -1---n-> TreeNode
|
8
|
+
# @leaves -1---n-> LeafNode
|
9
|
+
#
|
10
|
+
# define dsl to create Tree
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# tree = TreeNode.create do
|
14
|
+
# node "root" do
|
15
|
+
# leaf "l1"
|
16
|
+
# node "sub" do
|
17
|
+
# leaf "l3"
|
18
|
+
# end
|
19
|
+
# node "wo leaves"
|
20
|
+
# end
|
21
|
+
class TreeNode < AbsNode
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
# DSL create a root node
|
26
|
+
#
|
27
|
+
# tree = TreeNode.create do ... end
|
28
|
+
#
|
29
|
+
# @param [Class] class1 Subclass of TreeNode default TreeNode
|
30
|
+
# @param [Class] class2 Subclass of LeafNode default LeafNode
|
31
|
+
# @param [Object] block
|
32
|
+
def create(class1 = TreeNode, class2 = LeafNode, &block)
|
33
|
+
if class1.ancestors.include?(TreeNode) and class2.ancestors.include?(LeafNode)
|
34
|
+
@tree_node_class = class1
|
35
|
+
@leaf_node_class = class2
|
36
|
+
elsif class1.ancestors.include?(LeafNode) and class2 == LeafNode
|
37
|
+
@tree_node_class = self
|
38
|
+
@leaf_node_class = class1
|
39
|
+
end
|
40
|
+
|
41
|
+
if @tree_node_class.nil? || @leaf_node_class.nil?
|
42
|
+
raise "Must be specified class derived from TreeNode and LeafNode"
|
43
|
+
end
|
44
|
+
|
45
|
+
@scope_stack = []
|
46
|
+
class_eval(&block)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# DSL node add a child to the surrounding node
|
52
|
+
# TreeNode.create do
|
53
|
+
# node "..."
|
54
|
+
# end
|
55
|
+
def node(*args, &block)
|
56
|
+
parent_node = @scope_stack.length > 0 ? @scope_stack[-1] : nil
|
57
|
+
args << parent_node
|
58
|
+
tree_node = @tree_node_class.new(*args)
|
59
|
+
@scope_stack.push tree_node
|
60
|
+
if block
|
61
|
+
if block.arity == 0 || block.arity == -1
|
62
|
+
class_eval(&block)
|
63
|
+
elsif block.arity == 1
|
64
|
+
new_block = Proc.new { block.call(tree_node) }
|
65
|
+
class_eval(&new_block)
|
66
|
+
else
|
67
|
+
raise "block take too much arguments #{block.arity}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@scope_stack.pop
|
71
|
+
end
|
72
|
+
|
73
|
+
# DSL node add a leaf to the surround node
|
74
|
+
# TreeNode.create do
|
75
|
+
# leaf "..."
|
76
|
+
# end
|
77
|
+
def leaf(*args, &block)
|
78
|
+
tree_node = @scope_stack[-1]
|
79
|
+
args << tree_node
|
80
|
+
leaf_node = @leaf_node_class.new(*args)
|
81
|
+
if block
|
82
|
+
if block.arity == 0 || block.arity == -1
|
83
|
+
class_eval(&block)
|
84
|
+
elsif block.arity == 1
|
85
|
+
new_block = Proc.new { block.call(leaf_node) }
|
86
|
+
class_eval(&new_block)
|
87
|
+
else
|
88
|
+
raise "block take too much arguments #{block.arity}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
leaf_node
|
92
|
+
end
|
93
|
+
end # end class << self
|
94
|
+
|
95
|
+
# leaves of this node
|
96
|
+
attr_reader :leaves
|
97
|
+
|
98
|
+
# children i.e. other tree node
|
99
|
+
attr_reader :children
|
100
|
+
|
101
|
+
#
|
102
|
+
# @param [Object] content of this node
|
103
|
+
# @param [Object] parent of this node. If parent is nil, it is a root
|
104
|
+
def initialize(content, parent = nil)
|
105
|
+
@leaves = []
|
106
|
+
@children = []
|
107
|
+
super(content)
|
108
|
+
parent.add_child(self) if parent
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Test if is a root
|
113
|
+
#
|
114
|
+
# @return [Boolean] true if this node is a root
|
115
|
+
def root?
|
116
|
+
@parent.nil?
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# invalidate cached info
|
121
|
+
# invalidate propagates form parent to children and leaves
|
122
|
+
#
|
123
|
+
def invalidate
|
124
|
+
super
|
125
|
+
@children.each { |c| c.invalidate }
|
126
|
+
@leaves.each { |l| l.invalidate }
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# @return [FixNum] total number of nodes
|
131
|
+
#
|
132
|
+
def nr_nodes
|
133
|
+
nr = @leaves.length + @children.length
|
134
|
+
@children.inject(nr) { |sum, c| sum + c.nr_nodes }
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# @return [FixNum] total number of leaves
|
139
|
+
#
|
140
|
+
def nr_leaves
|
141
|
+
@leaves.length + @children.inject(0) { |sum, child| sum + child.nr_leaves }
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# @return [FixNum] total number of children
|
146
|
+
#
|
147
|
+
def nr_children
|
148
|
+
@children.length + @children.inject(0) { |sum, child| sum + child.nr_children }
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# Add a Leaf
|
153
|
+
# @param [LeafNode]
|
154
|
+
#
|
155
|
+
# @return self
|
156
|
+
#
|
157
|
+
def add_leaf(leaf)
|
158
|
+
return if leaf.parent == self
|
159
|
+
if not leaf.parent.nil?
|
160
|
+
leaf.remove_from_parent
|
161
|
+
end
|
162
|
+
leaf.parent = self
|
163
|
+
if @leaves.length > 0
|
164
|
+
@leaves.last.next = leaf
|
165
|
+
leaf.prev = @leaves.last
|
166
|
+
else
|
167
|
+
leaf.prev = nil
|
168
|
+
end
|
169
|
+
leaf.next = nil
|
170
|
+
leaf.invalidate
|
171
|
+
@leaves << leaf
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Add a Tree
|
177
|
+
# @param [LeafNode]
|
178
|
+
#
|
179
|
+
# @return self
|
180
|
+
#
|
181
|
+
def add_child(tree_node)
|
182
|
+
return if tree_node.parent == self
|
183
|
+
if not tree_node.parent.nil?
|
184
|
+
tree_node.remove_from_parent
|
185
|
+
else
|
186
|
+
tree_node.prefix_path = nil
|
187
|
+
end
|
188
|
+
tree_node.invalidate
|
189
|
+
tree_node.parent = self
|
190
|
+
if @children.length > 0
|
191
|
+
@children.last.next = tree_node
|
192
|
+
tree_node.prev = @children.last
|
193
|
+
else
|
194
|
+
tree_node.prev = nil
|
195
|
+
end
|
196
|
+
tree_node.next = nil
|
197
|
+
@children << tree_node
|
198
|
+
self
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Find a node down the hierarchy with content
|
203
|
+
# @param [Object,Regexp] content of searched node
|
204
|
+
# @return [Object, nil] nil if not found
|
205
|
+
#
|
206
|
+
def find(content = nil, &block)
|
207
|
+
if content and block_given?
|
208
|
+
raise "TreeNode::find - passed content AND block"
|
209
|
+
end
|
210
|
+
|
211
|
+
if content
|
212
|
+
if content.class == Regexp
|
213
|
+
block = proc { |l| l.content =~ content }
|
214
|
+
else
|
215
|
+
block = proc { |l| l.content == content }
|
216
|
+
end
|
217
|
+
end
|
218
|
+
return self if block.call(self)
|
219
|
+
|
220
|
+
leaf = @leaves.find { |l| block.call(l) }
|
221
|
+
return leaf if leaf
|
222
|
+
|
223
|
+
@children.each do |child|
|
224
|
+
node = child.find &block
|
225
|
+
return node if node
|
226
|
+
end
|
227
|
+
nil
|
228
|
+
end
|
229
|
+
|
230
|
+
#
|
231
|
+
# return the visitor
|
232
|
+
#
|
233
|
+
def accept(visitor)
|
234
|
+
visitor.enter_node(self)
|
235
|
+
@leaves.each do |leaf|
|
236
|
+
leaf.accept(visitor)
|
237
|
+
end
|
238
|
+
@children.each do |child|
|
239
|
+
child.accept(visitor)
|
240
|
+
end
|
241
|
+
visitor.exit_node(self)
|
242
|
+
visitor
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
# Format the content of tree
|
247
|
+
#
|
248
|
+
def to_str(prefix= "", tty_color = false)
|
249
|
+
str = ""
|
250
|
+
|
251
|
+
# print node itself
|
252
|
+
if root?
|
253
|
+
if tty_color
|
254
|
+
str << ANSI.red{ to_s } << "\n"
|
255
|
+
else
|
256
|
+
str << to_s << "\n"
|
257
|
+
end
|
258
|
+
else
|
259
|
+
str << prefix
|
260
|
+
if self.next
|
261
|
+
str << '|-- '
|
262
|
+
else
|
263
|
+
str << '`-- '
|
264
|
+
end
|
265
|
+
if tty_color
|
266
|
+
str << ANSI.red{ to_s } << "\n"
|
267
|
+
else
|
268
|
+
str << to_s << "\n"
|
269
|
+
end
|
270
|
+
prefix += self.next ? "| " : " "
|
271
|
+
end
|
272
|
+
|
273
|
+
# print leaves
|
274
|
+
@leaves.each do |leaf|
|
275
|
+
str << prefix
|
276
|
+
if !leaf.next.nil? or !@children.empty?
|
277
|
+
str << '|-- '
|
278
|
+
else
|
279
|
+
str << '`-- '
|
280
|
+
end
|
281
|
+
if tty_color
|
282
|
+
str << ANSI.green{ leaf.to_s } << "\n"
|
283
|
+
else
|
284
|
+
str << leaf.to_s << "\n"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# print children
|
289
|
+
@children.each do |child|
|
290
|
+
str << child.to_str(prefix, tty_color)
|
291
|
+
end
|
292
|
+
str
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
|
4
|
+
#
|
5
|
+
# More complex TreeNodeVisitor, define a simple dsl
|
6
|
+
# @example
|
7
|
+
# TreeNodeVisitor.new do
|
8
|
+
# on_enter_node do |node|
|
9
|
+
# puts "hello #{node}"
|
10
|
+
# end
|
11
|
+
# on_exit_node do |node|
|
12
|
+
# puts "bye #{node}"
|
13
|
+
# end
|
14
|
+
# on_leaf do |leaf|
|
15
|
+
# puts "how you do #{leaf}"
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
class TreeNodeVisitor
|
20
|
+
|
21
|
+
# @param [Object] delegate
|
22
|
+
def initialize(delegate = nil, &block)
|
23
|
+
@on_enter_tree_node_blocks = []
|
24
|
+
@on_exit_tree_node_blocks = []
|
25
|
+
@on_visit_leaf_node_blocks = []
|
26
|
+
@stack = []
|
27
|
+
@root = nil
|
28
|
+
@delegate = delegate
|
29
|
+
if block
|
30
|
+
instance_eval(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# called on tree node at start of the visit i.e. we start to visit the subtree
|
36
|
+
#
|
37
|
+
def enter_node(tree_node)
|
38
|
+
parent = @stack.last
|
39
|
+
if @delegate
|
40
|
+
@delegate.enter_node(tree_node) if @delegate.respond_to? :enter_node
|
41
|
+
else
|
42
|
+
@on_enter_tree_node_blocks.each do |b|
|
43
|
+
if b.arity == 1
|
44
|
+
b.call(tree_node)
|
45
|
+
elsif b.arity == 2
|
46
|
+
b.call(tree_node, parent)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@root = tree_node if @stack.empty?
|
51
|
+
@stack.push(tree_node)
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# called on tree node at end of the visit i.e. oll subtree are visited
|
56
|
+
#
|
57
|
+
def exit_node(tree_node)
|
58
|
+
parent = @stack.last
|
59
|
+
if @delegate
|
60
|
+
@delegate.exit_node(tree_node) if @delegate.respond_to? :exit_node
|
61
|
+
else
|
62
|
+
@on_exit_tree_node_blocks.each do |b|
|
63
|
+
if b.arity == 1
|
64
|
+
b.call(tree_node)
|
65
|
+
elsif b.arity == 2
|
66
|
+
b.call(tree_node, parent)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@stack.pop
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# called when visit leaf node
|
75
|
+
#
|
76
|
+
def visit_leaf(leaf_node)
|
77
|
+
parent = @stack.last
|
78
|
+
if @delegate
|
79
|
+
@delegate.visit_leaf(leaf_node) if @delegate.respond_to? :visit_leaf
|
80
|
+
else
|
81
|
+
@on_visit_leaf_node_blocks.each do |b|
|
82
|
+
if b.arity == 1
|
83
|
+
b.call(leaf_node)
|
84
|
+
elsif b.arity == 2
|
85
|
+
b.call(leaf_node, parent)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# add a block to be called when entering into a tree_node
|
93
|
+
#
|
94
|
+
def on_enter_node(&block)
|
95
|
+
raise "already defined a delegate" if @delegate
|
96
|
+
raise "block missing" unless block
|
97
|
+
@on_enter_tree_node_blocks << block
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# add a block to be called when exiting from a TreeNode
|
102
|
+
#
|
103
|
+
def on_exit_node(&block)
|
104
|
+
raise "already defined a delegate" if @delegate
|
105
|
+
raise "block missing" unless block
|
106
|
+
@on_exit_tree_node_blocks << block
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# add a block to be called when visiting a leaf node
|
111
|
+
#
|
112
|
+
def on_leaf(&block)
|
113
|
+
raise "already defined a delegate" if @delegate
|
114
|
+
raise "block missing" unless block
|
115
|
+
@on_visit_leaf_node_blocks << block
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
|
4
|
+
#
|
5
|
+
# Visit a directory tree
|
6
|
+
# not TreeNode related
|
7
|
+
#
|
8
|
+
class DirProcessor
|
9
|
+
|
10
|
+
def initialize( &action )
|
11
|
+
@processors = {}
|
12
|
+
@default_processor = action
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_processor( re, &action )
|
16
|
+
@processors[ re ] = action
|
17
|
+
end
|
18
|
+
|
19
|
+
def process( dirname )
|
20
|
+
@dirname = dirname
|
21
|
+
old_dirname = Dir.pwd
|
22
|
+
Dir.chdir( @dirname )
|
23
|
+
Dir["**/*"].each { |f|
|
24
|
+
pn = Pathname.new( f ).expand_path
|
25
|
+
# puts "#{self.class.name}#loadfromdir #{f}"
|
26
|
+
next if pn.directory?
|
27
|
+
process_file( pn )
|
28
|
+
}
|
29
|
+
Dir.chdir( old_dirname )
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def process_file( pn )
|
36
|
+
# puts "file: #{f}"
|
37
|
+
pair = @processors.find { |re,action| re =~ pn.to_s }
|
38
|
+
unless pair.nil?
|
39
|
+
pair[1].call( pn )
|
40
|
+
else
|
41
|
+
@default_processor.call( pn ) if @default_processor
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# It calls a block when visit a tree_node or leaf_node
|
5
|
+
#
|
6
|
+
class BlockTreeNodeVisitor < BasicTreeNodeVisitor
|
7
|
+
|
8
|
+
def initialize( &action )
|
9
|
+
@block = action
|
10
|
+
end
|
11
|
+
|
12
|
+
def enter_node( tree_node )
|
13
|
+
@block.call( tree_node )
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_leaf( leaf_node )
|
17
|
+
@block.call( leaf_node )
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# Builds a TreeNode from a filesystem directory
|
5
|
+
# It similar to CloneTreeNodeVisitor
|
6
|
+
#
|
7
|
+
class BuildDirTreeVisitor # < BasicTreeNodeVisitor
|
8
|
+
|
9
|
+
attr_reader :root
|
10
|
+
|
11
|
+
#
|
12
|
+
# Number of visited directory (aka nr_nodes - nr_leaf)
|
13
|
+
#
|
14
|
+
attr_reader :nr_directories
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# Number of visited directory (nr_leaves)
|
19
|
+
# @see AbsNode#nr_leaves
|
20
|
+
#
|
21
|
+
attr_reader :nr_files
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
super
|
25
|
+
@root = nil
|
26
|
+
@stack = []
|
27
|
+
@nr_directories = 0
|
28
|
+
@nr_files = 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def enter_node( pathname )
|
32
|
+
if @stack.empty?
|
33
|
+
tree_node = TreeNode.new( File.basename( pathname ) )
|
34
|
+
@root = tree_node
|
35
|
+
else
|
36
|
+
tree_node = TreeNode.new( File.basename( pathname ), @stack.last )
|
37
|
+
end
|
38
|
+
@nr_directories += 1
|
39
|
+
@stack.push( tree_node )
|
40
|
+
end
|
41
|
+
|
42
|
+
def cannot_enter_node(pathname, error)
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def exit_node( pathname )
|
47
|
+
@stack.pop
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_leaf( pathname )
|
51
|
+
@nr_files += 1
|
52
|
+
# connect the leaf_node created to the last tree_node on the stack
|
53
|
+
LeafNode.new( File.basename(pathname), @stack.last )
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end # end module TreeVisitor
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# Executes a block when enter in a node
|
5
|
+
# The block are defined from on_enter_X methods
|
6
|
+
# The blocks take as argument the node and the parent_node
|
7
|
+
#
|
8
|
+
class CallbackTreeNodeVisitor2
|
9
|
+
|
10
|
+
attr_reader :root
|
11
|
+
|
12
|
+
def initialize(delegate = nil)
|
13
|
+
super()
|
14
|
+
@stack = []
|
15
|
+
@root = nil
|
16
|
+
@delegate = delegate
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_enter_node(&block)
|
20
|
+
raise "already defined a delegate" if @delegate
|
21
|
+
@action_enter_tree_node = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_visit_leaf(&block)
|
25
|
+
raise "already defined a delegate" if @delegate
|
26
|
+
@action_visit_leaf_node = block
|
27
|
+
end
|
28
|
+
|
29
|
+
def enter_node(src_tree_node)
|
30
|
+
dst_parent_node = @stack.empty? ? nil : @stack.last
|
31
|
+
dst_tree_node = @action_enter_tree_node.call(src_tree_node, dst_parent_node) if @action_enter_tree_node
|
32
|
+
dst_tree_node = @delegate.enter_node(src_tree_node, dst_parent_node) if @delegate and @delegate.respond_to? :enter_node
|
33
|
+
@root = dst_tree_node if @stack.empty?
|
34
|
+
@stack.push(dst_tree_node)
|
35
|
+
end
|
36
|
+
|
37
|
+
def exit_node(src_tree_node)
|
38
|
+
@stack.pop
|
39
|
+
end
|
40
|
+
|
41
|
+
def visit_leaf(src_leaf_node)
|
42
|
+
parent_node = @stack.last
|
43
|
+
@action_visit_leaf_node.call(src_leaf_node, parent_node) if @action_visit_leaf_node
|
44
|
+
@delegate.visit_leaf(src_leaf_node, parent_node) if @delegate and @delegate.respond_to? :visit_leaf
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# Clone a tree_node, nodes are duplicated.
|
5
|
+
# Node content are not duplicated!
|
6
|
+
#
|
7
|
+
class CloneTreeNodeVisitor # < BasicTreeNodeVisitor
|
8
|
+
|
9
|
+
#
|
10
|
+
# Contains the cloned tree node after the visit
|
11
|
+
#
|
12
|
+
attr_reader :cloned_root
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@cloned_root = nil
|
17
|
+
@stack = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def enter_node( tree_node )
|
21
|
+
if @stack.empty?
|
22
|
+
cloned_tree_node = TreeNode.new( tree_node.content )
|
23
|
+
@cloned_root = cloned_tree_node
|
24
|
+
else
|
25
|
+
cloned_tree_node = TreeNode.new( tree_node.content, @stack.last )
|
26
|
+
end
|
27
|
+
@stack.push( cloned_tree_node )
|
28
|
+
end
|
29
|
+
|
30
|
+
def exit_node( tree_node )
|
31
|
+
@stack.pop
|
32
|
+
end
|
33
|
+
|
34
|
+
def visit_leaf( leaf_node )
|
35
|
+
LeafNode.new( leaf_node.content, @stack.last )
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# Simple visitor: show how calculate the depth of a tree
|
5
|
+
#
|
6
|
+
class DepthTreeNodeVisitor # < BasicTreeNodeVisitor
|
7
|
+
|
8
|
+
attr_reader :depth
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
@depth = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def enter_node( tree_node )
|
16
|
+
@depth += 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def exit_node( tree_node )
|
20
|
+
@depth -= 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit_leaf( leaf_node )
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module TreeVisitor
|
4
|
+
#
|
5
|
+
# Build hash with directory structure
|
6
|
+
#
|
7
|
+
class DirectoryToHashVisitor # < TreeVisitor::BasicTreeNodeVisitor
|
8
|
+
|
9
|
+
attr_reader :root
|
10
|
+
|
11
|
+
def initialize(pathname)
|
12
|
+
@stack = []
|
13
|
+
@node = {}
|
14
|
+
@root = @node
|
15
|
+
end
|
16
|
+
|
17
|
+
def enter_node(pathname)
|
18
|
+
subnode = {}
|
19
|
+
@node[File.basename(pathname)] = subnode
|
20
|
+
@stack.push(@node)
|
21
|
+
@node = subnode
|
22
|
+
end
|
23
|
+
|
24
|
+
def exit_node(pathname)
|
25
|
+
@node = @stack.pop
|
26
|
+
end
|
27
|
+
|
28
|
+
def visit_leaf(pathname)
|
29
|
+
@node[File.basename(pathname)] = File.stat(pathname).size
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TreeVisitor
|
3
|
+
#
|
4
|
+
# Print for every node the name
|
5
|
+
#
|
6
|
+
class FlatPrintTreeNodeVisitor # < BasicTreeNodeVisitor
|
7
|
+
|
8
|
+
def enter_node( tree_node )
|
9
|
+
puts tree_node.name
|
10
|
+
end
|
11
|
+
|
12
|
+
def exit_node( tree_node )
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_leaf( leaf_node )
|
16
|
+
puts leaf_node.name
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|