commonMain.io.jumpco.open.kfsm.kfsm.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kfsm-jvm Show documentation
Show all versions of kfsm-jvm Show documentation
Kotlin Finite-state machine
/*
* Copyright (c) 2019. Open JumpCO
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
package io.jumpco.open.kfsm
import kotlin.reflect.KClass
/**
* This represents an action that may be invoked during a transition.
* @param C The context: C will be available to the lambda.
*/
typealias StateAction = C.(Array) -> Unit
/**
* This represents a guard expression that may be used to select a specific transition.
* @param C The context: C will be available to the lambda
*/
typealias StateGuard = C.(Array) -> Boolean
/**
* This represents an expression to determine the state of the context. This is useful when creating an instance of a
* statemachine multiple times during the lifecycle of a context represented by a persisted or serializable entity.
* @param C The context:C will be available to the lambda.
* @param S The type of the state that should be returned by the lambda.
*/
typealias StateQuery = (C.() -> S)
/**
* Alias for a pair of S, String
*/
typealias StateMapItem = Pair
/**
* Alias for a list of pairs of S,String
*/
typealias StateMapList = List>
/**
* Alias for a lambda that will have the context as this and return a `StateMapList`
*/
typealias StateMapQuery = (C.() -> StateMapList)
/**
* This represents a default action that will be invoked on a change in state as defined with entry or exit.
* @param C The context: C will be available to the lambda
* @param S currentState: S and targetState: S will be available to the lambda.
*/
typealias DefaultChangeAction = C.(S, S, Array) -> Unit
/**
* This represents a default action for a specific event. These action will not cause changes in state.
* They are internal transitions.
* @param C The context: C will be available to the lambda
* @param S The currentState:S will be available to the lambda
* @param E The event: E will be available to the lambda
*/
typealias DefaultStateAction = C.(S, E, Array) -> Unit
/**
* This represents an event and targetState pair that can be written as `event to state`
* @param E The event: E will be the first element of the pair
* @param S The targetState: S will be the second element of the pair.
*/
typealias EventState = Pair
/**
* Represents the state of one or more pushed state maps and can be stored and used to initialise the state machine.
*/
typealias ExternalState = StateMapList
/**
* Represents the different kinds of transitions.
*/
enum class TransitionType {
/**
* Transitions are triggered and may change to a new state or remain at the same state while performing an action.
*/
NORMAL,
/**
* A push transition will place the current state map on a stack and make the named statemap the current map and change to the given state,
*/
PUSH,
/**
* A pop transition will pop the stack and make the transition current. If the pop transition provided a new targetMap or targetState that will result in push or normal transition behaviour.
*/
POP
}
/**
* Defines the start of a state machine DSL declaration
* @param validStates A set of the possible states supported by the top-level state map
* @param eventClass The class of the possible events
* @param contextClass The class of the context
* @sample io.jumpco.open.kfsm.TurnstileFSM.definition
*/
inline fun , C : Any> stateMachine(
validStates: Set,
eventClass: KClass,
contextClass: KClass,
handler: DslStateMachineHandler.() -> Unit
) = StateMachineBuilder(validStates).stateMachine(handler)
/**
* An extension function that evaluates the expression and invokes the provided `block` if true or the `otherwise` block is false.
*/
inline fun T.ifApply(expression: Boolean, block: T.() -> Unit, otherwise: T.() -> Unit): T {
return if (expression) {
this.apply(block)
} else {
this.apply(otherwise)
}
}
/**
* An extension function that evaluates the expression and invokes the provided `block` if true.
*/
inline fun T.ifApply(expression: Boolean, block: T.() -> Unit): T {
if (expression) {
this.apply(block)
}
return this
}
/**
* This class implements a simple generic stack.
*/
class Stack {
private val elements: MutableList = mutableListOf()
/**
* Push an element onto the stack.
* @param value The element will be pushed
*/
fun push(value: T) = elements.add(value)
/**
* Returns and removed the element on the top of the stack.
* @return The element on the top of the stack
* @throws Will throw IllegalStateException is the stack is empty
*/
fun pop(): T {
check(elements.isNotEmpty()) { "stack is empty" }
return elements.removeAt(elements.lastIndex)
}
/**
* @return true if the stack is empty
*/
fun isEmpty() = elements.isEmpty()
/**
*
* @return true if the stack is not empty
*/
fun isNotEmpty() = elements.isNotEmpty()
/**
* @return the element at the top of the stack without removing or `null` is the stack is empty.
*/
fun peek(): T? = elements.lastOrNull()
fun peekContent(): List = elements.toList()
}