/// <summary> /// Performs a transition from the given state to the start indicated by /// the transition defined by the state map matching the given event. /// </summary> /// <param name="startState">The state being transitioned from.</param> /// <param name="eventName">The event causing the transition.</param> /// <param name="transContext">The context of the request the transition is being /// performed for.</param> /// <returns>A <c>string</c> containing the name of the target of the transition. /// This may be a page name if the resulting state is a <c>PageState</c>, /// or an action name if the resulting state is a <c>ActionState</c></returns> public IState DoTransition( IState startState, string eventName, TransitionContext transContext) { IState targetState = null; StateManager sm = StateManager.GetInstance(); string evnt = eventName; // get the settings from config file StatesConfigHandler.StatesConfig statesConfig = (StatesConfigHandler.StatesConfig)ConfigurationManager.GetSection(TritonConfigurationSection.SectionName + "/" + SECTION_NAME); do { try { if (statesConfig.ManagerTrace) { TransitionSessionManager.AddEvent(transContext.Request.IP, startState.Id, evnt); } // get the transition for the given state and event Transition trans = startState.GetTransition(evnt); // if we didn't find a transition for the real event and there is // a default event defined, try for a transition for the default event if ((trans == null) && (statesConfig.DefaultEventName != null)) { LogManager.GetCurrentClassLogger().Info(msg => msg(string.Format( "No transition found for '{0}' on state {1}. Attempting default ['{2}'].", evnt, startState.Id, statesConfig.DefaultEventName))); trans = startState.GetTransition(statesConfig.DefaultEventName); } // make sure we found a transition matching the given event if (trans == null) { // TODO: this should be a custom Exception type throw new ApplicationException(string.Format( "No transition found: state= {0} event= {1} [{2}]", startState.Id, evnt, TransitionSessionManager.GetTrace(transContext))); } // get the State that is the destination of the tranistion targetState = sm.GetState(trans.ToState); if (targetState == null) { // TODO: this should be a custom Exception type throw new ApplicationException(string.Format( "State not found: state= {0} [{1}]", trans.ToState, TransitionSessionManager.GetTrace(transContext))); } // log the transition if logging is on if (statesConfig.LogTrace) { LogManager.GetCurrentClassLogger().Debug( traceMessage => traceMessage("Transition: {0} {1} -> {2} -> {3} {4}", startState.Id, string.IsNullOrEmpty(startState.Name) ? "" : "[" + startState.Name + "]", evnt, targetState.Id, string.IsNullOrEmpty(targetState.Name) ? "" : "[" + targetState.Name + "]")); } // set the current state that is being processed transContext.SetCurrentState(targetState); // handle the state try { // if there is a prerequisite on the state, recursively call DoTransition // for the prerequisite if (targetState.HasPrerequisite) { for (int p = 0; p < targetState.Prerequisite.Length; p++) { LogManager.GetCurrentClassLogger().Debug(traceMessage => traceMessage( "Begin prerequisite {0}", targetState.Prerequisite[p].Name)); DoTransition(sm.GetState(targetState.Prerequisite[p].StartStateId), targetState.Prerequisite[p].StartEvent, transContext); LogManager.GetCurrentClassLogger().Debug(traceMessage => traceMessage( "End prerequisite {0}", targetState.Prerequisite[p].Name)); } // need to reset the current state here since it will be the end state of the prerequisite otherwise transContext.SetCurrentState(targetState); // ?? what if ended on page state } evnt = targetState.Execute(transContext); } catch (Exception ex) { throw new ApplicationException(string.Format("Error executing state {0}.", targetState.Id), ex); } // the destination state becomes the start state for the next iteration startState = targetState; } catch (Exception e) { // to exit the loop if something goes wrong evnt = null; LogManager.GetCurrentClassLogger().Error(errorMessage => errorMessage(e.Message), e); throw; } // if we got an event back from the execution of the state, it // means continue processing states } while (evnt != null); return targetState; }