Skip to content

Commit

Permalink
Add methods for lazy state evaluation to hint api
Browse files Browse the repository at this point in the history
  • Loading branch information
nsk90 committed Dec 16, 2024
1 parent aaa74ad commit fa508f9
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 19 deletions.
4 changes: 3 additions & 1 deletion docs/pages/export.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ transitionConditionally<ValueEvent> {
metaInfo = buildExportMetaInfo {
// the library does not need to call "direction" lambda, this hint provides the result (state1) directly
addStateResolutionHint("when 1", state1)
// same, but uses lazy initialization for state (if the reference is not initializied yet)
addLazyStateResolutionHint("when 1", lazy { state1 } )
// calls "direction" lambda during export with specified Event and optional argument (lambda will return state2)
addEventAndArgumentResolutionHint("when 2", ValueEvent(2))
// you can specify set of states that represents parallel target states
addStateResolutionHint("when 3", setOf(state1, state2))
addParallelStatesResolutionHint("when 3", setOf(state1, state2))
// describes stay() behaviour without calling "direction" lambda
addStateResolutionHint("when 4", this@createStateMachine)
// resolves to stay() by calling "direction" lambda
Expand Down
4 changes: 3 additions & 1 deletion kstatemachine/api/kstatemachine.api
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ public abstract interface class ru/nsk/kstatemachine/metainfo/ExportMetaInfo : r

public abstract interface class ru/nsk/kstatemachine/metainfo/ExportMetaInfoBuilder : ru/nsk/kstatemachine/metainfo/ExportMetaInfo {
public abstract fun addEventAndArgumentResolutionHint (Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;)V
public abstract fun addStateResolutionHint (Ljava/lang/String;Ljava/util/Set;)V
public abstract fun addLazyParallelStatesResolutionHint (Ljava/lang/String;Ljava/util/Set;)V
public abstract fun addLazyStateResolutionHint (Ljava/lang/String;Lkotlin/Lazy;)V
public abstract fun addParallelStatesResolutionHint (Ljava/lang/String;Ljava/util/Set;)V
public abstract fun addStateResolutionHint (Ljava/lang/String;Lru/nsk/kstatemachine/state/IState;)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ru.nsk.kstatemachine.event.Event
import ru.nsk.kstatemachine.state.IState
import ru.nsk.kstatemachine.state.RedirectPseudoState
import ru.nsk.kstatemachine.transition.EventAndArgument
import kotlin.jvm.JvmName

/**
* Hint to be used with [ExportMetaInfo]
Expand All @@ -27,13 +28,15 @@ sealed interface ResolutionHint
internal class StateResolutionHint(
val description: String,
/** Allows to specify parallel target states. Must be non-empty */
val targetStates: Set<IState>,
lazyTargetStates: Set<Lazy<IState>>,
) : ResolutionHint {
init {
require(targetStates.isNotEmpty()) {
require(lazyTargetStates.isNotEmpty()) {
"targetStates must be non-empty, use single state or multiple states for parallel transitions"
}
}

val targetStates: Set<IState> by lazy { lazyTargetStates.map { it.value }.toSet() }
}

/**
Expand Down Expand Up @@ -77,8 +80,24 @@ interface ExportMetaInfoBuilder : ExportMetaInfo {
/** See [StateResolutionHint] */
fun addStateResolutionHint(description: String, targetState: IState)

/**
* Allows to specify state as lazy value.
* Lazy initializer should not be used to dynamically calculate a state.
* Use it only to delay state's variable access.
* See [StateResolutionHint]
*/
fun addLazyStateResolutionHint(description: String, targetState: Lazy<IState>)

/** See [StateResolutionHint] */
fun addStateResolutionHint(description: String, targetStates: Set<IState>)
fun addParallelStatesResolutionHint(description: String, targetStates: Set<IState>)

/**
* Allows to specify parallel states as lazy values.
* Lazy initializer should not be used to dynamically calculate state.
* Use it only to delay state's variable access.
* See [StateResolutionHint]
*/
fun addLazyParallelStatesResolutionHint(description: String, targetStates: Set<Lazy<IState>>)

/** See [EventAndArgumentResolutionHint] */
fun addEventAndArgumentResolutionHint(description: String, event: Event, argument: Any? = null)
Expand All @@ -87,17 +106,22 @@ interface ExportMetaInfoBuilder : ExportMetaInfo {
private data class ExportMetaInfoBuilderImpl(
override val resolutionHints: MutableSet<ResolutionHint> = mutableSetOf<ResolutionHint>(),
) : ExportMetaInfoBuilder {
override fun addEventAndArgumentResolutionHint(description: String, event: Event, argument: Any?) {
resolutionHints += EventAndArgumentResolutionHint(description, event, argument)
}
override fun addStateResolutionHint(description: String, targetState: IState) =
addLazyStateResolutionHint(description, lazyOf(targetState))

override fun addStateResolutionHint(description: String, targetState: IState) {
resolutionHints += StateResolutionHint(description, setOf(targetState))
}
override fun addLazyStateResolutionHint(description: String, targetState: Lazy<IState>) =
addLazyParallelStatesResolutionHint(description, setOf(targetState))

override fun addParallelStatesResolutionHint(description: String, targetStates: Set<IState>) =
addLazyParallelStatesResolutionHint(description, targetStates.map { lazyOf(it) }.toSet())

override fun addStateResolutionHint(description: String, targetStates: Set<IState>) {
override fun addLazyParallelStatesResolutionHint(description: String, targetStates: Set<Lazy<IState>>) {
resolutionHints += StateResolutionHint(description, targetStates)
}

override fun addEventAndArgumentResolutionHint(description: String, event: Event, argument: Any?) {
resolutionHints += EventAndArgumentResolutionHint(description, event, argument)
}
}

fun buildExportMetaInfo(builder: ExportMetaInfoBuilder.() -> Unit): ExportMetaInfo =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ fun main() = runBlocking {
lateinit var state2: State
lateinit var state3: State
val machine = createStateMachine(this) {
state2 = finalState("State2")
state3 = finalState("State3")

initialState("State1") {
transitionOn<FirstEvent> {
metaInfo = buildExportMetaInfo {
Expand All @@ -50,12 +47,15 @@ fun main() = runBlocking {
transitionOn<SecondEvent> {
metaInfo = buildExportMetaInfo {
// using StateResolutionHint does not require targetState lambda call
addStateResolutionHint("data == 123", state2)
addStateResolutionHint("else", state3)
addLazyStateResolutionHint("data == 123", lazy { state2 })
addLazyStateResolutionHint("else", lazy { state3 })
}
targetState = { if (event.data == 123) state2 else state3 }
}
}

state2 = finalState("State2")
state3 = finalState("State3")
}

println(machine.exportToPlantUml(showEventLabels = true, unsafeCallConditionalLambdas = true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ private suspend fun createTestMachine(coroutineStarterType: CoroutineStarterType
metaInfo = buildExportMetaInfo {
addStateResolutionHint("when 1", state1)
addEventAndArgumentResolutionHint("when 2", ValueEvent2(2))
addStateResolutionHint("when 3", setOf(state1, state2))
addParallelStatesResolutionHint("when 3", setOf(state1, state2))
addStateResolutionHint("when 4", this@createTestStateMachine)
addEventAndArgumentResolutionHint("else", ValueEvent2(5))
}
}
val choiceState = choiceState("choiceState") { if (true) state1 else state2 }
choiceState.metaInfo = buildExportMetaInfo {
addStateResolutionHint("if (true)", state1)
addStateResolutionHint(" ", state2)
addLazyStateResolutionHint(" ", lazy { state2 })
}
}
}
Expand Down

0 comments on commit fa508f9

Please sign in to comment.