/** * */ 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); } }