zcommon/src/main/java/org/zkoss/fsm/StateCtx.java

Summary

Maintainability
B
5 hrs
Test Coverage
/**
 * 
 */
package org.zkoss.fsm;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * A context for a State in a StateMachine, holding returning and transition
 * information.
 * @since 6.0.0
 * @author simonpai
 */
public class StateCtx<E, C, IN> {
    
    protected StateMachine<E, C, IN> _machine;
    
    // local properties //
    protected boolean _returnAll = false;
    protected final Set<C> _returners = new HashSet<C>();
    protected final Set<IN> _minorReturners = new HashSet<IN>();
    protected final Map<C, E> _transitions = new HashMap<C, E>();
    protected final Map<C, TransitionListener<IN, C>> _transitionListeners = 
        new HashMap<C, TransitionListener<IN, C>>();
    protected final Map<IN, E> _minorTransitions = new HashMap<IN, E>();
    protected final Map<IN, TransitionListener<IN, C>> _minorTransitionListeners = 
        new HashMap<IN, TransitionListener<IN, C>>();
    
    /**
     * Creates a new state context
     */
    public StateCtx() {
        init();
    }
    
    /**
     * Sets the owner state machine.
     */
    protected StateCtx<E, C, IN> setMaster(StateMachine<E, C, IN> master) {
        _machine = master;
        return this;
    }
    
    
    
    // definition //
    /**
     * Initialization method called at the constructor
     */
    protected void init(){}
    
    // returning //
    /**
     * Add an input class for returning to this state
     * @return this state
     */
    public StateCtx<E, C, IN> addReturningClasses(C ... inputClasses) {
        for(C c : inputClasses) _returners.add(c);
        return this;
    }
    
    /**
     * Add multiple input classes for returning to this state
     * @return this state
     */
    public StateCtx<E, C, IN> addReturningClasses(Collection<C> collection) {
        _returners.addAll(collection);
        return this;
    }
    
    /**
     * Add input characters for returning to this state
     * @return this state
     */
    public StateCtx<E, C, IN> addReturningInputs(IN ... inputs) {
        for(IN i : inputs) _minorReturners.add(i);
        return this;
    }
    
    /**
     * Add input characters for returning to this state
     * @return this state
     */
    public StateCtx<E, C, IN> addReturningInputs(Collection<IN> collection) {
        _minorReturners.addAll(collection);
        return this;
    }
    
    /**
     * Set whether returns to this state upon meeting unspecified characters 
     * @return this state
     */
    public StateCtx<E, C, IN> setReturningAll(boolean returnAll) {
        _returnAll = returnAll;
        return this;
    }
    
    // transition //
    /**
     * Add a transition rule
     * @return this state
     */
    public StateCtx<E, C, IN> addTransition(C inputClass, E destination) {
        return addTransition(inputClass, destination, null);
    }
    
    /**
     * Add a transition rule with a callback
     * @return this state
     */
    public StateCtx<E, C, IN> addTransition(C inputClass, E destination, 
            TransitionListener<IN, C> callback) {
        _transitions.put(inputClass, destination);
        if(callback == null)
            _transitionListeners.remove(inputClass);
        else
            _transitionListeners.put(inputClass, callback);
        return this;
    }
    
    /**
     * Add multiple transitions
     * @return this state
     */
    public StateCtx<E, C, IN> addTransitions(E destination, C ... inputClasses) {
        return addTransitions(destination, null, inputClasses);
    }
    
    /**
     * Add multiple transitions with a callback
     * @return this state
     */
    public StateCtx<E, C, IN> addTransitions(E destination, 
            TransitionListener<IN, C> callback, C ... inputClasses) {
        for(C c : inputClasses)
            addTransition(c, destination, callback);
        return this;
    }
    
    // route //
    /**
     * Add a transition. Same as {@link #addTransition(Object, Object)}, 
     * and only differs in return value.
     * @return destination state.
     */
    public StateCtx<E, C, IN> addRoute(C inputClass, E destination) {
        return addRoute(inputClass, destination, null);
    }
    
    /**
     * Add a transition with callback. 
     * Same as {@link #addTransition(Object, Object, TransitionListener)}, 
     * and only differs in return value.
     * @return destination state.
     */
    public StateCtx<E, C, IN> addRoute(C inputClass, E destination, 
            TransitionListener<IN, C> callback) {
        addTransition(inputClass, destination, callback);
        return _machine.getState(destination);
    }
    
    /**
     * Add multiple transitions. 
     * Same as {@link #addTransitions(Object, Object...)}, 
     * and only differs in return value.
     * @return destination state.
     */
    public StateCtx<E, C, IN> addRoutes(E destination, C ... inputClasses) {
        return addRoutes(destination, null, inputClasses);
    }
    
