1 module flow;
2 
3 
4 import std.range, std.algorithm, std.array, std.format;
5 import cushion;
6 
7 
8 /***************************************************************
9  * Interface defines the method of the state
10  * 
11  * This interface has an update method, that does anything
12  * and returns the next state.
13  */
14 interface TestFlow
15 {
16 	///
17 	TestFlow update() @safe;
18 }
19 
20 private struct StateTransitorPolicy(MyState, MyEvent)
21 {
22 	alias State       = MyState;
23 	alias Event       = MyEvent;
24 	enum  consumeMode = ConsumeMode.separate;
25 }
26 private alias StateTransitor(State, Event)
27 	= cushion.StateTransitor!(StateTransitorPolicy!(State,Event));
28 
29 /***************************************************************
30  * Base class of state pattern
31  * 
32  * This class is a class that inherits from the TestFlow interface,
33  * which is the base class for state patterns.
34  * TestFlow has a primitive implementation by passing through State.
35  * This provides an implementation for the `update` declared in
36  * the interface to return the next state specified in `setNext`.
37  * And some handlers [`onEnter`, `onExit`, `onEnterChild`, `onExitChild`]
38  * are available.
39  */
40 abstract class BaseTestFlow: State!TestFlow
41 {
42 	///
43 	abstract string name() const @property;
44 }
45 
46 /***************************************************************
47  * Concrete class of state pattern named `Child1Stm`
48  * 
49  * This class is a concrete class of state pattern.
50  * The implementation of the behavior that should be done by
51  * the `update` is complete, and STM is driven internally.
52  */
53 class Child1Stm: BaseTestFlow
54 {
55 private:
56 	mixin(loadStmFromCsv!"flow-child1"("#>"));
57 	Event[][] _stepData;
58 	Event[]   _step;
59 public:
60 	///
61 	StateTransitor!(State, Event) _stm;
62 	/// ditto
63 	alias _stm this;
64 	///
65 	this() @safe
66 	{
67 		_stm = makeStm();
68 		with (Event)
69 			_stepData = [[b],[b]];
70 	}
71 	///
72 	final void initialize() @safe
73 	{
74 		_step = _stepData.front;
75 		_stepData.popFront();
76 	}
77 	///
78 	override TestFlow update() @safe
79 	{
80 		if (emptyEvents)
81 		{
82 			_stm.put(_step.front);
83 			_step.popFront();
84 		}
85 		consume();
86 		return super.update();
87 	}
88 	///
89 	override string name() const @property
90 	{
91 		return _stm.matrixName;
92 	}
93 }
94 
95 /***************************************************************
96  * Concrete class of state pattern named `Child2Stm`
97  * 
98  * Implement a different state from `Child1Stm`
99  */
100 class Child2Stm: BaseTestFlow
101 {
102 private:
103 	mixin(loadStmFromCsv!"flow-child2"("#>"));
104 	Event[][] _stepData;
105 	Event[]   _step;
106 public:
107 	///
108 	StateTransitor!(State, Event) _stm;
109 	/// ditto
110 	alias _stm this;
111 	///
112 	this() @safe
113 	{
114 		_stm = makeStm();
115 		with (Event)
116 			_stepData = [[b],[a]];
117 	}
118 	///
119 	final void initialize() @safe
120 	{
121 		_step = _stepData.front;
122 		_stepData.popFront();
123 	}
124 	///
125 	override TestFlow update() @safe
126 	{
127 		if (emptyEvents)
128 		{
129 			_stm.put(_step.front);
130 			_step.popFront();
131 		}
132 		consume();
133 		return super.update();
134 	}
135 	///
136 	override string name() const @property
137 	{
138 		return _stm.matrixName;
139 	}
140 }
141 
142 /***************************************************************
143  * Concrete class of state pattern named `MainStm`
144  * 
145  * Implement a different state from `Child1Stm` / `Child2Stm`.
146  * This class acts as a parent to `Child1Stm` and `Child2Stm`.
147  * The `update` method of this class may return the child class
148  * `Child1Stm` or `Child2Stm` as the next state.
149  * This class is also "previous state", which backs when
150  * `Child1Stm` or `Child2Stm` ends.
151  * Also, in this class, there is a gimmick that can record the
152  * transition of state using each handler and output the result.
153  */
154 class MainStm: BaseTestFlow
155 {
156 private:
157 	Child1Stm _child1;
158 	Child2Stm _child2;
159 	
160 	mixin(loadStmFromCsv!"flow-main"("#>"));
161 	Event[] _step;
162 public:
163 	///
164 	StateTransitor!(State, Event) _stm;
165 	/// ditto
166 	alias _stm this;
167 	///
168 	void delegate() onError;
169 	///
170 	Appender!string message;
171 	///
172 	this() @safe
173 	{
174 		_stm = makeStm();
175 		_child1 = new Child1Stm;
176 		_child2 = new Child2Stm;
177 		
178 		void setMsgEv(Stm, Ev)(Stm stm, Ev e)
179 		{
180 			message.formattedWrite(
181 				"[%s-%s]onEvent:%s(%s)\n",
182 				stm.name, stm.stateNames[stm.currentState], stm.eventNames[e], e);
183 		}
184 		void setMsgSt(Stm, St)(Stm stm, St oldSt, St newSt)
185 		{
186 			message.formattedWrite(
187 				"[%s-%s]onStateChanged:%s(%s)->%s(%s)\n",
188 				stm.name, stm.stateNames[stm.currentState], stm.stateNames[oldSt], oldSt, stm.stateNames[newSt], newSt);
189 		}
190 		void setMsgEntCh(StmP, StmC)(StmP p, StmC c)
191 		{
192 			message.formattedWrite("[%s-%s]onEnterChild:>>>%s\n", p.name, p.stateNames[p.currentState], c.name);
193 		}
194 		void setMsgExitCh(StmP, StmC)(StmP p, StmC c)
195 		{
196 			message.formattedWrite("[%s-%s]onExitChild:<<<%s\n", p.name, p.stateNames[p.currentState], c.name);
197 		}
198 		
199 		addEventHandler(          (Event e)                  { setMsgEv(this, e); } );
200 		addStateChangedHandler(   (State oldSt, State newSt) { setMsgSt(this, oldSt, newSt); } );
201 		onEnterChild           ~= (TestFlow child)           { setMsgEntCh(this, cast(BaseTestFlow)child); };
202 		onExitChild            ~= (TestFlow child)           { setMsgExitCh(this, cast(BaseTestFlow)child); };
203 		
204 		_child1.addEventHandler( (Child1Stm.Event e)        { setMsgEv(_child1, e); });
205 		_child1.addStateChangedHandler((Child1Stm.State oldSt, Child1Stm.State newSt) { setMsgSt(_child1, oldSt, newSt); });
206 		
207 		_child2.addEventHandler( (Child2Stm.Event e)        {setMsgEv(_child2, e); });
208 		_child2.addStateChangedHandler((Child2Stm.State oldSt, Child2Stm.State newSt) { setMsgSt(_child2, oldSt, newSt); });
209 		
210 		onError     = ()
211 		{
212 			put(_stm, Event.test);
213 			// end
214 			setNext(null);
215 		};
216 		_child1.onExit ~= () { _stm.put(Event.stm1exit); };
217 		_child2.onExit ~= ()
218 		{
219 			if (_child2.currentState == _child2.State.init)
220 			{
221 				_stm.put(Event.stm2exit);
222 			}
223 			else
224 			{
225 				_stm.put(Event.stm2err);
226 			}
227 		};
228 		
229 		initialize();
230 	}
231 	///
232 	final void initialize() @safe
233 	{
234 		with (Event)
235 			_step = [test, test];
236 	}
237 	///
238 	override TestFlow update() @safe
239 	{
240 		if (emptyEvents)
241 		{
242 			_stm.put(_step.front);
243 			_step.popFront();
244 		}
245 		consume();
246 		return super.update();
247 	}
248 	///
249 	override string name() const @property
250 	{
251 		return _stm.matrixName;
252 	}
253 	///
254 	override string toString() const @safe
255 	{
256 		return message.data;
257 	}
258 }
259 
260 ///
261 @safe unittest
262 {
263 	// Create a MainStm
264 	auto stm = new MainStm;
265 	
266 	// Create a context class of TestFlow
267 	auto stFlow = new Flow!TestFlow(stm);
268 	
269 	// Iterate until `stFlow.current` is null.
270 	// This process advances the state transition in `stFlow`.
271 	while (stFlow.current)
272 	{
273 		stFlow.update();
274 	}
275 	
276 	// Finally check the result
277 	assert(stm.toString() == import("flow-result.txt"));
278 }