1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 
3 namespace System.Web.Razor
4 {
5     public abstract class StateMachine<TReturn>
6     {
State()7         protected delegate StateResult State();
8 
9         protected abstract State StartState { get; }
10 
11         protected State CurrentState { get; set; }
12 
Turn()13         protected virtual TReturn Turn()
14         {
15             if (CurrentState != null)
16             {
17                 StateResult result;
18                 do
19                 {
20                     // Keep running until we get a null result or output
21                     result = CurrentState();
22                     CurrentState = result.Next;
23                 }
24                 while (result != null && !result.HasOutput);
25 
26                 if (result == null)
27                 {
28                     return default(TReturn); // Terminated
29                 }
30                 return result.Output;
31             }
32             return default(TReturn);
33         }
34 
35         /// <summary>
36         /// Returns a result indicating that the machine should stop executing and return null output.
37         /// </summary>
Stop()38         protected StateResult Stop()
39         {
40             return null;
41         }
42 
43         /// <summary>
44         /// Returns a result indicating that this state has no output and the machine should immediately invoke the specified state
45         /// </summary>
46         /// <remarks>
47         /// By returning no output, the state machine will invoke the next state immediately, before returning
48         /// controller to the caller of <see cref="Turn"/>
49         /// </remarks>
Transition(State newState)50         protected StateResult Transition(State newState)
51         {
52             return new StateResult(newState);
53         }
54 
55         /// <summary>
56         /// Returns a result containing the specified output and indicating that the next call to
57         /// <see cref="Turn"/> should invoke the provided state.
58         /// </summary>
Transition(TReturn output, State newState)59         protected StateResult Transition(TReturn output, State newState)
60         {
61             return new StateResult(output, newState);
62         }
63 
64         /// <summary>
65         /// Returns a result indicating that this state has no output and the machine should remain in this state
66         /// </summary>
67         /// <remarks>
68         /// By returning no output, the state machine will re-invoke the current state again before returning
69         /// controller to the caller of <see cref="Turn"/>
70         /// </remarks>
Stay()71         protected StateResult Stay()
72         {
73             return new StateResult(CurrentState);
74         }
75 
76         /// <summary>
77         /// Returns a result containing the specified output and indicating that the next call to
78         /// <see cref="Turn"/> should re-invoke the current state.
79         /// </summary>
Stay(TReturn output)80         protected StateResult Stay(TReturn output)
81         {
82             return new StateResult(output, CurrentState);
83         }
84 
85         protected class StateResult
86         {
StateResult(State next)87             public StateResult(State next)
88             {
89                 HasOutput = false;
90                 Next = next;
91             }
92 
StateResult(TReturn output, State next)93             public StateResult(TReturn output, State next)
94             {
95                 HasOutput = true;
96                 Output = output;
97                 Next = next;
98             }
99 
100             public bool HasOutput { get; set; }
101             public TReturn Output { get; set; }
102             public State Next { get; set; }
103         }
104     }
105 }
106