All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.jumpco.open.kfsm.kfsm.kt Maven / Gradle / Ivy

There is a newer version: 1.9.0-RC1
Show newest version
/*
 * 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()
}