    /**
     * Add multiple transitions with a callback. 
     * Same as {@link #addTransitions(Object, TransitionListener, Object...)}, 
     * and only differs in return value.
     * @return destination state.
     */
    public StateCtx<E, C, IN> addRoutes(E destination, 
            TransitionListener<IN, C> callback, C ... inputClasses) {
        addTransitions(destination, callback, inputClasses);
        return _machine.getState(destination);
    }
    
    // minor transition //
    /**
     * Add a transition for a character.
     * @return this state
     */
    public StateCtx<E, C, IN> addMinorTransition(IN input, E destination) {
        return addMinorTransition(input, destination, null);
    }
    
    /**
     * Add a transition for a character with a callback
     * @return this state
     */
    public StateCtx<E, C, IN> addMinorTransition(IN input, E destination, 
            TransitionListener<IN, C> callback) {
        _minorTransitions.put(input, destination);
        if(callback == null)
            _minorTransitionListeners.remove(input);
        else
            _minorTransitionListeners.put(input, callback);
        return this;
    }
    
    /**
     * Add multiple transitions for characters
     * @return this state
     */
    public StateCtx<E, C, IN> addMinorTransitions(E destination, IN ... inputs) {
        return addMinorTransitions(destination, null, inputs);
    }
    
    /**
     * Add multiple transitions for characters with a callback
     * @return this state
     */
    public StateCtx<E, C, IN> addMinorTransitions(E destination, 
            TransitionListener<IN, C> callback, IN ... inputs) {
        for(IN i : inputs)
            addMinorTransition(i, destination, callback);
        return this;
    }
    
    // minor route //
    /**
     * Add a transition for a character. 
     * Same as {@link #addMinorRoute(Object, Object)}, 
     * and only differs in return value.
     * @return destination state
     */
    public StateCtx<E, C, IN> addMinorRoute(IN input, E destination) {
        return addMinorRoute(input, destination, null);
    }
    
    /**
     * Add a transition for a character with callback. 
     * Same as {@link #addMinorTransition(Object, Object, TransitionListener)}, 
     * and only differs in return value.
     * @return destination state
     */
    public StateCtx<E, C, IN> addMinorRoute(IN input, E destination, 
            TransitionListener<IN, C> callback) {
        addMinorTransition(input, destination, callback);
        return _machine.getState(destination);
    }
    
    /**
     * Add multiple transitions for characters. 
     * Same as {@link #addMinorTransitions(Object, Object...)}, 
     * and only differs in return value.
     * @return destination state
     */
    public StateCtx<E, C, IN> addMinorRoutes(E destination, IN ... inputs) {
        return addMinorRoutes(destination, null, inputs);
    }
    
    /**
     * Add multiple transitions for characters with a callback. 
     * Same as {@link #addMinorTransitions(Object, TransitionListener, Object...)}, 
     * and only differs in return value.
     * @return destination state
     */
    public StateCtx<E, C, IN> addMinorRoutes(E destination, 
            TransitionListener<IN, C> callback, IN ... inputs) {
        addMinorTransitions(destination, callback, inputs);
        return _machine.getState(destination);
    }
    
    
    
    // query //
    /**
     * Returns true if this state returns to itself by default, unless meeting
     * specified characters or classes.
     */
    public boolean isReturningAll(){
        return _returnAll;
    }
    
    /**
     * Returns true if this state returns to itself upon meeting the character
     * or the class.
     */
    public boolean isReturning(IN input, C inputClass) {
        return _returnAll || _returners.contains(inputClass) || 
            _minorReturners.contains(input);
    }
    
    /**
     * Returns true if the machine is leaving this state upon meeting the 
     * character or the class.
     */
    public boolean isLeaving(IN input, C inputClass) {
        return _transitions.containsKey(inputClass) || 
            _minorTransitions.containsKey(input);
    }
    
    /**
     * Returns the destination state upon meeting the character or the class.
     */
    public E getDestination(IN input, C inputClass) {
        E result = _minorTransitions.get(input);
        return (result != null)? result : _transitions.get(inputClass);
    }
    
    
    
    // event handler //
    /**
     * This method is called when the machine enters this state
     */
    protected void onLand(IN input, C inputClass, E origin) {}
    
    /**
     * This method is called when the machine returns to the same state
     */
    protected void onReturn(IN input, C inputClass) {}
    
    /**
     * This method is called when the machine rejects the input on this state
     */
    protected void onReject(IN input, C inputClass) {}
    
    /**
     * This method is called when the machine leaves this state
     */
    protected void onLeave(IN input, C inputClass, E destination) {}
    
    /**
     * This method is called when the machine stops on this state
     */
    protected void onStop(boolean endOfInput) {}
    
    public interface TransitionListener<IN, C> {
        public void onTransit(IN input, C inputClass);
    }
    
    
    
    // default operation //
    /*package*/ void doTransit(IN input, C inputClass) {
        TransitionListener<IN, C> c = _transitionListeners.get(inputClass);
        if(c != null) c.onTransit(input, inputClass);
        c = _minorTransitionListeners.get(input);
        if(c != null) c.onTransit(input, inputClass);
    }
}