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