Skip to content

Commit aaa74ad

Browse files
committed
Add unsafe export sample
1 parent 9f1e4f5 commit aaa74ad

File tree

9 files changed

+142
-77
lines changed

9 files changed

+142
-77
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ fun main() = runBlocking {
198198
* [Undo transition sample](./samples/src/commonMain/kotlin/ru/nsk/samples/UndoTransitionSample.kt)
199199
* [PlantUML nested states export sample](./samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt)
200200
* [Mermaid nested states export sample](./samples/src/commonMain/kotlin/ru/nsk/samples/MermaidExportSample.kt)
201-
* [PlantUML with MetaInfo export sample](./samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt)
201+
* [PlantUML with UmlMetaInfo export sample](./samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithUmlMetaInfoSample.kt)
202+
* [PlantUML with ExportMetaInfo unsafe export sample](https://github.com/KStateMachine/kstatemachine/tree/master/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlUnsafeExportWithExportMetaInfoSample.kt)
202203
* [Inherit transitions by grouping states sample](./samples/src/commonMain/kotlin/ru/nsk/samples/InheritTransitionsSample.kt)
203204
* [Minimal sealed classes sample](./samples/src/commonMain/kotlin/ru/nsk/samples/MinimalSealedClassesSample.kt)
204205
* [Usage without Kotlin Coroutines sample](./samples/src/commonMain/kotlin/ru/nsk/samples/StdLibMinimalSealedClassesSample.kt)

docs/pages/export.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ state("State1") {
7474
}
7575
```
7676

77-
See [PlantUML with MetaInfo export sample](https://github.com/KStateMachine/kstatemachine/tree/master/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt)
77+
See [PlantUML with UmlMetaInfo export sample](https://github.com/KStateMachine/kstatemachine/tree/master/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithUmlMetaInfoSample.kt)
7878

7979
## Export with `unsafeCallConditionalLambdas` flag
8080

@@ -125,20 +125,20 @@ transitionConditionally<ValueEvent> {
125125
}
126126
}
127127
metaInfo = buildExportMetaInfo {
128-
resolutionHints = setOf(
129-
// the library does not need to call "direction" lambda, this hint provides the result (state1) directly
130-
StateResolutionHint("when 1", state1),
131-
// calls "direction" lambda during export with specified Event and optional argument (lambda will return state2)
132-
EventAndArgumentResolutionHint("when 2", ValueEvent(2)),
133-
// you can specify set of states that represents parallel target states
134-
StateResolutionHint("when 3", setOf(state1, state2)),
135-
// describes stay() behaviour without calling "direction" lambda
136-
StateResolutionHint("when 4", this@createStateMachine),
137-
// resolves to stay() by calling "direction" lambda
138-
EventAndArgumentResolutionHint("when 4", ValueEvent(4)),
139-
// useless, does not affect export output as it resolves to noTransition()
140-
EventAndArgumentResolutionHint("else", ValueEvent(5)),
141-
)
128+
// the library does not need to call "direction" lambda, this hint provides the result (state1) directly
129+
addStateResolutionHint("when 1", state1)
130+
// calls "direction" lambda during export with specified Event and optional argument (lambda will return state2)
131+
addEventAndArgumentResolutionHint("when 2", ValueEvent(2))
132+
// you can specify set of states that represents parallel target states
133+
addStateResolutionHint("when 3", setOf(state1, state2))
134+
// describes stay() behaviour without calling "direction" lambda
135+
addStateResolutionHint("when 4", this@createStateMachine)
136+
// resolves to stay() by calling "direction" lambda
137+
addEventAndArgumentResolutionHint("when 4", ValueEvent(4))
138+
// useless, does not affect export output as it resolves to noTransition()
139+
addEventAndArgumentResolutionHint("else", ValueEvent(5))
142140
}
143141
}
144-
```
142+
```
143+
144+
See [PlantUML with ExportMetaInfo unsafe export sample](https://github.com/KStateMachine/kstatemachine/tree/master/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlUnsafeExportWithExportMetaInfoSample.kt)

kstatemachine/api/kstatemachine.api

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,22 +100,18 @@ public abstract interface class ru/nsk/kstatemachine/metainfo/CompositeMetaInfoB
100100
public abstract fun setMetaInfoSet (Ljava/util/Set;)V
101101
}
102102

103-
public final class ru/nsk/kstatemachine/metainfo/EventAndArgumentResolutionHint : ru/nsk/kstatemachine/metainfo/ResolutionHint {
104-
public fun <init> (Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;)V
105-
public synthetic fun <init> (Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
106-
public final fun getArgument ()Ljava/lang/Object;
107-
public final fun getDescription ()Ljava/lang/String;
108-
public final fun getEvent ()Lru/nsk/kstatemachine/event/Event;
109-
public final fun getEventAndArgument ()Lru/nsk/kstatemachine/transition/EventAndArgument;
110-
}
111-
112103
public abstract interface class ru/nsk/kstatemachine/metainfo/ExportMetaInfo : ru/nsk/kstatemachine/metainfo/MetaInfo {
113104
public abstract fun getResolutionHints ()Ljava/util/Set;
114105
}
115106

116107
public abstract interface class ru/nsk/kstatemachine/metainfo/ExportMetaInfoBuilder : ru/nsk/kstatemachine/metainfo/ExportMetaInfo {
117-
public abstract fun getResolutionHints ()Ljava/util/Set;
118-
public abstract fun setResolutionHints (Ljava/util/Set;)V
108+
public abstract fun addEventAndArgumentResolutionHint (Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;)V
109+
public abstract fun addStateResolutionHint (Ljava/lang/String;Ljava/util/Set;)V
110+
public abstract fun addStateResolutionHint (Ljava/lang/String;Lru/nsk/kstatemachine/state/IState;)V
111+
}
112+
113+
public final class ru/nsk/kstatemachine/metainfo/ExportMetaInfoBuilder$DefaultImpls {
114+
public static synthetic fun addEventAndArgumentResolutionHint$default (Lru/nsk/kstatemachine/metainfo/ExportMetaInfoBuilder;Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;ILjava/lang/Object;)V
119115
}
120116

121117
public final class ru/nsk/kstatemachine/metainfo/ExportMetaInfoKt {
@@ -137,13 +133,6 @@ public final class ru/nsk/kstatemachine/metainfo/MetaInfoKt {
137133
public abstract interface class ru/nsk/kstatemachine/metainfo/ResolutionHint {
138134
}
139135

140-
public final class ru/nsk/kstatemachine/metainfo/StateResolutionHint : ru/nsk/kstatemachine/metainfo/ResolutionHint {
141-
public fun <init> (Ljava/lang/String;Ljava/util/Set;)V
142-
public fun <init> (Ljava/lang/String;Lru/nsk/kstatemachine/state/IState;)V
143-
public final fun getDescription ()Ljava/lang/String;
144-
public final fun getTargetStates ()Ljava/util/Set;
145-
}
146-
147136
public abstract interface class ru/nsk/kstatemachine/metainfo/UmlMetaInfo : ru/nsk/kstatemachine/metainfo/MetaInfo {
148137
public abstract fun getUmlLabel ()Ljava/lang/String;
149138
public abstract fun getUmlNotes ()Ljava/util/List;

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/metainfo/ExportMetaInfo.kt

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,11 @@ sealed interface ResolutionHint
2424
* branches.
2525
* User is responsible to provide correct hints.
2626
*/
27-
class StateResolutionHint(
27+
internal class StateResolutionHint(
2828
val description: String,
2929
/** Allows to specify parallel target states. Must be non-empty */
3030
val targetStates: Set<IState>,
3131
) : ResolutionHint {
32-
constructor(
33-
description: String,
34-
targetState: IState,
35-
) : this(description, setOf(targetState))
36-
3732
init {
3833
require(targetStates.isNotEmpty()) {
3934
"targetStates must be non-empty, use single state or multiple states for parallel transitions"
@@ -49,10 +44,10 @@ class StateResolutionHint(
4944
* lambda branches.
5045
* User is responsible to provide correct hints.
5146
*/
52-
class EventAndArgumentResolutionHint(
47+
internal class EventAndArgumentResolutionHint(
5348
val description: String,
5449
val event: Event,
55-
val argument: Any? = null
50+
val argument: Any?
5651
) : ResolutionHint {
5752
val eventAndArgument = EventAndArgument(event, argument)
5853
}
@@ -79,12 +74,31 @@ interface ExportMetaInfo : MetaInfo {
7974
* use [buildExportMetaInfo] instead.
8075
*/
8176
interface ExportMetaInfoBuilder : ExportMetaInfo {
82-
override var resolutionHints: Set<ResolutionHint>
77+
/** See [StateResolutionHint] */
78+
fun addStateResolutionHint(description: String, targetState: IState)
79+
80+
/** See [StateResolutionHint] */
81+
fun addStateResolutionHint(description: String, targetStates: Set<IState>)
82+
83+
/** See [EventAndArgumentResolutionHint] */
84+
fun addEventAndArgumentResolutionHint(description: String, event: Event, argument: Any? = null)
8385
}
8486

8587
private data class ExportMetaInfoBuilderImpl(
86-
override var resolutionHints: Set<ResolutionHint> = emptySet(),
87-
) : ExportMetaInfoBuilder
88+
override val resolutionHints: MutableSet<ResolutionHint> = mutableSetOf<ResolutionHint>(),
89+
) : ExportMetaInfoBuilder {
90+
override fun addEventAndArgumentResolutionHint(description: String, event: Event, argument: Any?) {
91+
resolutionHints += EventAndArgumentResolutionHint(description, event, argument)
92+
}
93+
94+
override fun addStateResolutionHint(description: String, targetState: IState) {
95+
resolutionHints += StateResolutionHint(description, setOf(targetState))
96+
}
97+
98+
override fun addStateResolutionHint(description: String, targetStates: Set<IState>) {
99+
resolutionHints += StateResolutionHint(description, targetStates)
100+
}
101+
}
88102

89103
fun buildExportMetaInfo(builder: ExportMetaInfoBuilder.() -> Unit): ExportMetaInfo =
90104
ExportMetaInfoBuilderImpl().apply(builder).copy()

samples/src/commonMain/kotlin/ru/nsk/samples/MermaidExportSample.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package ru.nsk.samples
99

1010
import kotlinx.coroutines.runBlocking
1111
import ru.nsk.kstatemachine.event.Event
12+
import ru.nsk.kstatemachine.metainfo.UmlMetaInfo
1213
import ru.nsk.kstatemachine.state.*
1314
import ru.nsk.kstatemachine.statemachine.createStateMachine
1415
import ru.nsk.kstatemachine.visitors.export.exportToMermaid
@@ -18,6 +19,9 @@ private object MermaidExportSample {
1819
object SwitchEvent : Event
1920
}
2021

22+
/**
23+
* The sample shows basic Mermaid export
24+
*/
2125
fun main() = runBlocking {
2226
val machine = createStateMachine(this, name = "Nested states") {
2327
val state1 = initialState("State1")

samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ private object PlantUmlExportSample {
1818
object SwitchEvent : Event
1919
}
2020

21+
/**
22+
* The sample shows basic PlantUML export
23+
*/
2124
fun main() = runBlocking {
2225
val machine = createStateMachine(this, "Nested states") {
2326
val state1 = initialState("State1")

samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt renamed to samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithUmlMetaInfoSample.kt

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,41 @@ package ru.nsk.samples
99

1010
import kotlinx.coroutines.runBlocking
1111
import ru.nsk.kstatemachine.event.Event
12-
import ru.nsk.kstatemachine.metainfo.MetaInfo
12+
import ru.nsk.kstatemachine.metainfo.UmlMetaInfo
1313
import ru.nsk.kstatemachine.metainfo.buildUmlMetaInfo
14-
import ru.nsk.kstatemachine.state.State
1514
import ru.nsk.kstatemachine.state.finalState
1615
import ru.nsk.kstatemachine.state.initialState
17-
import ru.nsk.kstatemachine.state.transitionOn
16+
import ru.nsk.kstatemachine.state.transition
1817
import ru.nsk.kstatemachine.statemachine.createStateMachine
1918
import ru.nsk.kstatemachine.visitors.export.exportToPlantUml
20-
import ru.nsk.samples.PlantUmlExportWithMetaInfoSample.SwitchEvent
19+
import ru.nsk.samples.PlantUmlExportWithUmlMetaInfoSample.SwitchEvent
2120

22-
private object PlantUmlExportWithMetaInfoSample {
21+
private object PlantUmlExportWithUmlMetaInfoSample {
2322
object SwitchEvent : Event
2423
}
2524

2625
/**
27-
* The sample shows hot to use [MetaInfo] to beautify export output
26+
* The sample shows how to use [UmlMetaInfo] to beautify export output
2827
*/
2928
fun main() = runBlocking {
30-
lateinit var state2: State
3129
val machine = createStateMachine(this) {
3230
metaInfo = buildUmlMetaInfo { umlLabel = "Nested states sm" }
3331

34-
initialState("State1") {
35-
metaInfo = buildUmlMetaInfo { umlLabel = "State 1 Label" }
36-
transitionOn<SwitchEvent> {
37-
metaInfo = buildUmlMetaInfo { umlLabel = "Transition to State 2" }
38-
targetState = { state2 }
39-
}
40-
}
41-
42-
state2 = finalState("State2") {
32+
val state2 = finalState("State2") {
4333
metaInfo = buildUmlMetaInfo {
4434
umlLabel = "FinalState 2 Label"
4535
umlStateDescriptions = listOf("Description 1", "Description 2")
4636
umlNotes = listOf("Note 1", "Note 2")
4737
}
4838
}
39+
40+
initialState("State1") {
41+
metaInfo = buildUmlMetaInfo { umlLabel = "State 1 Label" }
42+
transition<SwitchEvent> {
43+
metaInfo = buildUmlMetaInfo { umlLabel = "Transition to State 2" }
44+
targetState = state2
45+
}
46+
}
4947
}
5048

5149
println(machine.exportToPlantUml(showEventLabels = true))
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Author: Mikhail Fedotov
3+
* Github: https://github.com/KStateMachine
4+
* Copyright (c) 2024.
5+
* All rights reserved.
6+
*/
7+
8+
package ru.nsk.samples
9+
10+
import kotlinx.coroutines.runBlocking
11+
import ru.nsk.kstatemachine.event.Event
12+
import ru.nsk.kstatemachine.metainfo.ExportMetaInfo
13+
import ru.nsk.kstatemachine.metainfo.buildExportMetaInfo
14+
import ru.nsk.kstatemachine.state.State
15+
import ru.nsk.kstatemachine.state.finalState
16+
import ru.nsk.kstatemachine.state.initialState
17+
import ru.nsk.kstatemachine.state.transitionOn
18+
import ru.nsk.kstatemachine.statemachine.createStateMachine
19+
import ru.nsk.kstatemachine.visitors.export.exportToPlantUml
20+
import ru.nsk.samples.PlantUmlUnsafeExportWithExportMetaInfoSample.FirstEvent
21+
import ru.nsk.samples.PlantUmlUnsafeExportWithExportMetaInfoSample.SecondEvent
22+
23+
private object PlantUmlUnsafeExportWithExportMetaInfoSample {
24+
class FirstEvent(val data: Int) : Event
25+
class SecondEvent(val data: Int) : Event
26+
}
27+
28+
/**
29+
* The sample shows how to use [ExportMetaInfo] to get complete export output even with conditional lambdas.
30+
* You have EventAndArgumentResolutionHint and EventAndArgumentResolutionHint alternative, you can choose one of them,
31+
* or use them together mixing in a single metaInfo if necessary.
32+
*/
33+
fun main() = runBlocking {
34+
lateinit var state2: State
35+
lateinit var state3: State
36+
val machine = createStateMachine(this) {
37+
state2 = finalState("State2")
38+
state3 = finalState("State3")
39+
40+
initialState("State1") {
41+
transitionOn<FirstEvent> {
42+
metaInfo = buildExportMetaInfo {
43+
// using EventAndArgumentResolutionHint calls targetState lambda with specified events
44+
addEventAndArgumentResolutionHint("data == 42", FirstEvent(42))
45+
addEventAndArgumentResolutionHint("else", FirstEvent(42))
46+
}
47+
targetState = { if (event.data == 42) state2 else state3 }
48+
}
49+
50+
transitionOn<SecondEvent> {
51+
metaInfo = buildExportMetaInfo {
52+
// using StateResolutionHint does not require targetState lambda call
53+
addStateResolutionHint("data == 123", state2)
54+
addStateResolutionHint("else", state3)
55+
}
56+
targetState = { if (event.data == 123) state2 else state3 }
57+
}
58+
}
59+
}
60+
61+
println(machine.exportToPlantUml(showEventLabels = true, unsafeCallConditionalLambdas = true))
62+
}

tests/src/commonTest/kotlin/ru/nsk/kstatemachine/metainfo/ExportMetaInfoTest.kt

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,8 @@ private suspend fun createTestMachine(coroutineStarterType: CoroutineStarterType
114114
transitionOn<ValueEvent1> {
115115
targetState = { if (event.value == 42) state11 else state12 }
116116
metaInfo = buildExportMetaInfo {
117-
resolutionHints = setOf(
118-
EventAndArgumentResolutionHint("if (event.value == 42)", ValueEvent1(42)),
119-
EventAndArgumentResolutionHint("else", ValueEvent1(0)),
120-
)
117+
addEventAndArgumentResolutionHint("if (event.value == 42)", ValueEvent1(42))
118+
addEventAndArgumentResolutionHint("else", ValueEvent1(0))
121119
}
122120
}
123121
}
@@ -134,21 +132,17 @@ private suspend fun createTestMachine(coroutineStarterType: CoroutineStarterType
134132
}
135133
}
136134
metaInfo = buildExportMetaInfo {
137-
resolutionHints = setOf(
138-
StateResolutionHint("when 1", state1),
139-
EventAndArgumentResolutionHint("when 2", ValueEvent2(2)),
140-
StateResolutionHint("when 3", setOf(state1, state2)),
141-
StateResolutionHint("when 4", this@createTestStateMachine),
142-
EventAndArgumentResolutionHint("else", ValueEvent2(5)),
143-
)
135+
addStateResolutionHint("when 1", state1)
136+
addEventAndArgumentResolutionHint("when 2", ValueEvent2(2))
137+
addStateResolutionHint("when 3", setOf(state1, state2))
138+
addStateResolutionHint("when 4", this@createTestStateMachine)
139+
addEventAndArgumentResolutionHint("else", ValueEvent2(5))
144140
}
145141
}
146142
val choiceState = choiceState("choiceState") { if (true) state1 else state2 }
147143
choiceState.metaInfo = buildExportMetaInfo {
148-
resolutionHints = setOf(
149-
StateResolutionHint("if (true)", state1),
150-
StateResolutionHint(" ", state2),
151-
)
144+
addStateResolutionHint("if (true)", state1)
145+
addStateResolutionHint(" ", state2)
152146
}
153147
}
154148
}

0 commit comments

Comments
 (0)