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

com.cmt.statemachine.impl.StateMachineImpl Maven / Gradle / Ivy

The newest version!
package com.cmt.statemachine.impl;

import com.cmt.statemachine.State;
import com.cmt.statemachine.StateMachine;
import com.cmt.statemachine.Transition;
import com.cmt.statemachine.Visitor;
import com.cmt.statemachine.util.EventUtil;
import com.cmt.statemachine.util.StateUtil;
import guru.nidi.graphviz.attribute.Color;
import guru.nidi.graphviz.attribute.Label;
import guru.nidi.graphviz.attribute.Style;
import guru.nidi.graphviz.engine.Format;
import guru.nidi.graphviz.engine.Graphviz;
import guru.nidi.graphviz.model.Graph;
import guru.nidi.graphviz.model.Node;

import java.io.File;
import java.io.IOException;
import java.util.*;

import static guru.nidi.graphviz.model.Factory.graph;
import static guru.nidi.graphviz.model.Factory.node;
import static guru.nidi.graphviz.model.Link.to;

/**
 * For performance consideration,
 * The state machine is made "stateless" on purpose.
 * Once it's built, it can be shared by multi-thread
 *
 * One side effect is since the state machine is stateless, we can not get current state from State Machine.
 *
 * @author Frank Zhang
 * @date 2020-02-07 5:40 PM
 */
public class StateMachineImpl implements StateMachine {

    private String machineId;

    private final Map> stateMap;

    private boolean ready;

    private S initialState;

    public StateMachineImpl(Map> stateMap){
        this.stateMap = stateMap;
    }

    @Override
    public  S fireEvent(S sourceStateId, E event, C request){
        isReady();
        Transition transition = routeTransition(sourceStateId, event, request);

        if (transition == null) {
            Debugger.debug("There is no appropriate Transition for " + event);
            return sourceStateId;
        }

        return transition.transit(request).getId();
    }

    @Override
    public  T fireEventWithResult(S sourceStateId, E event, R request) {
        isReady();
        Transition transition = routeTransition(sourceStateId, event, request);

        if (transition == null) {
            Debugger.debug("There is no appropriate Transition for " + event);
            return null;
        }

       return transition.transitWithResult(request);
    }

    @Override
    public  T fireEventWithResult(S sourceStateId, E event, C cond, R request) {
        this.isReady();
        Transition transition = this.routeTransition(sourceStateId, event, cond);
        if (transition == null) {
            Debugger.debug("There is no appropriate Transition for " + event);
            return null;
        } else {
            return transition.transitWithResult(cond,request);
        }
    }

    private  Transition routeTransition(S sourceStateId, E event, R request) {
        State sourceState = getState(sourceStateId);

        List> transitions = sourceState.getTransition(event);

        if (transitions == null || transitions.size() == 0) {
            return null;
        }

        Transition transit = null;
        for (Transition transition: transitions) {
            if (transition.getCondition() == null) {
                transit = transition;
            } else if (transition.getCondition().isSatisfied(request)) {
                transit = transition;
                break;
            }
        }

        return transit;
    }

    private State getState(S currentStateId) {
        State state = StateHelper.getState(stateMap, currentStateId);
        if(state == null){
            showStateMachine();
            throw new StateMachineException(currentStateId + " is not found, please check state machine");
        }
        return state;
    }

    private void isReady() {
        if(!ready){
            throw new StateMachineException("State machine is not built yet, can not work");
        }
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitOnEntry(this);
        for(State state: stateMap.values()){
            state.accept(visitor);
        }
        visitor.visitOnExit(this);
    }

    @Override
    public void showStateMachine() {
        SysOutVisitor sysOutVisitor = new SysOutVisitor();
        accept(sysOutVisitor);
    }

    @Override
    public void generatePlantUML(){
        PlantUMLVisitor plantUMLVisitor = new PlantUMLVisitor();
        accept(plantUMLVisitor);
    }

    @Override
    public S getInitialState() {
        return initialState;
    }

    @Override
    public void generateStateDiagram() {
        HashMap stateNodeMap = new HashMap<>(16);
        stateMap.keySet().forEach(s->{
            stateNodeMap.put(getStateDesc(s), node(getStateDesc(s)).with(Color.BLUE));
        });

        List nodeList = new ArrayList<>();
        stateMap.keySet().stream().forEach(s -> {
            Node sNode = stateNodeMap.get(getStateDesc(s));
            State sStateImpl = stateMap.get(s);
            Collection> sTransitions = sStateImpl.getTransitions();
            sTransitions.forEach(
                    transition -> {
                        nodeList.add(
                                sNode.link(to(stateNodeMap.get(getStateDesc(transition.getTarget().getId()))).with(Style.BOLD,Label.of(EventUtil.getEventDesc(transition.getEvent())), Color.GREEN)));
                    }
            );
        });
        Graph graph = graph("StateDiagram").directed().with(nodeList);
        try {
            Graphviz.fromGraph(graph).width(900).render(Format.PNG).toFile(new File("StateDiagram"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 状态描述字符串
     * @param s 状态
     * @return 如果状态指定了描述字段,则返回状态描述字段值,见 @StateConfig;
     *         反之,则返回 s.toString()
     */
    private String getStateDesc(S s){
        Object obj = StateUtil.getStateDescField(s);
        if (Objects.nonNull(obj) && obj instanceof String) {
            return obj.toString();
        }
        return s.toString();
    }

    @Override
    public String getMachineId() {
        return machineId;
    }

    public void setMachineId(String machineId) {
        this.machineId = machineId;
    }

    /**
     * Verify a state machine model.
     */
    public void verify() {
        if (initialState == null) {
            throw new StateMachineException("The state machine with id ["+machineId+"] has not set initial state");
        }
    }

    public void setReady(boolean ready) {
        this.ready = ready;
    }

    public void setInitialState(S initialState) {
        this.initialState = initialState;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy