/// <summary>
        /// Tells the state machine that an external action occured.
        /// This is the only way to make the state machine to possibly change its internal state.
        /// </summary>
        /// <param name="action">The action done that may change the state machine internal state.</param>
        /// <param name="data">A custom data related to the action performed.</param>
        /// <returns>Returns false if it is already processing an asynchronous action, true otherwise.</returns>
        public ActionResultType PerformAction(ActionToken action, object data)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (CurrentState == null)
            {
                throw new InvalidOperationException("State machine not yet initialized or has reached its final state.");
            }

            if (isPerformActionLocked)
            {
                return(ActionResultType.ErrorForbiddenFromSpecialEvents);
            }

            return(CurrentState.Handle(action, data, (st, d) =>
            {
                if (st == null)
                {
                    CurrentState = null;
                    OnCompleted();
                    return;
                }

                if (CurrentState.Token != st)
                {
                    PerformTransitionTo(st, d);
                }
            }));
        }
Exemple #2
0
        /// <summary>
        /// Registers a handler where the state transitions to another one for a given action.
        /// </summary>
        /// <param name="action">The action that makes the handler to be evaluated.</param>
        /// <param name="handler">The handler that evaluate and possibly performs the state transition.</param>
        protected void RegisterActionHandler(ActionToken action, Action <object, Action <StateToken, object> > handler)
        {
            if (dataHandlers.ContainsKey(action))
            {
                throw new InvalidOperationException(string.Format("Action '{0}' already registered.", action));
            }

            dataHandlers.Add(action, handler);
        }
 /// <summary>
 /// Tells the state machine that an external action occured.
 /// This is the only way to make the state machine to possibly change its internal state.
 /// </summary>
 /// <param name="action">The action done that may change the state machine internal state.</param>
 /// <returns>Returns false if it is already processing an asynchronous action, true otherwise.</returns>
 public ActionResultType PerformAction(ActionToken action)
 {
     return(PerformAction(action, null));
 }
Exemple #4
0
 /// <summary>
 /// Initializes the UnknownActionException instance.
 /// </summary>
 /// <param name="actionToken">The token of the action that produced the error.</param>
 /// <param name="stateToken">The token of the state that was active when the error has been produced.</param>
 public UnknownActionException(ActionToken actionToken, StateToken stateToken)
     : base(actionToken, stateToken)
 {
 }
Exemple #5
0
 /// <summary>
 /// Initializes the IllegalActionException instance.
 /// </summary>
 /// <param name="actionToken">The token of the action that produced the error.</param>
 /// <param name="stateToken">The token of the state that was active when the error has been produced.</param>
 /// <param name="message">Custom message explaining the error.</param>
 public IllegalActionException(ActionToken actionToken, StateToken stateToken, string message)
     : base(actionToken, stateToken, message)
 {
 }
Exemple #6
0
 /// <summary>
 /// Initializes the IllegalActionException instance.
 /// </summary>
 /// <param name="actionToken">The token of the action that produced the error.</param>
 /// <param name="stateToken">The token of the state that was active when the error has been produced.</param>
 public IllegalActionException(ActionToken actionToken, StateToken stateToken)
     : base(actionToken, stateToken)
 {
 }
Exemple #7
0
 /// <summary>
 /// Initializes the ActionExceptionBase instance.
 /// </summary>
 /// <param name="actionToken">The token of the action that produced the error.</param>
 /// <param name="stateToken">The token of the state that was active when the error has been produced.</param>
 /// <param name="message">Custom message explaining the error.</param>
 protected ActionExceptionBase(ActionToken actionToken, StateToken stateToken, string message)
     : base((message ?? string.Empty) + $" (action: {actionToken}, state: {stateToken})")
 {
     ActionToken = actionToken;
     StateToken  = stateToken;
 }
Exemple #8
0
 /// <summary>
 /// Initializes the ActionExceptionBase instance.
 /// </summary>
 /// <param name="actionToken">The token of the action that produced the error.</param>
 /// <param name="stateToken">The token of the state that was active when the error has been produced.</param>
 protected ActionExceptionBase(ActionToken actionToken, StateToken stateToken)
     : this(actionToken, stateToken, null)
 {
 }
Exemple #9
0
        /// <summary>
        /// Evaluates a handler that decides transition to the next state for a given action.
        /// </summary>
        /// <param name="action">The action that makes the handler to be evaluated.</param>
        /// <param name="data">A custom data related to the action performed.</param>
        /// <param name="callback">When called, performs the state transition.</param>
        internal ActionResultType Handle(ActionToken action, object data, Action <StateToken, object> callback)
        {
            if (isHandlingAsync)
            {
                return(ActionResultType.ErrorAlreadyPerformingAction);
            }

            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            Action <object, Action <StateToken> >         handler1 = null;
            Action <object, Action <StateToken, object> > handler2 = null;

            if (handlers.TryGetValue(action, out handler1) || dataHandlers.TryGetValue(action, out handler2))
            {
                isHandlingAsync = true;

                try
                {
                    bool callFlag = false;

                    if (handler1 != null)
                    {
                        handler1(data, st =>
                        {
                            if (callFlag)
                            {
                                return;
                            }
                            RunCallback(callback, st, data);
                            callFlag = true;
                        });
                        return(ActionResultType.Success);
                    }
                    else if (handler2 != null)
                    {
                        handler2(data, (st, d) =>
                        {
                            if (callFlag)
                            {
                                return;
                            }
                            RunCallback(callback, st, d);
                            callFlag = true;
                        });
                        return(ActionResultType.Success);
                    }
                    else
                    {
                        isHandlingAsync = false;
                    }
                }
                catch
                {
                    isHandlingAsync = false;
                    throw;
                }

                // action found but handler is null (should never happen because handler nullity is checked at the origin)
                throw new IllegalActionException(action, Token, "No handler definded for the current action.");
            }

            // no action found
            return(ActionResultType.ErrorUnknownAction);
        }
Exemple #10
0
 /// <summary>
 /// Registers a handler where the state does not transition on the given action.
 /// </summary>
 /// <param name="action">The action that do not ignite transition.</param>
 protected void RegisterNoopActionHandler(ActionToken action)
 {
     RegisterActionHandler(action, NoopHandler);
 }