diff --git a/.gitignore b/.gitignore index 61753c5..dc9d808 100644 --- a/.gitignore +++ b/.gitignore @@ -188,4 +188,5 @@ FakesAssemblies/ # LightSwitch generated files GeneratedArtifacts/ _Pvt_Extensions/ -ModelManifest.xml \ No newline at end of file +ModelManifest.xml +*.userprefs diff --git a/LiquidState/Configuration/AwaitableStateConfigurationHelper.cs b/LiquidState/Configuration/AwaitableStateConfigurationHelper.cs index bb50dd5..b768f4f 100644 --- a/LiquidState/Configuration/AwaitableStateConfigurationHelper.cs +++ b/LiquidState/Configuration/AwaitableStateConfigurationHelper.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using System.Threading.Tasks; using LiquidState.Common; using LiquidState.Representations; @@ -84,7 +85,7 @@ public AwaitableStateConfigurationHelper PermitReentry PermitReentry(TTrigger trigger, - Func onEntryAsyncAction) + Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -103,7 +104,7 @@ public AwaitableStateConfigurationHelper PermitReentry PermitReentryIf(Func predicate, - TTrigger trigger) + TTrigger trigger) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -112,7 +113,7 @@ public AwaitableStateConfigurationHelper PermitReentryIf(Func< } public AwaitableStateConfigurationHelper PermitReentryIf(Func> predicate, - TTrigger trigger) + TTrigger trigger) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -121,8 +122,8 @@ public AwaitableStateConfigurationHelper PermitReentryIf(Func< } public AwaitableStateConfigurationHelper PermitReentryIf(Func predicate, - TTrigger trigger, - Action onEntryAction) + TTrigger trigger, + Action onEntryAction) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -131,8 +132,8 @@ public AwaitableStateConfigurationHelper PermitReentryIf(Func< } public AwaitableStateConfigurationHelper PermitReentryIf(Func predicate, - ParameterizedTrigger trigger, - Action onEntryAction) + ParameterizedTrigger trigger, + Action onEntryAction) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -141,7 +142,7 @@ public AwaitableStateConfigurationHelper PermitReentryIf PermitReentryIf(Func> predicate, - TTrigger trigger, Action onEntryAction) + TTrigger trigger, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -168,7 +169,7 @@ public AwaitableStateConfigurationHelper Permit(TTrigger trigg } public AwaitableStateConfigurationHelper Permit(TTrigger trigger, TState resultingState, - Action onEntryAction) + Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -187,7 +188,7 @@ public AwaitableStateConfigurationHelper Permit( } public AwaitableStateConfigurationHelper Permit(TTrigger trigger, TState resultingState, - Func onEntryAsyncAction) + Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -206,7 +207,7 @@ public AwaitableStateConfigurationHelper Permit( } public AwaitableStateConfigurationHelper PermitIf(Func predicate, TTrigger trigger, - TState resultingState) + TState resultingState) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -215,7 +216,7 @@ public AwaitableStateConfigurationHelper PermitIf(Func p } public AwaitableStateConfigurationHelper PermitIf(Func> predicate, TTrigger trigger, - TState resultingState) + TState resultingState) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -224,7 +225,7 @@ public AwaitableStateConfigurationHelper PermitIf(Func PermitIf(Func predicate, TTrigger trigger, - TState resultingState, Action onEntryAction) + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -233,8 +234,8 @@ public AwaitableStateConfigurationHelper PermitIf(Func p } public AwaitableStateConfigurationHelper PermitIf(Func predicate, - ParameterizedTrigger trigger, - TState resultingState, Action onEntryAction) + ParameterizedTrigger trigger, + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -243,7 +244,7 @@ public AwaitableStateConfigurationHelper PermitIf(F } public AwaitableStateConfigurationHelper PermitIf(Func> predicate, TTrigger trigger, - TState resultingState, Action onEntryAction) + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -252,8 +253,8 @@ public AwaitableStateConfigurationHelper PermitIf(Func PermitIf(Func> predicate, - ParameterizedTrigger trigger, - TState resultingState, Action onEntryAction) + ParameterizedTrigger trigger, + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -262,7 +263,7 @@ public AwaitableStateConfigurationHelper PermitIf(F } public AwaitableStateConfigurationHelper PermitIf(Func predicate, TTrigger trigger, - TState resultingState, Func onEntryAsyncAction) + TState resultingState, Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -271,8 +272,8 @@ public AwaitableStateConfigurationHelper PermitIf(Func p } public AwaitableStateConfigurationHelper PermitIf(Func predicate, - ParameterizedTrigger trigger, - TState resultingState, Func onEntryAsyncAction) + ParameterizedTrigger trigger, + TState resultingState, Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -281,7 +282,7 @@ public AwaitableStateConfigurationHelper PermitIf(F } public AwaitableStateConfigurationHelper PermitIf(Func> predicate, TTrigger trigger, - TState resultingState, Func onEntryAsyncAction) + TState resultingState, Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -290,8 +291,8 @@ public AwaitableStateConfigurationHelper PermitIf(Func PermitIf(Func> predicate, - ParameterizedTrigger trigger, - TState resultingState, Func onEntryAsyncAction) + ParameterizedTrigger trigger, + TState resultingState, Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -321,7 +322,7 @@ public AwaitableStateConfigurationHelper IgnoreIf(Func FindOrCreateStateRepresentation(TState state, - Dictionary> config) + Dictionary> config) { Contract.Requires(state != null); Contract.Requires(config != null); @@ -331,7 +332,8 @@ internal static AwaitableStateRepresentation FindOrCreateState AwaitableStateRepresentation rep; if (config.TryGetValue(state, out rep)) { - if (rep != null) return rep; + if (rep != null) + return rep; } rep = new AwaitableStateRepresentation(state); @@ -340,36 +342,58 @@ internal static AwaitableStateRepresentation FindOrCreateState return rep; } - internal static AwaitableTriggerRepresentation FindOrCreateTriggerConfig(TTrigger trigger, - AwaitableStateRepresentation stateRepresentation) + internal static AwaitableTriggerRepresentation FindOrCreateTriggerRepresentation(TTrigger trigger, + object predicate, + AwaitableStateRepresentation stateRepresentation) { Contract.Requires(stateRepresentation != null); Contract.Requires(trigger != null); Contract.Ensures(Contract.Result>() != null); - var rep = FindTriggerRepresentation(trigger, stateRepresentation); - if (rep != null) return rep; + var repList = FindTriggerRepresentation(trigger, stateRepresentation); + if (repList != null && repList.Count == 1) + { + var rep = repList[0]; + if (rep.ConditionalTriggerPredicate == predicate) + { + Contract.Assume(rep.Trigger != null); + return rep; + } + } + + foreach (var rep in repList) + { + if (rep.Trigger.Equals(trigger) && + rep.ConditionalTriggerPredicate == predicate) + { + Contract.Assume(rep.Trigger != null); + return rep; + } + } - rep = new AwaitableTriggerRepresentation(trigger); - stateRepresentation.Triggers.Add(rep); - return rep; + var newRep = new AwaitableTriggerRepresentation(trigger); + stateRepresentation.Triggers.Add(newRep, null); + return newRep; } - internal static AwaitableTriggerRepresentation FindTriggerRepresentation(TTrigger trigger, - AwaitableStateRepresentation stateRepresentation) + internal static List> FindTriggerRepresentation(TTrigger trigger, + AwaitableStateRepresentation stateRepresentation) { - return stateRepresentation.Triggers.Find(x => x.Trigger.Equals(trigger)); + var list = from state in stateRepresentation.Triggers + where state.Key.Trigger.Equals(trigger) + select state.Key; + return list.ToList(); } private AwaitableStateConfigurationHelper PermitInternalSync(Func predicate, - TTrigger trigger, - TState resultingState, Action onEntryAction) + TTrigger trigger, + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); - var rep = FindOrCreateTriggerConfig(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAction; @@ -379,15 +403,15 @@ private AwaitableStateConfigurationHelper PermitInternalSync(F } private AwaitableStateConfigurationHelper PermitInternalSync(Func predicate, - ParameterizedTrigger trigger, - TState resultingState, Action onEntryAction) + ParameterizedTrigger trigger, + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); Contract.Assume(trigger.Trigger != null); - var rep = FindOrCreateTriggerConfig(trigger.Trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger.Trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAction; @@ -397,13 +421,13 @@ private AwaitableStateConfigurationHelper PermitInternalSync PermitInternalAsync(Func> predicate, - TTrigger trigger, - TState resultingState, Func onEntryAsyncAction) + TTrigger trigger, + TState resultingState, Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); - var rep = FindOrCreateTriggerConfig(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAsyncAction; @@ -424,7 +448,7 @@ private AwaitableStateConfigurationHelper PermitInternalAsync< Contract.Assume(trigger.Trigger != null); - var rep = FindOrCreateTriggerConfig(trigger.Trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger.Trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAsyncAction; @@ -436,13 +460,13 @@ private AwaitableStateConfigurationHelper PermitInternalAsync< } private AwaitableStateConfigurationHelper PermitInternalTriggerAsync(Func predicate, - TTrigger trigger, - TState resultingState, Func onEntryAsyncAction) + TTrigger trigger, + TState resultingState, Func onEntryAsyncAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); - var rep = FindOrCreateTriggerConfig(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAsyncAction; @@ -462,7 +486,7 @@ private AwaitableStateConfigurationHelper PermitInternalTrigge Contract.Assume(trigger.Trigger != null); - var rep = FindOrCreateTriggerConfig(trigger.Trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger.Trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAsyncAction; @@ -480,7 +504,7 @@ private AwaitableStateConfigurationHelper PermitInternalPredic Contract.Requires(trigger != null); Contract.Requires(resultingState != null); - var rep = FindOrCreateTriggerConfig(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAsyncAction; @@ -500,7 +524,7 @@ private AwaitableStateConfigurationHelper PermitInternalPredic Contract.Assume(trigger.Trigger != null); - var rep = FindOrCreateTriggerConfig(trigger.Trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger.Trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAsyncAction; @@ -511,11 +535,11 @@ private AwaitableStateConfigurationHelper PermitInternalPredic } private AwaitableStateConfigurationHelper IgnoreInternal(Func predicate, - TTrigger trigger) + TTrigger trigger) { Contract.Requires(trigger != null); - var rep = FindOrCreateTriggerConfig(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = null; rep.ConditionalTriggerPredicate = predicate; @@ -529,7 +553,7 @@ private AwaitableStateConfigurationHelper IgnoreInternalPredic { Contract.Requires(trigger != null); - var rep = FindOrCreateTriggerConfig(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = null; rep.ConditionalTriggerPredicate = predicate; diff --git a/LiquidState/Configuration/StateConfigurationHelper.cs b/LiquidState/Configuration/StateConfigurationHelper.cs index da8bc73..c77ecc4 100644 --- a/LiquidState/Configuration/StateConfigurationHelper.cs +++ b/LiquidState/Configuration/StateConfigurationHelper.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using LiquidState.Common; using LiquidState.Representations; @@ -17,7 +18,7 @@ public class StateConfigurationHelper private readonly StateRepresentation currentStateRepresentation; internal StateConfigurationHelper(Dictionary> config, - TState currentState) + TState currentState) { Contract.Requires(config != null); Contract.Requires(currentState != null); @@ -25,7 +26,6 @@ internal StateConfigurationHelper(Dictionary PermitReentryIf(Func pre } public StateConfigurationHelper PermitReentryIf(Func predicate, TTrigger trigger, - Action onEntryAction) + Action onEntryAction) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -85,8 +85,8 @@ public StateConfigurationHelper PermitReentryIf(Func pre } public StateConfigurationHelper PermitReentryIf(Func predicate, - ParameterizedTrigger trigger, - Action onEntryAction) + ParameterizedTrigger trigger, + Action onEntryAction) { Contract.Requires(trigger != null); Contract.Assume(currentStateRepresentation.State != null); @@ -117,7 +117,7 @@ public StateConfigurationHelper IgnoreIf(Func predicate, } public StateConfigurationHelper Permit(TTrigger trigger, TState resultingState, - Action onEntryAction) + Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -136,7 +136,7 @@ public StateConfigurationHelper Permit( } public StateConfigurationHelper PermitIf(Func predicate, TTrigger trigger, - TState resultingState) + TState resultingState) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -145,7 +145,7 @@ public StateConfigurationHelper PermitIf(Func predicate, } public StateConfigurationHelper PermitIf(Func predicate, TTrigger trigger, - TState resultingState, Action onEntryAction) + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -154,8 +154,8 @@ public StateConfigurationHelper PermitIf(Func predicate, } public StateConfigurationHelper PermitIf(Func predicate, - ParameterizedTrigger trigger, - TState resultingState, Action onEntryAction) + ParameterizedTrigger trigger, + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); @@ -164,7 +164,7 @@ public StateConfigurationHelper PermitIf(Func } internal static StateRepresentation FindOrCreateStateRepresentation(TState state, - Dictionary> config) + Dictionary> config) { Contract.Requires(state != null); Contract.Requires(config != null); @@ -184,38 +184,56 @@ internal static StateRepresentation FindOrCreateStateRepresent } internal static TriggerRepresentation FindOrCreateTriggerRepresentation(TTrigger trigger, - StateRepresentation stateRepresentation) + Func predicate, + StateRepresentation stateRepresentation) { Contract.Requires(stateRepresentation != null); Contract.Requires(trigger != null); Contract.Ensures(Contract.Result>() != null); - var rep = FindTriggerRepresentation(trigger, stateRepresentation); - if (rep != null) + var repList = FindTriggerRepresentation(trigger, stateRepresentation); + if (repList != null && repList.Count == 1) { - Contract.Assume(rep.Trigger != null); - return rep; + var rep = repList[0]; + if (rep.ConditionalTriggerPredicate == predicate) + { + Contract.Assume(rep.Trigger != null); + return rep; + } + } + + foreach (var rep in repList) + { + if (rep.Trigger.Equals(trigger) && + rep.ConditionalTriggerPredicate == predicate) + { + Contract.Assume(rep.Trigger != null); + return rep; + } } - rep = new TriggerRepresentation(trigger); - stateRepresentation.Triggers.Add(rep); - return rep; + var newRep = new TriggerRepresentation(trigger); + stateRepresentation.Triggers.Add(newRep, null); + return newRep; } - internal static TriggerRepresentation FindTriggerRepresentation(TTrigger trigger, - StateRepresentation stateRepresentation) + internal static List> FindTriggerRepresentation(TTrigger trigger, + StateRepresentation stateRepresentation) { - return stateRepresentation.Triggers.Find(x => x.Trigger.Equals(trigger)); + var list = from state in stateRepresentation.Triggers + where state.Key.Trigger.Equals(trigger) + select state.Key; + return list.ToList(); } private StateConfigurationHelper PermitInternal(Func predicate, TTrigger trigger, - TState resultingState, Action onEntryAction) + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); - var rep = FindOrCreateTriggerRepresentation(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAction; @@ -225,15 +243,15 @@ private StateConfigurationHelper PermitInternal(Func pre } private StateConfigurationHelper PermitInternal(Func predicate, - ParameterizedTrigger trigger, - TState resultingState, Action onEntryAction) + ParameterizedTrigger trigger, + TState resultingState, Action onEntryAction) { Contract.Requires(trigger != null); Contract.Requires(resultingState != null); Contract.Assume(trigger.Trigger != null); - var rep = FindOrCreateTriggerRepresentation(trigger.Trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger.Trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = FindOrCreateStateRepresentation(resultingState, config); rep.OnTriggerAction = onEntryAction; @@ -246,7 +264,7 @@ private StateConfigurationHelper IgnoreInternal(Func pre { Contract.Requires(trigger != null); - var rep = FindOrCreateTriggerRepresentation(trigger, currentStateRepresentation); + var rep = FindOrCreateTriggerRepresentation(trigger, predicate, currentStateRepresentation); rep.NextStateRepresentation = null; rep.ConditionalTriggerPredicate = predicate; diff --git a/LiquidState/Machines/AwaitableStateMachine.cs b/LiquidState/Machines/AwaitableStateMachine.cs index 8bbe39a..fc06206 100644 --- a/LiquidState/Machines/AwaitableStateMachine.cs +++ b/LiquidState/Machines/AwaitableStateMachine.cs @@ -1,4 +1,4 @@ -// Author: Prasanna V. Loganathar +// Author: Prasanna V. Loganathar // Created: 2:12 AM 27-11-2014 // Project: LiquidState // License: http://www.apache.org/licenses/LICENSE-2.0 @@ -22,7 +22,7 @@ public class AwaitableStateMachine : IAwaitableStateMachine> configDictionary; internal AwaitableStateMachine(TState initialState, - AwaitableStateMachineConfiguration configuration) + AwaitableStateMachineConfiguration configuration) { Contract.Requires(configuration != null); Contract.Requires(initialState != null); @@ -45,7 +45,8 @@ public async Task MoveToState(TState state, StateTransitionOption option = State { try { - if (!IsEnabled) return; + if (!IsEnabled) + return; await MoveToStateInternal(state, option).ConfigureAwait(false); } finally @@ -65,16 +66,16 @@ public async Task CanHandleTriggerAsync(TTrigger trigger) { foreach (var current in CurrentStateRepresentation.Triggers) { - if (current.Trigger.Equals(trigger)) + if (current.Key.Trigger.Equals(trigger)) { - if ((CheckFlag(current.TransitionFlags, AwaitableStateTransitionFlag.TriggerPredicateReturnsTask))) + if ((CheckFlag(current.Key.TransitionFlags, AwaitableStateTransitionFlag.TriggerPredicateReturnsTask))) { - var predicate = current.ConditionalTriggerPredicate as Func>; + var predicate = current.Key.ConditionalTriggerPredicate as Func>; return predicate == null || await predicate(); } else { - var predicate = current.ConditionalTriggerPredicate as Func; + var predicate = current.Key.ConditionalTriggerPredicate as Func; return predicate == null || predicate(); } } @@ -86,7 +87,7 @@ public bool CanTransitionTo(TState state) { foreach (var current in CurrentStateRepresentation.Triggers) { - if (current.NextStateRepresentation.State.Equals(state)) + if (current.Key.NextStateRepresentation.State.Equals(state)) return true; } @@ -104,13 +105,14 @@ public void Resume() } public async Task FireAsync(ParameterizedTrigger parameterizedTrigger, - TArgument argument) + TArgument argument) { if (Monitor.TryEnter()) { try { - if (!IsEnabled) return; + if (!IsEnabled) + return; await FireInternalAsync(parameterizedTrigger, argument).ConfigureAwait(false); } finally @@ -132,7 +134,8 @@ public async Task FireAsync(TTrigger trigger) { try { - if (!IsEnabled) return; + if (!IsEnabled) + return; await FireInternalAsync(trigger).ConfigureAwait(false); } finally @@ -162,7 +165,7 @@ public IEnumerable CurrentPermittedTriggers { get { - foreach (var triggerRepresentation in CurrentStateRepresentation.Triggers) + foreach (var triggerRepresentation in CurrentStateRepresentation.Triggers.Keys) { yield return triggerRepresentation.Trigger; } @@ -239,43 +242,75 @@ internal async Task PerformStopTransitionAsync() } internal async Task FireInternalAsync(ParameterizedTrigger parameterizedTrigger, - TArgument argument) + TArgument argument) { var trigger = parameterizedTrigger.Trigger; var triggerRep = AwaitableStateConfigurationHelper.FindTriggerRepresentation(trigger, CurrentStateRepresentation); - if (triggerRep == null) + if (triggerRep == null || triggerRep.Count == 0) { HandleInvalidTrigger(trigger); return; } - if (CheckFlag(triggerRep.TransitionFlags, AwaitableStateTransitionFlag.TriggerPredicateReturnsTask)) + AwaitableTriggerRepresentation rep = null; + + foreach (var item in triggerRep) { - var predicate = (Func>) triggerRep.ConditionalTriggerPredicate; - if (predicate != null) - if (!await predicate()) + if (CheckFlag(item.TransitionFlags, AwaitableStateTransitionFlag.TriggerPredicateReturnsTask)) + { + var predicate = (Func>)item.ConditionalTriggerPredicate; + if (predicate == null) { - HandleInvalidTrigger(trigger); - return; + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Default triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; } - } - else - { - var predicate = (Func) triggerRep.ConditionalTriggerPredicate; - if (predicate != null) - if (!predicate()) + else { - HandleInvalidTrigger(trigger); - return; + if (await predicate() && rep == null) + { + rep = item; + } + } + } + else + { + var predicate = (Func)item.ConditionalTriggerPredicate; + + if (predicate == null) + { + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Defaul triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; + } + else + { + if (predicate() && rep == null) + { + rep = item; + } } + } + } + + if (rep == null) + { + HandleInvalidTrigger(trigger); + return; } // Handle ignored trigger - if (triggerRep.NextStateRepresentation == null) + if (rep.NextStateRepresentation == null) { return; } @@ -284,11 +319,11 @@ internal async Task FireInternalAsync(ParameterizedTrigger triggerAction = null; Func triggerFunc = null; - if (CheckFlag(triggerRep.TransitionFlags, AwaitableStateTransitionFlag.TriggerActionReturnsTask)) + if (CheckFlag(rep.TransitionFlags, AwaitableStateTransitionFlag.TriggerActionReturnsTask)) { try { - triggerFunc = (Func) triggerRep.OnTriggerAction; + triggerFunc = (Func)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -300,7 +335,7 @@ internal async Task FireInternalAsync(ParameterizedTrigger) triggerRep.OnTriggerAction; + triggerAction = (Action)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -312,15 +347,15 @@ internal async Task FireInternalAsync(ParameterizedTrigger) CurrentStateRepresentation.OnExitAction; + var exit = (Func)CurrentStateRepresentation.OnExitAction; if (exit != null) await exit(); } else { - var exit = (Action) CurrentStateRepresentation.OnExitAction; + var exit = (Action)CurrentStateRepresentation.OnExitAction; if (exit != null) exit(); } @@ -338,17 +373,17 @@ internal async Task FireInternalAsync(ParameterizedTrigger) nextStateRep.OnEntryAction; + var entry = (Func)nextStateRep.OnEntryAction; if (entry != null) await entry(); } else { - var entry = (Action) nextStateRep.OnEntryAction; + var entry = (Action)nextStateRep.OnEntryAction; if (entry != null) entry(); } @@ -371,36 +406,68 @@ internal async Task FireInternalAsync(TTrigger trigger) AwaitableStateConfigurationHelper.FindTriggerRepresentation(trigger, CurrentStateRepresentation); - if (triggerRep == null) + if (triggerRep == null || triggerRep.Count == 0) { HandleInvalidTrigger(trigger); return; } - if (CheckFlag(triggerRep.TransitionFlags, AwaitableStateTransitionFlag.TriggerPredicateReturnsTask)) + AwaitableTriggerRepresentation rep = null; + + foreach (var item in triggerRep) { - var predicate = (Func>) triggerRep.ConditionalTriggerPredicate; - if (predicate != null) - if (!await predicate()) + if (CheckFlag(item.TransitionFlags, AwaitableStateTransitionFlag.TriggerPredicateReturnsTask)) + { + var predicate = (Func>)item.ConditionalTriggerPredicate; + if (predicate == null) { - HandleInvalidTrigger(trigger); - return; + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Defaul triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; } - } - else - { - var predicate = (Func) triggerRep.ConditionalTriggerPredicate; - if (predicate != null) - if (!predicate()) + else { - HandleInvalidTrigger(trigger); - return; + if (await predicate() && rep == null) + { + rep = item; + } + } + } + else + { + var predicate = (Func)item.ConditionalTriggerPredicate; + + if (predicate == null) + { + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Defaul triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; } + else + { + if (predicate() && rep == null) + { + rep = item; + } + } + } + } + + if (rep == null) + { + HandleInvalidTrigger(trigger); + return; } // Handle ignored trigger - if (triggerRep.NextStateRepresentation == null) + if (rep.NextStateRepresentation == null) { return; } @@ -409,11 +476,11 @@ internal async Task FireInternalAsync(TTrigger trigger) Action triggerAction = null; Func triggerFunc = null; - if (CheckFlag(triggerRep.TransitionFlags, AwaitableStateTransitionFlag.TriggerActionReturnsTask)) + if (CheckFlag(rep.TransitionFlags, AwaitableStateTransitionFlag.TriggerActionReturnsTask)) { try { - triggerFunc = (Func) triggerRep.OnTriggerAction; + triggerFunc = (Func)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -425,7 +492,7 @@ internal async Task FireInternalAsync(TTrigger trigger) { try { - triggerAction = (Action) triggerRep.OnTriggerAction; + triggerAction = (Action)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -437,16 +504,17 @@ internal async Task FireInternalAsync(TTrigger trigger) // Current exit if (CheckFlag(CurrentStateRepresentation.TransitionFlags, - AwaitableStateTransitionFlag.ExitReturnsTask)) + AwaitableStateTransitionFlag.ExitReturnsTask)) { - var exit = (Func) CurrentStateRepresentation.OnExitAction; + var exit = (Func)CurrentStateRepresentation.OnExitAction; if (exit != null) await exit(); } else { - var exit = (Action) CurrentStateRepresentation.OnExitAction; - if (exit != null) exit(); + var exit = (Action)CurrentStateRepresentation.OnExitAction; + if (exit != null) + exit(); } // Trigger entry @@ -462,18 +530,19 @@ internal async Task FireInternalAsync(TTrigger trigger) // Next state entry - var nextStateRep = triggerRep.NextStateRepresentation; + var nextStateRep = rep.NextStateRepresentation; if (CheckFlag(nextStateRep.TransitionFlags, AwaitableStateTransitionFlag.EntryReturnsTask)) { - var entry = (Func) nextStateRep.OnEntryAction; + var entry = (Func)nextStateRep.OnEntryAction; if (entry != null) await entry(); } else { - var entry = (Action) nextStateRep.OnEntryAction; - if (entry != null) entry(); + var entry = (Action)nextStateRep.OnEntryAction; + if (entry != null) + entry(); } // Set states @@ -484,13 +553,15 @@ internal async Task FireInternalAsync(TTrigger trigger) // Raise event var sc = StateChanged; - if (sc != null) sc.Invoke(previousState, CurrentStateRepresentation.State); + if (sc != null) + sc.Invoke(previousState, CurrentStateRepresentation.State); } private void HandleInvalidTrigger(TTrigger trigger) { var handler = UnhandledTriggerExecuted; - if (handler != null) handler.Invoke(trigger, CurrentStateRepresentation.State); + if (handler != null) + handler.Invoke(trigger, CurrentStateRepresentation.State); } private bool CheckFlag(AwaitableStateTransitionFlag source, AwaitableStateTransitionFlag flagToCheck) diff --git a/LiquidState/Machines/BlockingStateMachine.cs b/LiquidState/Machines/BlockingStateMachine.cs index aa8cbf7..d967031 100644 --- a/LiquidState/Machines/BlockingStateMachine.cs +++ b/LiquidState/Machines/BlockingStateMachine.cs @@ -1,4 +1,4 @@ -// Author: Prasanna V. Loganathar +// Author: Prasanna V. Loganathar // Created: 2:12 AM 27-11-2014 // Project: LiquidState // License: http://www.apache.org/licenses/LICENSE-2.0 @@ -41,7 +41,8 @@ public void MoveToState(TState state, StateTransitionOption option = StateTransi { lock (syncRoot) { - if (!IsEnabled) return; + if (!IsEnabled) + return; StateRepresentation rep; if (configDictionary.TryGetValue(state, out rep)) { @@ -69,9 +70,9 @@ public bool CanHandleTrigger(TTrigger trigger) { foreach (var current in CurrentStateRepresentation.Triggers) { - if (current.Trigger.Equals(trigger)) + if (current.Key.Trigger.Equals(trigger)) { - var predicate = current.ConditionalTriggerPredicate; + var predicate = current.Key.ConditionalTriggerPredicate; return predicate == null || predicate(); } } @@ -82,7 +83,7 @@ public bool CanTransitionTo(TState state) { foreach (var current in CurrentStateRepresentation.Triggers) { - if (current.NextStateRepresentation.State.Equals(state)) + if (current.Key.NextStateRepresentation.State.Equals(state)) return true; } @@ -103,32 +104,51 @@ public void Fire(ParameterizedTrigger parameteri { lock (syncRoot) { - if (!IsEnabled) return; + if (!IsEnabled) + return; var trigger = parameterizedTrigger.Trigger; var triggerRep = StateConfigurationHelper.FindTriggerRepresentation(trigger, - CurrentStateRepresentation); + CurrentStateRepresentation); - if (triggerRep == null) + if (triggerRep == null || triggerRep.Count == 0) { HandleInvalidTrigger(trigger); return; } var previousState = CurrentState; + TriggerRepresentation rep = null; - var predicate = triggerRep.ConditionalTriggerPredicate; - if (predicate != null) + foreach (var item in triggerRep) { - if (!predicate()) + var predicate = item.ConditionalTriggerPredicate; + if (predicate == null) + { + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Default triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; + } + else { - HandleInvalidTrigger(trigger); - return; + if (predicate() && rep == null) + { + rep = item; + } } } + if (rep == null) + { + HandleInvalidTrigger(trigger); + return; + } + // Handle ignored trigger - if (triggerRep.NextStateRepresentation == null) + if (rep.NextStateRepresentation == null) { return; } @@ -138,7 +158,7 @@ public void Fire(ParameterizedTrigger parameteri Action triggerAction = null; try { - triggerAction = (Action) triggerRep.OnTriggerAction; + triggerAction = (Action)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -152,10 +172,11 @@ public void Fire(ParameterizedTrigger parameteri ExecuteAction(currentExit); // Trigger entry - if (triggerAction != null) triggerAction.Invoke(argument); + if (triggerAction != null) + triggerAction.Invoke(argument); - var nextStateRep = triggerRep.NextStateRepresentation; + var nextStateRep = rep.NextStateRepresentation; // Next entry var nextEntry = nextStateRep.OnEntryAction; @@ -174,31 +195,50 @@ public void Fire(TTrigger trigger) { lock (syncRoot) { - if (!IsEnabled) return; + if (!IsEnabled) + return; var triggerRep = StateConfigurationHelper.FindTriggerRepresentation(trigger, - CurrentStateRepresentation); + CurrentStateRepresentation); - if (triggerRep == null) + if (triggerRep == null || triggerRep.Count == 0) { HandleInvalidTrigger(trigger); return; } var previousState = CurrentState; + TriggerRepresentation rep = null; - var predicate = triggerRep.ConditionalTriggerPredicate; - if (predicate != null) + foreach (var item in triggerRep) { - if (!predicate()) + var predicate = item.ConditionalTriggerPredicate; + if (predicate == null) { - HandleInvalidTrigger(trigger); - return; + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Defaul triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; } + else + { + if (predicate() && rep == null) + { + rep = item; + } + } + } + + if (rep == null) + { + HandleInvalidTrigger(trigger); + return; } // Handle ignored trigger - if (triggerRep.NextStateRepresentation == null) + if (rep.NextStateRepresentation == null) { return; } @@ -208,7 +248,7 @@ public void Fire(TTrigger trigger) Action triggerAction = null; try { - triggerAction = (Action) triggerRep.OnTriggerAction; + triggerAction = (Action)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -224,7 +264,7 @@ public void Fire(TTrigger trigger) // Trigger entry ExecuteAction(triggerAction); - var nextStateRep = triggerRep.NextStateRepresentation; + var nextStateRep = rep.NextStateRepresentation; // Next entry var nextEntry = nextStateRep.OnEntryAction; @@ -253,7 +293,7 @@ public IEnumerable CurrentPermittedTriggers { get { - foreach (var triggerRepresentation in CurrentStateRepresentation.Triggers) + foreach (var triggerRepresentation in CurrentStateRepresentation.Triggers.Keys) { yield return triggerRepresentation.Trigger; } @@ -267,13 +307,15 @@ public bool IsEnabled private void ExecuteAction(Action action) { - if (action != null) action.Invoke(); + if (action != null) + action.Invoke(); } private void HandleInvalidTrigger(TTrigger trigger) { var handler = UnhandledTriggerExecuted; - if (handler != null) handler.Invoke(trigger, CurrentStateRepresentation.State); + if (handler != null) + handler.Invoke(trigger, CurrentStateRepresentation.State); } } } diff --git a/LiquidState/Machines/StateMachine.cs b/LiquidState/Machines/StateMachine.cs index c9ffdf0..a77b0db 100644 --- a/LiquidState/Machines/StateMachine.cs +++ b/LiquidState/Machines/StateMachine.cs @@ -1,4 +1,4 @@ -// Author: Prasanna V. Loganathar +// Author: Prasanna V. Loganathar // Created: 2:12 AM 27-11-2014 // Project: LiquidState // License: http://www.apache.org/licenses/LICENSE-2.0 @@ -43,7 +43,8 @@ public void MoveToState(TState state, StateTransitionOption option = StateTransi { try { - if (!IsEnabled) return; + if (!IsEnabled) + return; StateRepresentation rep; if (configDictionary.TryGetValue(state, out rep)) { @@ -81,9 +82,9 @@ public bool CanHandleTrigger(TTrigger trigger) { foreach (var current in CurrentStateRepresentation.Triggers) { - if (current.Trigger.Equals(trigger)) + if (current.Key.Trigger.Equals(trigger)) { - var predicate = current.ConditionalTriggerPredicate; + var predicate = current.Key.ConditionalTriggerPredicate; return predicate == null || predicate(); } } @@ -94,7 +95,7 @@ public bool CanTransitionTo(TState state) { foreach (var current in CurrentStateRepresentation.Triggers) { - if (current.NextStateRepresentation.State.Equals(state)) + if (current.Key.NextStateRepresentation.State.Equals(state)) return true; } @@ -117,32 +118,51 @@ public void Fire(ParameterizedTrigger parameteri { try { - if (!IsEnabled) return; + if (!IsEnabled) + return; var trigger = parameterizedTrigger.Trigger; var triggerRep = StateConfigurationHelper.FindTriggerRepresentation(trigger, - CurrentStateRepresentation); + CurrentStateRepresentation); - if (triggerRep == null) + if (triggerRep == null || triggerRep.Count == 0) { HandleInvalidTrigger(trigger); return; } var previousState = CurrentState; + TriggerRepresentation rep = null; - var predicate = triggerRep.ConditionalTriggerPredicate; - if (predicate != null) + foreach (var item in triggerRep) { - if (!predicate()) + var predicate = item.ConditionalTriggerPredicate; + if (predicate == null) + { + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Defaul triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; + } + else { - HandleInvalidTrigger(trigger); - return; + if (predicate() && rep == null) + { + rep = item; + } } } + if (rep == null) + { + HandleInvalidTrigger(trigger); + return; + } + // Handle ignored trigger - if (triggerRep.NextStateRepresentation == null) + if (rep.NextStateRepresentation == null) { return; } @@ -152,7 +172,7 @@ public void Fire(ParameterizedTrigger parameteri Action triggerAction = null; try { - triggerAction = (Action) triggerRep.OnTriggerAction; + triggerAction = (Action)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -166,10 +186,11 @@ public void Fire(ParameterizedTrigger parameteri ExecuteAction(currentExit); // Trigger entry - if (triggerAction != null) triggerAction.Invoke(argument); + if (triggerAction != null) + triggerAction.Invoke(argument); - var nextStateRep = triggerRep.NextStateRepresentation; + var nextStateRep = rep.NextStateRepresentation; // Next entry var nextEntry = nextStateRep.OnEntryAction; @@ -200,31 +221,50 @@ public void Fire(TTrigger trigger) { try { - if (!IsEnabled) return; + if (!IsEnabled) + return; var triggerRep = StateConfigurationHelper.FindTriggerRepresentation(trigger, - CurrentStateRepresentation); + CurrentStateRepresentation); - if (triggerRep == null) + if (triggerRep == null || triggerRep.Count == 0) { HandleInvalidTrigger(trigger); return; } var previousState = CurrentState; + TriggerRepresentation rep = null; - var predicate = triggerRep.ConditionalTriggerPredicate; - if (predicate != null) + foreach (var item in triggerRep) { - if (!predicate()) + var predicate = item.ConditionalTriggerPredicate; + if (predicate == null) { - HandleInvalidTrigger(trigger); - return; + if (triggerRep.Count > 1) + { + throw new InvalidOperationException("Default triggers and conditional triggers cannot be used on the same resulting state"); + } + + rep = item; } + else + { + if (predicate() && rep == null) + { + rep = item; + } + } + } + + if (rep == null) + { + HandleInvalidTrigger(trigger); + return; } // Handle ignored trigger - if (triggerRep.NextStateRepresentation == null) + if (rep.NextStateRepresentation == null) { return; } @@ -234,7 +274,7 @@ public void Fire(TTrigger trigger) Action triggerAction = null; try { - triggerAction = (Action) triggerRep.OnTriggerAction; + triggerAction = (Action)rep.OnTriggerAction; } catch (InvalidCastException) { @@ -250,7 +290,7 @@ public void Fire(TTrigger trigger) // Trigger entry ExecuteAction(triggerAction); - var nextStateRep = triggerRep.NextStateRepresentation; + var nextStateRep = rep.NextStateRepresentation; // Next entry var nextEntry = nextStateRep.OnEntryAction; @@ -289,7 +329,7 @@ public IEnumerable CurrentPermittedTriggers { get { - foreach (var triggerRepresentation in CurrentStateRepresentation.Triggers) + foreach (var triggerRepresentation in CurrentStateRepresentation.Triggers.Keys) { yield return triggerRepresentation.Trigger; } @@ -303,13 +343,15 @@ public bool IsEnabled private void ExecuteAction(Action action) { - if (action != null) action.Invoke(); + if (action != null) + action.Invoke(); } private void HandleInvalidTrigger(TTrigger trigger) { var handler = UnhandledTriggerExecuted; - if (handler != null) handler.Invoke(trigger, CurrentStateRepresentation.State); + if (handler != null) + handler.Invoke(trigger, CurrentStateRepresentation.State); } } } diff --git a/LiquidState/Representations/AwaitableStateRepresentation.cs b/LiquidState/Representations/AwaitableStateRepresentation.cs index aca9aca..2c8b045 100644 --- a/LiquidState/Representations/AwaitableStateRepresentation.cs +++ b/LiquidState/Representations/AwaitableStateRepresentation.cs @@ -12,7 +12,7 @@ namespace LiquidState.Representations internal class AwaitableStateRepresentation { public readonly TState State; - public readonly List> Triggers; + public readonly Dictionary, Func> Triggers; public object OnEntryAction; public object OnExitAction; public AwaitableStateTransitionFlag TransitionFlags; @@ -26,7 +26,7 @@ internal AwaitableStateRepresentation(TState state) State = state; // Allocate with capacity as 1 to avoid wastage of memory. - Triggers = new List>(1); + Triggers = new Dictionary, Func>(1); } } diff --git a/LiquidState/Representations/Representation.cs b/LiquidState/Representations/Representation.cs index 34daddf..06c87a9 100644 --- a/LiquidState/Representations/Representation.cs +++ b/LiquidState/Representations/Representation.cs @@ -12,7 +12,7 @@ namespace LiquidState.Representations internal class StateRepresentation { public readonly TState State; - public readonly List> Triggers; + public readonly Dictionary, Func> Triggers; public Action OnEntryAction; public Action OnExitAction; @@ -23,7 +23,7 @@ internal StateRepresentation(TState state) State = state; // Allocate with capacity as 1 to avoid wastage of memory. - Triggers = new List>(1); + Triggers = new Dictionary, Func>(1); } }