Skip to content

Commit

Permalink
Changed the parameter passing order; bug fix
Browse files Browse the repository at this point in the history
Changed the sequence of parameters for callbacks taking both the
context and the triggering message for consistency with other language
versions.
Fixes #7 - transition selection was favouring outer states over inner
states.
  • Loading branch information
mesmo committed Nov 22, 2014
1 parent 2398b2a commit bd3abc8
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 104 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Welcome to state.cs

The current stable release is 5.0.1.
The current stable release is 5.1.0.

If you're using state.cs I'd love to hear about it; please e-mail me at [email protected]

Expand Down
2 changes: 1 addition & 1 deletion examples/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static void Main() {
Console.Write( "alamo> " );

// process lines read from the console
if( !model.Evaluate( context, Console.ReadLine() ) )
if( !model.Evaluate( Console.ReadLine(), context ) )
Console.WriteLine( "unknown command" );
}

Expand Down
2 changes: 1 addition & 1 deletion src/PseudoState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ internal override void BootstrapElement( bool deepHistoryAbove ) {
base.BootstrapElement( deepHistoryAbove );

if( this.Kind == PseudoStateKind.Terminate )
this.Enter += ( context, message, history ) => context.IsTerminated = true;
this.Enter += ( message, context, history ) => context.IsTerminated = true;
}
}
}
8 changes: 4 additions & 4 deletions src/Region.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ internal override void BootstrapElement( Boolean deepHistoryAbove ) {
vertex.BootstrapElement( deepHistoryAbove || ( this.Initial != null && this.Initial.Kind == PseudoStateKind.DeepHistory ) );
}

this.Leave += ( context, message, history ) => { var current = context[ this ]; if( current.Leave != null ) current.Leave( context, message, history ); };
this.Leave += ( message, context, history ) => { var current = context[ this ]; if( current.Leave != null ) current.Leave( message, context, history ); };

if( deepHistoryAbove || this.Initial == null || this.Initial.IsHistory )
this.EndEnter += ( context, message, history ) => ( history || this.Initial.IsHistory ? context[ this ] ?? this.Initial : this.Initial ).Enter( context, message, history || this.Initial.Kind == PseudoStateKind.DeepHistory );
this.EndEnter += ( message, context, history ) => ( history || this.Initial.IsHistory ? context[ this ] ?? this.Initial : this.Initial ).Enter( message, context, history || this.Initial.Kind == PseudoStateKind.DeepHistory );
else this.EndEnter += this.Initial.Enter;

base.BootstrapElement( deepHistoryAbove );
Expand All @@ -150,8 +150,8 @@ internal override void BootstrapTransitions() {
vertex.BootstrapTransitions();
}

internal Boolean Evaluate( TContext context, Object message ) {
return context[ this ].Evaluate( context, message );
internal Boolean Evaluate( Object message, TContext context ) {
return context[ this ].Evaluate( message, context );
}
}
}
58 changes: 30 additions & 28 deletions src/State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ public class State<TContext> : Vertex<TContext> where TContext : IContext<TConte

internal Region<TContext>[] regions;

private event Action<TContext, Object> exit;
private event Action<TContext, Object> entry;
private event Action<Object, TContext> exit;
private event Action<Object, TContext> entry;

/// <summary>
/// Creates a new instance of the State class.
Expand All @@ -67,7 +67,7 @@ public State( String name, Region<TContext> parent )
}

// Constructor used by FinalState
internal State( String name, Region<TContext> parent, Func<Transition<TContext>[], TContext, Object, Transition<TContext>> selector ) : base( name, parent, selector ) { }
internal State( String name, Region<TContext> parent, Func<Transition<TContext>[], Object, TContext, Transition<TContext>> selector ) : base( name, parent, selector ) { }

