@@ -4,85 +4,137 @@ package io.nexure.fsm
44 * - [S] - the type of state that the state machine handles
55 * - [E] - the type of events that the can trigger state changes
66 */
7- class StateMachineBuilder <S : Any , E : Any > private constructor(
8- private var initialState : S ? = null ,
9- private val transitions : List <Edge <S , E >> = emptyList(),
10- private val postInterceptors : List <(S , S , E ) -> Unit > = emptyList(),
11- ) {
12- constructor () : this (null , emptyList(), emptyList())
13-
14- /* *
15- * Set the initial state for this state machine. There must be exactly one initial state,
16- * no more or less. Failing to set an initial state for a state machine will cause an
17- * [InvalidStateMachineException] to be thrown when [build()] is invoked.
18- *
19- * Calling this method more than once, with a different initial state will also cause an
20- * [InvalidStateMachineException] to be thrown, but immediately upon the second call to this
21- * method rather when the state machine is built.
22- */
23- @Throws(InvalidStateMachineException ::class )
24- fun initial (state : S ): StateMachineBuilder <S , E > {
25- return if (initialState == null ) {
26- StateMachineBuilder (state, transitions, postInterceptors)
27- } else if (state == = initialState) {
28- StateMachineBuilder (initialState, transitions, postInterceptors)
29- } else {
30- throw InvalidStateMachineException (" There can only be one initial state" )
31- }
7+ sealed class StateMachineBuilder <S : Any , E : Any > {
8+ class Uninitialized <S : Any , E : Any > internal constructor(
9+ private val transitions : List <Edge <S , E >> = emptyList(),
10+ ) : StateMachineBuilder<S, E>() {
11+ /* *
12+ * Set the initial state for this state machine. There can only one initial state,
13+ * no more or less.
14+ */
15+ fun initial (state : S ): Initialized <S , E > = Initialized (state, transitions)
3216 }
3317
34- /* *
35- * Create a state transition from [source] state to [target] state that will be triggered by
36- * [event], and execute an optional [action] when doing the state transition. There can be
37- * multiple events that connect [source] and [target], but there must never be any ambiguous
38- * transitions.
39- *
40- * For example, having both of the following transitions, would NOT be permitted
41- * - `(S1, E1) -> S2`
42- * - `(S1, E1) -> S3`
43- *
44- * since it would not be clear if the new state should be ` S2` or `S3` when event `E1` is
45- * received.
46- */
47- fun connect (
48- source : S ,
49- target : S ,
50- event : E ,
51- ): StateMachineBuilder < S , E > = connect( Edge ( source, target, event))
52-
53- private fun connect ( edge : Edge < S , E >): StateMachineBuilder < S , E > =
54- StateMachineBuilder (initialState, transitions.plus(edge), postInterceptors )
18+ class Initialized < S : Any , E : Any > internal constructor(
19+ private val initialState : S ,
20+ private val transitions : List < Edge < S , E >>,
21+ ) : StateMachineBuilder<S, E>() {
22+ /* *
23+ * Create a state transition from [source] state to [target] state that will be triggered by
24+ * [event]. There can be multiple events that connect [source] and [target],
25+ * but there must never be any ambiguous transitions.
26+ *
27+ * For example, having both of the following transitions, would NOT be permitted
28+ * - `(S1, E1) -> S2`
29+ * - `(S1, E1) -> S3`
30+ *
31+ * since it would not be clear if the new state should be `S2` or `S3` when event `E1` is
32+ * received.
33+ */
34+ fun connect (
35+ source : S ,
36+ target : S ,
37+ event : E ,
38+ ): Initialized < S , E > = connect( Edge (source, target, event) )
5539
56- /* *
57- * Add an interceptor that is run _after_ a successful processing of an event by the state
58- * machine. This interceptor will not be run if the event was rejected by the state machine, or
59- * if there was an exception thrown while executing the state machine action (if any).
60- */
61- fun postIntercept (
62- interception : (source: S , target: S , event: E ) -> Unit
63- ): StateMachineBuilder <S , E > =
64- StateMachineBuilder (initialState, transitions, postInterceptors.plus(interception))
40+ private fun connect (edge : Edge <S , E >): Initialized <S , E > =
41+ Initialized (initialState, transitions.plus(edge))
6542
66- /* *
67- * @throws InvalidStateMachineException if the configured state machine is not valid. The main
68- * reasons for a state machine not being valid are:
69- * - No initial state
70- * - More than one initial state
71- * - The state machine is not connected (some states are not possible to reach from the initial
72- * state)
73- * - The same source state and event is defined twice
74- */
75- @Throws(InvalidStateMachineException ::class )
76- fun build (): StateMachine <S , E > {
77- val initState: S = initialState
78- ? : throw InvalidStateMachineException (" No initial state set for state machine" )
43+ /* *
44+ * @throws InvalidStateMachineException if the configured state machine is not valid. The main
45+ * reasons for a state machine not being valid are:
46+ * - No initial state
47+ * - More than one initial state
48+ * - The state machine is not connected (some states are not possible to reach from the initial
49+ * state)
50+ * - The same source state and event is defined twice
51+ */
52+ @Throws(InvalidStateMachineException ::class )
53+ fun build (): StateMachine <S , E > {
54+ StateMachineValidator .validate(initialState, transitions)
7955
80- StateMachineValidator .validate(initState, transitions)
56+ return StateMachineImpl (
57+ initialState,
58+ transitions,
59+ )
60+ }
61+ }
8162
82- return StateMachineImpl (
83- initState,
84- transitions,
85- postInterceptors
86- )
63+ companion object {
64+ operator fun <S : Any , E : Any > invoke (): Uninitialized <S , E > = Uninitialized ()
8765 }
8866}
67+ //
68+ // /**
69+ // * - [S] - the type of state that the state machine handles
70+ // * - [E] - the type of events that the can trigger state changes
71+ // */
72+ // class StateMachineBuilder<S : Any, E : Any> private constructor(
73+ // private var initialState: S? = null,
74+ // private val transitions: List<Edge<S, E>> = emptyList(),
75+ // ) {
76+ // constructor() : this(null, emptyList())
77+ //
78+ // /**
79+ // * Set the initial state for this state machine. There must be exactly one initial state,
80+ // * no more or less. Failing to set an initial state for a state machine will cause an
81+ // * [InvalidStateMachineException] to be thrown when [build()] is invoked.
82+ // *
83+ // * Calling this method more than once, with a different initial state will also cause an
84+ // * [InvalidStateMachineException] to be thrown, but immediately upon the second call to this
85+ // * method rather when the state machine is built.
86+ // */
87+ // @Throws(InvalidStateMachineException::class)
88+ // fun initial(state: S): StateMachineBuilder<S, E> {
89+ // return if (initialState == null) {
90+ // StateMachineBuilder(state, transitions)
91+ // } else if (state === initialState) {
92+ // StateMachineBuilder(initialState, transitions)
93+ // } else {
94+ // throw InvalidStateMachineException("There can only be one initial state")
95+ // }
96+ // }
97+ //
98+ // /**
99+ // * Create a state transition from [source] state to [target] state that will be triggered by
100+ // * [event]. There can be multiple events that connect [source] and [target],
101+ // * but there must never be any ambiguous transitions.
102+ // *
103+ // * For example, having both of the following transitions, would NOT be permitted
104+ // * - `(S1, E1) -> S2`
105+ // * - `(S1, E1) -> S3`
106+ // *
107+ // * since it would not be clear if the new state should be `S2` or `S3` when event `E1` is
108+ // * received.
109+ // */
110+ // fun connect(
111+ // source: S,
112+ // target: S,
113+ // event: E,
114+ // ): StateMachineBuilder<S, E> = connect(Edge(source, target, event))
115+ //
116+ // private fun connect(edge: Edge<S, E>): StateMachineBuilder<S, E> =
117+ // StateMachineBuilder(initialState, transitions.plus(edge))
118+ //
119+ // /**
120+ // * @throws InvalidStateMachineException if the configured state machine is not valid. The main
121+ // * reasons for a state machine not being valid are:
122+ // * - No initial state
123+ // * - More than one initial state
124+ // * - The state machine is not connected (some states are not possible to reach from the initial
125+ // * state)
126+ // * - The same source state and event is defined twice
127+ // */
128+ // @Throws(InvalidStateMachineException::class)
129+ // fun build(): StateMachine<S, E> {
130+ // val initState: S = initialState
131+ // ?: throw InvalidStateMachineException("No initial state set for state machine")
132+ //
133+ // StateMachineValidator.validate(initState, transitions)
134+ //
135+ // return StateMachineImpl(
136+ // initState,
137+ // transitions,
138+ // )
139+ // }
140+ // }
0 commit comments