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

com.googlecode.stateless4j.StateMachine Maven / Gradle / Ivy

The newest version!
package com.googlecode.stateless4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.googlecode.stateless4j.delegates.Action1;
import com.googlecode.stateless4j.delegates.Action2;
import com.googlecode.stateless4j.delegates.Func;
import com.googlecode.stateless4j.delegates.Func2;
import com.googlecode.stateless4j.resources.StateMachineResources;
import com.googlecode.stateless4j.transitions.Transition;
import com.googlecode.stateless4j.triggers.TriggerBehaviour;
import com.googlecode.stateless4j.triggers.TriggerWithParameters;
import com.googlecode.stateless4j.triggers.TriggerWithParameters1;
import com.googlecode.stateless4j.triggers.TriggerWithParameters2;
import com.googlecode.stateless4j.triggers.TriggerWithParameters3;
import com.googlecode.stateless4j.validation.Enforce;


    /// 
    /// Models behaviour as transitions between a finite set of states.
    /// 
    /// The type used to represent the states.
    /// The type used to represent the triggers that cause state transitions.
    public class StateMachine
    {
      final Map> _stateConfiguration = new HashMap>();
        final Map> _triggerConfiguration = new HashMap>();
        final Func _stateAccessor;
        final Action1 _stateMutator;
        Action2 _unhandledTriggerAction = new Action2() {
            
            public void doIt(TState state, TTrigger trigger) throws Exception {
                throw new Exception(
                        String.format(
                            StateMachineResources.NoTransitionsPermitted,
                            trigger, state));
            }
        
        };

        /// 
        /// Construct a state machine.
        /// 
        /// The initial state.
        public StateMachine(TState initialState)
        {
            final StateReference reference = new StateReference();
            reference.setState(initialState);
            _stateAccessor = new Func() {
                public TState call() {
                    return reference.getState();
                }
            };
            _stateMutator = new Action1() {
                public void doIt(TState s) {
                    reference.setState(s);
                };
                
            };
        }

        /// 
        /// The current state.
        /// 
        public TState getState()
        {
            return _stateAccessor.call();
        }
        
        private void setState(TState value)
        {
            _stateMutator.doIt(value);
        }

        /// 
        /// The currently-permissible trigger values.
        /// 
        public List getPermittedTriggers()
        {
            return getCurrentRepresentation().getPermittedTriggers();
        }

        StateRepresentation getCurrentRepresentation()
        {
            return GetRepresentation(getState());
        }

        StateRepresentation GetRepresentation(TState state)
        {

            if (!_stateConfiguration.containsKey(state))
            {
                StateRepresentation result = new StateRepresentation(state);
                _stateConfiguration.put(state, result);
            }

            return _stateConfiguration.get(state);
        }

        /// 
        /// Begin configuration of the entry/exit actions and allowed transitions
        /// when the state machine is in a particular state.
        /// 
        /// The state to configure.
        /// A configuration object through which the state can be configured.
        public StateConfiguration Configure(TState state) throws Exception
        {
            return new StateConfiguration(GetRepresentation(state), new Func2>() {
                
                public StateRepresentation call(TState arg0) {
                    return GetRepresentation(arg0);
                }
            });
        }

        /// 
        /// Transition from the current state via the specified trigger.
        /// The target state is determined by the configuration of the current state.
        /// Actions associated with leaving the current state and entering the new one
        /// will be invoked.
        /// 
        /// The trigger to fire.
        /// The current state does
        /// not allow the trigger to be fired.
        public void Fire(TTrigger trigger) throws Exception
        {
            publicFire(trigger, new Object[0]);
        }

        /// 
        /// Transition from the current state via the specified trigger.
        /// The target state is determined by the configuration of the current state.
        /// Actions associated with leaving the current state and entering the new one
        /// will be invoked.
        /// 
        /// Type of the first trigger argument.
        /// The trigger to fire.
        /// The first argument.
        /// The current state does
        /// not allow the trigger to be fired.
        public  void Fire(TriggerWithParameters1 trigger, TArg0 arg0) throws Exception
        {
            Enforce.ArgumentNotNull(trigger, "trigger");
            publicFire(trigger.getTrigger(), arg0);
        }

        /// 
        /// Transition from the current state via the specified trigger.
        /// The target state is determined by the configuration of the current state.
        /// Actions associated with leaving the current state and entering the new one
        /// will be invoked.
        /// 
        /// Type of the first trigger argument.
        /// Type of the second trigger argument.
        /// The first argument.
        /// The second argument.
        /// The trigger to fire.
        /// The current state does
        /// not allow the trigger to be fired.
        public  void Fire(TriggerWithParameters2 trigger, TArg0 arg0, TArg1 arg1) throws Exception
        {
            Enforce.ArgumentNotNull(trigger, "trigger");
            publicFire(trigger.getTrigger(), arg0, arg1);
        }

        /// 
        /// Transition from the current state via the specified trigger.
        /// The target state is determined by the configuration of the current state.
        /// Actions associated with leaving the current state and entering the new one
        /// will be invoked.
        /// 
        /// Type of the first trigger argument.
        /// Type of the second trigger argument.
        /// Type of the third trigger argument.
        /// The first argument.
        /// The second argument.
        /// The third argument.
        /// The trigger to fire.
        /// The current state does
        /// not allow the trigger to be fired.
        public  void Fire(TriggerWithParameters3 trigger, TArg0 arg0, TArg1 arg1, TArg2 arg2) throws Exception
        {
            Enforce.ArgumentNotNull(trigger, "trigger");
            publicFire(trigger.getTrigger(), arg0, arg1, arg2);
        }
        
        void publicFire(TTrigger trigger, Object... args) throws Exception
        {
            TriggerWithParameters configuration;
            if (_triggerConfiguration.containsKey(trigger)) {
                configuration = _triggerConfiguration.get(trigger);
                configuration.ValidateParameters(args);
            }

            TriggerBehaviour triggerBehaviour;
            try {
                triggerBehaviour = getCurrentRepresentation().TryFindHandler(trigger);
            } catch (Exception e) {
                _unhandledTriggerAction.doIt(getCurrentRepresentation().getUnderlyingState(), trigger);
                return;
            }

            TState source = getState();
            TState destination;
            try {
                destination = triggerBehaviour.ResultsInTransitionFrom(source, args);
                Transition transition = new Transition(source, destination, trigger);
                
                getCurrentRepresentation().Exit(transition);
                setState(transition.getDestination());
                getCurrentRepresentation().Enter(transition, args);
                
            } catch (Exception e) {
                
            }
        }

        /// 
        /// Override the default behaviour of throwing an exception when an unhandled trigger
        /// is fired.
        /// 
        /// An action to call when an unhandled trigger is fired.
        public void OnUnhandledTrigger(Action2 unhandledTriggerAction) throws Exception
        {
            if (unhandledTriggerAction == null) throw new Exception("unhandledTriggerAction");
            _unhandledTriggerAction = unhandledTriggerAction;
        }

        /// 
        /// Determine if the state machine is in the supplied state.
        /// 
        /// The state to test for.
        /// True if the current state is equal to, or a substate of,
        /// the supplied state.
        public Boolean IsInState(TState state)
        {
            return getCurrentRepresentation().IsIncludedIn(state);
        }

        /// 
        /// Returns true if  can be fired
        /// in the current state.
        /// 
        /// Trigger to test.
        /// True if the trigger can be fired, false otherwise.
        public Boolean CanFire(TTrigger trigger)
        {
            return getCurrentRepresentation().CanHandle(trigger);
        }

        /// 
        /// A human-readable representation of the state machine.
        /// 
        /// A description of the current state and permitted triggers.
        public String toString()
        {
            List permittedTriggers = getPermittedTriggers();
            List parameters = new ArrayList();
            
            for (TTrigger tTrigger : permittedTriggers) {
                parameters.add(tTrigger.toString());
            }
            
            return String.format(
                "StateMachine {{ State = {0}, PermittedTriggers = {{ {1} }}}}",
                getState(),
                StringUtils.join(parameters, ", "));
        }

        /// 
        /// Specify the arguments that must be supplied when a specific trigger is fired.
        /// 
        /// Type of the first trigger argument.
        /// The underlying trigger value.
        /// An object that can be passed to the Fire() method in order to 
        /// fire the parameterised trigger.
        public  TriggerWithParameters1 SetTriggerParameters(TTrigger trigger, Class classe0) throws Exception
        {
            TriggerWithParameters1 configuration = new TriggerWithParameters1(trigger, classe0);
            SaveTriggerConfiguration(configuration);
            return configuration;
        }

        /// 
        /// Specify the arguments that must be supplied when a specific trigger is fired.
        /// 
        /// Type of the first trigger argument.
        /// Type of the second trigger argument.
        /// The underlying trigger value.
        /// An object that can be passed to the Fire() method in order to 
        /// fire the parameterised trigger.
        public  TriggerWithParameters2 SetTriggerParameters(TTrigger trigger, Class classe0, Class classe1) throws Exception
        {
            TriggerWithParameters2 configuration = new TriggerWithParameters2(trigger, classe0, classe1);
            SaveTriggerConfiguration(configuration);
            return configuration;
        }

        /// 
        /// Specify the arguments that must be supplied when a specific trigger is fired.
        /// 
        /// Type of the first trigger argument.
        /// Type of the second trigger argument.
        /// Type of the third trigger argument.
        /// The underlying trigger value.
        /// An object that can be passed to the Fire() method in order to 
        /// fire the parameterised trigger.
        public  TriggerWithParameters3 SetTriggerParameters(TTrigger trigger, Class classe0, Class classe1, Class classe2) throws Exception
        {
            TriggerWithParameters3 configuration = new TriggerWithParameters3(trigger, classe0, classe1, classe2);
            SaveTriggerConfiguration(configuration);
            return configuration;
        }

        void SaveTriggerConfiguration(TriggerWithParameters trigger) throws Exception
        {
            if (_triggerConfiguration.containsKey(trigger.getTrigger()))
                throw new Exception(
                    String.format(StateMachineResources.CannotReconfigureParameters, trigger));

            _triggerConfiguration.put(trigger.getTrigger(), trigger);
        }
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy