From 77138b6030aa725444aec67f31ea3ce693e638ce Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Fri, 2 Feb 2024 09:27:34 +0100 Subject: [PATCH 1/7] display name in transitions --- .../ru/nsk/kstatemachine/DefaultTransition.kt | 7 +++- .../ru/nsk/kstatemachine/StateMachineImpl.kt | 3 +- .../kotlin/ru/nsk/kstatemachine/Transition.kt | 1 + .../ru/nsk/kstatemachine/TransitionBuilder.kt | 42 +++++++++---------- .../nsk/kstatemachine/TransitionStateApi.kt | 24 +++++++---- .../visitors/ExportPlantUmlVisitor.kt | 2 +- .../nsk/kstatemachine/CustomTransitionTest.kt | 6 +-- 7 files changed, 49 insertions(+), 36 deletions(-) diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt index 243fe9c..957599e 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt @@ -2,27 +2,30 @@ package ru.nsk.kstatemachine open class DefaultTransition( override val name: String?, + override val displayName: String, override val eventMatcher: EventMatcher, override val type: TransitionType, sourceState: IState, ) : InternalTransition { constructor( name: String?, + displayName: String, eventMatcher: EventMatcher, type: TransitionType, sourceState: IState, targetState: IState? - ) : this(name, eventMatcher, type, sourceState) { + ) : this(name, displayName, eventMatcher, type, sourceState) { targetStateDirectionProducer = { it.targetStateOrStay(targetState) } } constructor( name: String?, + displayName: String, eventMatcher: EventMatcher, type: TransitionType, sourceState: IState, targetStateDirectionProducer: TransitionDirectionProducer - ) : this(name, eventMatcher, type, sourceState) { + ) : this(name, displayName, eventMatcher, type, sourceState) { this.targetStateDirectionProducer = targetStateDirectionProducer } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt index e779315..e6036b3 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt @@ -37,7 +37,7 @@ internal class StateMachineImpl( } if (isUndoEnabled) { val undoState = addState(UndoState()) - transition("undo transition", undoState) + transition("undo transition", targetState = undoState) } } @@ -297,6 +297,7 @@ internal suspend inline fun makeStartTransitionParams( ): TransitionParams<*> { val transition = DefaultTransition( "Starting", + "", EventMatcher.isInstanceOf(), TransitionType.LOCAL, sourceState, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt index 2592e61..1f74eff 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt @@ -11,6 +11,7 @@ import ru.nsk.kstatemachine.visitors.VisitorAcceptor */ interface Transition : VisitorAcceptor { val name: String? + val displayName: String val eventMatcher: EventMatcher val sourceState: IState val type: TransitionType diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt index 1661648..ed423d5 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt @@ -3,7 +3,7 @@ package ru.nsk.kstatemachine import ru.nsk.kstatemachine.TransitionDirectionProducerPolicy.* @StateMachineDslMarker -abstract class TransitionBuilder(protected val name: String?, protected val sourceState: IState) { +abstract class TransitionBuilder(protected val name: String?, protected val displayName: String, protected val sourceState: IState) { val listeners = mutableListOf() lateinit var eventMatcher: EventMatcher var type = TransitionType.LOCAL @@ -11,13 +11,13 @@ abstract class TransitionBuilder(protected val name: String?, protect abstract fun build(): Transition } -abstract class BaseGuardedTransitionBuilder(name: String?, sourceState: IState) : - TransitionBuilder(name, sourceState) { +abstract class BaseGuardedTransitionBuilder(name: String?, displayName: String, sourceState: IState) : + TransitionBuilder(name, displayName, sourceState) { var guard: suspend EventAndArgument.() -> Boolean = { true } } -abstract class GuardedTransitionBuilder(name: String?, sourceState: IState) : - BaseGuardedTransitionBuilder(name, sourceState) { +abstract class GuardedTransitionBuilder(name: String?, displayName: String, sourceState: IState) : + BaseGuardedTransitionBuilder(name, displayName, sourceState) { var targetState: S? = null override fun build(): Transition { @@ -33,14 +33,14 @@ abstract class GuardedTransitionBuilder(name: String?, so } } - val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) listeners.forEach { transition.addListener(it) } return transition } } -abstract class GuardedTransitionOnBuilder(name: String?, sourceState: IState) : - BaseGuardedTransitionBuilder(name, sourceState) { +abstract class GuardedTransitionOnBuilder(name: String?, displayName: String, sourceState: IState) : + BaseGuardedTransitionBuilder(name, displayName, sourceState) { lateinit var targetState: suspend EventAndArgument.() -> S override fun build(): Transition { @@ -56,14 +56,14 @@ abstract class GuardedTransitionOnBuilder(name: String?, } } - val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) listeners.forEach { transition.addListener(it) } return transition } } -class ConditionalTransitionBuilder(name: String?, sourceState: IState) : - TransitionBuilder(name, sourceState) { +class ConditionalTransitionBuilder(name: String?, displayName: String, sourceState: IState) : + TransitionBuilder(name, displayName, sourceState) { lateinit var direction: suspend EventAndArgument.() -> TransitionDirection override fun build(): Transition { @@ -75,7 +75,7 @@ class ConditionalTransitionBuilder(name: String?, sourceState: IState } } - val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) listeners.forEach { transition.addListener(it) } return transition } @@ -84,17 +84,17 @@ class ConditionalTransitionBuilder(name: String?, sourceState: IState /** * Any [Event] (with any data) can lead to [State] */ -class UnitGuardedTransitionBuilder(name: String?, sourceState: IState) : - GuardedTransitionBuilder(name, sourceState) +class UnitGuardedTransitionBuilder(name: String?, displayName: String, sourceState: IState) : + GuardedTransitionBuilder(name, displayName, sourceState) -class UnitGuardedTransitionOnBuilder(name: String?, sourceState: IState) : - GuardedTransitionOnBuilder(name, sourceState) +class UnitGuardedTransitionOnBuilder(name: String?, displayName: String, sourceState: IState) : + GuardedTransitionOnBuilder(name, displayName, sourceState) /** * Type safe argument transition builder */ -class DataGuardedTransitionBuilder, D : Any>(name: String?, sourceState: IState) : - BaseGuardedTransitionBuilder(name, sourceState) { +class DataGuardedTransitionBuilder, D : Any>(name: String?, displayName: String, sourceState: IState) : + BaseGuardedTransitionBuilder(name, displayName, sourceState) { /** User should initialize this filed */ lateinit var targetState: DataState @@ -112,7 +112,7 @@ class DataGuardedTransitionBuilder, D : Any>(name: String?, sou } } - val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) listeners.forEach { transition.addListener(it) } return transition } @@ -121,8 +121,8 @@ class DataGuardedTransitionBuilder, D : Any>(name: String?, sou /** * Type safe argument transitionOn builder */ -class DataGuardedTransitionOnBuilder, D : Any>(name: String?, sourceState: IState) : - GuardedTransitionOnBuilder>(name, sourceState) +class DataGuardedTransitionOnBuilder, D : Any>(name: String?, displayName: String, sourceState: IState) : + GuardedTransitionOnBuilder>(name, displayName, sourceState) inline fun TransitionBuilder.onTriggered( crossinline block: suspend (TransitionParams) -> Unit diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt index c8d74db..c4a70e4 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt @@ -49,9 +49,10 @@ inline fun TransitionStateApi.requireTransition() = */ inline fun TransitionStateApi.transition( name: String? = null, + displayName: String = "", targetState: State? = null, type: TransitionType = LOCAL, -): Transition = addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), targetState)) +): Transition = addTransition(DefaultTransition(name, displayName, matcherForEvent(asState()), type, asState(), targetState)) /** * Creates transition. @@ -61,9 +62,10 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transition( name: String? = null, + displayName: String = "", block: UnitGuardedTransitionBuilder.() -> Unit, ): Transition { - val builder = UnitGuardedTransitionBuilder(name, asState()).apply { + val builder = UnitGuardedTransitionBuilder(name, displayName, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -80,9 +82,10 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transitionOn( name: String? = null, + displayName: String = "", block: UnitGuardedTransitionOnBuilder.() -> Unit, ): Transition { - val builder = UnitGuardedTransitionOnBuilder(name, asState()).apply { + val builder = UnitGuardedTransitionOnBuilder(name, displayName, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -95,9 +98,10 @@ inline fun TransitionStateApi.transitionOn( */ inline fun TransitionStateApi.transitionConditionally( name: String? = null, + displayName: String = "", block: ConditionalTransitionBuilder.() -> Unit, ): Transition { - val builder = ConditionalTransitionBuilder(name, asState()).apply { + val builder = ConditionalTransitionBuilder(name, displayName, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -111,10 +115,11 @@ inline fun TransitionStateApi.transitionConditionally( */ inline fun , D : Any> TransitionStateApi.dataTransition( name: String? = null, + displayName: String = "", targetState: DataState, type: TransitionType = LOCAL, ): Transition { - return addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), targetState)) + return addTransition(DefaultTransition(name, displayName, matcherForEvent(asState()), type, asState(), targetState)) } /** @@ -122,9 +127,10 @@ inline fun , D : Any> TransitionStateApi.dataTransition */ inline fun , D : Any> DataTransitionStateApi.dataTransition( name: String? = null, + displayName: String = "", type: TransitionType = LOCAL, ): Transition { - return addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), null)) + return addTransition(DefaultTransition(name, displayName, matcherForEvent(asState()), type, asState(), null)) } /** @@ -132,9 +138,10 @@ inline fun , D : Any> DataTransitionStateApi.dataTra */ inline fun , D : Any> TransitionStateApi.dataTransition( name: String? = null, + displayName: String = "", block: DataGuardedTransitionBuilder.() -> Unit, ): Transition { - val builder = DataGuardedTransitionBuilder(name, asState()).apply { + val builder = DataGuardedTransitionBuilder(name, displayName, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -146,9 +153,10 @@ inline fun , D : Any> TransitionStateApi.dataTransition */ inline fun , D : Any> TransitionStateApi.dataTransitionOn( name: String? = null, + displayName: String = "", block: DataGuardedTransitionOnBuilder.() -> Unit, ): Transition { - val builder = DataGuardedTransitionOnBuilder(name, asState()).apply { + val builder = DataGuardedTransitionOnBuilder(name, displayName, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt index 6dde0e5..c9c3a6d 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt @@ -135,7 +135,7 @@ internal class ExportPlantUmlVisitor( private fun transitionLabel(transition: Transition<*>): String { val entries = listOfNotNull( - transition.name, + transition.displayName.trim().takeIf { it.isNotBlank() } ?: transition.name, transition.eventMatcher.eventClass.simpleName.takeIf { showEventLabels }, ) return label(entries.joinToString()) diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt index df33f5c..3c93f01 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt @@ -9,8 +9,8 @@ import ru.nsk.kstatemachine.EventMatcher.Companion.isInstanceOf private object CustomTransitionTestData { class CustomEvent(val value: Int) : Event - class CustomTransition(name: String, sourceState: IState, targetState: IState) : - DefaultTransition(name, isInstanceOf(), TransitionType.LOCAL, sourceState, targetState) { + class CustomTransition(name: String, displayName: String, sourceState: IState, targetState: IState) : + DefaultTransition(name, displayName, isInstanceOf(), TransitionType.LOCAL, sourceState, targetState) { override suspend fun isMatchingEvent(event: Event): Boolean { return super.isMatchingEvent(event) && event is CustomEvent && event.value == 42 } @@ -33,7 +33,7 @@ class CustomTransitionTest : StringSpec({ val state2 = state("state2") initialState("state1") { - val transition = CustomTransition("customTransition", this, state2).apply { + val transition = CustomTransition("customTransition", "test display name",this, state2).apply { onTriggered { callbacks.onTransitionTriggered(it.event) } } addTransition(transition) From 9dbdecd36ab121087ec29f294bca1d4432b5bbea Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Mon, 5 Feb 2024 12:47:26 +0100 Subject: [PATCH 2/7] display name in states --- .../kstatemachine/CoroutinesStateMachine.kt | 4 ++ .../ru/nsk/kstatemachine/BaseStateImpl.kt | 5 ++- .../nsk/kstatemachine/CoroutineAbstraction.kt | 2 + .../ru/nsk/kstatemachine/DefaultDataState.kt | 12 ++++-- .../nsk/kstatemachine/DefaultHistoryState.kt | 2 +- .../ru/nsk/kstatemachine/DefaultState.kt | 20 +++++++--- .../kotlin/ru/nsk/kstatemachine/IState.kt | 39 +++++++++++++------ .../ru/nsk/kstatemachine/StateMachine.kt | 2 + .../ru/nsk/kstatemachine/StateMachineImpl.kt | 7 ++-- .../kotlin/ru/nsk/kstatemachine/UndoState.kt | 2 +- .../visitors/ExportPlantUmlVisitor.kt | 2 +- .../nsk/kstatemachine/ExportToPlantUmlTest.kt | 2 +- .../ru/nsk/kstatemachine/StateMachineTest.kt | 2 +- 13 files changed, 70 insertions(+), 31 deletions(-) diff --git a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt index 6eca153..6cd128a 100644 --- a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt +++ b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.CoroutineScope */ suspend fun createStateMachine( scope: CoroutineScope, + displayName: String? = null, name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, @@ -25,6 +26,7 @@ suspend fun createStateMachine( init: suspend BuildingStateMachine.() -> Unit ) = CoroutinesLibCoroutineAbstraction(scope).createStateMachine( name, + displayName, childMode, start, autoDestroyOnStatesReuse, @@ -36,6 +38,7 @@ suspend fun createStateMachine( fun createStateMachineBlocking( scope: CoroutineScope, name: String? = null, + displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, autoDestroyOnStatesReuse: Boolean = true, @@ -46,6 +49,7 @@ fun createStateMachineBlocking( runBlocking { createStateMachine( name, + displayName, childMode, start, autoDestroyOnStatesReuse, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt index 0749ab7..34b4c3a 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt @@ -7,7 +7,10 @@ import ru.nsk.kstatemachine.TransitionType.EXTERNAL /** * Base [IState] implementation for all states */ -open class BaseStateImpl(override val name: String?, override val childMode: ChildMode) : InternalState() { +open class BaseStateImpl( + override val name: String?, + override val displayName: String?, + override val childMode: ChildMode) : InternalState() { private class Data { val listeners = mutableSetOf() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt index 3d19adf..22ca638 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt @@ -44,6 +44,7 @@ internal class StdLibCoroutineAbstraction : CoroutineAbstraction { suspend fun CoroutineAbstraction.createStateMachine( name: String?, + displayName: String?, childMode: ChildMode, start: Boolean, autoDestroyOnStatesReuse: Boolean, @@ -52,6 +53,7 @@ suspend fun CoroutineAbstraction.createStateMachine( init: suspend BuildingStateMachine.() -> Unit ): StateMachine = StateMachineImpl( name, + displayName, childMode, autoDestroyOnStatesReuse, enableUndo, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt index d1e1417..4651d4f 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt @@ -5,17 +5,19 @@ import ru.nsk.kstatemachine.ChildMode.EXCLUSIVE /** inline constructor function */ inline fun defaultDataState( name: String? = null, + displayName: String? = null, defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), -) = DefaultDataState(name, defaultData, childMode, dataExtractor) +) = DefaultDataState(name, displayName, defaultData, childMode, dataExtractor) open class DefaultDataState( name: String? = null, + displayName: String? = null, override val defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, private val dataExtractor: DataExtractor, -) : BaseStateImpl(name, childMode), DataState { +) : BaseStateImpl(name, displayName, childMode), DataState { private var _data: D? = null override val data: D get() = checkNotNull(_data) { "Data is not set. Is $this state active?" } @@ -72,12 +74,14 @@ open class DefaultDataState( /** inline constructor function */ inline fun defaultFinalDataState( name: String? = null, + displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), -): DefaultFinalDataState = DefaultFinalDataState(name, defaultData, dataExtractor) +): DefaultFinalDataState = DefaultFinalDataState(name, displayName, defaultData, dataExtractor) open class DefaultFinalDataState( name: String? = null, + displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor -) : DefaultDataState(name, defaultData, EXCLUSIVE, dataExtractor), FinalDataState +) : DefaultDataState(name, displayName, defaultData, EXCLUSIVE, dataExtractor), FinalDataState diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt index 396eaf3..b71af25 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt @@ -7,7 +7,7 @@ open class DefaultHistoryState( name: String? = null, private var _defaultState: IState? = null, final override val historyType: HistoryType = HistoryType.SHALLOW -) : BasePseudoState(name), HistoryState { +) : BasePseudoState(name, null), HistoryState { override val defaultState get() = checkNotNull(_defaultState) { "Internal error, default state is not set" } private var _storedState: IState? = null diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt index b34febe..595c7bf 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt @@ -5,15 +5,22 @@ import ru.nsk.kstatemachine.ChildMode.EXCLUSIVE /** * The most common state */ -open class DefaultState(name: String? = null, childMode: ChildMode = EXCLUSIVE) : - BaseStateImpl(name, childMode), State +open class DefaultState( + name: String? = null, + displayName: String? = null, + childMode: ChildMode = EXCLUSIVE +) : BaseStateImpl(name, displayName, childMode), State -open class DefaultFinalState(name: String? = null) : DefaultState(name), FinalState +open class DefaultFinalState( + name: String? = null, + displayName: String? = null +) : DefaultState(name, displayName), FinalState open class DefaultChoiceState( name: String? = null, + displayName: String? = null, private val choiceAction: suspend EventAndArgument<*>.() -> State -) : BasePseudoState(name), RedirectPseudoState { +) : BasePseudoState(name, displayName), RedirectPseudoState { override suspend fun resolveTargetState(eventAndArgument: EventAndArgument<*>) = eventAndArgument.choiceAction().also { log { "$this resolved to $it" } } @@ -21,8 +28,9 @@ open class DefaultChoiceState( open class DefaultChoiceDataState( name: String? = null, + displayName: String? = null, private val choiceAction: suspend EventAndArgument<*>.() -> DataState, -) : DataState, BasePseudoState(name), RedirectPseudoState { +) : DataState, BasePseudoState(name, displayName), RedirectPseudoState { override suspend fun resolveTargetState(eventAndArgument: EventAndArgument<*>) = eventAndArgument.choiceAction().also { log { "$this resolved to $it" } } @@ -32,7 +40,7 @@ open class DefaultChoiceDataState( override val lastData: D get() = error("PseudoState $this can not have lastData") } -open class BasePseudoState(name: String?) : BaseStateImpl(name, EXCLUSIVE), PseudoState { +open class BasePseudoState(name: String?, displayName: String?) : BaseStateImpl(name, displayName, EXCLUSIVE), PseudoState { override suspend fun doEnter(transitionParams: TransitionParams<*>) = internalError() override suspend fun doExit(transitionParams: TransitionParams<*>) = internalError() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt index 8a69b32..d11d500 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt @@ -13,6 +13,7 @@ import kotlin.reflect.KClass @StateMachineDslMarker interface IState : TransitionStateApi, VisitorAcceptor { val name: String? + val displayName: String? val states: Set val initialState: IState? val parent: IState? @@ -203,37 +204,41 @@ fun IState.machineOrNull(): StateMachine? = if (this is StateMachine) this else */ fun IState.state( name: String? = null, + displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, init: StateBlock? = null -) = addState(DefaultState(name, childMode), init) +) = addState(DefaultState(name, displayName, childMode), init) inline fun IState.dataState( name: String? = null, + displayName: String? = null, defaultData: D? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), noinline init: StateBlock>? = null -) = addState(defaultDataState(name, defaultData, childMode, dataExtractor), init) +) = addState(defaultDataState(name, displayName, defaultData, childMode, dataExtractor), init) /** * A shortcut for [state] and [IState.setInitialState] calls */ fun IState.initialState( name: String? = null, + displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, init: StateBlock? = null -) = addInitialState(DefaultState(name, childMode), init) +) = addInitialState(DefaultState(name, displayName, childMode), init) /** * @param defaultData is necessary for initial [DataState] */ inline fun IState.initialDataState( name: String? = null, + displayName: String? = null, defaultData: D, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), noinline init: StateBlock>? = null -) = addInitialState(defaultDataState(name, defaultData, childMode, dataExtractor), init) +) = addInitialState(defaultDataState(name, displayName, defaultData, childMode, dataExtractor), init) /** * A shortcut for [IState.addState] and [IState.setInitialState] calls @@ -259,33 +264,43 @@ fun IState.initialFinalState(name: String? = null, init: StateBlock? inline fun IState.finalDataState( name: String? = null, + displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), noinline init: StateBlock>? = null -) = addFinalState(defaultFinalDataState(name, defaultData, dataExtractor), init) +) = addFinalState(defaultFinalDataState(name, displayName, defaultData, dataExtractor), init) inline fun IState.initialFinalDataState( name: String? = null, + displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), noinline init: StateBlock>? = null -) = addInitialState(defaultFinalDataState(name, defaultData, dataExtractor), init) +) = addInitialState(defaultFinalDataState(name, displayName, defaultData, dataExtractor), init) -fun IState.choiceState(name: String? = null, choiceAction: suspend EventAndArgument<*>.() -> State) = - addState(DefaultChoiceState(name, choiceAction)) +fun IState.choiceState( + name: String? = null, + displayName: String? = null, + choiceAction: suspend EventAndArgument<*>.() -> State +) = addState(DefaultChoiceState(name, displayName, choiceAction)) -fun IState.initialChoiceState(name: String? = null, choiceAction: suspend EventAndArgument<*>.() -> State) = - addInitialState(DefaultChoiceState(name, choiceAction)) +fun IState.initialChoiceState( + name: String? = null, + displayName: String? = null, + choiceAction: suspend EventAndArgument<*>.() -> State +) = addInitialState(DefaultChoiceState(name, displayName, choiceAction)) fun IState.choiceDataState( name: String? = null, + displayName: String? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState -) = addState(DefaultChoiceDataState(name, choiceAction)) +) = addState(DefaultChoiceDataState(name, displayName, choiceAction)) fun IState.initialChoiceDataState( name: String? = null, + displayName: String? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState -) = addInitialState(DefaultChoiceDataState(name, choiceAction)) +) = addInitialState(DefaultChoiceDataState(name, displayName, choiceAction)) fun IState.historyState( name: String? = null, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt index ec24916..74269ec 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt @@ -212,6 +212,7 @@ interface BuildingStateMachine : StateMachine { */ fun createStdLibStateMachine( name: String? = null, + displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, autoDestroyOnStatesReuse: Boolean = true, @@ -223,6 +224,7 @@ fun createStdLibStateMachine( runBlocking { createStateMachine( name, + displayName, childMode, start, autoDestroyOnStatesReuse, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt index e6036b3..87955e8 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt @@ -7,8 +7,8 @@ import ru.nsk.kstatemachine.visitors.CleanupVisitor /** * Defines state machine API for internal library usage. */ -abstract class InternalStateMachine(name: String?, childMode: ChildMode) : - BuildingStateMachine, DefaultState(name, childMode) { +abstract class InternalStateMachine(name: String?, displayName: String?, childMode: ChildMode) : + BuildingStateMachine, DefaultState(name, displayName, childMode) { internal abstract suspend fun startFrom(state: IState, argument: Any?) internal abstract suspend fun startFrom(state: DataState, data: D, argument: Any?) internal abstract fun delayListenerException(exception: Exception) @@ -16,12 +16,13 @@ abstract class InternalStateMachine(name: String?, childMode: ChildMode) : internal class StateMachineImpl( name: String?, + displayName: String?, childMode: ChildMode, override val autoDestroyOnStatesReuse: Boolean, override val isUndoEnabled: Boolean, override val doNotThrowOnMultipleTransitionsMatch: Boolean, override val coroutineAbstraction: CoroutineAbstraction, -) : InternalStateMachine(name, childMode) { +) : InternalStateMachine(name, displayName, childMode) { private val _machineListeners = mutableSetOf() override val machineListeners: Collection get() = _machineListeners override var logger: StateMachine.Logger = StateMachine.Logger {} diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/UndoState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/UndoState.kt index f703243..208d32a 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/UndoState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/UndoState.kt @@ -2,7 +2,7 @@ package ru.nsk.kstatemachine private data class StateAndEvent(val state: IState, val eventAndArgument: EventAndArgument<*>) -internal class UndoState : BasePseudoState("undoState") { +internal class UndoState : BasePseudoState("undoState", null) { private val stack = mutableListOf() override suspend fun recursiveAfterTransitionComplete(transitionParams: TransitionParams<*>) { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt index c9c3a6d..d484410 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt @@ -150,7 +150,7 @@ internal class ExportPlantUmlVisitor( const val CHOICE = "<>" fun IState.graphName(): String { - val name = name?.replace(" ", "_") ?: "State${hashCode()}" + val name = displayName?.replace(" ", "_") ?: "State${hashCode()}" return if (this !is StateMachine) name else "${name}_StateMachine" } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt index 0c71be7..ca29bae 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt @@ -251,7 +251,7 @@ class ExportToPlantUmlTest : StringSpec({ "plantUml export parallel states" { val machine = createTestStateMachine(coroutineStarterType, name = "Parallel states") { - initialState("parallel states", ChildMode.PARALLEL) { + initialState("parallel states", "parallel states",ChildMode.PARALLEL) { state("State1") { val state11 = initialState("State11") val state12 = state("State12") diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt index 678eded..81a045b 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt @@ -86,7 +86,7 @@ class StateMachineTest : StringSpec({ second.onEntry { println("$name entered") } val transition = DefaultTransition( - "transition", EventMatcher.isInstanceOf(), TransitionType.LOCAL, first, second + "transition", "", EventMatcher.isInstanceOf(), TransitionType.LOCAL, first, second ) transition.onTriggered { println("${it.transition.name} triggered") } From 02cf53fc6bd4fe5ccf2a041afb5517bed0825fff Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Sun, 11 Feb 2024 12:41:05 +0100 Subject: [PATCH 3/7] Introduce MetaInfo --- .../kstatemachine/CoroutinesStateMachine.kt | 8 +-- .../ru/nsk/kstatemachine/BaseStateImpl.kt | 4 +- .../nsk/kstatemachine/CoroutineAbstraction.kt | 4 +- .../ru/nsk/kstatemachine/DefaultDataState.kt | 18 +++---- .../ru/nsk/kstatemachine/DefaultState.kt | 20 +++---- .../ru/nsk/kstatemachine/DefaultTransition.kt | 14 ++--- .../kotlin/ru/nsk/kstatemachine/IState.kt | 52 +++++++++---------- .../kotlin/ru/nsk/kstatemachine/MetaInfo.kt | 11 ++++ .../ru/nsk/kstatemachine/StateMachine.kt | 4 +- .../ru/nsk/kstatemachine/StateMachineImpl.kt | 10 ++-- .../kotlin/ru/nsk/kstatemachine/Transition.kt | 2 +- .../ru/nsk/kstatemachine/TransitionBuilder.kt | 42 +++++++-------- .../nsk/kstatemachine/TransitionStateApi.kt | 39 ++++++++------ .../visitors/ExportPlantUmlVisitor.kt | 29 ++++++----- .../ru/nsk/samples/ComplexSyntaxSample.kt | 2 +- .../nsk/samples/InheritTransitionsSample.kt | 2 +- .../ru/nsk/samples/MermaidExportSample.kt | 2 +- .../ru/nsk/samples/PlantUmlExportSample.kt | 14 ++--- .../nsk/kstatemachine/CustomTransitionTest.kt | 6 +-- .../nsk/kstatemachine/ExportToPlantUmlTest.kt | 10 ++-- .../ru/nsk/kstatemachine/StateMachineTest.kt | 2 +- .../kotlin/ru/nsk/kstatemachine/TestUtils.kt | 8 +++ .../nsk/kstatemachine/TestingStartFromTest.kt | 4 +- .../ru/nsk/kstatemachine/TransitionTest.kt | 2 +- .../kstatemachine/TypesafeTransitionTest.kt | 6 +-- 25 files changed, 172 insertions(+), 143 deletions(-) create mode 100644 kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt diff --git a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt index 6cd128a..1970fc5 100644 --- a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt +++ b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt @@ -16,45 +16,45 @@ import kotlinx.coroutines.CoroutineScope */ suspend fun createStateMachine( scope: CoroutineScope, - displayName: String? = null, name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, + metaInfo: StateMetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ) = CoroutinesLibCoroutineAbstraction(scope).createStateMachine( name, - displayName, childMode, start, autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, + metaInfo, init ) fun createStateMachineBlocking( scope: CoroutineScope, name: String? = null, - displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, + metaInfo: StateMetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ) = with(CoroutinesLibCoroutineAbstraction(scope)) { runBlocking { createStateMachine( name, - displayName, childMode, start, autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, + metaInfo, init ) } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt index 34b4c3a..cf5acd0 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt @@ -9,8 +9,8 @@ import ru.nsk.kstatemachine.TransitionType.EXTERNAL */ open class BaseStateImpl( override val name: String?, - override val displayName: String?, - override val childMode: ChildMode) : InternalState() { + override val childMode: ChildMode, + override val metaInfo: StateMetaInfo?) : InternalState() { private class Data { val listeners = mutableSetOf() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt index 22ca638..8d78b59 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt @@ -44,21 +44,21 @@ internal class StdLibCoroutineAbstraction : CoroutineAbstraction { suspend fun CoroutineAbstraction.createStateMachine( name: String?, - displayName: String?, childMode: ChildMode, start: Boolean, autoDestroyOnStatesReuse: Boolean, enableUndo: Boolean, doNotThrowOnMultipleTransitionsMatch: Boolean, + metaInfo: StateMetaInfo?, init: suspend BuildingStateMachine.() -> Unit ): StateMachine = StateMachineImpl( name, - displayName, childMode, autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, this, + metaInfo, ).apply { init() if (start) start() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt index 4651d4f..d854647 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt @@ -5,19 +5,19 @@ import ru.nsk.kstatemachine.ChildMode.EXCLUSIVE /** inline constructor function */ inline fun defaultDataState( name: String? = null, - displayName: String? = null, defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), -) = DefaultDataState(name, displayName, defaultData, childMode, dataExtractor) + metaInfo: StateMetaInfo? = null, +) = DefaultDataState(name, defaultData, childMode, dataExtractor, metaInfo) open class DefaultDataState( name: String? = null, - displayName: String? = null, override val defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, private val dataExtractor: DataExtractor, -) : BaseStateImpl(name, displayName, childMode), DataState { + metaInfo: StateMetaInfo? = null, +) : BaseStateImpl(name, childMode, metaInfo), DataState { private var _data: D? = null override val data: D get() = checkNotNull(_data) { "Data is not set. Is $this state active?" } @@ -74,14 +74,14 @@ open class DefaultDataState( /** inline constructor function */ inline fun defaultFinalDataState( name: String? = null, - displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), -): DefaultFinalDataState = DefaultFinalDataState(name, displayName, defaultData, dataExtractor) + metaInfo: StateMetaInfo? = null, +): DefaultFinalDataState = DefaultFinalDataState(name, defaultData, dataExtractor, metaInfo) open class DefaultFinalDataState( name: String? = null, - displayName: String? = null, defaultData: D? = null, - dataExtractor: DataExtractor -) : DefaultDataState(name, displayName, defaultData, EXCLUSIVE, dataExtractor), FinalDataState + dataExtractor: DataExtractor, + metaInfo: StateMetaInfo? = null +) : DefaultDataState(name, defaultData, EXCLUSIVE, dataExtractor, metaInfo), FinalDataState diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt index 595c7bf..dfa4c9a 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt @@ -7,20 +7,20 @@ import ru.nsk.kstatemachine.ChildMode.EXCLUSIVE */ open class DefaultState( name: String? = null, - displayName: String? = null, - childMode: ChildMode = EXCLUSIVE -) : BaseStateImpl(name, displayName, childMode), State + childMode: ChildMode = EXCLUSIVE, + metaInfo: StateMetaInfo? = null +) : BaseStateImpl(name, childMode, metaInfo), State open class DefaultFinalState( name: String? = null, - displayName: String? = null -) : DefaultState(name, displayName), FinalState + metaInfo: StateMetaInfo? = null +) : DefaultState(name, metaInfo = metaInfo), FinalState open class DefaultChoiceState( name: String? = null, - displayName: String? = null, + metaInfo: StateMetaInfo? = null, private val choiceAction: suspend EventAndArgument<*>.() -> State -) : BasePseudoState(name, displayName), RedirectPseudoState { +) : BasePseudoState(name, metaInfo), RedirectPseudoState { override suspend fun resolveTargetState(eventAndArgument: EventAndArgument<*>) = eventAndArgument.choiceAction().also { log { "$this resolved to $it" } } @@ -28,9 +28,9 @@ open class DefaultChoiceState( open class DefaultChoiceDataState( name: String? = null, - displayName: String? = null, + metaInfo: StateMetaInfo? = null, private val choiceAction: suspend EventAndArgument<*>.() -> DataState, -) : DataState, BasePseudoState(name, displayName), RedirectPseudoState { +) : DataState, BasePseudoState(name, metaInfo), RedirectPseudoState { override suspend fun resolveTargetState(eventAndArgument: EventAndArgument<*>) = eventAndArgument.choiceAction().also { log { "$this resolved to $it" } } @@ -40,7 +40,7 @@ open class DefaultChoiceDataState( override val lastData: D get() = error("PseudoState $this can not have lastData") } -open class BasePseudoState(name: String?, displayName: String?) : BaseStateImpl(name, displayName, EXCLUSIVE), PseudoState { +open class BasePseudoState(name: String?, metaInfo: StateMetaInfo?) : BaseStateImpl(name, EXCLUSIVE, metaInfo), PseudoState { override suspend fun doEnter(transitionParams: TransitionParams<*>) = internalError() override suspend fun doExit(transitionParams: TransitionParams<*>) = internalError() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt index 957599e..86962cf 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt @@ -2,30 +2,30 @@ package ru.nsk.kstatemachine open class DefaultTransition( override val name: String?, - override val displayName: String, override val eventMatcher: EventMatcher, override val type: TransitionType, sourceState: IState, + override val metaInfo: TransitionMetaInfo?, ) : InternalTransition { constructor( name: String?, - displayName: String, eventMatcher: EventMatcher, type: TransitionType, sourceState: IState, - targetState: IState? - ) : this(name, displayName, eventMatcher, type, sourceState) { + targetState: IState?, + metaInfo: TransitionMetaInfo? + ) : this(name, eventMatcher, type, sourceState, metaInfo) { targetStateDirectionProducer = { it.targetStateOrStay(targetState) } } constructor( name: String?, - displayName: String, eventMatcher: EventMatcher, type: TransitionType, sourceState: IState, - targetStateDirectionProducer: TransitionDirectionProducer - ) : this(name, displayName, eventMatcher, type, sourceState) { + targetStateDirectionProducer: TransitionDirectionProducer, + metaInfo: TransitionMetaInfo? + ) : this(name, eventMatcher, type, sourceState,metaInfo) { this.targetStateDirectionProducer = targetStateDirectionProducer } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt index d11d500..5980ca3 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt @@ -13,7 +13,7 @@ import kotlin.reflect.KClass @StateMachineDslMarker interface IState : TransitionStateApi, VisitorAcceptor { val name: String? - val displayName: String? + val metaInfo: StateMetaInfo? val states: Set val initialState: IState? val parent: IState? @@ -204,41 +204,41 @@ fun IState.machineOrNull(): StateMachine? = if (this is StateMachine) this else */ fun IState.state( name: String? = null, - displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, - init: StateBlock? = null -) = addState(DefaultState(name, displayName, childMode), init) + init: StateBlock? = null, + metaInfo: StateMetaInfo? = null +) = addState(DefaultState(name, childMode, metaInfo), init) inline fun IState.dataState( name: String? = null, - displayName: String? = null, defaultData: D? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), + metaInfo: StateMetaInfo? = null, noinline init: StateBlock>? = null -) = addState(defaultDataState(name, displayName, defaultData, childMode, dataExtractor), init) +) = addState(defaultDataState(name, defaultData, childMode, dataExtractor, metaInfo), init) /** * A shortcut for [state] and [IState.setInitialState] calls */ fun IState.initialState( name: String? = null, - displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, + metaInfo: StateMetaInfo? = null, init: StateBlock? = null -) = addInitialState(DefaultState(name, displayName, childMode), init) +) = addInitialState(DefaultState(name, childMode, metaInfo), init) /** * @param defaultData is necessary for initial [DataState] */ inline fun IState.initialDataState( name: String? = null, - displayName: String? = null, defaultData: D, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), + metaInfo: StateMetaInfo? = null, noinline init: StateBlock>? = null -) = addInitialState(defaultDataState(name, displayName, defaultData, childMode, dataExtractor), init) +) = addInitialState(defaultDataState(name, defaultData, childMode, dataExtractor, metaInfo), init) /** * A shortcut for [IState.addState] and [IState.setInitialState] calls @@ -256,51 +256,51 @@ fun IState.addInitialState(state: S, init: StateBlock? = null): fun IState.addFinalState(state: S, init: StateBlock? = null) = addState(state, init) -fun IState.finalState(name: String? = null, init: StateBlock? = null) = - addFinalState(DefaultFinalState(name), init) +fun IState.finalState(name: String? = null, metaInfo: StateMetaInfo? = null, init: StateBlock? = null) = + addFinalState(DefaultFinalState(name, metaInfo), init) -fun IState.initialFinalState(name: String? = null, init: StateBlock? = null) = - addInitialState(DefaultFinalState(name), init) +fun IState.initialFinalState(name: String? = null, metaInfo: StateMetaInfo? = null, init: StateBlock? = null) = + addInitialState(DefaultFinalState(name, metaInfo), init) inline fun IState.finalDataState( name: String? = null, - displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), + metaInfo: StateMetaInfo? = null, noinline init: StateBlock>? = null -) = addFinalState(defaultFinalDataState(name, displayName, defaultData, dataExtractor), init) +) = addFinalState(defaultFinalDataState(name, defaultData, dataExtractor, metaInfo), init) inline fun IState.initialFinalDataState( name: String? = null, - displayName: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), + metaInfo: StateMetaInfo? = null, noinline init: StateBlock>? = null -) = addInitialState(defaultFinalDataState(name, displayName, defaultData, dataExtractor), init) +) = addInitialState(defaultFinalDataState(name, defaultData, dataExtractor, metaInfo), init) fun IState.choiceState( name: String? = null, - displayName: String? = null, + metaInfo: StateMetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> State -) = addState(DefaultChoiceState(name, displayName, choiceAction)) +) = addState(DefaultChoiceState(name, metaInfo, choiceAction)) fun IState.initialChoiceState( name: String? = null, - displayName: String? = null, + metaInfo: StateMetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> State -) = addInitialState(DefaultChoiceState(name, displayName, choiceAction)) +) = addInitialState(DefaultChoiceState(name, metaInfo, choiceAction)) fun IState.choiceDataState( name: String? = null, - displayName: String? = null, + metaInfo: StateMetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState -) = addState(DefaultChoiceDataState(name, displayName, choiceAction)) +) = addState(DefaultChoiceDataState(name, metaInfo, choiceAction)) fun IState.initialChoiceDataState( name: String? = null, - displayName: String? = null, + metaInfo: StateMetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState -) = addInitialState(DefaultChoiceDataState(name, displayName, choiceAction)) +) = addInitialState(DefaultChoiceDataState(name, metaInfo, choiceAction)) fun IState.historyState( name: String? = null, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt new file mode 100644 index 0000000..d5c319b --- /dev/null +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt @@ -0,0 +1,11 @@ +package ru.nsk.kstatemachine + +sealed interface MetaInfo +interface StateMetaInfo : MetaInfo +interface TransitionMetaInfo : MetaInfo +interface UmlMetaInfo: StateMetaInfo, TransitionMetaInfo { + val umlLabel: String +} +fun umlLabel(label: String) = object : UmlMetaInfo { + override val umlLabel = label +} \ No newline at end of file diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt index 74269ec..c098e8c 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt @@ -212,24 +212,24 @@ interface BuildingStateMachine : StateMachine { */ fun createStdLibStateMachine( name: String? = null, - displayName: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, + metaInfo: StateMetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ): StateMachine { return with(StdLibCoroutineAbstraction()) { runBlocking { createStateMachine( name, - displayName, childMode, start, autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, + metaInfo, init ) } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt index 87955e8..c4aef7f 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt @@ -7,8 +7,8 @@ import ru.nsk.kstatemachine.visitors.CleanupVisitor /** * Defines state machine API for internal library usage. */ -abstract class InternalStateMachine(name: String?, displayName: String?, childMode: ChildMode) : - BuildingStateMachine, DefaultState(name, displayName, childMode) { +abstract class InternalStateMachine(name: String?, metaInfo: StateMetaInfo?, childMode: ChildMode) : + BuildingStateMachine, DefaultState(name, childMode, metaInfo) { internal abstract suspend fun startFrom(state: IState, argument: Any?) internal abstract suspend fun startFrom(state: DataState, data: D, argument: Any?) internal abstract fun delayListenerException(exception: Exception) @@ -16,13 +16,13 @@ abstract class InternalStateMachine(name: String?, displayName: String?, childMo internal class StateMachineImpl( name: String?, - displayName: String?, childMode: ChildMode, override val autoDestroyOnStatesReuse: Boolean, override val isUndoEnabled: Boolean, override val doNotThrowOnMultipleTransitionsMatch: Boolean, override val coroutineAbstraction: CoroutineAbstraction, -) : InternalStateMachine(name, displayName, childMode) { + metaInfo: StateMetaInfo?, +) : InternalStateMachine(name, metaInfo, childMode) { private val _machineListeners = mutableSetOf() override val machineListeners: Collection get() = _machineListeners override var logger: StateMachine.Logger = StateMachine.Logger {} @@ -298,11 +298,11 @@ internal suspend inline fun makeStartTransitionParams( ): TransitionParams<*> { val transition = DefaultTransition( "Starting", - "", EventMatcher.isInstanceOf(), TransitionType.LOCAL, sourceState, targetState, + null ) return TransitionParams( diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt index 1f74eff..654186b 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt @@ -11,7 +11,7 @@ import ru.nsk.kstatemachine.visitors.VisitorAcceptor */ interface Transition : VisitorAcceptor { val name: String? - val displayName: String + val metaInfo: TransitionMetaInfo? val eventMatcher: EventMatcher val sourceState: IState val type: TransitionType diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt index ed423d5..04bcae9 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt @@ -3,7 +3,7 @@ package ru.nsk.kstatemachine import ru.nsk.kstatemachine.TransitionDirectionProducerPolicy.* @StateMachineDslMarker -abstract class TransitionBuilder(protected val name: String?, protected val displayName: String, protected val sourceState: IState) { +abstract class TransitionBuilder(protected val name: String?, protected val metaInfo: TransitionMetaInfo?, protected val sourceState: IState) { val listeners = mutableListOf() lateinit var eventMatcher: EventMatcher var type = TransitionType.LOCAL @@ -11,13 +11,13 @@ abstract class TransitionBuilder(protected val name: String?, protect abstract fun build(): Transition } -abstract class BaseGuardedTransitionBuilder(name: String?, displayName: String, sourceState: IState) : - TransitionBuilder(name, displayName, sourceState) { +abstract class BaseGuardedTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + TransitionBuilder(name, metaInfo, sourceState) { var guard: suspend EventAndArgument.() -> Boolean = { true } } -abstract class GuardedTransitionBuilder(name: String?, displayName: String, sourceState: IState) : - BaseGuardedTransitionBuilder(name, displayName, sourceState) { +abstract class GuardedTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { var targetState: S? = null override fun build(): Transition { @@ -33,14 +33,14 @@ abstract class GuardedTransitionBuilder(name: String?, di } } - val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction, metaInfo) listeners.forEach { transition.addListener(it) } return transition } } -abstract class GuardedTransitionOnBuilder(name: String?, displayName: String, sourceState: IState) : - BaseGuardedTransitionBuilder(name, displayName, sourceState) { +abstract class GuardedTransitionOnBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { lateinit var targetState: suspend EventAndArgument.() -> S override fun build(): Transition { @@ -56,14 +56,14 @@ abstract class GuardedTransitionOnBuilder(name: String?, } } - val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction, metaInfo) listeners.forEach { transition.addListener(it) } return transition } } -class ConditionalTransitionBuilder(name: String?, displayName: String, sourceState: IState) : - TransitionBuilder(name, displayName, sourceState) { +class ConditionalTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + TransitionBuilder(name, metaInfo, sourceState) { lateinit var direction: suspend EventAndArgument.() -> TransitionDirection override fun build(): Transition { @@ -75,7 +75,7 @@ class ConditionalTransitionBuilder(name: String?, displayName: String } } - val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction, metaInfo) listeners.forEach { transition.addListener(it) } return transition } @@ -84,17 +84,17 @@ class ConditionalTransitionBuilder(name: String?, displayName: String /** * Any [Event] (with any data) can lead to [State] */ -class UnitGuardedTransitionBuilder(name: String?, displayName: String, sourceState: IState) : - GuardedTransitionBuilder(name, displayName, sourceState) +class UnitGuardedTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + GuardedTransitionBuilder(name, metaInfo, sourceState) -class UnitGuardedTransitionOnBuilder(name: String?, displayName: String, sourceState: IState) : - GuardedTransitionOnBuilder(name, displayName, sourceState) +class UnitGuardedTransitionOnBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + GuardedTransitionOnBuilder(name, metaInfo, sourceState) /** * Type safe argument transition builder */ -class DataGuardedTransitionBuilder, D : Any>(name: String?, displayName: String, sourceState: IState) : - BaseGuardedTransitionBuilder(name, displayName, sourceState) { +class DataGuardedTransitionBuilder, D : Any>(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { /** User should initialize this filed */ lateinit var targetState: DataState @@ -112,7 +112,7 @@ class DataGuardedTransitionBuilder, D : Any>(name: String?, dis } } - val transition = DefaultTransition(name, displayName, eventMatcher, type, sourceState, direction) + val transition = DefaultTransition(name, eventMatcher, type, sourceState, direction, metaInfo) listeners.forEach { transition.addListener(it) } return transition } @@ -121,8 +121,8 @@ class DataGuardedTransitionBuilder, D : Any>(name: String?, dis /** * Type safe argument transitionOn builder */ -class DataGuardedTransitionOnBuilder, D : Any>(name: String?, displayName: String, sourceState: IState) : - GuardedTransitionOnBuilder>(name, displayName, sourceState) +class DataGuardedTransitionOnBuilder, D : Any>(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : + GuardedTransitionOnBuilder>(name, metaInfo, sourceState) inline fun TransitionBuilder.onTriggered( crossinline block: suspend (TransitionParams) -> Unit diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt index c4a70e4..5a617f5 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt @@ -49,10 +49,17 @@ inline fun TransitionStateApi.requireTransition() = */ inline fun TransitionStateApi.transition( name: String? = null, - displayName: String = "", targetState: State? = null, type: TransitionType = LOCAL, -): Transition = addTransition(DefaultTransition(name, displayName, matcherForEvent(asState()), type, asState(), targetState)) + metaInfo: TransitionMetaInfo? = null, +): Transition = addTransition(DefaultTransition( + name, + matcherForEvent(asState()), + type, + asState(), + targetState, + metaInfo +)) /** * Creates transition. @@ -62,10 +69,10 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transition( name: String? = null, - displayName: String = "", + metaInfo: TransitionMetaInfo? = null, block: UnitGuardedTransitionBuilder.() -> Unit, ): Transition { - val builder = UnitGuardedTransitionBuilder(name, displayName, asState()).apply { + val builder = UnitGuardedTransitionBuilder(name, metaInfo, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -82,10 +89,10 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transitionOn( name: String? = null, - displayName: String = "", + metaInfo: TransitionMetaInfo? = null, block: UnitGuardedTransitionOnBuilder.() -> Unit, ): Transition { - val builder = UnitGuardedTransitionOnBuilder(name, displayName, asState()).apply { + val builder = UnitGuardedTransitionOnBuilder(name, metaInfo, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -98,10 +105,10 @@ inline fun TransitionStateApi.transitionOn( */ inline fun TransitionStateApi.transitionConditionally( name: String? = null, - displayName: String = "", + metaInfo: TransitionMetaInfo? = null, block: ConditionalTransitionBuilder.() -> Unit, ): Transition { - val builder = ConditionalTransitionBuilder(name, displayName, asState()).apply { + val builder = ConditionalTransitionBuilder(name, metaInfo, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -115,11 +122,11 @@ inline fun TransitionStateApi.transitionConditionally( */ inline fun , D : Any> TransitionStateApi.dataTransition( name: String? = null, - displayName: String = "", targetState: DataState, type: TransitionType = LOCAL, + metaInfo: TransitionMetaInfo? = null, ): Transition { - return addTransition(DefaultTransition(name, displayName, matcherForEvent(asState()), type, asState(), targetState)) + return addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), targetState, metaInfo)) } /** @@ -127,10 +134,10 @@ inline fun , D : Any> TransitionStateApi.dataTransition */ inline fun , D : Any> DataTransitionStateApi.dataTransition( name: String? = null, - displayName: String = "", type: TransitionType = LOCAL, + metaInfo: TransitionMetaInfo? = null, ): Transition { - return addTransition(DefaultTransition(name, displayName, matcherForEvent(asState()), type, asState(), null)) + return addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), null, metaInfo)) } /** @@ -138,10 +145,10 @@ inline fun , D : Any> DataTransitionStateApi.dataTra */ inline fun , D : Any> TransitionStateApi.dataTransition( name: String? = null, - displayName: String = "", + metaInfo: TransitionMetaInfo? = null, block: DataGuardedTransitionBuilder.() -> Unit, ): Transition { - val builder = DataGuardedTransitionBuilder(name, displayName, asState()).apply { + val builder = DataGuardedTransitionBuilder(name, metaInfo, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -153,10 +160,10 @@ inline fun , D : Any> TransitionStateApi.dataTransition */ inline fun , D : Any> TransitionStateApi.dataTransitionOn( name: String? = null, - displayName: String = "", + metaInfo: TransitionMetaInfo? = null, block: DataGuardedTransitionOnBuilder.() -> Unit, ): Transition { - val builder = DataGuardedTransitionOnBuilder(name, displayName, asState()).apply { + val builder = DataGuardedTransitionOnBuilder(name, metaInfo, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt index d484410..544e780 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt @@ -52,26 +52,25 @@ internal class ExportPlantUmlVisitor( when (state) { is HistoryState, is UndoState -> return is RedirectPseudoState -> { - val stateName = state.graphName() - line("state $stateName $CHOICE") + line("state $CHOICE ${state.displayName()}") if (unsafeCallConditionalLambdas) { val targetState = state.resolveTargetState( EventAndArgument(ExportPlantUmlEvent, null) ) as InternalState - crossLevelTransitions += "$stateName --> ${targetState.targetGraphName()}" + crossLevelTransitions += "${state.graphName()} --> ${targetState.targetGraphName()}" } } - else -> line("state ${state.graphName()}") + else -> line("state ${state.displayName()}") } } else { if (state !is StateMachine) { // ignore composed machines - line("state ${state.graphName()} {") + line("state ${state.displayName()} {") ++indent processStateBody(state) --indent line("}") } else { - line("state ${state.graphName()}") + line("state ${state.displayName()}") } } } @@ -134,11 +133,11 @@ internal class ExportPlantUmlVisitor( private fun line(text: String) = builder.appendLine(SINGLE_INDENT.repeat(indent) + text) private fun transitionLabel(transition: Transition<*>): String { - val entries = listOfNotNull( - transition.displayName.trim().takeIf { it.isNotBlank() } ?: transition.name, + val text = listOfNotNull( + (transition.metaInfo as? UmlMetaInfo)?.umlLabel ?: transition.name, transition.eventMatcher.eventClass.simpleName.takeIf { showEventLabels }, - ) - return label(entries.joinToString()) + ).joinToString() + return " : $text".takeIf { text.isNotBlank() } ?: "" } private companion object { @@ -150,10 +149,16 @@ internal class ExportPlantUmlVisitor( const val CHOICE = "<>" fun IState.graphName(): String { - val name = displayName?.replace(" ", "_") ?: "State${hashCode()}" + val name = name?.replace(" ", "_") ?: "State${hashCode()}" return if (this !is StateMachine) name else "${name}_StateMachine" } + fun IState.displayName(): String { + return (this.metaInfo as? UmlMetaInfo)?.umlLabel?.let { label -> + graphName() + " as \"$label\"" + } ?: graphName() + } + fun InternalState.targetGraphName(): String { return if (this is HistoryState) { val prefix = requireInternalParent().graphName() @@ -165,8 +170,6 @@ internal class ExportPlantUmlVisitor( graphName() } } - - fun label(text: String?) = if (!text.isNullOrBlank()) " : $text" else "" } } diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt index dfa9459..4373c01 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt @@ -20,7 +20,7 @@ private object ComplexSyntaxSample { fun main() = runBlocking { val machine = createStateMachine( this, // coroutine scope used for this machine - "Traffic lights" // StateMachine name is optional + name = "Traffic lights" // StateMachine name is optional ) { // Create and setup states val greenState = initialState("Green") // State name is optional diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/InheritTransitionsSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/InheritTransitionsSample.kt index 600a16d..0a27dc9 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/InheritTransitionsSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/InheritTransitionsSample.kt @@ -16,7 +16,7 @@ private object InheritTransitionsSample { * Nested states allow grouping states and inherit their parent transitions */ fun main() = runBlocking { - val machine = createStateMachine(this, "Nested states") { + val machine = createStateMachine(this, name = "Nested states") { logger = StateMachine.Logger { println(it()) } val state2 = finalState("State2") diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/MermaidExportSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/MermaidExportSample.kt index c0ce1e6..42c1cf8 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/MermaidExportSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/MermaidExportSample.kt @@ -10,7 +10,7 @@ private object MermaidExportSample { } fun main() = runBlocking { - val machine = createStateMachine(this, "Nested states") { + val machine = createStateMachine(this, name = "Nested states") { val state1 = initialState("State1") val state3 = finalState("State3") diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt index d4583ed..6660096 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt @@ -10,15 +10,15 @@ private object PlantUmlExportSample { } fun main() = runBlocking { - val machine = createStateMachine(this, "Nested states") { - val state1 = initialState("State1") - val state3 = finalState("State3") + val machine = createStateMachine(this, metaInfo = umlLabel("Nested states")) { + val state1 = initialState("State1", metaInfo = umlLabel("State 1")) + val state3 = finalState("State3", metaInfo = umlLabel("State 3")) - val state2 = state("State2") { - transition { targetState = state3 } - transition("back") { targetState = state1 } + val state2 = state("State2", metaInfo = umlLabel("State 2")) { + transition(metaInfo = umlLabel("That's all")) { targetState = state3 } + transition(metaInfo = umlLabel("back to State 1")) { targetState = state1 } - val finalSubState = finalState("Final subState") + val finalSubState = finalState(metaInfo = umlLabel("Final sub state")) initialState("Initial subState") { transition { targetState = finalSubState } } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt index 3c93f01..d488221 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt @@ -9,8 +9,8 @@ import ru.nsk.kstatemachine.EventMatcher.Companion.isInstanceOf private object CustomTransitionTestData { class CustomEvent(val value: Int) : Event - class CustomTransition(name: String, displayName: String, sourceState: IState, targetState: IState) : - DefaultTransition(name, displayName, isInstanceOf(), TransitionType.LOCAL, sourceState, targetState) { + class CustomTransition(name: String, metaInfo: TransitionMetaInfo?, sourceState: IState, targetState: IState) : + DefaultTransition(name, isInstanceOf(), TransitionType.LOCAL, sourceState, targetState, metaInfo) { override suspend fun isMatchingEvent(event: Event): Boolean { return super.isMatchingEvent(event) && event is CustomEvent && event.value == 42 } @@ -33,7 +33,7 @@ class CustomTransitionTest : StringSpec({ val state2 = state("state2") initialState("state1") { - val transition = CustomTransition("customTransition", "test display name",this, state2).apply { + val transition = CustomTransition("customTransition", null,this, state2).apply { onTriggered { callbacks.onTransitionTriggered(it.event) } } addTransition(transition) diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt index ca29bae..524114a 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt @@ -78,12 +78,12 @@ State3 --> [*] private const val PLANTUML_PARALLEL_STATES_RESULT = """@startuml hide empty description state parallel_states { - state State1 { + state State1 as "State 1" { state State11 state State12 [*] --> State11 - State11 --> State12 + State11 --> State12 : to State 12 State12 --> State11 } -- @@ -251,13 +251,13 @@ class ExportToPlantUmlTest : StringSpec({ "plantUml export parallel states" { val machine = createTestStateMachine(coroutineStarterType, name = "Parallel states") { - initialState("parallel states", "parallel states",ChildMode.PARALLEL) { - state("State1") { + initialState("parallel states", null, ChildMode.PARALLEL) { + state("State1", umlLabel("State 1")) { val state11 = initialState("State11") val state12 = state("State12") state11 { - transition { targetState = state12 } + transition(metaInfo = umlLabel("to State 12")) { targetState = state12 } } state12 { transition { targetState = state11 } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt index 81a045b..b2bfd23 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt @@ -86,7 +86,7 @@ class StateMachineTest : StringSpec({ second.onEntry { println("$name entered") } val transition = DefaultTransition( - "transition", "", EventMatcher.isInstanceOf(), TransitionType.LOCAL, first, second + "transition", null, EventMatcher.isInstanceOf(), TransitionType.LOCAL, first, second ) transition.onTriggered { println("${it.transition.name} triggered") } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt index 1228037..a2d0624 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt @@ -1,3 +1,4 @@ +@file:OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) package ru.nsk.kstatemachine import io.mockk.MockKVerificationScope @@ -5,7 +6,9 @@ import io.mockk.clearMocks import io.mockk.mockk import io.mockk.verifySequence import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.newSingleThreadContext import kotlin.coroutines.EmptyCoroutineContext @@ -82,6 +85,7 @@ fun createTestStateMachine( ) = when (coroutineStarterType) { CoroutineStarterType.STD_LIB -> createStdLibStateMachine( name, + null, childMode, start, autoDestroyOnStatesReuse, @@ -92,6 +96,7 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_EMPTY_CONTEXT -> createStateMachineBlocking( CoroutineScope(EmptyCoroutineContext), name, + null, childMode, start, autoDestroyOnStatesReuse, @@ -102,6 +107,7 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_UNCONFINED_DISPATCHER -> createStateMachineBlocking( CoroutineScope(Dispatchers.Unconfined), name, + null, childMode, start, autoDestroyOnStatesReuse, @@ -112,6 +118,7 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_SINGLE_THREAD_DISPATCHER -> createStateMachineBlocking( CoroutineScope(newSingleThreadContext("")), name, + null, childMode, start, autoDestroyOnStatesReuse, @@ -122,6 +129,7 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER -> createStateMachineBlocking( CoroutineScope(Dispatchers.Default.limitedParallelism(1)), name, + null, childMode, start, autoDestroyOnStatesReuse, diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestingStartFromTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestingStartFromTest.kt index 8d4c544..25b5093 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestingStartFromTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestingStartFromTest.kt @@ -57,7 +57,7 @@ class TestingStartFromTest : StringSpec({ initialState("state1") { callbacks.listen(this) } - state2 = dataState("state2", 1) { + state2 = dataState("state2", defaultData = 1) { callbacks.listen(this) } } @@ -72,7 +72,7 @@ class TestingStartFromTest : StringSpec({ initialState("state1") { callbacks.listen(this) } - state2 = dataState("state2", 1) { + state2 = dataState("state2", defaultData = 1) { callbacks.listen(this) } } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TransitionTest.kt index 3b8f0b4..a0fe6f8 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TransitionTest.kt @@ -106,7 +106,7 @@ class TransitionTest : StringSpec({ val machine = createTestStateMachine(coroutineStarterType) { finalState = finalState() initialState { - transition("transition1", finalState) + transition("transition1", targetState = finalState) } } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TypesafeTransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TypesafeTransitionTest.kt index f2acea2..820d235 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TypesafeTransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TypesafeTransitionTest.kt @@ -213,7 +213,7 @@ class TypesafeTransitionTest : StringSpec({ "create self targeted data transition in DataState" { createTestStateMachine(coroutineStarterType) { - initialDataState("state1", 42) { + initialDataState("state1", defaultData = 42) { dataTransition(targetState = this) } } @@ -221,7 +221,7 @@ class TypesafeTransitionTest : StringSpec({ "create self targeted data transition in DataState via builder" { createTestStateMachine(coroutineStarterType) { - initialDataState("state1", 42) { + initialDataState("state1", defaultData = 42) { dataTransition { targetState = this@initialDataState } @@ -231,7 +231,7 @@ class TypesafeTransitionTest : StringSpec({ "create target-less data transition in DataState" { createTestStateMachine(coroutineStarterType) { - initialDataState("state1", 42) { + initialDataState("state1", defaultData = 42) { // this method is only available for DataState dataTransition() } From fdddfe0acb70f07704cdc6cb9056f873a5e29f37 Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Sun, 11 Feb 2024 14:51:56 +0100 Subject: [PATCH 4/7] Deal with negative hash code --- .../src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt | 4 ++-- .../ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt index 5980ca3..7168a00 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt @@ -205,8 +205,8 @@ fun IState.machineOrNull(): StateMachine? = if (this is StateMachine) this else fun IState.state( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, - init: StateBlock? = null, - metaInfo: StateMetaInfo? = null + metaInfo: StateMetaInfo? = null, + init: StateBlock? = null ) = addState(DefaultState(name, childMode, metaInfo), init) inline fun IState.dataState( diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt index 544e780..ce0b42a 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/ExportPlantUmlVisitor.kt @@ -149,7 +149,7 @@ internal class ExportPlantUmlVisitor( const val CHOICE = "<>" fun IState.graphName(): String { - val name = name?.replace(" ", "_") ?: "State${hashCode()}" + val name = (name ?: "State${hashCode()}").replace(Regex("[ -]"), "_") return if (this !is StateMachine) name else "${name}_StateMachine" } From 7f7a7254f35db52206b8703970f068c6cb62c9ab Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Tue, 13 Feb 2024 08:38:57 +0100 Subject: [PATCH 5/7] Review comments addressed --- .../kstatemachine/CoroutinesStateMachine.kt | 4 +-- .../ru/nsk/kstatemachine/BaseStateImpl.kt | 3 +- .../nsk/kstatemachine/CoroutineAbstraction.kt | 2 +- .../ru/nsk/kstatemachine/DefaultDataState.kt | 8 ++--- .../nsk/kstatemachine/DefaultHistoryState.kt | 5 +-- .../ru/nsk/kstatemachine/DefaultState.kt | 10 +++--- .../ru/nsk/kstatemachine/DefaultTransition.kt | 6 ++-- .../kotlin/ru/nsk/kstatemachine/IState.kt | 31 ++++++++-------- .../kotlin/ru/nsk/kstatemachine/MetaInfo.kt | 4 +-- .../ru/nsk/kstatemachine/StateMachine.kt | 2 +- .../ru/nsk/kstatemachine/StateMachineImpl.kt | 6 ++-- .../kotlin/ru/nsk/kstatemachine/Transition.kt | 2 +- .../ru/nsk/kstatemachine/TransitionBuilder.kt | 18 +++++----- .../nsk/kstatemachine/TransitionStateApi.kt | 16 ++++----- .../ru/nsk/samples/ComplexSyntaxSample.kt | 2 +- .../ru/nsk/samples/PlantUmlExportSample.kt | 14 ++++---- .../samples/PlantUmlExportWithLabelsSample.kt | 35 +++++++++++++++++++ .../nsk/kstatemachine/CustomTransitionTest.kt | 2 +- 18 files changed, 103 insertions(+), 67 deletions(-) create mode 100644 samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt diff --git a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt index 1970fc5..45551d2 100644 --- a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt +++ b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt @@ -22,7 +22,7 @@ suspend fun createStateMachine( autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ) = CoroutinesLibCoroutineAbstraction(scope).createStateMachine( name, @@ -43,7 +43,7 @@ fun createStateMachineBlocking( autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ) = with(CoroutinesLibCoroutineAbstraction(scope)) { runBlocking { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt index cf5acd0..6b83d15 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt @@ -10,7 +10,8 @@ import ru.nsk.kstatemachine.TransitionType.EXTERNAL open class BaseStateImpl( override val name: String?, override val childMode: ChildMode, - override val metaInfo: StateMetaInfo?) : InternalState() { + override val metaInfo: MetaInfo? +) : InternalState() { private class Data { val listeners = mutableSetOf() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt index 8d78b59..9bb9900 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt @@ -49,7 +49,7 @@ suspend fun CoroutineAbstraction.createStateMachine( autoDestroyOnStatesReuse: Boolean, enableUndo: Boolean, doNotThrowOnMultipleTransitionsMatch: Boolean, - metaInfo: StateMetaInfo?, + metaInfo: MetaInfo?, init: suspend BuildingStateMachine.() -> Unit ): StateMachine = StateMachineImpl( name, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt index d854647..b849500 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt @@ -8,7 +8,7 @@ inline fun defaultDataState( defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, ) = DefaultDataState(name, defaultData, childMode, dataExtractor, metaInfo) open class DefaultDataState( @@ -16,7 +16,7 @@ open class DefaultDataState( override val defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, private val dataExtractor: DataExtractor, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, ) : BaseStateImpl(name, childMode, metaInfo), DataState { private var _data: D? = null override val data: D get() = checkNotNull(_data) { "Data is not set. Is $this state active?" } @@ -76,12 +76,12 @@ inline fun defaultFinalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, ): DefaultFinalDataState = DefaultFinalDataState(name, defaultData, dataExtractor, metaInfo) open class DefaultFinalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor, - metaInfo: StateMetaInfo? = null + metaInfo: MetaInfo? = null ) : DefaultDataState(name, defaultData, EXCLUSIVE, dataExtractor, metaInfo), FinalDataState diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt index b71af25..e86495f 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultHistoryState.kt @@ -6,8 +6,9 @@ package ru.nsk.kstatemachine open class DefaultHistoryState( name: String? = null, private var _defaultState: IState? = null, - final override val historyType: HistoryType = HistoryType.SHALLOW -) : BasePseudoState(name, null), HistoryState { + final override val historyType: HistoryType = HistoryType.SHALLOW, + metaInfo: MetaInfo? = null +) : BasePseudoState(name, metaInfo), HistoryState { override val defaultState get() = checkNotNull(_defaultState) { "Internal error, default state is not set" } private var _storedState: IState? = null diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt index dfa4c9a..c041b6a 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultState.kt @@ -8,17 +8,17 @@ import ru.nsk.kstatemachine.ChildMode.EXCLUSIVE open class DefaultState( name: String? = null, childMode: ChildMode = EXCLUSIVE, - metaInfo: StateMetaInfo? = null + metaInfo: MetaInfo? = null ) : BaseStateImpl(name, childMode, metaInfo), State open class DefaultFinalState( name: String? = null, - metaInfo: StateMetaInfo? = null + metaInfo: MetaInfo? = null ) : DefaultState(name, metaInfo = metaInfo), FinalState open class DefaultChoiceState( name: String? = null, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, private val choiceAction: suspend EventAndArgument<*>.() -> State ) : BasePseudoState(name, metaInfo), RedirectPseudoState { @@ -28,7 +28,7 @@ open class DefaultChoiceState( open class DefaultChoiceDataState( name: String? = null, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, private val choiceAction: suspend EventAndArgument<*>.() -> DataState, ) : DataState, BasePseudoState(name, metaInfo), RedirectPseudoState { @@ -40,7 +40,7 @@ open class DefaultChoiceDataState( override val lastData: D get() = error("PseudoState $this can not have lastData") } -open class BasePseudoState(name: String?, metaInfo: StateMetaInfo?) : BaseStateImpl(name, EXCLUSIVE, metaInfo), PseudoState { +open class BasePseudoState(name: String?, metaInfo: MetaInfo?) : BaseStateImpl(name, EXCLUSIVE, metaInfo), PseudoState { override suspend fun doEnter(transitionParams: TransitionParams<*>) = internalError() override suspend fun doExit(transitionParams: TransitionParams<*>) = internalError() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt index 86962cf..ff556ba 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt @@ -5,7 +5,7 @@ open class DefaultTransition( override val eventMatcher: EventMatcher, override val type: TransitionType, sourceState: IState, - override val metaInfo: TransitionMetaInfo?, + override val metaInfo: MetaInfo?, ) : InternalTransition { constructor( name: String?, @@ -13,7 +13,7 @@ open class DefaultTransition( type: TransitionType, sourceState: IState, targetState: IState?, - metaInfo: TransitionMetaInfo? + metaInfo: MetaInfo? ) : this(name, eventMatcher, type, sourceState, metaInfo) { targetStateDirectionProducer = { it.targetStateOrStay(targetState) } } @@ -24,7 +24,7 @@ open class DefaultTransition( type: TransitionType, sourceState: IState, targetStateDirectionProducer: TransitionDirectionProducer, - metaInfo: TransitionMetaInfo? + metaInfo: MetaInfo? ) : this(name, eventMatcher, type, sourceState,metaInfo) { this.targetStateDirectionProducer = targetStateDirectionProducer } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt index 7168a00..b8662b5 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt @@ -13,7 +13,7 @@ import kotlin.reflect.KClass @StateMachineDslMarker interface IState : TransitionStateApi, VisitorAcceptor { val name: String? - val metaInfo: StateMetaInfo? + val metaInfo: MetaInfo? val states: Set val initialState: IState? val parent: IState? @@ -205,7 +205,7 @@ fun IState.machineOrNull(): StateMachine? = if (this is StateMachine) this else fun IState.state( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, init: StateBlock? = null ) = addState(DefaultState(name, childMode, metaInfo), init) @@ -214,7 +214,7 @@ inline fun IState.dataState( defaultData: D? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null ) = addState(defaultDataState(name, defaultData, childMode, dataExtractor, metaInfo), init) @@ -224,7 +224,7 @@ inline fun IState.dataState( fun IState.initialState( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, init: StateBlock? = null ) = addInitialState(DefaultState(name, childMode, metaInfo), init) @@ -236,7 +236,7 @@ inline fun IState.initialDataState( defaultData: D, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null ) = addInitialState(defaultDataState(name, defaultData, childMode, dataExtractor, metaInfo), init) @@ -256,17 +256,17 @@ fun IState.addInitialState(state: S, init: StateBlock? = null): fun IState.addFinalState(state: S, init: StateBlock? = null) = addState(state, init) -fun IState.finalState(name: String? = null, metaInfo: StateMetaInfo? = null, init: StateBlock? = null) = +fun IState.finalState(name: String? = null, metaInfo: MetaInfo? = null, init: StateBlock? = null) = addFinalState(DefaultFinalState(name, metaInfo), init) -fun IState.initialFinalState(name: String? = null, metaInfo: StateMetaInfo? = null, init: StateBlock? = null) = +fun IState.initialFinalState(name: String? = null, metaInfo: MetaInfo? = null, init: StateBlock? = null) = addInitialState(DefaultFinalState(name, metaInfo), init) inline fun IState.finalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null ) = addFinalState(defaultFinalDataState(name, defaultData, dataExtractor, metaInfo), init) @@ -274,36 +274,37 @@ inline fun IState.initialFinalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null ) = addInitialState(defaultFinalDataState(name, defaultData, dataExtractor, metaInfo), init) fun IState.choiceState( name: String? = null, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> State ) = addState(DefaultChoiceState(name, metaInfo, choiceAction)) fun IState.initialChoiceState( name: String? = null, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> State ) = addInitialState(DefaultChoiceState(name, metaInfo, choiceAction)) fun IState.choiceDataState( name: String? = null, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState ) = addState(DefaultChoiceDataState(name, metaInfo, choiceAction)) fun IState.initialChoiceDataState( name: String? = null, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState ) = addInitialState(DefaultChoiceDataState(name, metaInfo, choiceAction)) fun IState.historyState( name: String? = null, defaultState: IState? = null, - historyType: HistoryType = HistoryType.SHALLOW -) = addState(DefaultHistoryState(name, defaultState, historyType)) \ No newline at end of file + historyType: HistoryType = HistoryType.SHALLOW, + metaInfo: MetaInfo? = null +) = addState(DefaultHistoryState(name, defaultState, historyType, metaInfo)) \ No newline at end of file diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt index d5c319b..aa72e4b 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt @@ -1,9 +1,7 @@ package ru.nsk.kstatemachine sealed interface MetaInfo -interface StateMetaInfo : MetaInfo -interface TransitionMetaInfo : MetaInfo -interface UmlMetaInfo: StateMetaInfo, TransitionMetaInfo { +interface UmlMetaInfo: MetaInfo { val umlLabel: String } fun umlLabel(label: String) = object : UmlMetaInfo { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt index c098e8c..74f1d95 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt @@ -217,7 +217,7 @@ fun createStdLibStateMachine( autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, - metaInfo: StateMetaInfo? = null, + metaInfo: MetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ): StateMachine { return with(StdLibCoroutineAbstraction()) { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt index c4aef7f..c4deceb 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt @@ -7,7 +7,7 @@ import ru.nsk.kstatemachine.visitors.CleanupVisitor /** * Defines state machine API for internal library usage. */ -abstract class InternalStateMachine(name: String?, metaInfo: StateMetaInfo?, childMode: ChildMode) : +abstract class InternalStateMachine(name: String?, childMode: ChildMode, metaInfo: MetaInfo?) : BuildingStateMachine, DefaultState(name, childMode, metaInfo) { internal abstract suspend fun startFrom(state: IState, argument: Any?) internal abstract suspend fun startFrom(state: DataState, data: D, argument: Any?) @@ -21,8 +21,8 @@ internal class StateMachineImpl( override val isUndoEnabled: Boolean, override val doNotThrowOnMultipleTransitionsMatch: Boolean, override val coroutineAbstraction: CoroutineAbstraction, - metaInfo: StateMetaInfo?, -) : InternalStateMachine(name, metaInfo, childMode) { + metaInfo: MetaInfo?, +) : InternalStateMachine(name, childMode, metaInfo) { private val _machineListeners = mutableSetOf() override val machineListeners: Collection get() = _machineListeners override var logger: StateMachine.Logger = StateMachine.Logger {} diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt index 654186b..2697ec4 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/Transition.kt @@ -11,7 +11,7 @@ import ru.nsk.kstatemachine.visitors.VisitorAcceptor */ interface Transition : VisitorAcceptor { val name: String? - val metaInfo: TransitionMetaInfo? + val metaInfo: MetaInfo? val eventMatcher: EventMatcher val sourceState: IState val type: TransitionType diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt index 04bcae9..a9f17d3 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt @@ -3,7 +3,7 @@ package ru.nsk.kstatemachine import ru.nsk.kstatemachine.TransitionDirectionProducerPolicy.* @StateMachineDslMarker -abstract class TransitionBuilder(protected val name: String?, protected val metaInfo: TransitionMetaInfo?, protected val sourceState: IState) { +abstract class TransitionBuilder(protected val name: String?, protected val metaInfo: MetaInfo?, protected val sourceState: IState) { val listeners = mutableListOf() lateinit var eventMatcher: EventMatcher var type = TransitionType.LOCAL @@ -11,12 +11,12 @@ abstract class TransitionBuilder(protected val name: String?, protect abstract fun build(): Transition } -abstract class BaseGuardedTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +abstract class BaseGuardedTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : TransitionBuilder(name, metaInfo, sourceState) { var guard: suspend EventAndArgument.() -> Boolean = { true } } -abstract class GuardedTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +abstract class GuardedTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { var targetState: S? = null @@ -39,7 +39,7 @@ abstract class GuardedTransitionBuilder(name: String?, me } } -abstract class GuardedTransitionOnBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +abstract class GuardedTransitionOnBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { lateinit var targetState: suspend EventAndArgument.() -> S @@ -62,7 +62,7 @@ abstract class GuardedTransitionOnBuilder(name: String?, } } -class ConditionalTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +class ConditionalTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : TransitionBuilder(name, metaInfo, sourceState) { lateinit var direction: suspend EventAndArgument.() -> TransitionDirection @@ -84,16 +84,16 @@ class ConditionalTransitionBuilder(name: String?, metaInfo: Transitio /** * Any [Event] (with any data) can lead to [State] */ -class UnitGuardedTransitionBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +class UnitGuardedTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : GuardedTransitionBuilder(name, metaInfo, sourceState) -class UnitGuardedTransitionOnBuilder(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +class UnitGuardedTransitionOnBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : GuardedTransitionOnBuilder(name, metaInfo, sourceState) /** * Type safe argument transition builder */ -class DataGuardedTransitionBuilder, D : Any>(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +class DataGuardedTransitionBuilder, D : Any>(name: String?, metaInfo: MetaInfo?, sourceState: IState) : BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { /** User should initialize this filed */ lateinit var targetState: DataState @@ -121,7 +121,7 @@ class DataGuardedTransitionBuilder, D : Any>(name: String?, met /** * Type safe argument transitionOn builder */ -class DataGuardedTransitionOnBuilder, D : Any>(name: String?, metaInfo: TransitionMetaInfo?, sourceState: IState) : +class DataGuardedTransitionOnBuilder, D : Any>(name: String?, metaInfo: MetaInfo?, sourceState: IState) : GuardedTransitionOnBuilder>(name, metaInfo, sourceState) inline fun TransitionBuilder.onTriggered( diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt index 5a617f5..217012d 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt @@ -51,7 +51,7 @@ inline fun TransitionStateApi.transition( name: String? = null, targetState: State? = null, type: TransitionType = LOCAL, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, ): Transition = addTransition(DefaultTransition( name, matcherForEvent(asState()), @@ -69,7 +69,7 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transition( name: String? = null, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, block: UnitGuardedTransitionBuilder.() -> Unit, ): Transition { val builder = UnitGuardedTransitionBuilder(name, metaInfo, asState()).apply { @@ -89,7 +89,7 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transitionOn( name: String? = null, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, block: UnitGuardedTransitionOnBuilder.() -> Unit, ): Transition { val builder = UnitGuardedTransitionOnBuilder(name, metaInfo, asState()).apply { @@ -105,7 +105,7 @@ inline fun TransitionStateApi.transitionOn( */ inline fun TransitionStateApi.transitionConditionally( name: String? = null, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, block: ConditionalTransitionBuilder.() -> Unit, ): Transition { val builder = ConditionalTransitionBuilder(name, metaInfo, asState()).apply { @@ -124,7 +124,7 @@ inline fun , D : Any> TransitionStateApi.dataTransition name: String? = null, targetState: DataState, type: TransitionType = LOCAL, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, ): Transition { return addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), targetState, metaInfo)) } @@ -135,7 +135,7 @@ inline fun , D : Any> TransitionStateApi.dataTransition inline fun , D : Any> DataTransitionStateApi.dataTransition( name: String? = null, type: TransitionType = LOCAL, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, ): Transition { return addTransition(DefaultTransition(name, matcherForEvent(asState()), type, asState(), null, metaInfo)) } @@ -145,7 +145,7 @@ inline fun , D : Any> DataTransitionStateApi.dataTra */ inline fun , D : Any> TransitionStateApi.dataTransition( name: String? = null, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, block: DataGuardedTransitionBuilder.() -> Unit, ): Transition { val builder = DataGuardedTransitionBuilder(name, metaInfo, asState()).apply { @@ -160,7 +160,7 @@ inline fun , D : Any> TransitionStateApi.dataTransition */ inline fun , D : Any> TransitionStateApi.dataTransitionOn( name: String? = null, - metaInfo: TransitionMetaInfo? = null, + metaInfo: MetaInfo? = null, block: DataGuardedTransitionOnBuilder.() -> Unit, ): Transition { val builder = DataGuardedTransitionOnBuilder(name, metaInfo, asState()).apply { diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt index 4373c01..dfa9459 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt @@ -20,7 +20,7 @@ private object ComplexSyntaxSample { fun main() = runBlocking { val machine = createStateMachine( this, // coroutine scope used for this machine - name = "Traffic lights" // StateMachine name is optional + "Traffic lights" // StateMachine name is optional ) { // Create and setup states val greenState = initialState("Green") // State name is optional diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt index 6660096..d4583ed 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportSample.kt @@ -10,15 +10,15 @@ private object PlantUmlExportSample { } fun main() = runBlocking { - val machine = createStateMachine(this, metaInfo = umlLabel("Nested states")) { - val state1 = initialState("State1", metaInfo = umlLabel("State 1")) - val state3 = finalState("State3", metaInfo = umlLabel("State 3")) + val machine = createStateMachine(this, "Nested states") { + val state1 = initialState("State1") + val state3 = finalState("State3") - val state2 = state("State2", metaInfo = umlLabel("State 2")) { - transition(metaInfo = umlLabel("That's all")) { targetState = state3 } - transition(metaInfo = umlLabel("back to State 1")) { targetState = state1 } + val state2 = state("State2") { + transition { targetState = state3 } + transition("back") { targetState = state1 } - val finalSubState = finalState(metaInfo = umlLabel("Final sub state")) + val finalSubState = finalState("Final subState") initialState("Initial subState") { transition { targetState = finalSubState } } diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt new file mode 100644 index 0000000..b27c88f --- /dev/null +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt @@ -0,0 +1,35 @@ +package ru.nsk.samples + +import kotlinx.coroutines.runBlocking +import ru.nsk.kstatemachine.* +import ru.nsk.kstatemachine.visitors.exportToPlantUml +import ru.nsk.samples.PlantUmlExportWithLabelsSample.SwitchEvent + +private object PlantUmlExportWithLabelsSample { + object SwitchEvent : Event +} + +fun main() = runBlocking { + val machine = createStateMachine(this, metaInfo = umlLabel("Nested states")) { + val state1 = initialState("State1", metaInfo = umlLabel("State 1")) + val state3 = finalState("State3", metaInfo = umlLabel("State 3")) + + val state2 = state("State2", metaInfo = umlLabel("State 2")) { + transition(metaInfo = umlLabel("That's all")) { targetState = state3 } + transition(metaInfo = umlLabel("back to State 1")) { targetState = state1 } + + val finalSubState = finalState(metaInfo = umlLabel("Final sub state")) + initialState("Initial subState") { + transition { targetState = finalSubState } + } + } + + state1 { + transition("to ${state2.name}") { targetState = state2 } + transition { targetState = this@state1 } + transition() + } + } + + println(machine.exportToPlantUml()) +} diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt index d488221..ba0e2fd 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/CustomTransitionTest.kt @@ -9,7 +9,7 @@ import ru.nsk.kstatemachine.EventMatcher.Companion.isInstanceOf private object CustomTransitionTestData { class CustomEvent(val value: Int) : Event - class CustomTransition(name: String, metaInfo: TransitionMetaInfo?, sourceState: IState, targetState: IState) : + class CustomTransition(name: String, metaInfo: MetaInfo?, sourceState: IState, targetState: IState) : DefaultTransition(name, isInstanceOf(), TransitionType.LOCAL, sourceState, targetState, metaInfo) { override suspend fun isMatchingEvent(event: Event): Boolean { return super.isMatchingEvent(event) && event is CustomEvent && event.value == 42 From 7665caf7050d8733d4cc30c1b1b49cfb4ff62969 Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Tue, 13 Feb 2024 08:56:22 +0100 Subject: [PATCH 6/7] Make MetaInfo optionally set in builders --- .../kstatemachine/CoroutinesStateMachine.kt | 4 -- .../ru/nsk/kstatemachine/BaseStateImpl.kt | 2 +- .../nsk/kstatemachine/CoroutineAbstraction.kt | 2 - .../ru/nsk/kstatemachine/DefaultDataState.kt | 6 +- .../ru/nsk/kstatemachine/DefaultTransition.kt | 2 +- .../kotlin/ru/nsk/kstatemachine/IState.kt | 44 ++++++-------- .../ru/nsk/kstatemachine/StateMachine.kt | 2 - .../ru/nsk/kstatemachine/StateMachineImpl.kt | 8 +-- .../ru/nsk/kstatemachine/TransitionBuilder.kt | 35 +++++------ .../nsk/kstatemachine/TransitionStateApi.kt | 15 ++--- .../samples/PlantUmlExportWithLabelsSample.kt | 35 ----------- .../PlantUmlExportWithMetaInfoSample.kt | 59 +++++++++++++++++++ .../nsk/kstatemachine/ExportToPlantUmlTest.kt | 10 +++- .../ru/nsk/kstatemachine/StateMachineTest.kt | 2 +- .../kotlin/ru/nsk/kstatemachine/TestUtils.kt | 5 -- 15 files changed, 115 insertions(+), 116 deletions(-) delete mode 100644 samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt create mode 100644 samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt diff --git a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt index 45551d2..6eca153 100644 --- a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt +++ b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutinesStateMachine.kt @@ -22,7 +22,6 @@ suspend fun createStateMachine( autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, - metaInfo: MetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ) = CoroutinesLibCoroutineAbstraction(scope).createStateMachine( name, @@ -31,7 +30,6 @@ suspend fun createStateMachine( autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, - metaInfo, init ) @@ -43,7 +41,6 @@ fun createStateMachineBlocking( autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, - metaInfo: MetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ) = with(CoroutinesLibCoroutineAbstraction(scope)) { runBlocking { @@ -54,7 +51,6 @@ fun createStateMachineBlocking( autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, - metaInfo, init ) } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt index 6b83d15..a6ad1ee 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt @@ -10,7 +10,7 @@ import ru.nsk.kstatemachine.TransitionType.EXTERNAL open class BaseStateImpl( override val name: String?, override val childMode: ChildMode, - override val metaInfo: MetaInfo? + override var metaInfo: MetaInfo? = null ) : InternalState() { private class Data { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt index 9bb9900..3d19adf 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/CoroutineAbstraction.kt @@ -49,7 +49,6 @@ suspend fun CoroutineAbstraction.createStateMachine( autoDestroyOnStatesReuse: Boolean, enableUndo: Boolean, doNotThrowOnMultipleTransitionsMatch: Boolean, - metaInfo: MetaInfo?, init: suspend BuildingStateMachine.() -> Unit ): StateMachine = StateMachineImpl( name, @@ -58,7 +57,6 @@ suspend fun CoroutineAbstraction.createStateMachine( enableUndo, doNotThrowOnMultipleTransitionsMatch, this, - metaInfo, ).apply { init() if (start) start() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt index b849500..7ada076 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultDataState.kt @@ -8,8 +8,7 @@ inline fun defaultDataState( defaultData: D? = null, childMode: ChildMode = EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: MetaInfo? = null, -) = DefaultDataState(name, defaultData, childMode, dataExtractor, metaInfo) +) = DefaultDataState(name, defaultData, childMode, dataExtractor) open class DefaultDataState( name: String? = null, @@ -76,8 +75,7 @@ inline fun defaultFinalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: MetaInfo? = null, -): DefaultFinalDataState = DefaultFinalDataState(name, defaultData, dataExtractor, metaInfo) +): DefaultFinalDataState = DefaultFinalDataState(name, defaultData, dataExtractor) open class DefaultFinalDataState( name: String? = null, diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt index ff556ba..e154ff3 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/DefaultTransition.kt @@ -25,7 +25,7 @@ open class DefaultTransition( sourceState: IState, targetStateDirectionProducer: TransitionDirectionProducer, metaInfo: MetaInfo? - ) : this(name, eventMatcher, type, sourceState,metaInfo) { + ) : this(name, eventMatcher, type, sourceState, metaInfo) { this.targetStateDirectionProducer = targetStateDirectionProducer } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt index b8662b5..c782d50 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/IState.kt @@ -13,7 +13,6 @@ import kotlin.reflect.KClass @StateMachineDslMarker interface IState : TransitionStateApi, VisitorAcceptor { val name: String? - val metaInfo: MetaInfo? val states: Set val initialState: IState? val parent: IState? @@ -23,6 +22,8 @@ interface IState : TransitionStateApi, VisitorAcceptor { val listeners: Collection val childMode: ChildMode + var metaInfo: MetaInfo? + fun addListener(listener: L): L fun removeListener(listener: Listener) @@ -205,18 +206,16 @@ fun IState.machineOrNull(): StateMachine? = if (this is StateMachine) this else fun IState.state( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, - metaInfo: MetaInfo? = null, init: StateBlock? = null -) = addState(DefaultState(name, childMode, metaInfo), init) +) = addState(DefaultState(name, childMode), init) inline fun IState.dataState( name: String? = null, defaultData: D? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null -) = addState(defaultDataState(name, defaultData, childMode, dataExtractor, metaInfo), init) +) = addState(defaultDataState(name, defaultData, childMode, dataExtractor), init) /** * A shortcut for [state] and [IState.setInitialState] calls @@ -224,9 +223,8 @@ inline fun IState.dataState( fun IState.initialState( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, - metaInfo: MetaInfo? = null, init: StateBlock? = null -) = addInitialState(DefaultState(name, childMode, metaInfo), init) +) = addInitialState(DefaultState(name, childMode), init) /** * @param defaultData is necessary for initial [DataState] @@ -236,9 +234,8 @@ inline fun IState.initialDataState( defaultData: D, childMode: ChildMode = ChildMode.EXCLUSIVE, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null -) = addInitialState(defaultDataState(name, defaultData, childMode, dataExtractor, metaInfo), init) +) = addInitialState(defaultDataState(name, defaultData, childMode, dataExtractor), init) /** * A shortcut for [IState.addState] and [IState.setInitialState] calls @@ -256,55 +253,48 @@ fun IState.addInitialState(state: S, init: StateBlock? = null): fun IState.addFinalState(state: S, init: StateBlock? = null) = addState(state, init) -fun IState.finalState(name: String? = null, metaInfo: MetaInfo? = null, init: StateBlock? = null) = - addFinalState(DefaultFinalState(name, metaInfo), init) +fun IState.finalState(name: String? = null, init: StateBlock? = null) = + addFinalState(DefaultFinalState(name), init) -fun IState.initialFinalState(name: String? = null, metaInfo: MetaInfo? = null, init: StateBlock? = null) = - addInitialState(DefaultFinalState(name, metaInfo), init) +fun IState.initialFinalState(name: String? = null, init: StateBlock? = null) = + addInitialState(DefaultFinalState(name), init) inline fun IState.finalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null -) = addFinalState(defaultFinalDataState(name, defaultData, dataExtractor, metaInfo), init) +) = addFinalState(defaultFinalDataState(name, defaultData, dataExtractor), init) inline fun IState.initialFinalDataState( name: String? = null, defaultData: D? = null, dataExtractor: DataExtractor = defaultDataExtractor(), - metaInfo: MetaInfo? = null, noinline init: StateBlock>? = null -) = addInitialState(defaultFinalDataState(name, defaultData, dataExtractor, metaInfo), init) +) = addInitialState(defaultFinalDataState(name, defaultData, dataExtractor), init) fun IState.choiceState( name: String? = null, - metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> State -) = addState(DefaultChoiceState(name, metaInfo, choiceAction)) +) = addState(DefaultChoiceState(name, choiceAction = choiceAction)) fun IState.initialChoiceState( name: String? = null, - metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> State -) = addInitialState(DefaultChoiceState(name, metaInfo, choiceAction)) +) = addInitialState(DefaultChoiceState(name, choiceAction = choiceAction)) fun IState.choiceDataState( name: String? = null, - metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState -) = addState(DefaultChoiceDataState(name, metaInfo, choiceAction)) +) = addState(DefaultChoiceDataState(name, choiceAction = choiceAction)) fun IState.initialChoiceDataState( name: String? = null, - metaInfo: MetaInfo? = null, choiceAction: suspend EventAndArgument<*>.() -> DataState -) = addInitialState(DefaultChoiceDataState(name, metaInfo, choiceAction)) +) = addInitialState(DefaultChoiceDataState(name, choiceAction = choiceAction)) fun IState.historyState( name: String? = null, defaultState: IState? = null, historyType: HistoryType = HistoryType.SHALLOW, - metaInfo: MetaInfo? = null -) = addState(DefaultHistoryState(name, defaultState, historyType, metaInfo)) \ No newline at end of file +) = addState(DefaultHistoryState(name, defaultState, historyType)) \ No newline at end of file diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt index 74f1d95..ec24916 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt @@ -217,7 +217,6 @@ fun createStdLibStateMachine( autoDestroyOnStatesReuse: Boolean = true, enableUndo: Boolean = false, doNotThrowOnMultipleTransitionsMatch: Boolean = false, - metaInfo: MetaInfo? = null, init: suspend BuildingStateMachine.() -> Unit ): StateMachine { return with(StdLibCoroutineAbstraction()) { @@ -229,7 +228,6 @@ fun createStdLibStateMachine( autoDestroyOnStatesReuse, enableUndo, doNotThrowOnMultipleTransitionsMatch, - metaInfo, init ) } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt index c4deceb..4c97bc2 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt @@ -7,8 +7,8 @@ import ru.nsk.kstatemachine.visitors.CleanupVisitor /** * Defines state machine API for internal library usage. */ -abstract class InternalStateMachine(name: String?, childMode: ChildMode, metaInfo: MetaInfo?) : - BuildingStateMachine, DefaultState(name, childMode, metaInfo) { +abstract class InternalStateMachine(name: String?, childMode: ChildMode) : + BuildingStateMachine, DefaultState(name, childMode) { internal abstract suspend fun startFrom(state: IState, argument: Any?) internal abstract suspend fun startFrom(state: DataState, data: D, argument: Any?) internal abstract fun delayListenerException(exception: Exception) @@ -21,8 +21,7 @@ internal class StateMachineImpl( override val isUndoEnabled: Boolean, override val doNotThrowOnMultipleTransitionsMatch: Boolean, override val coroutineAbstraction: CoroutineAbstraction, - metaInfo: MetaInfo?, -) : InternalStateMachine(name, childMode, metaInfo) { +) : InternalStateMachine(name, childMode) { private val _machineListeners = mutableSetOf() override val machineListeners: Collection get() = _machineListeners override var logger: StateMachine.Logger = StateMachine.Logger {} @@ -31,6 +30,7 @@ internal class StateMachineImpl( override var listenerExceptionHandler = StateMachine.ListenerExceptionHandler { throw it } private var _isDestroyed: Boolean = false override val isDestroyed get() = _isDestroyed + override var metaInfo: MetaInfo? = null init { transitionConditionally("start transition") { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt index a9f17d3..d649788 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionBuilder.kt @@ -3,21 +3,22 @@ package ru.nsk.kstatemachine import ru.nsk.kstatemachine.TransitionDirectionProducerPolicy.* @StateMachineDslMarker -abstract class TransitionBuilder(protected val name: String?, protected val metaInfo: MetaInfo?, protected val sourceState: IState) { +abstract class TransitionBuilder(protected val name: String?, protected val sourceState: IState) { val listeners = mutableListOf() lateinit var eventMatcher: EventMatcher var type = TransitionType.LOCAL + var metaInfo: MetaInfo? = null abstract fun build(): Transition } -abstract class BaseGuardedTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - TransitionBuilder(name, metaInfo, sourceState) { +abstract class BaseGuardedTransitionBuilder(name: String?, sourceState: IState) : + TransitionBuilder(name, sourceState) { var guard: suspend EventAndArgument.() -> Boolean = { true } } -abstract class GuardedTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { +abstract class GuardedTransitionBuilder(name: String?, sourceState: IState) : + BaseGuardedTransitionBuilder(name, sourceState) { var targetState: S? = null override fun build(): Transition { @@ -39,8 +40,8 @@ abstract class GuardedTransitionBuilder(name: String?, me } } -abstract class GuardedTransitionOnBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { +abstract class GuardedTransitionOnBuilder(name: String?, sourceState: IState) : + BaseGuardedTransitionBuilder(name, sourceState) { lateinit var targetState: suspend EventAndArgument.() -> S override fun build(): Transition { @@ -62,8 +63,8 @@ abstract class GuardedTransitionOnBuilder(name: String?, } } -class ConditionalTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - TransitionBuilder(name, metaInfo, sourceState) { +class ConditionalTransitionBuilder(name: String?, sourceState: IState) : + TransitionBuilder(name, sourceState) { lateinit var direction: suspend EventAndArgument.() -> TransitionDirection override fun build(): Transition { @@ -84,17 +85,17 @@ class ConditionalTransitionBuilder(name: String?, metaInfo: MetaInfo? /** * Any [Event] (with any data) can lead to [State] */ -class UnitGuardedTransitionBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - GuardedTransitionBuilder(name, metaInfo, sourceState) +class UnitGuardedTransitionBuilder(name: String?, sourceState: IState) : + GuardedTransitionBuilder(name, sourceState) -class UnitGuardedTransitionOnBuilder(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - GuardedTransitionOnBuilder(name, metaInfo, sourceState) +class UnitGuardedTransitionOnBuilder(name: String?, sourceState: IState) : + GuardedTransitionOnBuilder(name, sourceState) /** * Type safe argument transition builder */ -class DataGuardedTransitionBuilder, D : Any>(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - BaseGuardedTransitionBuilder(name, metaInfo, sourceState) { +class DataGuardedTransitionBuilder, D : Any>(name: String?, sourceState: IState) : + BaseGuardedTransitionBuilder(name, sourceState) { /** User should initialize this filed */ lateinit var targetState: DataState @@ -121,8 +122,8 @@ class DataGuardedTransitionBuilder, D : Any>(name: String?, met /** * Type safe argument transitionOn builder */ -class DataGuardedTransitionOnBuilder, D : Any>(name: String?, metaInfo: MetaInfo?, sourceState: IState) : - GuardedTransitionOnBuilder>(name, metaInfo, sourceState) +class DataGuardedTransitionOnBuilder, D : Any>(name: String?, sourceState: IState) : + GuardedTransitionOnBuilder>(name, sourceState) inline fun TransitionBuilder.onTriggered( crossinline block: suspend (TransitionParams) -> Unit diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt index 217012d..0914b30 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/TransitionStateApi.kt @@ -69,10 +69,9 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transition( name: String? = null, - metaInfo: MetaInfo? = null, block: UnitGuardedTransitionBuilder.() -> Unit, ): Transition { - val builder = UnitGuardedTransitionBuilder(name, metaInfo, asState()).apply { + val builder = UnitGuardedTransitionBuilder(name, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -89,10 +88,9 @@ inline fun TransitionStateApi.transition( */ inline fun TransitionStateApi.transitionOn( name: String? = null, - metaInfo: MetaInfo? = null, block: UnitGuardedTransitionOnBuilder.() -> Unit, ): Transition { - val builder = UnitGuardedTransitionOnBuilder(name, metaInfo, asState()).apply { + val builder = UnitGuardedTransitionOnBuilder(name, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -105,10 +103,9 @@ inline fun TransitionStateApi.transitionOn( */ inline fun TransitionStateApi.transitionConditionally( name: String? = null, - metaInfo: MetaInfo? = null, block: ConditionalTransitionBuilder.() -> Unit, ): Transition { - val builder = ConditionalTransitionBuilder(name, metaInfo, asState()).apply { + val builder = ConditionalTransitionBuilder(name, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -145,10 +142,9 @@ inline fun , D : Any> DataTransitionStateApi.dataTra */ inline fun , D : Any> TransitionStateApi.dataTransition( name: String? = null, - metaInfo: MetaInfo? = null, block: DataGuardedTransitionBuilder.() -> Unit, ): Transition { - val builder = DataGuardedTransitionBuilder(name, metaInfo, asState()).apply { + val builder = DataGuardedTransitionBuilder(name, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } @@ -160,10 +156,9 @@ inline fun , D : Any> TransitionStateApi.dataTransition */ inline fun , D : Any> TransitionStateApi.dataTransitionOn( name: String? = null, - metaInfo: MetaInfo? = null, block: DataGuardedTransitionOnBuilder.() -> Unit, ): Transition { - val builder = DataGuardedTransitionOnBuilder(name, metaInfo, asState()).apply { + val builder = DataGuardedTransitionOnBuilder(name, asState()).apply { eventMatcher = matcherForEvent(asState()) block() } diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt deleted file mode 100644 index b27c88f..0000000 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithLabelsSample.kt +++ /dev/null @@ -1,35 +0,0 @@ -package ru.nsk.samples - -import kotlinx.coroutines.runBlocking -import ru.nsk.kstatemachine.* -import ru.nsk.kstatemachine.visitors.exportToPlantUml -import ru.nsk.samples.PlantUmlExportWithLabelsSample.SwitchEvent - -private object PlantUmlExportWithLabelsSample { - object SwitchEvent : Event -} - -fun main() = runBlocking { - val machine = createStateMachine(this, metaInfo = umlLabel("Nested states")) { - val state1 = initialState("State1", metaInfo = umlLabel("State 1")) - val state3 = finalState("State3", metaInfo = umlLabel("State 3")) - - val state2 = state("State2", metaInfo = umlLabel("State 2")) { - transition(metaInfo = umlLabel("That's all")) { targetState = state3 } - transition(metaInfo = umlLabel("back to State 1")) { targetState = state1 } - - val finalSubState = finalState(metaInfo = umlLabel("Final sub state")) - initialState("Initial subState") { - transition { targetState = finalSubState } - } - } - - state1 { - transition("to ${state2.name}") { targetState = state2 } - transition { targetState = this@state1 } - transition() - } - } - - println(machine.exportToPlantUml()) -} diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt new file mode 100644 index 0000000..33858d1 --- /dev/null +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/PlantUmlExportWithMetaInfoSample.kt @@ -0,0 +1,59 @@ +package ru.nsk.samples + +import kotlinx.coroutines.runBlocking +import ru.nsk.kstatemachine.* +import ru.nsk.kstatemachine.visitors.exportToPlantUml +import ru.nsk.samples.PlantUmlExportWithMetaInfoSample.SwitchEvent + +private object PlantUmlExportWithMetaInfoSample { + object SwitchEvent : Event +} + +fun main() = runBlocking { + val machine = createStateMachine(this) { + // label for state machine + metaInfo = umlLabel("Nested states sm") + + val state1 = initialState("State1") { + // label for state + metaInfo = umlLabel("State 1") + } + val state3 = finalState("State3") { + // label for state + metaInfo = umlLabel("State 3") + } + + val state2 = state("State2") { + // label for state + metaInfo = umlLabel("State 2") + transition { + // label for transition + metaInfo = umlLabel("That's all") + targetState = state3 + } + transition { + // label for transition + metaInfo = umlLabel("back to State 1") + targetState = state1 + } + val finalSubState = finalState { + // label for state + metaInfo = umlLabel("Final sub state") + } + initialState("Initial subState") { + transition { targetState = finalSubState } + } + } + + state1 { + transition { + metaInfo = umlLabel("go to ${state2.name}") + targetState = state2 + } + transition { targetState = this@state1 } + transition() + } + } + + println(machine.exportToPlantUml()) +} \ No newline at end of file diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt index 524114a..bb2cd5f 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/ExportToPlantUmlTest.kt @@ -251,13 +251,17 @@ class ExportToPlantUmlTest : StringSpec({ "plantUml export parallel states" { val machine = createTestStateMachine(coroutineStarterType, name = "Parallel states") { - initialState("parallel states", null, ChildMode.PARALLEL) { - state("State1", umlLabel("State 1")) { + initialState("parallel states", ChildMode.PARALLEL) { + state("State1") { + metaInfo = umlLabel("State 1") val state11 = initialState("State11") val state12 = state("State12") state11 { - transition(metaInfo = umlLabel("to State 12")) { targetState = state12 } + transition { + metaInfo = umlLabel("to State 12") + targetState = state12 + } } state12 { transition { targetState = state11 } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt index b2bfd23..cba2c32 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt @@ -86,7 +86,7 @@ class StateMachineTest : StringSpec({ second.onEntry { println("$name entered") } val transition = DefaultTransition( - "transition", null, EventMatcher.isInstanceOf(), TransitionType.LOCAL, first, second + "transition", EventMatcher.isInstanceOf(), TransitionType.LOCAL, first, null ) transition.onTriggered { println("${it.transition.name} triggered") } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt index a2d0624..9e8cf1b 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt @@ -85,7 +85,6 @@ fun createTestStateMachine( ) = when (coroutineStarterType) { CoroutineStarterType.STD_LIB -> createStdLibStateMachine( name, - null, childMode, start, autoDestroyOnStatesReuse, @@ -96,7 +95,6 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_EMPTY_CONTEXT -> createStateMachineBlocking( CoroutineScope(EmptyCoroutineContext), name, - null, childMode, start, autoDestroyOnStatesReuse, @@ -107,7 +105,6 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_UNCONFINED_DISPATCHER -> createStateMachineBlocking( CoroutineScope(Dispatchers.Unconfined), name, - null, childMode, start, autoDestroyOnStatesReuse, @@ -118,7 +115,6 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_SINGLE_THREAD_DISPATCHER -> createStateMachineBlocking( CoroutineScope(newSingleThreadContext("")), name, - null, childMode, start, autoDestroyOnStatesReuse, @@ -129,7 +125,6 @@ fun createTestStateMachine( CoroutineStarterType.COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER -> createStateMachineBlocking( CoroutineScope(Dispatchers.Default.limitedParallelism(1)), name, - null, childMode, start, autoDestroyOnStatesReuse, From 32b29ed1a00c09049d83793d21562cdd16d3cda7 Mon Sep 17 00:00:00 2001 From: Alex Yanyev Date: Fri, 16 Feb 2024 08:29:22 +0100 Subject: [PATCH 7/7] Make MetaInfo not seaaled --- .../src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt index aa72e4b..ecf2154 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/MetaInfo.kt @@ -1,6 +1,6 @@ package ru.nsk.kstatemachine -sealed interface MetaInfo +interface MetaInfo interface UmlMetaInfo: MetaInfo { val umlLabel: String }