Skip to content

Commit f480336

Browse files
committed
Drop idea of FinishedDataEvent
There is not clear way to use its generic parameter, so data filed is added to simple FinishedEvent. User will use cast to get its value, but it is more clear than using multiple event types spiced with some "library magic".
1 parent 2fe739a commit f480336

File tree

5 files changed

+45
-32
lines changed

5 files changed

+45
-32
lines changed

docs/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,9 @@ Notifications about finishing are available in two forms:
411411
Transition for `FinishedEvent` is detected by the library and matched by special kind of `EventMatcher`,
412412
so such transition is triggered only for `FinishedEvent` that corresponds to this state.
413413
`FinishingEvent` generated by finishing of another state will not trigger such transition.
414+
415+
If `FinalState` that triggered `FinishedEvent` is also a `DataState` then its data field will be copied into `FinishedEvent`.
414416
See [transition on FinishedEvent sample](https://github.com/nsk90/kstatemachine/tree/master/samples/src/main/kotlin/ru/nsk/samples/FinishedEventSample.kt)
415-
.
416417

417418
## Nested states
418419

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ru.nsk.kstatemachine
22

3+
import ru.nsk.kstatemachine.ChildMode.*
34
import ru.nsk.kstatemachine.TransitionType.EXTERNAL
45
import ru.nsk.kstatemachine.TreeAlgorithms.findPathFromTargetToLca
56
import ru.nsk.kstatemachine.visitors.GetActiveStatesVisitor
@@ -68,7 +69,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
6869

6970
override fun <S : IState> addState(state: S, init: StateBlock<S>?): S {
7071
check(!machine.isRunning) { "Can not add state after state machine started" }
71-
if (childMode == ChildMode.PARALLEL) {
72+
if (childMode == PARALLEL) {
7273
require(state !is IFinalState) { "Can not add IFinalState in parallel child mode" }
7374
require(state !is PseudoState) { "Can not add PseudoState in parallel child mode" }
7475
}
@@ -87,7 +88,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
8788

8889
override fun setInitialState(state: IState) {
8990
require(states.contains(state)) { "$state is not part of $this machine, use addState() first" }
90-
check(childMode == ChildMode.EXCLUSIVE) { "Can not set initial state in parallel child mode" }
91+
check(childMode == EXCLUSIVE) { "Can not set initial state in parallel child mode" }
9192
check(!machine.isRunning) { "Can not change initial state after state machine started" }
9293

9394
data.initialState = state as InternalState
@@ -135,7 +136,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
135136
}
136137

137138
override fun afterChildFinished(finishedChild: InternalState, transitionParams: TransitionParams<*>) {
138-
if (childMode == ChildMode.PARALLEL && states.all { it.isFinished }) {
139+
if (childMode == PARALLEL && states.all { it.isFinished }) {
139140
data.isFinished = true
140141
notifyStateFinish(finishedChild, transitionParams)
141142
}
@@ -162,7 +163,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
162163
if (states.isEmpty()) return
163164

164165
when (childMode) {
165-
ChildMode.EXCLUSIVE -> {
166+
EXCLUSIVE -> {
166167
val initialState = checkNotNull(initialState) {
167168
"Initial state is not set, call setInitialState() first"
168169
}
@@ -171,7 +172,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
171172
initialState.recursiveEnterInitialStates(transitionParams)
172173
}
173174

174-
ChildMode.PARALLEL -> data.states.forEach {
175+
PARALLEL -> data.states.forEach {
175176
handleStateEntry(it, transitionParams)
176177
if (it !is StateMachine) // inner state machine manages its internal state by its own
177178
it.recursiveEnterInitialStates(transitionParams)
@@ -211,12 +212,12 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
211212
private fun requireCurrentState() = requireNotNull(data.currentState) { "Current state is not set" }
212213

213214
override fun getCurrentStates() = when (childMode) {
214-
ChildMode.EXCLUSIVE -> listOfNotNull(data.currentState)
215-
ChildMode.PARALLEL -> data.states.toList()
215+
EXCLUSIVE -> listOfNotNull(data.currentState)
216+
PARALLEL -> data.states.toList()
216217
}
217218

218219
private fun setCurrentState(state: InternalState, transitionParams: TransitionParams<*>) {
219-
require(childMode == ChildMode.EXCLUSIVE) { "Cannot set current state in child mode $childMode" }
220+
require(childMode == EXCLUSIVE) { "Cannot set current state in child mode $childMode" }
220221
require(states.contains(state)) { "$state is not a child of $this" }
221222

222223
if (data.currentState == state && transitionParams.transition.type != EXTERNAL) return
@@ -234,8 +235,8 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
234235

235236
private fun handleStateEntry(state: InternalState, transitionParams: TransitionParams<*>) {
236237
val finished = when (childMode) {
237-
ChildMode.EXCLUSIVE -> state is IFinalState
238-
ChildMode.PARALLEL -> states.all { it.isFinished }
238+
EXCLUSIVE -> state is IFinalState
239+
PARALLEL -> states.all { it.isFinished }
239240
}
240241
data.isFinished = finished
241242

@@ -257,10 +258,10 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
257258

258259
private fun makeFinishedEvent(state: InternalState): FinishedEvent {
259260
// check for both interfaces as client is not forced to use FinalDataState
260-
return if (state is DataState<*> && state is IFinalState) // possible in exclusive child mode only
261-
FinishedDataEventImpl(this, state.data)
261+
return if (childMode == EXCLUSIVE && state is DataState<*> && state is IFinalState)
262+
FinishedEvent(this, state.data)
262263
else
263-
FinishedEventImpl(this)
264+
FinishedEvent(this)
264265
}
265266

266267
internal fun switchToTargetState(

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/TransitionParams.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,12 @@ interface GeneratedEvent : Event
2626
* Special event generated by the library when a state is finished.
2727
* Transitions use special event matcher by default to match only related events.
2828
*/
29-
interface FinishedEvent : GeneratedEvent {
30-
val state: IState
31-
}
32-
33-
interface FinishedDataEvent<D : Any> : FinishedEvent, DataEvent<D>
34-
35-
internal class FinishedEventImpl(override val state: IState) : FinishedEvent
36-
internal class FinishedDataEventImpl<D : Any>(override val state: IState, override val data: D) : FinishedDataEvent<D>
29+
class FinishedEvent internal constructor(val state: IState, val data: Any? = null): GeneratedEvent
3730

3831
/**
3932
* Initial event which is processed on state machine start
4033
*/
41-
class StartEvent internal constructor() : Event
34+
class StartEvent internal constructor() : GeneratedEvent
4235

4336
/**
4437
* System event which is used by the library to wrap original event and argument,

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,8 @@ inline fun <reified E : DataEvent<D>, D : Any> TransitionStateApi.dataTransition
148148
return addTransition(builder.build())
149149
}
150150

151-
inline fun <reified E : Event> matcherForEvent(state: IState): EventMatcher<E> {
152-
@Suppress("UNCHECKED_CAST")
153-
return if (E::class == FinishedEvent::class)
154-
finishedEventMatcher(state) as EventMatcher<E>
155-
else
156-
isInstanceOf()
151+
@Suppress("UNCHECKED_CAST")
152+
inline fun <reified E : Event> matcherForEvent(state: IState) = when {
153+
E::class == FinishedEvent::class -> finishedEventMatcher(state) as EventMatcher<E>
154+
else -> isInstanceOf()
157155
}

kstatemachine/src/test/kotlin/ru/nsk/kstatemachine/FinishedEventTest.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class FinishedEventTest : StringSpec({
5757
machine.isFinished shouldBe false
5858
}
5959

60-
"FinishedDataEvent" {
60+
"FinishedEvent with data" {
6161
val callbacks = mockkCallbacks()
6262
data class IntEvent(override val data: Int) : DataEvent<Int>
6363
val intData = 42
@@ -74,7 +74,7 @@ class FinishedEventTest : StringSpec({
7474
}
7575
}
7676

77-
transitionOn<FinishedDataEvent<Int>> {
77+
transitionOn<FinishedEvent> {
7878
targetState = {
7979
event.data shouldBe intData
8080
state2
@@ -85,7 +85,7 @@ class FinishedEventTest : StringSpec({
8585
}
8686
machine.processEvent(IntEvent(intData))
8787
verifySequence {
88-
callbacks.onTriggeredTransition(ofType<FinishedDataEvent<Int>>())
88+
callbacks.onTriggeredTransition(ofType<FinishedEvent>())
8989
}
9090
}
9191

@@ -109,4 +109,24 @@ class FinishedEventTest : StringSpec({
109109
callbacks.onTriggeredTransition(ofType<FinishedEvent>())
110110
}
111111
}
112+
113+
"FinishedEvent from start" {
114+
val callbacks = mockkCallbacks()
115+
116+
createStateMachine {
117+
initialState {
118+
setInitialState(finalState())
119+
transition<FinishedEvent> {
120+
onTriggered {
121+
it.event.data shouldBe null
122+
callbacks.onTriggeredTransition(it.event)
123+
}
124+
}
125+
}
126+
}
127+
128+
verifySequence {
129+
callbacks.onTriggeredTransition(ofType<FinishedEvent>())
130+
}
131+
}
112132
})

0 commit comments

Comments
 (0)