using System; using System.Collections; using System.Collections.Generic; using Chernobyl.Collections.Generic; namespace Chernobyl.StateMachine { /// /// A default implementation of the interface. This /// class is used to provide a basic state changing mechanism for states and /// make it easier to create new types. /// public class State : IState { /// /// This event is raised when this state has become the active state. /// This can happen if this instances' has been /// set and the has had /// its set to this instance. It can also /// happen if this instance has decided to activate itself. This event /// will only be raised once when the state has become active. It will not be /// invoked a second time unless it is deactivated first. /// public event EventHandler Entered { add { _entered += value; } remove { _entered -= value; } } /// /// This event is raised when this state is no longer an active state. /// This can happen if this instances' has /// been set and the has /// had its set to this instance. It can also /// happen if this instance has decided to deactivate itself. This event /// will only be raised once when the state has been deactivated. It will /// not be raised a second time unless it was previously in an active /// state. /// public event EventHandler Left { add { _left += value; } remove { _left -= value; } } /// /// The that this is a /// sub-state of or null if this is not sub-state /// of another state. When set this property will set the incoming /// instances property /// to this instance (unless the value set is null of course). If this /// property is set to null (or in other words, this state is /// deactivated), two things will occur: (1) the event /// will be raised and (2), immediately prior to raising the /// event, this state will deactivate its child state /// (or sub-state) which will deactivate its child states and so and so /// forth. Deactivating sub-states is done prior to invoking the /// event so that sub-states will be deactivated from /// the lowest sub-state to the highest sub-state. For example, in a /// state machine setup like so: state1 -> state2 -> state3 -> state4 /// If state2 was deactivated, then state4 would be deactivated first /// (i.e., its event would be raised first), /// then state3, and finally state2. /// public IState ParentState { get { return _parentState; } set { SetParent(ref _parentState, value, this, _entered, _left); } } /// /// The currently active sub-state of this or null /// if this doesn't currently have an active /// sub-state. When set this property will set the incoming /// instances property /// to this instance (unless the value set is null, in which case, the /// this instances /// is set to null). /// public IState ChildState { get { return _childState; } set { SetChild(ref _childState, value, this); } } /// /// Returns an enumerator that iterates through a down the children of /// an system starting at this . /// /// /// The that allows iteration over /// a set of linked instance. /// public IEnumerator GetLinkedEnumerator() { return new LinkedEnumerator(this, LinkedNextState); } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through /// the collection. /// public IEnumerator GetEnumerator() { return GetLinkedEnumerator(); } /// /// A helper method for types deriving from . This /// method is used in the property to properly /// set the parent and handle any other tasks that are associated with /// setting that property. This method will ensure that the parent isn't /// set twice if the is the same as the /// , ensures that the /// of the old parent is set to null (if /// applicable), ensures that the of the /// is set to null (if applicable), and ensures /// the and are /// invoked using and /// respectively. /// /// The previous value of the /// and the instance that is to take the /// new value (if applicable). /// The value being set on /// . /// The instance that was the child of /// and will be the child of /// . Typically this is the value of 'this'. /// The method that is to be invoked if the /// event is to be raised. /// The method that is to be invoked if the /// event is to be raised. public static void SetParent(ref IState parent, IState newParent, IState child, EventHandler entered, EventHandler left) { // Make sure we don't enter or leave a state more than once. if (parent != newParent) { IState previousParentState = parent; parent = newParent; // The previous parent should no longer reference this // IState as it's child. if (previousParentState != null && previousParentState.ChildState == child) previousParentState.ChildState = null; if (parent != null) { if (entered != null) entered(child, EventArgs.Empty); parent.ChildState = child; } else { // Because this state is being deactivated, that means // that all of the sub-states of this state need to be // deactivated. We can do that by setting our child // state to null which will deactivate our sub-state and // it's sub-states and so on and so forth. child.ChildState = null; if (left != null) left(child, EventArgs.Empty); } } } /// /// A helper method for types deriving from . This /// method is used in the property to properly /// set the child and handle any other tasks that are associated with /// setting that property. This method will ensure that the parent isn't /// set twice if the is the same as the /// , and ensures that the /// of the old old child is set to null /// (if applicable). /// /// The previous value of the /// and the instance that is to take the /// new value (if applicable). /// The value being set on /// . /// The instance that was the parent of /// and will be the parent of /// . Typically this is the value of 'this'. public static void SetChild(ref IState child, IState newChild, IState parent) { if (child != newChild) { IState previousChildState = child; child = newChild; if (child != null) child.ParentState = parent; if (previousChildState != null && previousChildState.ParentState == parent) { // The previous child should no longer reference this // IState as it's parent. previousChildState.ParentState = null; } } } /// /// Retrieves the of the passed in /// state if it has one. This method is /// passed to the to allow it to /// iterate over the linked machine. /// /// The whose /// is to be retrieved. /// The next in the link or the /// . /// True if this has a child state and /// is valid upon exit of this method. False if /// otherwise. public static bool LinkedNextState(IState current, out IState next) { next = current.ChildState; return next != null; } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate /// through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// The backing field to . /// EventHandler _entered; /// /// The backing field to . /// EventHandler _left; /// /// The backing field to . /// IState _parentState; /// /// The backing field to . /// IState _childState; } }