using System;
using System.Collections.Generic;
using System.Linq;
namespace Wayne.Lib.StateEngine
{
public class StateLookupEntry
{
#region Fields
public string NextStateFactoryName { get; internal set; }
public HistoryType HistoryType { get; private set; }
#endregion
#region Construction
public StateLookupEntry(string nextStateFactoryName, HistoryType historyType)
{
NextStateFactoryName = nextStateFactoryName;
HistoryType = historyType;
}
#endregion
}
///
/// StateTransitionLookup is used by the State machine to find the state to
/// change to when a transition occurs. It uses a dataset that contains the actual data.
///
public class StateTransitionLookup
{
#region Fields
private readonly StateTypeContainer stateTypeContainer;
#endregion
#region Construction
///
/// Constructor
///
public StateTransitionLookup(StateTypeContainer stateTypeContainer)
{
LookupTable = new Dictionary>();
this.stateTypeContainer = stateTypeContainer;
}
public Dictionary> LookupTable { get; private set; }
#endregion
#region Method: GetNextState
///
/// Performs a Lookup for the next state when in the source state, and
/// are going to perform the specified transition
///
///
/// The state the state machine is in when performing transition
///
///
/// The requested transition.
///
///
/// Returns the next state that should be entered based on the transition.
///
public StateEntry GetNextState(string sourceStateFactoryName, Wayne.Lib.StateEngine.Transition transition)
{
if (transition == null)
return null;
StateLookupEntry stateLookupEntry;
//Try look up with the current state name
if (TryLookup(sourceStateFactoryName, transition.Type, out stateLookupEntry))
{
return new StateEntry(transition, stateLookupEntry.NextStateFactoryName, stateLookupEntry.HistoryType);
}
//If not matching with current state name, try lookup recursively with AnyState
if (sourceStateFactoryName != AnyState.FactoryName) //Unless we are already in recursion.
{
return GetNextState(AnyState.FactoryName, transition);
}
return null;
}
public bool TryLookup(string sourceStateFactoryName, object transitionType, out StateLookupEntry stateLookupEntry)
{
stateLookupEntry = null;
Dictionary lookup1;
if (LookupTable.TryGetValue(sourceStateFactoryName, out lookup1))
{
return lookup1.TryGetValue(transitionType, out stateLookupEntry);
}
return false;
}
#endregion
#region Method: AddTransition
///
/// Internal add transition.
///
///
///
///
///
///
private void InternalAddTransition(string sourceStateFactoryName, object transitionType, string nextStateType, HistoryType historyType, bool replace)
{
if (historyType == HistoryType.Explicit)
throw new StateEngineException("Cannot configure a transition to have a explicit history type. It is only used internally when working with explicit transitions");
var stateLookup = GetTransitionsForState(sourceStateFactoryName);
var exists = stateLookup.ContainsKey(transitionType);
if (exists && !replace)
{
throw new ArgumentException(string.Format("The State-Transition combination \"{0}-{1}\" has already been defined.", sourceStateFactoryName, Transition.GetTransitionName(transitionType)));
}
if (!exists && replace)
{
throw new ArgumentException(string.Format("The State-Transition combination \"{0}-{1}\" is not defined.", sourceStateFactoryName, Transition.GetTransitionName(transitionType)));
}
StateLookupEntry lookupEntry = new StateLookupEntry(nextStateType, historyType);
stateLookup[transitionType] = lookupEntry;
}
///
/// Returns a transition dictionary for the specified state.
///
///
///
public Dictionary GetTransitionsForState(string sourceStateFactoryName)
{
Dictionary result;
if (!LookupTable.TryGetValue(sourceStateFactoryName, out result))
{
LookupTable.Add(sourceStateFactoryName, result = new Dictionary());
}
return result;
}
///
/// Adds a transition from state of class TFromState to the class TToState, when the transitiontype
/// is inserted.
///
/// Class type of the source state.
/// Class type of target state.
/// Transition type.
public void AddTransition(object transitionType)
where TFromState : State
where TToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
InternalAddTransition(typeof(TFromState).FullName, transitionType, typeof(TToState).FullName, HistoryType.None, false);
}
///
/// Adds a transition from state of class fromStateFactoryName to the class toStateFactoryName, when the transitiontype
/// is inserted.
///
/// String containig the factory name for the fromState.
/// String containig the factory name for the toState.
/// Transition type.
public void AddTransition(string fromStateFactoryName, string toStateFactoryName, object transitionType)
{
InternalAddTransition(fromStateFactoryName, transitionType, toStateFactoryName, HistoryType.None, false);
}
///
/// Adds a transition from state of class TFromState to the class TToState, when the transitiontype
/// is inserted.
///
/// Class type of the source state.
/// Class type of target state.
/// Transition type.
///
public void AddTransition(object transitionType, HistoryType historyType)
where TFromState : State
where TToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
InternalAddTransition(typeof(TFromState).FullName, transitionType, typeof(TToState).FullName, historyType, false);
}
#endregion
#region Method: ReplaceTransition (Specifying both From and To state)
///
/// Replaces the existing transition from a state and the given TransitionType, to another state.
///
/// Class type of the source state.
/// Class type of new target state.
/// Transition type.
public void ReplaceTransition(object transitionType)
where TFromOldState : State
where TToNewState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
InternalAddTransition(typeof(TFromOldState).FullName, transitionType, typeof(TToNewState).FullName, HistoryType.None, true);
}
///
/// Replaces the existing transition from a state and the given TransitionType, to another state.
///
/// String containig the factory name for the fromState.
/// String containig the factory name for the new toState.
/// Transition type.
public void ReplaceTransition(string fromOldStateFactoryName, string toNewStateFactoryName, object transitionType)
{
InternalAddTransition(fromOldStateFactoryName, transitionType, toNewStateFactoryName, HistoryType.None, true);
}
///
/// Replaces the existing transition from a state and the given TransitionType, to another state.
///
/// Class type of the source state.
/// Class type of new target state.
/// Transition type.
///
public void ReplaceTransition(object transitionType, HistoryType historyType)
where TFromOldState : State
where TToNewState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
InternalAddTransition(typeof(TFromOldState).FullName, transitionType, typeof(TToNewState).FullName, historyType, true);
}
#endregion
#region Method: ReplaceTransitions (Specifying only To state)
///
/// Replaces all the existing transitions to the given OldToState to the NewToState.
///
/// Class type of old target state.
/// Class type of new target state.
public void ReplaceTransitions()
where TOldToState : State
where TNewToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
ReplaceTransitions(typeof(TOldToState).FullName, typeof(TNewToState).FullName);
}
///
/// Replaces all the existing transitions to the given OldToState to the NewToState.
///
/// String containig the factory name for the old ToState.
/// String containig the factory name for the new ToState.
public void ReplaceTransitions(string oldToStateFactoryName, string newToStateFactoryName)
{
LookupTable.Values
.SelectMany(x => x.Values)
.Where(x => x.NextStateFactoryName == oldToStateFactoryName)
.ForEach(x => x.NextStateFactoryName = newToStateFactoryName);
}
#endregion
#region Method: IntersectTransition
///
/// Puts a state in-between two states that has a transition between them.
///
///
///
///
///
public void IntersectTransition(object transitionType)
where TFromState : State
where TIntersectingState : State
where TOldToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
stateTypeContainer.Register();
IntersectTransition(transitionType, transitionType);
}
///
/// Puts a state in-between two states that has a transition between them.
///
///
///
///
///
///
public void IntersectTransition(object fromTransitionType, object toTransitionType)
where TFromState : State
where TIntersectingState : State
where TOldToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
stateTypeContainer.Register();
StateLookupEntry dummy;
if (!TryLookup(typeof(TFromState).FullName, fromTransitionType, out dummy))
{
throw new StateEngineException(string.Format("No previous Transition {0}, from {1} to {2} exists to intersect.", Transition.GetTransitionName(fromTransitionType), typeof(TFromState).FullName, typeof(TOldToState).FullName));
}
//If the Transition TFromState to TOldState with toTransitionType doesn't exist throw an Error.
ReplaceTransition(fromTransitionType);
AddTransition(toTransitionType);
}
#endregion
#region Method: ReplaceState
///
/// Replaces all the existing transitions to the given OldToState to the NewToState.
/// Also adds the transitions from the OldToState to also from the NewToState.
///
/// Class type of old target state.
/// Class type of new target state.
public void ReplaceState()
where TOldToState : State
where TNewToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
ReplaceState(typeof(TOldToState).FullName, typeof(TNewToState).FullName, false);
}
///
/// Replaces all the existing transitions to the given OldToState to the NewToState.
/// Also adds the transitions from the OldToState to also from the NewToState.
///
/// Default=false. Should all existing transitions *From* this state be removed?
/// Class type of old target state.
/// Class type of new target state.
public void ReplaceState(bool removeAllTransitionsFromState)
where TOldToState : State
where TNewToState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
ReplaceState(typeof(TOldToState).FullName, typeof(TNewToState).FullName, removeAllTransitionsFromState);
}
///
/// Replaces all the existing transitions to the given OldToState to the NewToState.
/// Also adds the transitions from the OldToState to also from the NewToState.
///
/// String containig the factory name for the old ToState.
/// String containig the factory name for the new ToState.
public void ReplaceState(string oldToStateFactoryName, string newToStateFactoryName)
{
ReplaceState(oldToStateFactoryName, newToStateFactoryName, false);
}
///
/// Replaces all the existing transitions to the given OldToState to the NewToState.
/// Also adds the transitions from the OldToState to also from the NewToState.
/// Note: 'OverrideState' also exists, to be used e.g. when replacing state that transists to itself
///
/// The factory name for the old ToState.
/// The factory name for the new ToState.
/// Default=false. Should all existing transitions *From* this state be removed?
public void ReplaceState(string oldToStateFactoryName, string newToStateFactoryName, bool removeAllTransitionsFromState)
{
var transitionsForState = GetTransitionsForState(oldToStateFactoryName);
//Get a list of the self-ref transitions
var selfRefTransitions = transitionsForState
.Where(x => x.Value.NextStateFactoryName == oldToStateFactoryName)
.ToArray();
//Update all transitions going FROM the old state
var transitions = transitionsForState;
LookupTable.Remove(oldToStateFactoryName);
if (!removeAllTransitionsFromState)
{
LookupTable.Add(newToStateFactoryName, transitions);
}
//Update all transitions going TO the old state.
LookupTable.Values
.SelectMany(x => x.Values)
.Where(x => x.NextStateFactoryName == oldToStateFactoryName)
.ForEach(x => x.NextStateFactoryName = newToStateFactoryName);
//Warning :
//Add super-weird old broken self links for backwards compatibility
selfRefTransitions.ForEach(t =>
{
transitionsForState.Remove(t.Key);
InternalAddTransition(oldToStateFactoryName, t.Key, newToStateFactoryName, t.Value.HistoryType, false);
});
}
#endregion
#region Method: OverrideState
///
/// Makes an override of a state.
/// This could be used when you have made an inheritance of a state and want to replace the old one.
///
/// The state to override.
/// The overrider.
public void OverrideState()
where TOldState : State
where TNewState : State
{
stateTypeContainer.Register();
stateTypeContainer.Register();
string oldToStateFactoryName = typeof(TOldState).FullName;
string newToStateFactoryName = typeof(TNewState).FullName;
OverrideState(oldToStateFactoryName, newToStateFactoryName);
}
///
/// Makes an override of a state.
/// This could be used when you have made an inheritance of a state and want to replace the old one.
///
///
///
public void OverrideState(string oldToStateFactoryName, string newToStateFactoryName)
{
//Update all transitions going FROM the old state
var transitions = GetTransitionsForState(oldToStateFactoryName);
LookupTable.Remove(oldToStateFactoryName);
LookupTable.Add(newToStateFactoryName, transitions);
//Update all transitions going TO the old state.
LookupTable.Values
.SelectMany(x => x.Values)
.Where(x => x.NextStateFactoryName == oldToStateFactoryName)
.ForEach(x => x.NextStateFactoryName = newToStateFactoryName);
}
#endregion
#region Methods: GetLists
///
/// Returns a list of state factory names that is in the lookup table.
///
/// Should the AnyStates be included?
public string[] GetStateNameList(bool includeAnyStates)
{
var stateNames = LookupTable.Keys
.Concat(LookupTable
.Values
.SelectMany(dict => dict.Values.Select(entry => entry.NextStateFactoryName)))
.Distinct();
if (!includeAnyStates)
{
stateNames = stateNames.Where(x => x != AnyState.FactoryName);
}
return stateNames.ToArray();
}
#endregion
}
}