MINT-statemachine 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +135 -0
- data/LICENSE +16 -0
- data/MINT-statemachine.gemspec +27 -0
- data/README.rdoc +69 -0
- data/Rakefile +88 -0
- data/TODO +2 -0
- data/lib/statemachine.rb +26 -0
- data/lib/statemachine/action_invokation.rb +83 -0
- data/lib/statemachine/builder.rb +383 -0
- data/lib/statemachine/generate/dot_graph.rb +1 -0
- data/lib/statemachine/generate/dot_graph/dot_graph_statemachine.rb +127 -0
- data/lib/statemachine/generate/java.rb +1 -0
- data/lib/statemachine/generate/java/java_statemachine.rb +265 -0
- data/lib/statemachine/generate/src_builder.rb +48 -0
- data/lib/statemachine/generate/util.rb +50 -0
- data/lib/statemachine/parallelstate.rb +196 -0
- data/lib/statemachine/state.rb +102 -0
- data/lib/statemachine/statemachine.rb +279 -0
- data/lib/statemachine/stub_context.rb +26 -0
- data/lib/statemachine/superstate.rb +53 -0
- data/lib/statemachine/transition.rb +76 -0
- data/lib/statemachine/version.rb +17 -0
- data/spec/action_invokation_spec.rb +101 -0
- data/spec/builder_spec.rb +243 -0
- data/spec/default_transition_spec.rb +111 -0
- data/spec/generate/dot_graph/dot_graph_stagemachine_spec.rb +27 -0
- data/spec/generate/java/java_statemachine_spec.rb +349 -0
- data/spec/history_spec.rb +107 -0
- data/spec/noodle.rb +23 -0
- data/spec/sm_action_parameterization_spec.rb +99 -0
- data/spec/sm_activation_spec.rb +116 -0
- data/spec/sm_entry_exit_actions_spec.rb +99 -0
- data/spec/sm_odds_n_ends_spec.rb +67 -0
- data/spec/sm_parallel_state_spec.rb +207 -0
- data/spec/sm_simple_spec.rb +26 -0
- data/spec/sm_super_state_spec.rb +55 -0
- data/spec/sm_turnstile_spec.rb +76 -0
- data/spec/spec_helper.rb +121 -0
- data/spec/transition_spec.rb +107 -0
- metadata +115 -0
@@ -0,0 +1 @@
|
|
1
|
+
require 'statemachine/generate/java/java_statemachine'
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'statemachine/generate/util'
|
2
|
+
require 'statemachine/generate/src_builder'
|
3
|
+
|
4
|
+
module Statemachine
|
5
|
+
class Statemachine
|
6
|
+
|
7
|
+
attr_reader :states
|
8
|
+
|
9
|
+
def to_java(options = {})
|
10
|
+
generator = Generate::Java::JavaStatemachine.new(self, options)
|
11
|
+
generator.generate!
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module Generate
|
17
|
+
module Java
|
18
|
+
class JavaStatemachine
|
19
|
+
|
20
|
+
include Generate::Util
|
21
|
+
|
22
|
+
HEADER1 = "// This file was generated by the Ruby Statemachine Library (http://slagyr.github.com/statemachine)."
|
23
|
+
HEADER2 = "// Generated at "
|
24
|
+
|
25
|
+
def initialize(sm, options)
|
26
|
+
@sm = sm
|
27
|
+
@output_dir = options[:output]
|
28
|
+
@classname = options[:name]
|
29
|
+
@context_classname = "#{@classname}Context"
|
30
|
+
@package = options[:package]
|
31
|
+
raise "Please specify an output directory. (:output => 'where/you/want/your/code')" if @output_dir.nil?
|
32
|
+
raise "Output dir '#{@output_dir}' doesn't exist." if !File.exist?(@output_dir)
|
33
|
+
raise "Please specify a name for the statemachine. (:name => 'SomeName')" if @classname.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate!
|
37
|
+
explore_sm
|
38
|
+
create_file(src_file(@classname), build_statemachine_src)
|
39
|
+
create_file(src_file(@context_classname), build_context_src)
|
40
|
+
say "Statemachine generated."
|
41
|
+
end
|
42
|
+
|
43
|
+
private ###########################################
|
44
|
+
|
45
|
+
def explore_sm
|
46
|
+
events = []
|
47
|
+
actions = []
|
48
|
+
@sm.states.values.each do |state|
|
49
|
+
state.transitions.values.each do |transition|
|
50
|
+
events << transition.event
|
51
|
+
add_action(actions, transition.action)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
@event_names = events.uniq.map {|e| e.to_s.camalized(:lower)}.sort
|
55
|
+
|
56
|
+
@sm.states.values.each do |state|
|
57
|
+
add_action(actions, state.entry_action)
|
58
|
+
add_action(actions, state.exit_action)
|
59
|
+
end
|
60
|
+
@action_names = actions.uniq.map {|e| e.to_s.camalized(:lower)}.sort
|
61
|
+
|
62
|
+
@startstate = @sm.get_state(@sm.startstate).resolve_startstate
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_action(actions, action)
|
66
|
+
return if action.nil?
|
67
|
+
raise "Actions must be symbols in order to generation Java code. (#{action})" unless action.is_a?(Symbol)
|
68
|
+
actions << action
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_statemachine_src
|
72
|
+
src = begin_src
|
73
|
+
src << "public class #{@classname}" << endl
|
74
|
+
begin_scope(src)
|
75
|
+
|
76
|
+
add_instance_variables(src)
|
77
|
+
add_constructor(src)
|
78
|
+
add_statemachine_boilerplate_code(src)
|
79
|
+
add_event_delegation(src)
|
80
|
+
add_statemachine_exception(src)
|
81
|
+
add_base_state(src)
|
82
|
+
add_state_implementations(src)
|
83
|
+
|
84
|
+
end_scope(src)
|
85
|
+
return src.to_s
|
86
|
+
end
|
87
|
+
|
88
|
+
def add_instance_variables (src)
|
89
|
+
src << "// Instance variables" << endl
|
90
|
+
concrete_states = @sm.states.values.reject { |state| state.id.nil? || !state.concrete? }.sort { |a, b| a.id <=> b.id }
|
91
|
+
concrete_states.each do |state|
|
92
|
+
name = state.id.to_s
|
93
|
+
src << "public final State #{name.upcase} = new #{name.camalized}State(this);" << endl
|
94
|
+
end
|
95
|
+
superstates = @sm.states.values.reject { |state| state.concrete? }.sort { |a, b| a.id <=> b.id }
|
96
|
+
superstates.each do |superstate|
|
97
|
+
startstate = superstate.resolve_startstate
|
98
|
+
src << "public final State #{superstate.id.to_s.upcase} = #{startstate.id.to_s.upcase};" << endl
|
99
|
+
end
|
100
|
+
src << "private State state = #{@startstate.id.to_s.upcase};" << endl
|
101
|
+
src << endl
|
102
|
+
src << "private #{@context_classname} context;" << endl
|
103
|
+
src << endl
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_constructor(src)
|
107
|
+
src << "// Statemachine constructor" << endl
|
108
|
+
add_method(src, nil, @classname, "#{@context_classname} context") do
|
109
|
+
src << "this.context = context;" << endl
|
110
|
+
entered_states = []
|
111
|
+
entry_state = @startstate
|
112
|
+
while entry_state != @sm.root
|
113
|
+
entered_states << entry_state
|
114
|
+
entry_state = entry_state.superstate
|
115
|
+
end
|
116
|
+
entered_states.reverse.each do |state|
|
117
|
+
src << "context.#{state.entry_action.to_s.camalized(:lower)}();" << endl if state.entry_action
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_statemachine_boilerplate_code(src)
|
123
|
+
src << "// The following is boiler plate code standard to all statemachines" << endl
|
124
|
+
add_one_liner(src, @context_classname, "getContext", nil, "return context")
|
125
|
+
add_one_liner(src, "State", "getState", nil, "return state")
|
126
|
+
add_one_liner(src, "void", "setState", "State newState", "state = newState")
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_event_delegation(src)
|
130
|
+
src << "// Event delegation" << endl
|
131
|
+
@event_names.each do |event|
|
132
|
+
add_one_liner(src, "void", event, nil, "state.#{event}()")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def add_statemachine_exception(src)
|
137
|
+
src << "// Standard exception class added to all statemachines." << endl
|
138
|
+
src << "public static class StatemachineException extends RuntimeException" << endl
|
139
|
+
begin_scope(src)
|
140
|
+
src << "public StatemachineException(State state, String event)" << endl
|
141
|
+
begin_scope(src)
|
142
|
+
src << "super(\"Missing transition from '\" + state.getClass().getSimpleName() + \"' with the '\" + event + \"' event.\");" << endl
|
143
|
+
end_scope(src)
|
144
|
+
end_scope(src)
|
145
|
+
src << endl
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_base_state(src)
|
149
|
+
src << "// The base state" << endl
|
150
|
+
src << "public static abstract class State" << endl
|
151
|
+
begin_scope(src)
|
152
|
+
src << "protected #{@classname} statemachine;" << endl
|
153
|
+
src << endl
|
154
|
+
add_one_liner(src, nil, "State", "#{@classname} statemachine", "this.statemachine = statemachine")
|
155
|
+
@event_names.each do |event|
|
156
|
+
add_one_liner(src, "void", event, nil, "throw new StatemachineException(this, \"#{event}\")")
|
157
|
+
end
|
158
|
+
end_scope(src)
|
159
|
+
src << endl
|
160
|
+
end
|
161
|
+
|
162
|
+
def add_state_implementations(src)
|
163
|
+
src << "// State implementations" << endl
|
164
|
+
@sm.states.keys.reject{|k| k.nil? }.sort.each do |state_id|
|
165
|
+
state = @sm.states[state_id]
|
166
|
+
state_name = state.id.to_s.camalized
|
167
|
+
base_class = state.superstate == @sm.root ? "State" : state.superstate.id.to_s.camalized
|
168
|
+
|
169
|
+
add_concrete_state_class(src, state, state_name, base_class) if state_id
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def add_concrete_state_class(src, state, state_name, base_class)
|
174
|
+
src << "public static class #{state_name}State extends State" << endl
|
175
|
+
src << "{" << endl
|
176
|
+
src.indent!
|
177
|
+
add_one_liner(src, nil, "#{state_name}State", "#{@classname} statemachine", "super(statemachine)")
|
178
|
+
state.transitions.keys.sort.each do |event_id|
|
179
|
+
transition = state.transitions[event_id]
|
180
|
+
add_state_event_handler(transition, src)
|
181
|
+
end
|
182
|
+
src.undent!
|
183
|
+
src << "}" << endl
|
184
|
+
src << endl
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_state_event_handler(transition, src)
|
188
|
+
event_name = transition.event.to_s.camalized(:lower)
|
189
|
+
exits, entries = transition.exits_and_entries(@sm.get_state(transition.origin_id), @sm.get_state(transition.destination_id))
|
190
|
+
add_method(src, "void", event_name, nil) do
|
191
|
+
exits.each do |exit|
|
192
|
+
src << "statemachine.getContext().#{exit.exit_action.to_s.camalized(:lower)}();" << endl if exit.exit_action
|
193
|
+
end
|
194
|
+
src << "statemachine.getContext().#{transition.action.to_s.camalized(:lower)}();" << endl if transition.action
|
195
|
+
src << "statemachine.setState(statemachine.#{transition.destination_id.to_s.upcase});" << endl
|
196
|
+
entries.each do |entry|
|
197
|
+
src << "statemachine.getContext().#{entry.entry_action.to_s.camalized(:lower)}();" << endl if entry.entry_action
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def add_one_liner(src, return_type, name, params, body)
|
203
|
+
add_method(src, return_type, name, params) do
|
204
|
+
src << "#{body};" << endl
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def add_method(src, return_type, name, params)
|
209
|
+
src << "public #{return_type} #{name}(#{params})".sub(' ' * 2, ' ') << endl
|
210
|
+
begin_scope(src)
|
211
|
+
yield
|
212
|
+
end_scope(src)
|
213
|
+
src << endl
|
214
|
+
end
|
215
|
+
|
216
|
+
def begin_scope(src)
|
217
|
+
src << "{" << endl
|
218
|
+
src.indent!
|
219
|
+
end
|
220
|
+
|
221
|
+
def end_scope(src)
|
222
|
+
src.undent! << "}" << endl
|
223
|
+
end
|
224
|
+
|
225
|
+
def build_context_src
|
226
|
+
src = begin_src
|
227
|
+
src << "public interface #{@context_classname}" << endl
|
228
|
+
begin_scope(src)
|
229
|
+
src << "// Actions" << endl
|
230
|
+
@action_names.each do |event|
|
231
|
+
src << "void #{event}();" << endl
|
232
|
+
end
|
233
|
+
end_scope(src)
|
234
|
+
return src.to_s
|
235
|
+
end
|
236
|
+
|
237
|
+
def begin_src
|
238
|
+
src = SrcBuilder.new
|
239
|
+
src << HEADER1 << endl
|
240
|
+
src << HEADER2 << timestamp << endl
|
241
|
+
src << "package #{@package};" << endl
|
242
|
+
src << endl
|
243
|
+
return src
|
244
|
+
end
|
245
|
+
|
246
|
+
def create_file(filename, content)
|
247
|
+
establish_directory(File.dirname(filename))
|
248
|
+
say "Writing to file: #{filename}"
|
249
|
+
File.open(filename, 'w') do |file|
|
250
|
+
file.write(content)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def src_file(name)
|
255
|
+
path = @output_dir
|
256
|
+
if @package
|
257
|
+
@package.split(".").each { |segment| path = File.join(path, segment) }
|
258
|
+
end
|
259
|
+
return File.join(path, "#{name}.java")
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Statemachine
|
2
|
+
module Generate
|
3
|
+
class SrcBuilder
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@src = ""
|
7
|
+
@is_newline = true
|
8
|
+
@indents = 0
|
9
|
+
@indent_size = 2
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(content)
|
13
|
+
if content == :endl
|
14
|
+
newline!
|
15
|
+
else
|
16
|
+
add_indents if @is_newline
|
17
|
+
@src += content.to_s
|
18
|
+
end
|
19
|
+
return self
|
20
|
+
end
|
21
|
+
|
22
|
+
def newline!
|
23
|
+
@src += "\n"
|
24
|
+
@is_newline = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
return @src
|
29
|
+
end
|
30
|
+
|
31
|
+
def indent!
|
32
|
+
@indents += 1
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def undent!
|
37
|
+
@indents -= 1
|
38
|
+
return self
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_indents
|
42
|
+
@src += (" " * (@indent_size * @indents))
|
43
|
+
@is_newline = false
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Statemachine
|
4
|
+
module Generate
|
5
|
+
module Util
|
6
|
+
|
7
|
+
def create_file(filename, content)
|
8
|
+
establish_directory(File.dirname(filename))
|
9
|
+
File.open(filename, 'w') do |file|
|
10
|
+
file.write(content)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def establish_directory(path)
|
15
|
+
return if File.exist?(path)
|
16
|
+
establish_directory(File.dirname(path))
|
17
|
+
Dir.mkdir(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def timestamp
|
21
|
+
return DateTime.now.strftime("%H:%M:%S %B %d, %Y")
|
22
|
+
end
|
23
|
+
|
24
|
+
def endl
|
25
|
+
return :endl
|
26
|
+
end
|
27
|
+
|
28
|
+
def say(message)
|
29
|
+
if !defined?($IS_TEST)
|
30
|
+
puts message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class String
|
39
|
+
def camalized(starting_case = :upper)
|
40
|
+
value = self.downcase.gsub(/[_| |\-][a-z]/) { |match| match[-1..-1].upcase }
|
41
|
+
value = value[0..0].upcase + value[1..-1] if starting_case == :upper
|
42
|
+
return value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Symbol
|
47
|
+
def <=>(other)
|
48
|
+
return to_s <=> other.to_s
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module Statemachine
|
2
|
+
|
3
|
+
class Parallelstate< Superstate
|
4
|
+
|
5
|
+
attr_accessor :parallel_statemachines, :id
|
6
|
+
attr_reader :startstate_ids
|
7
|
+
|
8
|
+
def initialize(id, superstate, statemachine)
|
9
|
+
super(id, superstate, statemachine)
|
10
|
+
@parallel_statemachines=[]
|
11
|
+
@startstate_ids=[]
|
12
|
+
end
|
13
|
+
|
14
|
+
# def startstate_id= id
|
15
|
+
# if @parallel_statemachines.size>0
|
16
|
+
# @startstate_ids[@parallel_statemachines.size-1]= id
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def startstate_id
|
21
|
+
# if (@parallel_statemachines.size>0 and @parallel_statemachines.size==@startstate_ids.size)
|
22
|
+
# return true
|
23
|
+
# end
|
24
|
+
# nil
|
25
|
+
# end
|
26
|
+
|
27
|
+
def context= c
|
28
|
+
@parallel_statemachines.each do |s|
|
29
|
+
s.context=c
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def activate(terminal_state = nil)
|
34
|
+
@statemachine.state = self
|
35
|
+
|
36
|
+
@parallel_statemachines.each_with_index do |s,i|
|
37
|
+
s.activation = @statemachine.activation
|
38
|
+
s.reset(@startstate_ids[i])
|
39
|
+
end
|
40
|
+
@parallel_statemachines.each do |s|
|
41
|
+
next if terminal_state and s.has_state(terminal_state)
|
42
|
+
@statemachine.activation.call(s.state,self.abstract_states,self.states) if @statemachine.activation
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_statemachine(statemachine)
|
48
|
+
statemachine.is_parallel=self
|
49
|
+
@parallel_statemachines.push(statemachine)
|
50
|
+
statemachine.context = @statemachine.context
|
51
|
+
@startstate_ids << @startstate_id
|
52
|
+
@startstate_id = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_statemachine_with(id)
|
56
|
+
@parallel_statemachines.each do |s|
|
57
|
+
return s if s.has_state(id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def non_default_transition_for(event)
|
62
|
+
p "check parallel for #{event}"
|
63
|
+
transition = @transitions[event]
|
64
|
+
return transition if transition
|
65
|
+
|
66
|
+
transition = transition_for(event)
|
67
|
+
|
68
|
+
transition = @superstate.non_default_transition_for(event) if @superstate and not transition
|
69
|
+
return transition
|
70
|
+
end
|
71
|
+
|
72
|
+
def In(id)
|
73
|
+
@parallel_statemachines.each do |s|
|
74
|
+
return true if s.In(id.to_sym)
|
75
|
+
end
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
def state= id
|
80
|
+
@parallel_statemachines.each do |s|
|
81
|
+
if s.has_state(id)
|
82
|
+
s.state=id
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
return false
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_state(id)
|
90
|
+
@parallel_statemachines.each do |s|
|
91
|
+
if s.has_state(id)
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_state(id)
|
99
|
+
# if state = @statemachine.get_state(id)
|
100
|
+
# return state
|
101
|
+
# end
|
102
|
+
@parallel_statemachines.each do |s|
|
103
|
+
if state = s.get_state(id)
|
104
|
+
return state
|
105
|
+
end
|
106
|
+
end
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def process_event(event, *args)
|
111
|
+
exceptions = []
|
112
|
+
result = false
|
113
|
+
# TODO fix needed: respond_to checks superstates lying out of the parallel state as well, in case an event is
|
114
|
+
# defined outside the parallel statemachine it gets processed twice!
|
115
|
+
|
116
|
+
@parallel_statemachines.each_with_index do |s,i|
|
117
|
+
if s.respond_to? event
|
118
|
+
s.process_event(event,*args)
|
119
|
+
result = true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
# Resets all of the statemachines back to theirs starting state.
|
126
|
+
def reset
|
127
|
+
@parallel_statemachines.each_with_index do |s,i|
|
128
|
+
s.reset(@startstate_ids[i])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def concrete?
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
def startstate
|
137
|
+
return @statemachine.get_state(@startstate_id)
|
138
|
+
end
|
139
|
+
|
140
|
+
def resolve_startstate
|
141
|
+
return self
|
142
|
+
end
|
143
|
+
|
144
|
+
def substate_exiting(substate)
|
145
|
+
@history_id = substate.id
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_substates(*substate_ids)
|
149
|
+
do_substate_adding(substate_ids)
|
150
|
+
end
|
151
|
+
|
152
|
+
def default_history=(state_id)
|
153
|
+
@history_id = @default_history_id = state_id
|
154
|
+
end
|
155
|
+
|
156
|
+
def states
|
157
|
+
return @parallel_statemachines.map &:state
|
158
|
+
end
|
159
|
+
|
160
|
+
def transition_for(event)
|
161
|
+
@parallel_statemachines.each do |s|
|
162
|
+
# puts "checke parallel #{s.id} for #{event}"
|
163
|
+
transition = s.get_state(s.state).non_default_transition_for(event)
|
164
|
+
transition = s.get_state(s.state).default_transition if not transition
|
165
|
+
return transition if transition
|
166
|
+
end
|
167
|
+
return @superstate.default_transition if @superstate
|
168
|
+
|
169
|
+
# super.transition_for(event)
|
170
|
+
end
|
171
|
+
|
172
|
+
def enter(args=[])
|
173
|
+
# reset
|
174
|
+
super(args)
|
175
|
+
end
|
176
|
+
def to_s
|
177
|
+
return "'#{id}' parallel"
|
178
|
+
end
|
179
|
+
|
180
|
+
def abstract_states
|
181
|
+
abstract_states=[]
|
182
|
+
|
183
|
+
if (@superstate)
|
184
|
+
abstract_states=@superstate.abstract_states
|
185
|
+
end
|
186
|
+
@parallel_statemachines.each do |s|
|
187
|
+
abstract_states += s.abstract_states
|
188
|
+
end
|
189
|
+
abstract_states.uniq
|
190
|
+
end
|
191
|
+
def is_parallel
|
192
|
+
true
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|