/// <summary>
/// Sets optional exit behavior that is called when leaving the State.
Expand All @@ -76,9 +76,9 @@ internal State( String name, Region<TContext> parent, Func<Transition<TContext>[
/// <param name="behavior">One or more actions that take both the state machine context and the triggering message as parameters.</param>
/// <returns>Returns the State itself.</returns>
/// <remarks>If the type of the triggering message does not match TMessage the behavior will not be called.</remarks>
public State<TContext> Exit<TMessage>( params Action<TContext, TMessage>[] behavior ) where TMessage : class {
public State<TContext> Exit<TMessage>( params Action<TMessage, TContext>[] behavior ) where TMessage : class {
foreach( var exit in behavior )
this.exit += ( state, message ) => { if( message is TMessage ) exit( state, message as TMessage ); };
this.exit += ( message, context ) => { if( message is TMessage ) exit( message as TMessage, context ); };

this.Root.Clean = false;

Expand All @@ -94,7 +94,7 @@ public State<TContext> Exit<TMessage>( params Action<TContext, TMessage>[] behav
/// <remarks>If the type of the triggering message does not match TMessage the behavior will not be called.</remarks>
public State<TContext> Exit<TMessage>( params Action<TMessage>[] behavior ) where TMessage : class {
foreach( var exit in behavior )
this.exit += ( state, message ) => { if( message is TMessage ) exit( message as TMessage ); };
this.exit += ( message, context ) => { if( message is TMessage ) exit( message as TMessage ); };

this.Root.Clean = false;

Expand All @@ -108,7 +108,7 @@ public State<TContext> Exit<TMessage>( params Action<TMessage>[] behavior ) wher
/// <returns>Returns the State itself.</returns>
public State<TContext> Exit( params Action<TContext>[] behavior ) {
foreach( var exit in behavior )
this.exit += ( state, message ) => exit( state );
this.exit += ( message, context ) => exit( context );

this.Root.Clean = false;

Expand All @@ -122,7 +122,7 @@ public State<TContext> Exit( params Action<TContext>[] behavior ) {
/// <returns>Returns the State itself.</returns>
public State<TContext> Exit( params Action[] behavior ) {
foreach( var exit in behavior )
this.exit += ( state, message ) => exit();
this.exit += ( message, context ) => exit();

this.Root.Clean = false;

Expand All @@ -136,9 +136,9 @@ public State<TContext> Exit( params Action[] behavior ) {
/// <param name="behavior">One or more actions that take both the state machine context and the triggering message as parameters.</param>
/// <returns>Returns the State itself.</returns>
/// <remarks>If the type of the triggering message does not match TMessage the behavior will not be called.</remarks>
public State<TContext> Entry<TMessage>( params Action<TContext, TMessage>[] behavior ) where TMessage : class {
public State<TContext> Entry<TMessage>( params Action<TMessage, TContext>[] behavior ) where TMessage : class {
foreach( var entry in behavior )
this.entry += ( state, message ) => { if( message is TMessage ) entry( state, message as TMessage ); };
this.entry += ( message, context ) => { if( message is TMessage ) entry( message as TMessage, context ); };

this.Root.Clean = false;

Expand All @@ -154,7 +154,7 @@ public State<TContext> Entry<TMessage>( params Action<TContext, TMessage>[] beha
/// <remarks>If the type of the triggering message does not match TMessage the behavior will not be called.</remarks>
public State<TContext> Entry<TMessage>( params Action<TMessage>[] behavior ) where TMessage : class {
foreach( var entry in behavior )
this.entry += ( state, message ) => { if( message is TMessage ) entry( message as TMessage ); };
this.entry += ( message, context ) => { if( message is TMessage ) entry( message as TMessage ); };

this.Root.Clean = false;

Expand All @@ -168,7 +168,7 @@ public State<TContext> Entry<TMessage>( params Action<TMessage>[] behavior ) whe
/// <returns>Returns the State itself.</returns>
public State<TContext> Entry( params Action<TContext>[] behavior ) {
foreach( var entry in behavior )
this.entry += ( state, message ) => entry( state );
this.entry += ( message, context ) => entry( context );

this.Root.Clean = false;

Expand All @@ -182,7 +182,7 @@ public State<TContext> Entry( params Action<TContext>[] behavior ) {
/// <returns>Returns the State itself.</returns>
public State<TContext> Entry( params Action[] behavior ) {
foreach( var entry in behavior )
this.entry += ( state, message ) => entry();
this.entry += ( message, context ) => entry();

this.Root.Clean = false;

Expand All @@ -198,7 +198,7 @@ public State<TContext> Entry( params Action[] behavior ) {
/// <remarks>
/// An internal transition does not exit or enter any states, however the transitions effect will be invoked if the guard condition of the transition is met
/// </remarks>
public Transition<TContext> When<TMessage>( Func<TContext, TMessage, Boolean> guard ) where TMessage : class {
public Transition<TContext> When<TMessage>( Func<TMessage, TContext, Boolean> guard ) where TMessage : class {
return this.To( null ).When( guard );
}

Expand All @@ -218,8 +218,8 @@ public Transition<TContext> When<TMessage>( Func<TMessage, Boolean> guard ) wher
/// <param name="context">The state machine context.</param>
/// <param name="message">The message that triggered the state transition.</param>
/// <param name="history">A flag denoting if history semantics were in play during the transition.</param>
protected virtual void OnExit( TContext context, Object message, Boolean history ) {
this.exit( context, message );
protected virtual void OnExit( Object message, TContext context, Boolean history ) {
this.exit( message, context );
}

/// <summary>
Expand All @@ -228,8 +228,8 @@ protected virtual void OnExit( TContext context, Object message, Boolean history
/// <param name="context">The state machine context.</param>
/// <param name="message">The message that triggered the state transition.</param>
/// <param name="history">A flag denoting if history semantics were in play during the transition.</param>
protected virtual void OnEntry( TContext context, Object message, Boolean history ) {
this.entry( context, message );
protected virtual void OnEntry( Object message, TContext context, Boolean history ) {
this.entry( message, context );
}

internal override Boolean IsComplete( TContext context ) {
Expand Down Expand Up @@ -260,7 +260,7 @@ internal override void BootstrapElement( Boolean deepHistoryAbove ) {
region.Reset();
region.BootstrapElement( deepHistoryAbove );

this.Leave += ( context, message, history ) => region.Leave( context, message, history );
this.Leave += ( message, context, history ) => region.Leave( message, context, history );
this.EndEnter += region.Enter;
}
}
Expand All @@ -273,7 +273,7 @@ internal override void BootstrapElement( Boolean deepHistoryAbove ) {
if( this.entry != null )
this.BeginEnter += this.OnEntry;

this.BeginEnter += ( context, message, history ) => context[ this.Region ] = this;
this.BeginEnter += ( message, context, history ) => context[ this.Region ] = this;

this.Enter = this.BeginEnter + this.EndEnter;
}
Expand All @@ -286,7 +286,7 @@ internal override void BootstrapTransitions() {
base.BootstrapTransitions();
}

internal override void BootstrapEnter( ref Action<TContext, object, bool> traverse, StateMachineElement<TContext> next ) {
internal override void BootstrapEnter( ref Action<Object, TContext, Boolean> traverse, StateMachineElement<TContext> next ) {
base.BootstrapEnter( ref traverse, next );

if( this.IsOrthogonal )
Expand All @@ -295,17 +295,19 @@ internal override void BootstrapEnter( ref Action<TContext, object, bool> traver
traverse += region.Enter;
}

internal override Boolean Evaluate( TContext context, Object message ) {
var processed = base.Evaluate( context, message );
internal override Boolean Evaluate( Object message, TContext context ) {
var processed = false;

if( this.IsComposite )
for( int i = 0, l = this.regions.Length; i < l; ++i )
if( this.regions[ i ].Evaluate( message, context ) )
processed = true;

if( !processed )
if( this.IsComposite )
for( int i = 0, l = this.regions.Length; i < l; ++i )
if( this.regions[ i ].Evaluate( context, message ) )
processed = true;
processed = base.Evaluate( message, context );

if( processed == true && message != this )
this.EvaluateCompletions( context, this, false );
this.EvaluateCompletions( this, context, false );

return processed;
}
Expand Down
6 changes: 3 additions & 3 deletions src/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void Initialise( TContext context, Boolean autoInitialise = true ) {
if( !this.Clean && autoInitialise )
this.Initialise();

this.Enter( context, null, false );
this.Enter( null, context, false );
}

/// <summary>
Expand All @@ -113,15 +113,15 @@ public Boolean IsComplete( TContext context ) {
/// <remarks>
/// Note that due to the potential for orthogonal Regions in composite States, it is possible for multiple transitions to be triggered.
/// </remarks>
public Boolean Evaluate( TContext context, Object message, Boolean autoInitialise = true ) {
public Boolean Evaluate( Object message, TContext context, Boolean autoInitialise = true ) {
if( !this.Clean && autoInitialise )
this.Initialise();

Boolean processed = false;

if( !context.IsTerminated )
for( int i = 0, l = this.regions.Length; i < l; ++i )
if( this.regions[ i ].Evaluate( context, message ) )
if( this.regions[ i ].Evaluate( message, context ) )
processed = true;

return processed;
Expand Down
14 changes: 7 additions & 7 deletions src/StateMachineElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ public abstract class StateMachineElement<TContext> : NamedElement where TContex
/// </summary>
public IEnumerable<StateMachineElement<TContext>> Ancestors { get { if( this.Parent != null ) foreach( var namedElement in this.Parent.Ancestors ) yield return namedElement; yield return this; } } // yield! please...

internal Action<TContext, Object, Boolean> Leave;
internal Action<TContext, Object, Boolean> BeginEnter;
internal Action<TContext, Object, Boolean> EndEnter;
internal Action<TContext, Object, Boolean> Enter;
internal Action<Object, TContext, Boolean> Leave;
internal Action<Object, TContext, Boolean> BeginEnter;
internal Action<Object, TContext, Boolean> EndEnter;
internal Action<Object, TContext, Boolean> Enter;

internal StateMachineElement( String name, StateMachineElement<TContext> parent ) : base( name, parent ) { }

Expand All @@ -42,15 +42,15 @@ internal void Reset() {

internal virtual void BootstrapElement( Boolean deepHistoryAbove ) {
#if DEBUG
this.Leave += ( context, message, history ) => Console.WriteLine( "{0} leave {1}", context, this.QualifiedName );
this.BeginEnter += ( context, message, history ) => Console.WriteLine( "{0} enter {1}", context, this.QualifiedName );
this.Leave += ( message, context, history ) => Console.WriteLine( "{0} leave {1}", context, this.QualifiedName );
this.BeginEnter += ( message, context, history ) => Console.WriteLine( "{0} enter {1}", context, this.QualifiedName );
#endif
this.Enter = this.BeginEnter + this.EndEnter;
}

internal abstract void BootstrapTransitions();

internal virtual void BootstrapEnter( ref Action<TContext, Object, Boolean> traverse, StateMachineElement<TContext> next ) {
internal virtual void BootstrapEnter( ref Action<Object, TContext, Boolean> traverse, StateMachineElement<TContext> next ) {
traverse += this.BeginEnter;
}
}
Expand Down
Loading

0 comments on commit bd3abc8

Please sign in to comment.