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

org.squirrelframework.foundation.fsm.impl.TransitionImpl Maven / Gradle / Ivy

package org.squirrelframework.foundation.fsm.impl;

import org.squirrelframework.foundation.fsm.*;

import java.util.List;

class TransitionImpl, S, E, C> implements MutableTransition {
    
    private ImmutableState sourceState;
    
    private ImmutableState targetState;
    
    private E event;
    
    private final Actions actions = FSM.newActions();
    
    private Condition condition = Conditions.always();
    
    private TransitionType type = TransitionType.EXTERNAL;
    
    private int priority;

    @Override
    public ImmutableState getSourceState() {
        return sourceState;
    }

    @Override
    public ImmutableState getTargetState() {
        return targetState;
    }

    @Override
    public List> getActions() {
        return actions.getAll();
    }

    @Override
    public ImmutableState transit(final StateContext stateContext) {
        stateContext.getExecutor().begin("TRANSITION__"+this.toString());
        for(final Action action : getActions()) {
            stateContext.getExecutor().defer(action,
                    sourceState.getStateId(), targetState.getStateId(), stateContext.getEvent(),
                    stateContext.getContext(), stateContext.getStateMachine().getThis());
        }
        return targetState;
    }

    @Override
    public void setSourceState(ImmutableState state) {
        this.sourceState = state;
    }

    @Override
    public void setTargetState(ImmutableState state) {
        this.targetState = state;
    }

    @Override
    public void addAction(Action newAction) {
        actions.add(newAction);
    }

    @Override
    public void addActions(List> newActions) {
        actions.addAll(newActions);
    }

    @Override
    public Condition getCondition() {
        return condition;
    }

    @Override
    public void setCondition(Condition condition) {
        this.condition = condition;
    }

    @Override
    public E getEvent() {
        return event;
    }

    @Override
    public void setEvent(E event) {
        this.event = event;
    }
    
    @Override
    public TransitionType getType() {
        return type;
    }
    
    @Override
    public void setType(TransitionType type) {
        this.type = type;
    }
    
    @Override
    public int getPriority() {
        return priority;
    }
    
    @Override
    public void setPriority(int priority) {
        this.priority = priority;
    }
    
    private void doTransit(ImmutableState source, ImmutableState target, StateContext stateContext) {
        if (target.isChildStateOf(source) && type == TransitionType.EXTERNAL) {
            // exit and re-enter current state for external transition to child state
            source.exit(stateContext);
            source.entry(stateContext);
        }
        doTransitInternal(source, target, stateContext);
    }
    
    /**
     * Recursively traverses the state hierarchy, exiting states along the way, performing the action, and entering states to the target.
     * 
* There exist the following transition scenarios: *
    *
  • 0. there is no target state (internal transition) --> handled outside this method.
  • *
  • 1. The source and target state are the same (self transition) --> perform the transition directly: Exit source state, perform * transition actions and enter target state
  • *
  • 2. The target state is a direct or indirect sub-state of the source state --> perform the transition actions, then traverse the * hierarchy from the source state down to the target state, entering each state along the way. No state is exited. *
  • 3. The source state is a sub-state of the target state --> traverse the hierarchy from the source up to the target, exiting each * state along the way. Then perform transition actions. Finally enter the target state.
  • *
  • 4. The source and target state share the same super-state
  • *
  • 5. All other scenarios: *
      *
    • a. The source and target states reside at the same level in the hierarchy but do not share the same direct super-state
    • *
    • b. The source state is lower in the hierarchy than the target state
    • *
    • c. The target state is lower in the hierarchy than the source state
    • *
    *
* * @param source the source state * @param target the target state * @param stateContext the state context */ private void doTransitInternal(ImmutableState source, ImmutableState target, StateContext stateContext) { if (source == this.getTargetState()) { // Handles 1. // Handles 3. after traversing from the source to the target. if(type==TransitionType.LOCAL) { // not exit and re-enter the composite (source) state for // local transition transit(stateContext); } else { source.exit(stateContext); transit(stateContext); getTargetState().entry(stateContext); } } else if (source == target) { // Handles 2. after traversing from the target to the source. transit(stateContext); } else if (source.getParentState() == target.getParentState()) { // Handles 4. // Handles 5a. after traversing the hierarchy until a common ancestor if found. source.exit(stateContext); transit(stateContext); target.entry(stateContext); } else { // traverses the hierarchy until one of the above scenarios is met. if (source.getLevel() > target.getLevel()) { // Handles 3. // Handles 5b. source.exit(stateContext); doTransitInternal(source.getParentState(), target, stateContext); } else if (source.getLevel() < target.getLevel()) { // Handles 2. // Handles 5c. doTransitInternal(source, target.getParentState(), stateContext); target.entry(stateContext); } else { // Handles 5a. source.exit(stateContext); doTransitInternal(source.getParentState(), target.getParentState(), stateContext); target.entry(stateContext); } } } @Override public void internalFire(StateContext stateContext) { // Fix issue17 if(type==TransitionType.INTERNAL && stateContext. getSourceState().getStateId()!=targetState.getStateId()) { return; } if(condition.isSatisfied(stateContext.getContext())) { ImmutableState newState = stateContext.getSourceState(); if(type==TransitionType.INTERNAL) { newState = transit(stateContext); } else { // exit origin states unwindSubStates(stateContext.getSourceState(), stateContext); // perform transition actions doTransit(getSourceState(), getTargetState(), stateContext); // enter new states newState = getTargetState().enterByHistory(stateContext); } stateContext.getResult().setAccepted(true).setTargetState(newState); } } private void unwindSubStates(ImmutableState orgState, StateContext stateContext) { for (ImmutableState state=orgState; state!=getSourceState(); state=state.getParentState()) { if(state!=null) { state.exit(stateContext); } } } @Override public void accept(Visitor visitor) { visitor.visitOnEntry(this); visitor.visitOnExit(this); } @Override public boolean isMatch(S fromState, S toState, E event, int priority) { if(toState==null && !getTargetState().isFinalState()) return false; if(toState!=null && !getTargetState().isFinalState() && !getTargetState().getStateId().equals(toState)) return false; if(!getEvent().equals(event)) return false; if(getPriority()!=priority) return false; return true; } @Override public boolean isMatch(S fromState, S toState, E event, int priority, Class condClazz, TransitionType type) { if(!isMatch(fromState, toState, event, priority)) return false; if(getCondition().getClass()!=condClazz) return false; if(!getType().equals(type)) return false; return true; } @Override public final String toString() { return sourceState + "-[" + event.toString() +", "+ condition.name()+", "+priority+", "+type+"]->" + targetState; } @Override public void verify() { if(type==TransitionType.INTERNAL && sourceState!=targetState) { throw new RuntimeException(String.format("Internal transition source state '%s' " + "and target state '%s' must be same.", sourceState, targetState)); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy