/src/Automatonymous/States/StateMachineState.cs

https://github.com/MassTransit/Automatonymous · C# · 300 lines · 245 code · 53 blank · 2 comment · 32 complexity · 8452f93cc39bf2d1951643f74c661347 MD5 · raw file

  1. namespace Automatonymous.States
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using Behaviors;
  8. using Contexts;
  9. using Events;
  10. using GreenPipes;
  11. public class StateMachineState<TInstance> :
  12. State<TInstance>,
  13. IEquatable<State>
  14. where TInstance : class
  15. {
  16. readonly Dictionary<Event, ActivityBehaviorBuilder<TInstance>> _behaviors;
  17. readonly Dictionary<Event, StateEventFilter<TInstance>> _ignoredEvents;
  18. readonly AutomatonymousStateMachine<TInstance> _machine;
  19. readonly EventObserver<TInstance> _observer;
  20. readonly HashSet<State<TInstance>> _subStates;
  21. public StateMachineState(AutomatonymousStateMachine<TInstance> machine, string name,
  22. EventObserver<TInstance> observer, State<TInstance> superState = null)
  23. {
  24. _machine = machine;
  25. Name = name;
  26. _observer = observer;
  27. _behaviors = new Dictionary<Event, ActivityBehaviorBuilder<TInstance>>();
  28. _ignoredEvents = new Dictionary<Event, StateEventFilter<TInstance>>();
  29. Enter = new TriggerEvent(name + ".Enter");
  30. Ignore(Enter);
  31. Leave = new TriggerEvent(name + ".Leave");
  32. Ignore(Leave);
  33. BeforeEnter = new DataEvent<State>(name + ".BeforeEnter");
  34. Ignore(BeforeEnter);
  35. AfterLeave = new DataEvent<State>(name + ".AfterLeave");
  36. Ignore(AfterLeave);
  37. _subStates = new HashSet<State<TInstance>>();
  38. SuperState = superState;
  39. superState?.AddSubstate(this);
  40. }
  41. public bool Equals(State other)
  42. {
  43. return string.CompareOrdinal(Name, other?.Name ?? "") == 0;
  44. }
  45. public State<TInstance> SuperState { get; }
  46. public string Name { get; }
  47. public Event Enter { get; }
  48. public Event Leave { get; }
  49. public Event<State> BeforeEnter { get; }
  50. public Event<State> AfterLeave { get; }
  51. public void Accept(StateMachineVisitor visitor)
  52. {
  53. visitor.Visit(this, _ =>
  54. {
  55. foreach (var behavior in _behaviors)
  56. {
  57. behavior.Key.Accept(visitor);
  58. behavior.Value.Behavior.Accept(visitor);
  59. }
  60. });
  61. }
  62. public void Probe(ProbeContext context)
  63. {
  64. var scope = context.CreateScope("state");
  65. scope.Add("name", Name);
  66. if (_subStates.Any())
  67. {
  68. var subStateScope = scope.CreateScope("substates");
  69. foreach (var subState in _subStates)
  70. subStateScope.Add("name", subState.Name);
  71. }
  72. if (_behaviors.Any())
  73. foreach (var behavior in _behaviors)
  74. {
  75. var eventScope = scope.CreateScope("event");
  76. behavior.Key.Probe(eventScope);
  77. behavior.Value.Behavior.Probe(eventScope.CreateScope("behavior"));
  78. }
  79. var ignored = _ignoredEvents.Where(x => IsRealEvent(x.Key)).ToList();
  80. if (ignored.Any())
  81. foreach (var ignoredEvent in ignored)
  82. ignoredEvent.Key.Probe(scope.CreateScope("event-ignored"));
  83. }
  84. async Task State<TInstance>.Raise(EventContext<TInstance> context)
  85. {
  86. if (!_behaviors.TryGetValue(context.Event, out var activities))
  87. {
  88. if (_ignoredEvents.TryGetValue(context.Event, out var filter) && filter.Filter(context))
  89. return;
  90. if (SuperState != null)
  91. try
  92. {
  93. await SuperState.Raise(context).ConfigureAwait(false);
  94. return;
  95. }
  96. catch (UnhandledEventException)
  97. {
  98. // the exception is better if it's from the substate
  99. }
  100. await _machine.UnhandledEvent(context, this).ConfigureAwait(false);
  101. return;
  102. }
  103. try
  104. {
  105. await _observer.PreExecute(context).ConfigureAwait(false);
  106. await activities.Behavior.Execute(new EventBehaviorContext<TInstance>(context)).ConfigureAwait(false);
  107. await _observer.PostExecute(context).ConfigureAwait(false);
  108. }
  109. catch (Exception ex)
  110. {
  111. await _observer.ExecuteFault(context, ex).ConfigureAwait(false);
  112. throw;
  113. }
  114. }
  115. async Task State<TInstance>.Raise<T>(EventContext<TInstance, T> context)
  116. {
  117. if (!_behaviors.TryGetValue(context.Event, out var activities))
  118. {
  119. if (_ignoredEvents.TryGetValue(context.Event, out var filter) && filter.Filter(context))
  120. return;
  121. if (SuperState != null)
  122. try
  123. {
  124. await SuperState.Raise(context).ConfigureAwait(false);
  125. return;
  126. }
  127. catch (UnhandledEventException)
  128. {
  129. // the exception is better if it's from the substate
  130. }
  131. await _machine.UnhandledEvent(context, this).ConfigureAwait(false);
  132. return;
  133. }
  134. try
  135. {
  136. await _observer.PreExecute(context).ConfigureAwait(false);
  137. await activities.Behavior.Execute(new EventBehaviorContext<TInstance, T>(context)).ConfigureAwait(false);
  138. await _observer.PostExecute(context).ConfigureAwait(false);
  139. }
  140. catch (Exception ex)
  141. {
  142. await _observer.ExecuteFault(context, ex).ConfigureAwait(false);
  143. throw;
  144. }
  145. }
  146. public void Bind(Event @event, Activity<TInstance> activity)
  147. {
  148. if (!_behaviors.TryGetValue(@event, out var builder))
  149. {
  150. builder = new ActivityBehaviorBuilder<TInstance>();
  151. _behaviors.Add(@event, builder);
  152. }
  153. builder.Add(activity);
  154. }
  155. public void Ignore(Event @event)
  156. {
  157. _ignoredEvents[@event] = new AllStateEventFilter<TInstance>();
  158. }
  159. public void Ignore<T>(Event<T> @event, StateMachineEventFilter<TInstance, T> filter)
  160. {
  161. _ignoredEvents[@event] = new SelectedStateEventFilter<TInstance, T>(filter);
  162. }
  163. public void AddSubstate(State<TInstance> subState)
  164. {
  165. if (subState == null)
  166. throw new ArgumentNullException(nameof(subState));
  167. if (Name.Equals(subState.Name))
  168. throw new ArgumentException("A state cannot be a substate of itself", nameof(subState));
  169. _subStates.Add(subState);
  170. }
  171. public bool HasState(State<TInstance> state)
  172. {
  173. return Name.Equals(state.Name) || _subStates.Any(s => s.HasState(state));
  174. }
  175. public bool IsStateOf(State<TInstance> state)
  176. {
  177. return Name.Equals(state.Name) || SuperState != null && SuperState.IsStateOf(state);
  178. }
  179. public IEnumerable<Event> Events
  180. {
  181. get
  182. {
  183. if (SuperState != null)
  184. return SuperState.Events.Union(GetStateEvents()).Distinct();
  185. return GetStateEvents();
  186. }
  187. }
  188. public int CompareTo(State other)
  189. {
  190. return string.CompareOrdinal(Name, other.Name);
  191. }
  192. bool IsRealEvent(Event @event)
  193. {
  194. if (Equals(@event, Enter) || Equals(@event, Leave) || Equals(@event, BeforeEnter) || Equals(@event, AfterLeave))
  195. return false;
  196. return true;
  197. }
  198. IEnumerable<Event> GetStateEvents()
  199. {
  200. return _behaviors.Keys
  201. .Union(_ignoredEvents.Keys)
  202. .Where(IsRealEvent)
  203. .Distinct();
  204. }
  205. public override bool Equals(object obj)
  206. {
  207. if (ReferenceEquals(null, obj))
  208. return false;
  209. if (ReferenceEquals(this, obj))
  210. return true;
  211. var other = obj as State;
  212. return other != null && Equals(other);
  213. }
  214. public override int GetHashCode()
  215. {
  216. return Name?.GetHashCode() ?? 0;
  217. }
  218. public static bool operator ==(State<TInstance> left, StateMachineState<TInstance> right)
  219. {
  220. return Equals(left, right);
  221. }
  222. public static bool operator !=(State<TInstance> left, StateMachineState<TInstance> right)
  223. {
  224. return !Equals(left, right);
  225. }
  226. public static bool operator ==(StateMachineState<TInstance> left, State<TInstance> right)
  227. {
  228. return Equals(left, right);
  229. }
  230. public static bool operator !=(StateMachineState<TInstance> left, State<TInstance> right)
  231. {
  232. return !Equals(left, right);
  233. }
  234. public static bool operator ==(StateMachineState<TInstance> left, StateMachineState<TInstance> right)
  235. {
  236. return Equals(left, right);
  237. }
  238. public static bool operator !=(StateMachineState<TInstance> left, StateMachineState<TInstance> right)
  239. {
  240. return !Equals(left, right);
  241. }
  242. public override string ToString()
  243. {
  244. return $"{Name} (State)";
  245. }
  246. }
  247. }