-
Notifications
You must be signed in to change notification settings - Fork 3
/
composite.rb
226 lines (199 loc) · 6.36 KB
/
composite.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# A general-purpose library for implementing the "Composite pattern" --
# in other words, a tree with an arbitrary level of branching from each internal node
# The tree can be traversed both upwards and downwards, as well as from one sibling
# to the preceding or following siblings
# Nodes can either be "branch" or "leaf" nodes -- "branches" can have children,
# but "leaves" cannot
# Example usage:
# class MyLeaf
# include Composite::Leaf
# end
# class MyBranch
# include Composite::Branch
# can_contain MyLeaf, AnotherNodeType # other types of child nodes cannot be added as children
# # if 'can_contain' is omitted, any node type can be added as child
# end
# node1,node2,node3 = MyBranch.new,MyLeaf.new,MyLeaf.new
# node1.add(node2)
# node1.children.include?(node2) # => true
# node1.children.include?(node3) # => false
# node3.add_after(node2)
# node1.children.include?(node3) # => true
# node1.descendants.to_a # => [node2, node3]
# node2.following.to_a # => [node3]
# node3.preceding.to_a # => [node2]
# node1.remove(node3)
# node2.replace_with(node3)
require 'set'
module Composite
module Node
def next; @__next__ end
def previous; @__prev__ end
def parent; @__parent__ end
alias :prev :previous
# Modifying the tree structure
def add_before(node)
raise "Can't add #{self} before a node with no parent" if node.parent.nil?
raise "#{node.parent.class} cannot contain nodes of type #{self.class}" unless node.parent.can_contain?(self.class)
raise "Can't add an node before itself" if node == self
raise "#{self} cannot be its own ancestor" if node.ancestors.include? self
@__parent__.remove(self) if @__parent__
@__parent__ = node.parent
@__parent__.first_child = self if @__parent__.first_child == node
@__next__ = node
@__prev__ = node.prev
node.prev = self
@__prev__.next = self if @__prev__
end
def add_after(node)
raise "Can't add #{self} after an node with no parent" if node.parent.nil?
raise "#{node.parent.class} cannot contain nodes of type #{self.class}" if (not node.parent.can_contain?(self.class))
raise "Can't add an node after itself" if node == self
raise "#{self} cannot be its own ancestor" if node.ancestors.include? self
@__parent__.remove(self) if @__parent__
@__parent__ = node.parent
@__parent__.last_child = self if @__parent__.last_child == node
@__prev__ = node
@__next__ = node.next
node.next = self
@__next__.prev = self if @__next__
end
def replace_with(node)
raise "Can't replace an node with itself" if node == self
raise "Can't replace #{self} with its own ancestor" if ancestors.include? node
raise "#{node} can't replace an node with no parent" if @__parent__.nil?
node.parent.remove(node) if node.parent
node.parent = @__parent__
@__parent__.first_child = node if @__parent__.first_child == self
@__parent__.last_child = node if @__parent__.last_child == self
node.next = @__next__
node.prev = @__prev__
@__next__.prev = node if @__next__
@__prev__.next = node if @__prev__
@__next__ = @__prev__ = @__parent__ = nil
end
# Iterators
def ancestors
return enum_for(:ancestors) if not block_given?
node = self
while (node = node.parent)
yield node
end
end
def siblings
return enum_for(:siblings) if not block_given?
parent.children { |node| yield node if node != self }
end
def preceding
return enum_for(:preceding) if not block_given?
node = self
while (node = node.prev)
yield node
end
end
def following
return enum_for(:following) if not block_given?
node = self
while (node = node.next)
yield node
end
end
# Query methods
def before?(node)
following.include?(node)
end
def after?(node)
preceding.include?(node)
end
protected
def next=(node) @__next__ = node end
def previous=(node) @__prev__ = node end
def parent=(node) @__parent__ = node end
def first_child=(node) @__first__ = node end
def last_child=(node) @__last__ = node end
alias :prev= :previous=
end
end
# Branch, or internal nodes
module Composite::Branch
include Composite::Node
def first_child; @__first__; end
def last_child; @__last__; end
def add(child)
raise "#{self.class} cannot contain nodes of type #{child.class}" if not can_contain?(child)
raise "Can't add #{self} to itself" if child == self
raise "#{child} cannot be added to its own descendant" if ancestors.include? child
child.parent.remove(child) if child.parent
if @__last__
child.add_after(@__last__)
else
@__first__ = @__last__ = child
child.parent = self
end
end
def remove(child)
raise "#{child.inspect} is not a child of #{self.inspect}" if child.parent != self
@__first__ = child.next if @__first__ == child
@__last__ = child.prev if @__last__ == child
child.prev.next = child.next if child.prev
child.next.prev = child.prev if child.next
child.parent = child.next = child.prev = nil
end
def children
return enum_for(:children) if not block_given?
node = @__first__
while node
yield node
node = node.next
end
end
def descendants
return enum_for(:descendants) if not block_given?
children do |node|
yield node
if node.respond_to? :descendants
node.descendants { |desc| yield desc }
end
end
end
def reject_children!
node = @__first__
while node
next_node = node.next
remove(node) if yield node
node = next_node
end
end
def reject_descendants!(&block)
node = @__first__
while node
next_node = node.next
if yield node
remove(node)
elsif node.respond_to? :reject_descendants!
node.reject_descendants!(&block)
end
node = next_node
end
end
private
def self.included(klass)
klass.class_eval do
def self.can_contain?(klass)
(not defined?(@__can_contain__)) || @__can_contain__.include?(klass)
end
def can_contain?(klass)
self.class.can_contain?(klass)
end
private
def self.can_contain(*classes)
(@__can_contain__ ||= Set.new).merge(classes)
end
end
end
end
# Leaf nodes
module Composite::Leaf
include Composite::Node
undef_method :first_child=, :last_child=
end