##// END OF EJS Templates
RunnableComponent.Dispose(bool,Exception) changed to standart Dispose(bool)...
cin -
r208:7d07503621fe v2
parent child
Show More
@@ -1,52 +1,52
1 1 using System;
2 2 using Implab.Components;
3 3
4 4 namespace Implab.Test.Mock {
5 5 class MockRunnableComponent : RunnableComponent {
6 6 public MockRunnableComponent(bool initialized) : base(initialized) {
7 7 }
8 8
9 9 public MockRunnableComponent(bool initialized, bool reusable) : base(initialized, reusable) {
10 10 }
11 11
12 12 public Action MockInit {
13 13 get;
14 14 set;
15 15 }
16 16
17 17 public Func<IPromise> MockStart {
18 18 get;
19 19 set;
20 20 }
21 21
22 22 public Func<IPromise> MockStop {
23 23 get;
24 24 set;
25 25 }
26 26
27 public Action<bool, Exception> MockDispose {
27 public Action<bool> MockDispose {
28 28 get;
29 29 set;
30 30 }
31 31
32 32 protected override IPromise OnStart() {
33 33 return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart);
34 34 }
35 35
36 36 protected override IPromise OnStop() {
37 37 return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop);
38 38 }
39 39
40 40 protected override void OnInitialize() {
41 41 if (MockInit != null)
42 42 MockInit();
43 43 }
44 44
45 protected override void Dispose(bool disposing, Exception lastError) {
45 protected override void Dispose(bool disposing) {
46 46 if (MockDispose != null)
47 MockDispose(disposing, lastError);
48 base.Dispose(disposing, lastError);
47 MockDispose(disposing);
48 base.Dispose(disposing);
49 49 }
50 50 }
51 51 }
52 52
@@ -1,230 +1,228
1 1 using System;
2 2 using System.Reflection;
3 3 using System.Threading;
4 4 using Implab.Parallels;
5 5 using Implab.Components;
6 6 using Implab.Test.Mock;
7 7
8 8 #if MONO
9 9
10 10 using NUnit.Framework;
11 11 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
12 12 using TestMethodAttribute = NUnit.Framework.TestAttribute;
13 13 using AssertFailedException = NUnit.Framework.AssertionException;
14 14 #else
15 15
16 16 using Microsoft.VisualStudio.TestTools.UnitTesting;
17 17
18 18 #endif
19 19
20 20 namespace Implab.Test {
21 21 [TestClass]
22 22 public class RunnableComponentTests {
23 23
24 24 static void ShouldThrow(Action action) {
25 25 try {
26 26 action();
27 27 Assert.Fail();
28 28 } catch (AssertFailedException) {
29 29 throw;
30 30 } catch {
31 31 }
32 32 }
33 33
34 34
35 35
36 36 [TestMethod]
37 37 public void NormalFlowTest() {
38 38 var comp = new MockRunnableComponent(false);
39 39
40 40 Assert.AreEqual(ExecutionState.Created, comp.State);
41 41
42 42 comp.Initialize();
43 43
44 44 Assert.AreEqual(ExecutionState.Ready, comp.State);
45 45
46 46 comp.Start().Join(1000);
47 47
48 48 Assert.AreEqual(ExecutionState.Running, comp.State);
49 49
50 50 comp.Stop().Join(1000);
51 51
52 52 Assert.AreEqual(ExecutionState.Disposed, comp.State);
53 53
54 54 }
55 55
56 56 [TestMethod]
57 57 public void InitFailTest() {
58 58 var comp = new MockRunnableComponent(false) {
59 59 MockInit = () => {
60 60 throw new Exception("BAD");
61 61 }
62 62 };
63 63
64 64 ShouldThrow(() => comp.Start());
65 65 ShouldThrow(() => comp.Stop());
66 66 Assert.AreEqual(ExecutionState.Created, comp.State);
67 67
68 68 ShouldThrow(comp.Initialize);
69 69
70 70 Assert.AreEqual(ExecutionState.Failed, comp.State);
71 71
72 72 ShouldThrow(() => comp.Start());
73 73 ShouldThrow(() => comp.Stop());
74 74 Assert.AreEqual(ExecutionState.Failed, comp.State);
75 75
76 76 comp.Dispose();
77 77 Assert.AreEqual(ExecutionState.Disposed, comp.State);
78 78 }
79 79
80 80 [TestMethod]
81 81 public void DisposedTest() {
82 82
83 83 var comp = new MockRunnableComponent(false);
84 84 comp.Dispose();
85 85
86 86 ShouldThrow(() => comp.Start());
87 87 ShouldThrow(() => comp.Stop());
88 88 ShouldThrow(comp.Initialize);
89 89
90 90 Assert.AreEqual(ExecutionState.Disposed, comp.State);
91 91 }
92 92
93 93 [TestMethod]
94 94 public void ShouldCallDisposeOnStop() {
95 95 var comp = new MockRunnableComponent(true);
96 96
97 97 bool disposed = false;
98 comp.MockDispose = (disposing, error) => {
98 comp.MockDispose = (disposing) => {
99 99 disposed = true;
100 100 };
101 101
102 102 comp.Start().Join(1000);
103 103 comp.Stop().Join(1000);
104 104
105 105 ShouldThrow(() => comp.Start());
106 106 ShouldThrow(() => comp.Stop());
107 107 ShouldThrow(comp.Initialize);
108 108
109 109 Assert.AreEqual(ExecutionState.Disposed, comp.State);
110 110 Assert.IsTrue(disposed);
111 111 }
112 112
113 113 [TestMethod]
114 114 public void ShouldNotCallDisposeOnStop() {
115 115 var comp = new MockRunnableComponent(true, true);
116 116
117 117 bool disposed = false;
118 comp.MockDispose = (disposing, error) => {
118 comp.MockDispose = (disposing) => {
119 119 disposed = true;
120 120 };
121 121
122 122 comp.Start().Join(1000);
123 123 comp.Stop().Join(1000);
124 124
125 125 Assert.AreEqual(ExecutionState.Ready, comp.State);
126 126 Assert.IsFalse(disposed);
127 127 }
128 128
129 129 [TestMethod]
130 130 public void SelfDisposeOnStop() {
131 131 var comp = new MockRunnableComponent(true, true);
132 132
133 133 bool disposed = false;
134 Exception lastError = null;
135 comp.MockDispose = (disposing, error) => {
134 comp.MockDispose = (disposing) => {
136 135 disposed = true;
137 lastError = error;
138 136 };
139 137
140 138 comp.Start().Join(1000);
141 139 comp.Stop().Join(1000);
142 140
143 141 Assert.AreEqual(ExecutionState.Ready, comp.State);
144 142 Assert.IsFalse(disposed);
145 143
146 144 comp.MockStop = () => {
147 145 comp.Dispose();
148 146 return Promise.Success;
149 147 };
150 148
151 149 comp.Start().Join(1000);
152 150 comp.Stop().Join(1000);
153 151
154 152 Assert.AreEqual(ExecutionState.Disposed, comp.State);
155 153 Assert.IsTrue(disposed);
156 154 }
157 155
158 156 [TestMethod]
159 157 public void StartCancelTest() {
160 158 var comp = new MockRunnableComponent(true) {
161 159 MockStart = () => PromiseHelper.Sleep(100000, 0)
162 160 };
163 161
164 162 var p = comp.Start();
165 163 Assert.AreEqual(ExecutionState.Starting, comp.State);
166 164 p.Cancel();
167 165 ShouldThrow(() => p.Join(1000));
168 166 Assert.AreEqual(ExecutionState.Failed, comp.State);
169 167
170 168 Assert.IsTrue(comp.LastError is OperationCanceledException);
171 169
172 170 comp.Dispose();
173 171 }
174 172
175 173 [TestMethod]
176 174 public void StartStopTest() {
177 175 var stop = new Signal();
178 176 var comp = new MockRunnableComponent(true) {
179 177 MockStart = () => PromiseHelper.Sleep(100000, 0),
180 178 MockStop = () => AsyncPool.RunThread(stop.Wait)
181 179 };
182 180
183 181 var p1 = comp.Start();
184 182 var p2 = comp.Stop();
185 183 // should enter stopping state
186 184
187 185 ShouldThrow(p1.Join);
188 186 Assert.IsTrue(p1.IsCancelled);
189 187 Assert.AreEqual(ExecutionState.Stopping, comp.State);
190 188
191 189 stop.Set();
192 190 p2.Join(1000);
193 191 Assert.AreEqual(ExecutionState.Disposed, comp.State);
194 192 }
195 193
196 194 [TestMethod]
197 195 public void StartStopFailTest() {
198 196 var comp = new MockRunnableComponent(true) {
199 197 MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); })
200 198 };
201 199
202 200 comp.Start();
203 201 var p = comp.Stop();
204 202 // if Start fails to cancel, should fail to stop
205 203 ShouldThrow(() => p.Join(1000));
206 204 Assert.AreEqual(ExecutionState.Failed, comp.State);
207 205 Assert.IsNotNull(comp.LastError);
208 206 Assert.AreEqual("I'm dead", comp.LastError.Message);
209 207 }
210 208
211 209 [TestMethod]
212 210 public void StopCancelTest() {
213 211 var comp = new MockRunnableComponent(true) {
214 212 MockStop = () => PromiseHelper.Sleep(100000, 0)
215 213 };
216 214
217 215 comp.Start();
218 216 var p = comp.Stop();
219 217 Assert.AreEqual(ExecutionState.Stopping, comp.State);
220 218 p.Cancel();
221 219 ShouldThrow(() => p.Join(1000));
222 220 Assert.AreEqual(ExecutionState.Failed, comp.State);
223 221 Assert.IsTrue(comp.LastError is OperationCanceledException);
224 222
225 223 comp.Dispose();
226 224 }
227 225
228 226 }
229 227 }
230 228
@@ -1,103 +1,105
1 1 using Implab.Diagnostics;
2 2 using System;
3 using System.Diagnostics.CodeAnalysis;
3 4 using System.Threading;
4 5
5 6 namespace Implab.Components {
6 7 /// <summary>
7 8 /// Base class the objects which support disposing.
8 9 /// </summary>
9 10 public class Disposable : IDisposable {
10 11
11 12 int m_disposed;
12 13
13 14 public event EventHandler Disposed;
14 15
15 16 public bool IsDisposed {
16 17 get {
17 18 Thread.MemoryBarrier();
18 19 return m_disposed != 0;
19 20 }
20 21 }
21 22
22 23 /// <summary>
23 24 /// Asserts the object is not disposed.
24 25 /// </summary>
25 26 /// <exception cref="ObjectDisposedException">The object is disposed</exception>
26 27 /// <remarks>
27 28 /// УспСшная ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π½Π΅ освобоТдСн Π΅Ρ‰Π΅ Π½Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΎΠ½ Π½Π΅
28 29 /// Π±ΡƒΠ΄Π΅Ρ‚ освобоТдСн сразу послС Π½Π΅Π΅, поэтому ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΠΈΠ΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ Π΄ΠΎΠ»ΠΆΠ½Ρ‹
29 30 /// ΡƒΡ‡ΠΈΡ‚Ρ‹Π²Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ освобоТдСн ΠΈΠ· ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ°.
30 31 /// Π”Π°Π½Π½Ρ‹ ΠΌΠ΅Ρ‚ΠΎΠ΄ слуТит для упрощСния ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ ошибок ΠΏΡ€ΠΈ использовании ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° послС Π΅Π³ΠΎ
31 32 /// освобоТдСния.
32 33 /// </remarks>
33 34 /// <example>
34 35 /// // ΠΏΡ€ΠΈΠΌΠ΅Ρ€ синхронизированного освобоТдСния рСсурсов
35 36 /// class FileStore : Disposable {
36 37 /// readonly TextWriter m_file;
37 38 /// readonly obejct m_sync = new object();
38 39 ///
39 40 /// public FileStore(string file) {
40 41 /// m_file = new TextWriter(File.OpenWrite(file));
41 42 /// }
42 43 ///
43 44 /// public void Write(string text) {
44 45 /// lock(m_sync) {
45 46 /// AssertNotDisposed();
46 47 /// m_file.Write(text);
47 48 /// }
48 49 /// }
49 50 ///
50 51 /// protected override void Dispose(bool disposing) {
51 52 /// if (disposing)
52 53 /// lock(m_sync) {
53 54 /// m_file.Dipose();
54 55 /// base.Dispose(true);
55 56 /// }
56 57 /// else
57 58 /// base.Dispose(false);
58 59 /// }
59 60 /// }
60 61 /// <example>
61 62 protected void AssertNotDisposed() {
62 63 Thread.MemoryBarrier();
63 64 if (m_disposed != 0)
64 65 throw new ObjectDisposedException(ToString());
65 66 }
66 67 /// <summary>
67 68 /// Π’Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ событиС <see cref="Disposed"/>
68 69 /// </summary>
69 70 /// <param name="disposing">ΠŸΡ€ΠΈΠ·Π½Π°ΠΊ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ Π½ΡƒΠΆΠ½ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡ‚ΡŒ рСсурсы, ΠΈΠ½Π°Ρ‡Π΅ Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄
70 71 /// Π²Ρ‹Π·Π²Π°Π½ сборщиком мусора ΠΈ Π½ΡƒΠΆΠ½ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π°Ρ‚ΡŒ Π’ΠžΠ›Π¬ΠšΠž нСуправляСмыС рСсурсы Π’ΠžΠ›Π¬ΠšΠž этого
71 72 /// ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°.</param>
72 73 /// <remarks>
73 74 /// Π”Π°Π½Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ вызываСтся Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎ ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π· Π΄Π°ΠΆΠ΅ ΠΏΡ€ΠΈ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΌ Π²Ρ‹Π·ΠΎΠ²Π΅ <see cref="Dispose()"/>
74 75 /// ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ².
75 76 /// </remarks>
76 77 protected virtual void Dispose(bool disposing) {
77 78 if (disposing) {
78 79 EventHandler temp = Disposed;
79 80 if (temp != null)
80 81 temp(this, EventArgs.Empty);
81 82 }
82 83 }
83 84
85 [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")]
84 86 public void Dispose() {
85 87 if (Interlocked.Increment(ref m_disposed) == 1) {
86 88 Dispose(true);
87 89 GC.SuppressFinalize(this);
88 90 }
89 91 }
90 92
91 93 /// <summary>
92 94 /// ЗаписываСт сообщСниС ΠΎΠ± ΡƒΡ‚Π΅Ρ‡ΠΊΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°.
93 95 /// </summary>
94 96 protected virtual void ReportObjectLeaks() {
95 97 TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this);
96 98 }
97 99
98 100 ~Disposable() {
99 101 Dispose(false);
100 102 ReportObjectLeaks();
101 103 }
102 104 }
103 105 } No newline at end of file
@@ -1,22 +1,30
1 1 using System;
2 2
3 3 namespace Implab.Components {
4 public interface IRunnable {
5 4 /// <summary>
6 /// Starts this instance.
5 /// Interface for the component which performs a long running task.
6 /// </summary>
7 /// <remarks>
8 /// <para>The component also should implement <see cref="IDisposable"/> interface to be able to release used resources.</para>
9 /// <para>All methods of this interface must be a thread safe. If the operation is not applicable in the current state the
10 /// method should throw an exception and keep the current state unchanged.</para>
11 /// </remarks>
12 public interface IRunnable : IDisposable {
13 /// <summary>
14 /// Starts this instance
7 15 /// </summary>
8 16 IPromise Start();
9 17
10 18 /// <summary>
11 /// Stops this instance. After the instance is stopped it can't be started again, stopping should be treated as gracefull and async dispose.
19 /// Stops this instance, after the instance is stopped it can move to Failed, Ready or Disposed state, in case with the last it can't be reused.
12 20 /// </summary>
13 21 IPromise Stop();
14 22
15 23 ExecutionState State { get; }
16 24
17 25 event EventHandler<StateChangeEventArgs> StateChanged;
18 26
19 27 Exception LastError { get; }
20 28 }
21 29 }
22 30
@@ -1,155 +1,155
1 1 using System;
2 2 using System.Threading;
3 3 using Implab.Diagnostics;
4 4
5 5 namespace Implab.Components {
6 6 public class PollingComponent : RunnableComponent {
7 7 readonly Timer m_timer;
8 8 readonly Func<Func<ICancellationToken, IPromise>, IPromise> m_dispatcher;
9 9 readonly TimeSpan m_interval;
10 10
11 11 readonly object m_lock = new object();
12 12
13 13 ActionTask m_pending;
14 14
15 15 protected PollingComponent(TimeSpan interval, Func<Func<ICancellationToken, IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) {
16 16 m_timer = new Timer(OnInternalTick);
17 17
18 18 m_interval = interval;
19 19 m_dispatcher = dispatcher;
20 20 }
21 21
22 22 protected override IPromise OnStart() {
23 23 m_timer.Change(TimeSpan.Zero, m_interval);
24 24
25 25 return base.OnStart();
26 26 }
27 27
28 28 void OnInternalTick(object state) {
29 29 if (StartTick()) {
30 30 try {
31 31 if (m_dispatcher != null) {
32 32 var result = m_dispatcher(OnTick);
33 33 m_pending.CancellationRequested(result.Cancel);
34 34 AwaitTick(result);
35 35 } else {
36 36 AwaitTick(OnTick(m_pending));
37 37 }
38 38 } catch (Exception error) {
39 39 HandleTickError(error);
40 40 }
41 41 }
42 42 }
43 43
44 44 /// <summary>
45 45 /// Checks wheather there is no running handler in the component and marks that the handler is starting.
46 46 /// </summary>
47 47 /// <returns>boolean value, true - the new tick handler may be invoked, false - a tick handler is already running or a component isn't running.</returns>
48 48 /// <remarks>
49 49 /// If the component is stopping no new handlers can be run. Every successful call to this method must be completed with either AwaitTick or HandleTickError handlers.
50 50 /// </remarks>
51 51 protected virtual bool StartTick() {
52 52 lock (m_lock) {
53 53 if (State != ExecutionState.Running || m_pending != null)
54 54 return false;
55 55 // actually the component may be in a not running state here (stopping, disposed or what ever),
56 56 // but OnStop method also locks on the same object and will handle operation in m_pending
57 57 m_pending = new ActionTask(
58 58 () => {
59 59 // only one operation is running, it's safe to assing m_pending from it
60 60 m_pending = null;
61 61 },
62 62 ex => {
63 63 try {
64 64 OnTickError(ex);
65 65 // Analysis disable once EmptyGeneralCatchClause
66 66 } catch {
67 67 } finally {
68 68 m_pending = null;
69 69 }
70 70 // suppress error
71 71 },
72 72 ex => {
73 73 try {
74 74 OnTickCancel(ex);
75 75 // Analysis disable once EmptyGeneralCatchClause
76 76 } catch {
77 77 } finally {
78 78 m_pending = null;
79 79 }
80 80 // supress cancellation
81 81 },
82 82 false
83 83 );
84 84 return true;
85 85 }
86 86 }
87 87
88 88 /// <summary>
89 89 /// Awaits the tick.
90 90 /// </summary>
91 91 /// <param name="tick">Tick.</param>
92 92 /// <remarks>
93 93 /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled.
94 94 /// </remarks>
95 95 void AwaitTick(IPromise tick) {
96 96 if (tick == null) {
97 97 m_pending.Resolve();
98 98 } else {
99 99 tick.On(
100 100 m_pending.Resolve,
101 101 m_pending.Reject,
102 102 m_pending.CancelOperation
103 103 );
104 104 }
105 105 }
106 106
107 107 /// <summary>
108 108 /// Handles the tick error.
109 109 /// </summary>
110 110 /// <remarks>
111 111 /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled.
112 112 /// </remarks>
113 113 void HandleTickError(Exception error) {
114 114 m_pending.Reject(error);
115 115 }
116 116
117 117 protected virtual void OnTickError(Exception error) {
118 118 }
119 119
120 120 protected virtual void OnTickCancel(Exception error) {
121 121 }
122 122
123 123 /// <summary>
124 124 /// Invoked when the timer ticks, use this method to implement your logic
125 125 /// </summary>
126 126 protected virtual IPromise OnTick(ICancellationToken cancellationToken) {
127 127 return Promise.Success;
128 128 }
129 129
130 130 protected override IPromise OnStop() {
131 131 m_timer.Change(-1, -1);
132 132
133 133 // the component is in the stopping state
134 134 lock (m_lock) {
135 135 // after this lock no more pending operations could be created
136 136 var pending = m_pending;
137 137 // m_pending could be fulfilled and set to null already
138 138 if (pending != null) {
139 139 pending.Cancel();
140 140 return pending.Then(base.OnStop);
141 141 }
142 142 }
143 143
144 144 return base.OnStop();
145 145 }
146 146
147 protected override void Dispose(bool disposing, Exception lastError) {
147 protected override void Dispose(bool disposing) {
148 148 if (disposing)
149 Safe.Dispose(m_timer);
149 m_timer.Dispose();
150 150
151 base.Dispose(disposing, lastError);
151 base.Dispose(disposing);
152 152 }
153 153 }
154 154 }
155 155
@@ -1,375 +1,368
1 1 using System;
2 using System.Diagnostics.CodeAnalysis;
2 3
3 4 namespace Implab.Components {
4 5 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
5 6 enum Commands {
6 7 Ok = 0,
7 8 Fail,
8 9 Init,
9 10 Start,
10 11 Stop,
11 12 Dispose,
12 13 Reset,
13 14 Last = Reset
14 15 }
15 16
16 17 class StateMachine {
17 18 static readonly ExecutionState[,] _transitions;
18 19
19 20 static StateMachine() {
20 21 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
21 22
22 23 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init);
23 24 Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose);
24 25
25 26 Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok);
26 27 Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail);
27 28
28 29 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
29 30 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);
30 31
31 32 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok);
32 33 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail);
33 34 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop);
34 35 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose);
35 36
36 37 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail);
37 38 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop);
38 39 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);
39 40
40 41 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
41 42 Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
42 43 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose);
43 44
44 45 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
45 46 Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset);
46 47 }
47 48
48 49 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
49 50 _transitions[(int)s1, (int)cmd] = s2;
50 51 }
51 52
52 53 public ExecutionState State {
53 54 get;
54 55 private set;
55 56 }
56 57
57 58 public StateMachine(ExecutionState initial) {
58 59 State = initial;
59 60 }
60 61
61 62 public bool Move(Commands cmd) {
62 63 var next = _transitions[(int)State, (int)cmd];
63 64 if (next == ExecutionState.Undefined)
64 65 return false;
65 66 State = next;
66 67 return true;
67 68 }
68 69 }
69 70
70 71 IPromise m_pending;
71 72 Exception m_lastError;
72 73
73 74 readonly StateMachine m_stateMachine;
74 75 readonly bool m_reusable;
75 76 public event EventHandler<StateChangeEventArgs> StateChanged;
76 77
77 78 /// <summary>
78 79 /// Initializes component state.
79 80 /// </summary>
80 81 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
81 82 /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param>
82 83 protected RunnableComponent(bool initialized, bool reusable) {
83 84 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
84 85 m_reusable = reusable;
85 86 DisposeTimeout = 10000;
86 87 }
87 88
88 89 /// <summary>
89 90 /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop.
90 91 /// </summary>
91 92 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
92 93 protected RunnableComponent(bool initialized) : this(initialized, false) {
93 94 }
94 95
95 96 /// <summary>
96 97 /// Gets or sets the timeout to wait for the pending operation to complete. If the pending operation doesn't finish than the component will be disposed anyway.
97 98 /// </summary>
98 99 protected int DisposeTimeout {
99 100 get;
100 101 set;
101 102 }
102 103
103 104 void ThrowInvalidCommand(Commands cmd) {
104 105 if (m_stateMachine.State == ExecutionState.Disposed)
105 106 throw new ObjectDisposedException(ToString());
106 107
107 108 throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
108 109 }
109 110
110 111 bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) {
111 112 ExecutionState prev, current;
112 113 lock (m_stateMachine) {
113 114 if (m_stateMachine.State != state)
114 115 return false;
115 116
116 117 prev = m_stateMachine.State;
117 118 if (!m_stateMachine.Move(cmd))
118 119 ThrowInvalidCommand(cmd);
119 120 current = m_stateMachine.State;
120 121
121 122 m_pending = pending;
122 123 m_lastError = error;
123 124 }
124 125 if (prev != current)
125 126 OnStateChanged(prev, current, error);
126 127 return true;
127 128 }
128 129
129 130 bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) {
130 131 ExecutionState prev, current;
131 132 lock (m_stateMachine) {
132 133 if (m_pending != expected)
133 134 return false;
134 135 prev = m_stateMachine.State;
135 136 if (!m_stateMachine.Move(cmd))
136 137 ThrowInvalidCommand(cmd);
137 138 current = m_stateMachine.State;
138 139 m_pending = pending;
139 140 m_lastError = error;
140 141 }
141 142 if (prev != current)
142 143 OnStateChanged(prev, current, error);
143 144 return true;
144 145 }
145 146
146 147 IPromise Move(Commands cmd, IPromise pending, Exception error) {
147 148 ExecutionState prev, current;
148 149 IPromise ret;
149 150 lock (m_stateMachine) {
150 151 prev = m_stateMachine.State;
151 152 if (!m_stateMachine.Move(cmd))
152 153 ThrowInvalidCommand(cmd);
153 154 current = m_stateMachine.State;
154 155
155 156 ret = m_pending;
156 157 m_pending = pending;
157 158 m_lastError = error;
158 159
159 160 }
160 161 if(prev != current)
161 162 OnStateChanged(prev, current, error);
162 163 return ret;
163 164 }
164 165
165 166 protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) {
166 167 var h = StateChanged;
167 168 if (h != null)
168 169 h(this, new StateChangeEventArgs {
169 170 State = current,
170 171 LastError = error
171 172 });
172 173 }
173 174
174 175 /// <summary>
175 176 /// Moves the component from running to failed state.
176 177 /// </summary>
177 178 /// <param name="error">The exception which is describing the error.</param>
178 179 protected bool Fail(Exception error) {
179 180 return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
180 181 }
181 182
182 183 /// <summary>
183 184 /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
184 185 /// </summary>
185 186 /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't
186 187 /// in <see cref="ExecutionState.Failed"/> state.</returns>
187 188 /// <remarks>
188 189 /// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/>
189 190 /// moves component to <see cref="ExecutionState.Initializing"/>.
190 191 /// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved
191 192 /// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/>
192 193 /// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller.
193 194 /// </remarks>
194 195 protected bool ResetState() {
195 196 if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
196 197 return false;
197 198
198 199 try {
199 200 OnResetState();
200 201 Move(Commands.Ok, null, null);
201 202 return true;
202 203 } catch (Exception err) {
203 204 Move(Commands.Fail, null, err);
204 205 throw;
205 206 }
206 207 }
207 208
208 209 /// <summary>
209 210 /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
210 211 /// </summary>
211 212 /// <remarks>
212 213 /// Default implementation throws <see cref="NotImplementedException"/> which will cause the component
213 214 /// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state.
214 215 /// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state.
215 216 /// </remarks>
216 217 protected virtual void OnResetState() {
217 218 throw new NotImplementedException();
218 219 }
219 220
220 221 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
221 222 IPromise promise = null;
222 223 IPromise prev;
223 224
224 225 var task = new ActionChainTask(action, null, null, true);
225 226
226 227 Action<Exception> errorOrCancel = e => {
227 228 if (e == null)
228 229 e = new OperationCanceledException();
229 230 MoveIfPending(Commands.Fail, null, e, promise);
230 231 throw new PromiseTransientException(e);
231 232 };
232 233
233 234 promise = task.Then(
234 235 () => MoveIfPending(Commands.Ok, null, null, promise),
235 236 errorOrCancel,
236 237 errorOrCancel
237 238 );
238 239
239 240 prev = Move(cmd, promise, null);
240 241
241 242 if (prev == null)
242 243 task.Resolve();
243 244 else
244 245 chain(prev, task);
245 246
246 247 return promise;
247 248 }
248 249
249 250
250 251 #region IInitializable implementation
251 252
252 253 public void Initialize() {
253 254 Move(Commands.Init, null, null);
254 255
255 256 try {
256 257 OnInitialize();
257 258 Move(Commands.Ok, null, null);
258 259 } catch (Exception err) {
259 260 Move(Commands.Fail, null, err);
260 261 throw;
261 262 }
262 263 }
263 264
264 265 protected virtual void OnInitialize() {
265 266 }
266 267
267 268 #endregion
268 269
269 270 #region IRunnable implementation
270 271
271 272 public IPromise Start() {
272 273 return InvokeAsync(Commands.Start, OnStart, null);
273 274 }
274 275
275 276 protected virtual IPromise OnStart() {
276 277 return Promise.Success;
277 278 }
278 279
279 280 public IPromise Stop() {
280 281 var pending = InvokeAsync(Commands.Stop, OnStop, StopPending);
281 282 return m_reusable ? pending : pending.Then(Dispose);
282 283 }
283 284
284 285 protected virtual IPromise OnStop() {
285 286 return Promise.Success;
286 287 }
287 288
288 289 /// <summary>
289 290 /// Stops the current operation if one exists.
290 291 /// </summary>
291 292 /// <param name="current">Current.</param>
292 293 /// <param name="stop">Stop.</param>
293 294 protected virtual void StopPending(IPromise current, IDeferred stop) {
294 295 if (current == null) {
295 296 stop.Resolve();
296 297 } else {
297 298 // связваСм Ρ‚Π΅ΠΊΡƒΡ‰ΡƒΡŽ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΡŽ с ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ остановки
298 299 current.On(
299 300 stop.Resolve, // Ссли тСкущая опСрация Π·Π°Π²Π΅Ρ€Ρ‰ΠΈΠ»Π°ΡΡŒ, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°Ρ‡ΠΈΠ½Π°Ρ‚ΡŒ остановку
300 301 stop.Reject, // Ссли тСкущая опСрация Π΄Π°Π»Π° ΠΎΡˆΠΈΠ±ΠΊΡƒ - Ρ‚ΠΎ всС ΠΏΠ»ΠΎΡ…ΠΎ, нСльзя ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Ρ‚ΡŒ
301 302 e => stop.Resolve() // Ссли тСкущая ΠΎΡ‚ΠΌΠ΅Π½ΠΈΠ»Π°ΡΡŒ, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°Ρ‡ΠΈΠ½Π°Ρ‚ΡŒ остановку
302 303 );
303 304 // посылаСм Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ сигнал остановки
304 305 current.Cancel();
305 306 }
306 307 }
307 308
308 309 public ExecutionState State {
309 310 get {
310 311 return m_stateMachine.State;
311 312 }
312 313 }
313 314
314 315 public Exception LastError {
315 316 get {
316 317 return m_lastError;
317 318 }
318 319 }
319 320
320 321 #endregion
321 322
322 323 #region IDisposable implementation
323 324
324 325 /// <summary>
325 326 /// Releases all resource used by the <see cref="Implab.Components.RunnableComponent"/> object.
326 327 /// </summary>
327 328 /// <remarks>
328 329 /// <para>Will not try to stop the component, it will just release all resources.
329 330 /// To cleanup the component gracefully use <see cref="Stop()"/> method.</para>
330 331 /// <para>
331 332 /// In normal cases the <see cref="Dispose()"/> method shouldn't be called, the call to the <see cref="Stop()"/>
332 333 /// method is sufficient to cleanup the component. Call <see cref="Dispose()"/> only to cleanup after errors,
333 334 /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may
334 335 /// lead to the data loss by the component.
335 336 /// </para></remarks>
337 [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")]
336 338 public void Dispose() {
337 339 IPromise pending;
338 340
339 341 lock (m_stateMachine) {
340 342 if (m_stateMachine.State == ExecutionState.Disposed)
341 343 return;
342 pending = Move(Commands.Dispose, null, null);
344 Move(Commands.Dispose, null, null);
343 345 }
344 346
345 347 GC.SuppressFinalize(this);
346 if (pending != null) {
347 pending.Cancel();
348 pending.Timeout(DisposeTimeout).On(
349 () => Dispose(true, null),
350 err => Dispose(true, err),
351 reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason))
352 );
353 } else {
354 Dispose(true, null);
355 }
348 Dispose(true);
356 349 }
357 350
358 351 ~RunnableComponent() {
359 Dispose(false, null);
352 Dispose(false);
360 353 }
361 354
362 355 #endregion
363 356
364 357 /// <summary>
365 358 /// Releases all resources used by the component, called automatically, override this method to implement your cleanup.
366 359 /// </summary>
367 360 /// <param name="disposing">true if this method is called during normal dispose process.</param>
368 /// <param name="lastError">The last error which occured during the component stop.</param>
369 protected virtual void Dispose(bool disposing, Exception lastError) {
361 /// <param name="pending">The operation which is currenty pending</param>
362 protected virtual void Dispose(bool disposing) {
370 363
371 364 }
372 365
373 366 }
374 367 }
375 368
@@ -1,293 +1,293
1 1 using System;
2 2 using System.Diagnostics;
3 3 using System.IO;
4 4 using Implab.Automaton;
5 5 using Implab.Automaton.RegularExpressions;
6 6 using System.Linq;
7 7 using Implab.Components;
8 8 using System.Collections.Generic;
9 9
10 10 namespace Implab.Formats.JSON {
11 11 /// <summary>
12 12 /// Pull парсСр JSON Π΄Π°Π½Π½Ρ‹Ρ….
13 13 /// </summary>
14 14 /// <remarks>
15 15 /// Π‘Π»Π΅Π΄ΡƒΠ΅Ρ‚ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΈΠ½Ρ‚Π΅Ρ€ΠΏΡ€Π΅Ρ‚Π°Ρ†ΠΈΡŽ свойства <see cref="Level"/>,
16 16 /// ΠΎΠ½ΠΎ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ влоТСнности ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², ΠΎΠ΄Π½Π°ΠΊΠΎ Π·Π°ΠΊΡ€Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΉ
17 17 /// элСмСнт ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΠΈ массива ΠΈΠΌΠ΅Π΅Ρ‚ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ мСньшС, Ρ‡Π΅ΠΌ сам ΠΎΠ±ΡŠΠ΅ΠΊΡ‚.
18 18 /// <code>
19 19 /// { // Level = 1
20 20 /// "name" : "Peter", // Level = 1
21 21 /// "address" : { // Level = 2
22 22 /// city : "Stern" // Level = 2
23 23 /// } // Level = 1
24 24 /// } // Level = 0
25 25 /// </code>
26 26 /// </remarks>
27 27 public class JSONParser : Disposable {
28 28
29 29 enum MemberContext {
30 30 MemberName,
31 31 MemberValue
32 32 }
33 33
34 34 #region Parser rules
35 35 struct ParserContext {
36 36 readonly int[,] m_dfa;
37 37 int m_state;
38 38
39 39 readonly JSONElementContext m_elementContext;
40 40
41 41 public ParserContext(int[,] dfa, int state, JSONElementContext context) {
42 42 m_dfa = dfa;
43 43 m_state = state;
44 44 m_elementContext = context;
45 45 }
46 46
47 47 public bool Move(JsonTokenType token) {
48 48 var next = m_dfa[m_state, (int)token];
49 49 if (next == AutomatonConst.UNREACHABLE_STATE)
50 50 return false;
51 51 m_state = next;
52 52 return true;
53 53 }
54 54
55 55 public JSONElementContext ElementContext {
56 56 get { return m_elementContext; }
57 57 }
58 58 }
59 59
60 60 static readonly ParserContext _jsonContext;
61 61 static readonly ParserContext _objectContext;
62 62 static readonly ParserContext _arrayContext;
63 63
64 64 static JSONParser() {
65 65
66 66 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
67 67 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
68 68
69 69 var objectExpression = memberExpression
70 70 .Cat(
71 71 MakeToken(JsonTokenType.ValueSeparator)
72 72 .Cat(memberExpression)
73 73 .EClosure()
74 74 )
75 75 .Optional()
76 76 .Cat(MakeToken(JsonTokenType.EndObject))
77 77 .End();
78 78
79 79 var arrayExpression = valueExpression
80 80 .Cat(
81 81 MakeToken(JsonTokenType.ValueSeparator)
82 82 .Cat(valueExpression)
83 83 .EClosure()
84 84 )
85 85 .Optional()
86 86 .Cat(MakeToken(JsonTokenType.EndArray))
87 87 .End();
88 88
89 89 var jsonExpression = valueExpression.End();
90 90
91 91 _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None);
92 92 _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object);
93 93 _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array);
94 94 }
95 95
96 96 static Token MakeToken(params JsonTokenType[] input) {
97 97 return Token.New( input.Select(t => (int)t).ToArray() );
98 98 }
99 99
100 100 static ParserContext CreateParserContext(Token expr, JSONElementContext context) {
101 101
102 102 var dfa = new DFATable();
103 103 var builder = new RegularExpressionVisitor(dfa);
104 104 expr.Accept(builder);
105 105 builder.BuildDFA();
106 106
107 107 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
108 108 }
109 109
110 110 #endregion
111 111
112 112 readonly JSONScanner m_scanner;
113 113 MemberContext m_memberContext;
114 114
115 115 JSONElementType m_elementType;
116 116 object m_elementValue;
117 117 string m_memberName = String.Empty;
118 118
119 119 Stack<ParserContext> m_stack = new Stack<ParserContext>();
120 120 ParserContext m_context = _jsonContext;
121 121
122 122 /// <summary>
123 123 /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ парсСр Π½Π° основС строки, содСрТащСй JSON
124 124 /// </summary>
125 125 /// <param name="text"></param>
126 126 public JSONParser(string text) {
127 127 Safe.ArgumentNotEmpty(text, "text");
128 128 m_scanner = new JSONScanner(text);
129 129 }
130 130
131 131 /// <summary>
132 132 /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ экзСмпляр парсСра, Π½Π° основС тСкстового ΠΏΠΎΡ‚ΠΎΠΊΠ°.
133 133 /// </summary>
134 134 /// <param name="reader">ВСкстовый ΠΏΠΎΡ‚ΠΎΠΊ.</param>
135 135 public JSONParser(TextReader reader) {
136 136 Safe.ArgumentNotNull(reader, "reader");
137 137 m_scanner = new JSONScanner(reader);
138 138 }
139 139
140 140 public int Level {
141 141 get { return m_stack.Count; }
142 142 }
143 143
144 144 /// <summary>
145 145 /// Π’ΠΈΠΏ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ элСмСнта Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ стоит парсСр.
146 146 /// </summary>
147 147 public JSONElementType ElementType {
148 148 get { return m_elementType; }
149 149 }
150 150
151 151 /// <summary>
152 152 /// Имя элСмСнта - имя свойства Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°. Для элСмСнтов массивов ΠΈ ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠ³ΠΎ всСгда
153 153 /// пустая строка.
154 154 /// </summary>
155 155 public string ElementName {
156 156 get { return m_memberName; }
157 157 }
158 158
159 159 /// <summary>
160 160 /// Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ элСмСнта. Волько для элСмСнтов Ρ‚ΠΈΠΏΠ° <see cref="JSONElementType.Value"/>, для ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… <c>null</c>
161 161 /// </summary>
162 162 public object ElementValue {
163 163 get { return m_elementValue; }
164 164 }
165 165
166 166 /// <summary>
167 167 /// Π§ΠΈΡ‚Π°Π΅Ρ‚ ΡΠ»Π΅ΡŽΡƒΠ΄ΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΈΠ· ΠΏΠΎΡ‚ΠΎΠΊΠ°
168 168 /// </summary>
169 169 /// <returns><c>true</c> - опСрация чтСния ΠΏΡ€ΠΎΡˆΠ»Π° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ, <c>false</c> - ΠΊΠΎΠ½Π΅Ρ† Π΄Π°Π½Π½Ρ‹Ρ…</returns>
170 170 public bool Read() {
171 171 object tokenValue;
172 172 JsonTokenType tokenType;
173 173
174 174 m_memberName = String.Empty;
175 175
176 176 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
177 177 if(!m_context.Move(tokenType))
178 178 UnexpectedToken(tokenValue, tokenType);
179 179
180 180 switch (tokenType) {
181 181 case JsonTokenType.BeginObject:
182 182 m_stack.Push(m_context);
183 183 m_context = _objectContext;
184 184
185 185 m_elementValue = null;
186 186 m_memberContext = MemberContext.MemberName;
187 187 m_elementType = JSONElementType.BeginObject;
188 188 return true;
189 189 case JsonTokenType.EndObject:
190 190 if (m_stack.Count == 0)
191 191 UnexpectedToken(tokenValue, tokenType);
192 192 m_context = m_stack.Pop();
193 193
194 194 m_elementValue = null;
195 195 m_elementType = JSONElementType.EndObject;
196 196 return true;
197 197 case JsonTokenType.BeginArray:
198 198 m_stack.Push(m_context);
199 199 m_context = _arrayContext;
200 200
201 201 m_elementValue = null;
202 202 m_memberContext = MemberContext.MemberValue;
203 203 m_elementType = JSONElementType.BeginArray;
204 204 return true;
205 205 case JsonTokenType.EndArray:
206 206 if (m_stack.Count == 0)
207 207 UnexpectedToken(tokenValue, tokenType);
208 208 m_context = m_stack.Pop();
209 209
210 210 m_elementValue = null;
211 211 m_elementType = JSONElementType.EndArray;
212 212 return true;
213 213 case JsonTokenType.String:
214 214 if (m_memberContext == MemberContext.MemberName) {
215 215 m_memberName = (string)tokenValue;
216 216 break;
217 217 }
218 218 m_elementType = JSONElementType.Value;
219 219 m_elementValue = tokenValue;
220 220 return true;
221 221 case JsonTokenType.Number:
222 222 m_elementType = JSONElementType.Value;
223 223 m_elementValue = tokenValue;
224 224 return true;
225 225 case JsonTokenType.Literal:
226 226 m_elementType = JSONElementType.Value;
227 227 m_elementValue = ParseLiteral((string)tokenValue);
228 228 return true;
229 229 case JsonTokenType.NameSeparator:
230 230 m_memberContext = MemberContext.MemberValue;
231 231 break;
232 232 case JsonTokenType.ValueSeparator:
233 233 m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
234 234 break;
235 235 default:
236 236 UnexpectedToken(tokenValue, tokenType);
237 237 break;
238 238 }
239 239 }
240 240 if (m_context.ElementContext != JSONElementContext.None)
241 241 throw new ParserException("Unexpedted end of data");
242 242
243 243 EOF = true;
244 244
245 245 return false;
246 246 }
247 247
248 248 object ParseLiteral(string literal) {
249 249 switch (literal) {
250 250 case "null":
251 251 return null;
252 252 case "false":
253 253 return false;
254 254 case "true":
255 255 return true;
256 256 default:
257 257 UnexpectedToken(literal, JsonTokenType.Literal);
258 258 return null; // avoid compliler error
259 259 }
260 260 }
261 261
262 262 void UnexpectedToken(object value, JsonTokenType tokenType) {
263 263 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
264 264 }
265 265
266 266
267 267 /// <summary>
268 268 /// ΠŸΡ€ΠΈΠ·Π½Π°ΠΊ ΠΊΠΎΠ½Ρ†Π° ΠΏΠΎΡ‚ΠΎΠΊΠ°
269 269 /// </summary>
270 270 public bool EOF {
271 271 get;
272 272 private set;
273 273 }
274 274
275 275 protected override void Dispose(bool disposing) {
276 276 if (disposing)
277 Safe.Dispose(m_scanner);
277 m_scanner.Dispose();
278 278 }
279 279
280 280 /// <summary>
281 281 /// ΠŸΠ΅Ρ€Π΅Ρ…ΠΎΠ΄ΠΈΡ‚ Π² ΠΊΠΎΠ½Π΅Ρ† Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°.
282 282 /// </summary>
283 283 public void SeekElementEnd() {
284 284 var level = Level - 1;
285 285
286 286 Debug.Assert(level >= 0);
287 287
288 288 while (Level != level)
289 289 Read();
290 290 }
291 291 }
292 292
293 293 }
@@ -1,109 +1,109
1 1 using System;
2 2 using System.Globalization;
3 3 using Implab.Automaton;
4 4 using System.Text;
5 5 using Implab.Components;
6 6 using System.IO;
7 7
8 8 namespace Implab.Formats.JSON {
9 9 /// <summary>
10 10 /// Π‘ΠΊΠ°Π½Π½Π΅Ρ€ (лСксСр), Ρ€Π°Π·Π±ΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ ΠΏΠΎΡ‚ΠΎΠΊ символов Π½Π° Ρ‚ΠΎΠΊΠ΅Π½Ρ‹ JSON.
11 11 /// </summary>
12 12 public class JSONScanner : Disposable {
13 13 readonly StringBuilder m_builder = new StringBuilder();
14 14
15 15 readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression;
16 16 readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression;
17 17
18 18
19 19 readonly TextScanner m_scanner;
20 20
21 21 /// <summary>
22 22 /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ экзСмпляр сканнСра
23 23 /// </summary>
24 24 public JSONScanner(string text) {
25 25 Safe.ArgumentNotEmpty(text, "text");
26 26
27 27 m_scanner = new StringScanner(text);
28 28 }
29 29
30 30 public JSONScanner(TextReader reader, int bufferMax, int chunkSize) {
31 31 Safe.ArgumentNotNull(reader, "reader");
32 32
33 33 m_scanner = new ReaderScanner(reader, bufferMax, chunkSize);
34 34 }
35 35
36 36 public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){
37 37 }
38 38
39 39 /// <summary>
40 40 /// Π§ΠΈΡ‚Π°Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ лСксичСский элСмСнт ΠΈΠ· Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ….
41 41 /// </summary>
42 42 /// <param name="tokenValue">Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½ΠΎΠ³ΠΎ Ρ‚ΠΎΠΊΠ΅Π½Π°.</param>
43 43 /// <param name="tokenType">Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Ρ‚ΠΈΠΏ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½ΠΎΠ³ΠΎ Ρ‚ΠΎΠΊΠ΅Π½Π°.</param>
44 44 /// <returns><c>true</c> - Ρ‡Ρ‚Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΎ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ. <c>false</c> - достигнут ΠΊΠΎΠ½Π΅Ρ† Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ…</returns>
45 45 /// <remarks>Π’ случС Ссли Ρ‚ΠΎΠΊΠ΅Π½ Π½Π΅ распознаСтся, Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅. ЗначСния Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ² ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ, Ρ‚.Π΅.
46 46 /// Π² строках ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ экранированныС символы, числа становтся Ρ‚ΠΈΠΏΠ° double.</remarks>
47 47 public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
48 48 JSONGrammar.TokenType[] tag;
49 49 while (m_jsonContext.Execute(m_scanner, out tag)) {
50 50 switch (tag[0]) {
51 51 case JSONGrammar.TokenType.StringBound:
52 52 tokenValue = ReadString();
53 53 tokenType = JsonTokenType.String;
54 54 break;
55 55 case JSONGrammar.TokenType.Number:
56 56 tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture);
57 57 tokenType = JsonTokenType.Number;
58 58 break;
59 59 case JSONGrammar.TokenType.Whitespace:
60 60 continue;
61 61 default:
62 62 tokenType = (JsonTokenType)tag[0];
63 63 tokenValue = m_scanner.GetTokenValue();
64 64 break;
65 65 }
66 66 return true;
67 67 }
68 68 tokenValue = null;
69 69 tokenType = JsonTokenType.None;
70 70 return false;
71 71 }
72 72
73 73 string ReadString() {
74 74 int pos = 0;
75 75 var buf = new char[6]; // the buffer for unescaping chars
76 76
77 77 JSONGrammar.TokenType[] tag;
78 78 m_builder.Clear();
79 79
80 80 while (m_stringContext.Execute(m_scanner, out tag)) {
81 81 switch (tag[0]) {
82 82 case JSONGrammar.TokenType.StringBound:
83 83 return m_builder.ToString();
84 84 case JSONGrammar.TokenType.UnescapedChar:
85 85 m_scanner.CopyTokenTo(m_builder);
86 86 break;
87 87 case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
88 88 m_scanner.CopyTokenTo(buf, 0);
89 89 m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2));
90 90 pos++;
91 91 break;
92 92 case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence
93 93 m_scanner.CopyTokenTo(buf, 0);
94 94 m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1]));
95 95 break;
96 96 }
97 97
98 98 }
99 99
100 100 throw new ParserException("Unexpected end of data");
101 101 }
102 102
103 103 protected override void Dispose(bool disposing) {
104 104 if (disposing)
105 Safe.Dispose(m_scanner);
105 m_scanner.Dispose();
106 106 base.Dispose(disposing);
107 107 }
108 108 }
109 109 }
@@ -1,420 +1,448
1 1 using System.Threading;
2 2 using System;
3 3 using Implab.Diagnostics;
4 4 using System.Collections.Generic;
5 5 using System.Linq;
6 6
7 7 namespace Implab {
8 8 public static class PromiseExtensions {
9 9 public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
10 10 Safe.ArgumentNotNull(that, "that");
11 11 var context = SynchronizationContext.Current;
12 12 if (context == null)
13 13 return that;
14 14
15 15 var p = new SyncContextPromise<T>(context);
16 16 p.CancellationRequested(that.Cancel);
17 17
18 18 that.On(
19 19 p.Resolve,
20 20 p.Reject,
21 21 p.CancelOperation
22 22 );
23 23 return p;
24 24 }
25 25
26 26 public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) {
27 27 Safe.ArgumentNotNull(that, "that");
28 28 Safe.ArgumentNotNull(context, "context");
29 29
30 30 var p = new SyncContextPromise<T>(context);
31 31 p.CancellationRequested(that.Cancel);
32 32
33 33 that.On(
34 34 p.Resolve,
35 35 p.Reject,
36 36 p.CancelOperation
37 37 );
38 38 return p;
39 39 }
40 40
41 41 /// <summary>
42 42 /// Ensures the dispatched.
43 43 /// </summary>
44 44 /// <returns>The dispatched.</returns>
45 45 /// <param name="that">That.</param>
46 46 /// <param name="head">Head.</param>
47 47 /// <param name="cleanup">Cleanup.</param>
48 48 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
49 49 /// <typeparam name="T">The 2nd type parameter.</typeparam>
50 50 public static TPromise EnsureDispatched<TPromise, T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise {
51 51 Safe.ArgumentNotNull(that, "that");
52 52 Safe.ArgumentNotNull(head, "head");
53 53
54 54 that.On(() => head.On(cleanup), PromiseEventType.Cancelled);
55 55
56 56 return that;
57 57 }
58 58
59 59 /// <summary>
60 60 /// Adds a cancellation point to the chain of promises. When a cancellation request reaches the cancellation point the operation is
61 61 /// cancelled immediatelly, and the request is passed towards. If the operation at the higher level can not be cancelled is't result
62 62 /// will be collected with <paramref name="cleanup"/> callback.
63 63 /// </summary>
64 64 /// <typeparam name="T">The type of the promise result.</typeparam>
65 65 /// <param name="that">The promise to which the cancellation point should be attached.</param>
66 66 /// <param name="cleanup">The callback which is used to cleanup the result of the operation if the cancellation point is cancelled already.</param>
67 67 /// <returns>The promise</returns>
68 68 public static IPromise<T> CancellationPoint<T>(this IPromise<T> that, Action<T> cleanup) {
69 69 var meduim = new Promise<T>();
70 70
71 71 that.On(meduim.Resolve, meduim.Reject, meduim.CancelOperation);
72 72
73 73 meduim.CancellationRequested(that.Cancel);
74 74 meduim.CancellationRequested(meduim.CancelOperation);
75 75
76 76 if (cleanup != null)
77 77 meduim.On((Action<T>)null, null, (e) => {
78 78 that.On(cleanup);
79 79 });
80 80
81 81 return meduim;
82 82 }
83 83
84 84 public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult, T> callback) {
85 85 Safe.ArgumentNotNull(that, "that");
86 86 Safe.ArgumentNotNull(callback, "callback");
87 87 var op = TraceContext.Instance.CurrentOperation;
88 88 return ar => {
89 89 TraceContext.Instance.EnterLogicalOperation(op, false);
90 90 try {
91 91 that.Resolve(callback(ar));
92 92 } catch (Exception err) {
93 93 that.Reject(err);
94 94 } finally {
95 95 TraceContext.Instance.Leave();
96 96 }
97 97 };
98 98 }
99 99
100 100 static void CancelByTimeoutCallback(object cookie) {
101 101 ((ICancellable)cookie).Cancel(new TimeoutException());
102 102 }
103 103
104 104 /// <summary>
105 105 /// Cancells promise after the specified timeout is elapsed.
106 106 /// </summary>
107 107 /// <param name="that">The promise to cancel on timeout.</param>
108 108 /// <param name="milliseconds">The timeout in milliseconds.</param>
109 109 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
110 110 public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise {
111 111 Safe.ArgumentNotNull(that, "that");
112 112 var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1);
113 113 that.On(timer.Dispose, PromiseEventType.All);
114 114 return that;
115 115 }
116 116
117 117 public static IPromise PromiseAll(this IEnumerable<IPromise> that) {
118 118 Safe.ArgumentNotNull(that, "that");
119 119 return PromiseAll(that.ToList());
120 120 }
121 121
122 122 public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that) {
123 return PromiseAll(that, null);
124 }
125
126 public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that, Action<T> cleanup) {
123 127 Safe.ArgumentNotNull(that, "that");
124 return PromiseAll(that.ToList());
128 return PromiseAll(that.ToList(), cleanup);
125 129 }
126 130
127 131 public static IPromise PromiseAll(this ICollection<IPromise> that) {
128 132 Safe.ArgumentNotNull(that, "that");
129 133
130 134 int count = that.Count;
131 135 int errors = 0;
132 136 var medium = new Promise();
133 137
134 138 if (count == 0) {
135 139 medium.Resolve();
136 140 return medium;
137 141 }
138 142
139 143 medium.On(() => {
140 144 foreach (var p2 in that)
141 145 p2.Cancel();
142 146 }, PromiseEventType.ErrorOrCancel);
143 147
144 148 foreach (var p in that)
145 149 p.On(
146 150 () => {
147 151 if (Interlocked.Decrement(ref count) == 0)
148 152 medium.Resolve();
149 153 },
150 154 error => {
151 155 if (Interlocked.Increment(ref errors) == 1)
152 156 medium.Reject(
153 157 new Exception("The dependency promise is failed", error)
154 158 );
155 159 },
156 160 reason => {
157 161 if (Interlocked.Increment(ref errors) == 1)
158 162 medium.Cancel(
159 163 new Exception("The dependency promise is cancelled")
160 164 );
161 165 }
162 166 );
163 167
164 168 return medium;
165 169 }
166 170
167 171 public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that) {
172 return PromiseAll(that, null);
173 }
174
175 /// <summary>
176 /// Creates a new promise which will be satisfied when all promises are satisfied.
177 /// </summary>
178 /// <typeparam name="T"></typeparam>
179 /// <param name="that"></param>
180 /// <param name="cleanup">A callback used to cleanup already resolved promises in case of an error</param>
181 /// <returns></returns>
182 public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that, Action<T> cleanup) {
168 183 Safe.ArgumentNotNull(that, "that");
169 184
170 185 int count = that.Count;
186
187 if (count == 0)
188 return Promise<T[]>.FromResult(new T[0]);
189
171 190 int errors = 0;
172 191 var medium = new Promise<T[]>();
173 192 var results = new T[that.Count];
174 193
175 194 medium.On(() => {
176 foreach (var p2 in that)
195 foreach (var p2 in that) {
177 196 p2.Cancel();
197 if (cleanup != null)
198 p2.On(cleanup);
199 }
178 200 }, PromiseEventType.ErrorOrCancel);
179 201
180 202 int i = 0;
181 203 foreach (var p in that) {
182 204 var idx = i;
183 205 p.On(
184 206 x => {
185 207 results[idx] = x;
186 208 if (Interlocked.Decrement(ref count) == 0)
187 209 medium.Resolve(results);
188 210 },
189 211 error => {
190 212 if (Interlocked.Increment(ref errors) == 1)
191 213 medium.Reject(
192 214 new Exception("The dependency promise is failed", error)
193 215 );
194 216 },
195 217 reason => {
196 218 if (Interlocked.Increment(ref errors) == 1)
197 219 medium.Cancel(
198 220 new Exception("The dependency promise is cancelled", reason)
199 221 );
200 222 }
201 223 );
202 224 i++;
203 225 }
204 226
205 227 return medium;
206 228 }
207 229
208 230 public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) {
209 231 Safe.ArgumentNotNull(that, "that");
210 232
211 233 var d = new ActionTask(success, error, cancel, false);
212 234 that.On(d.Resolve, d.Reject, d.CancelOperation);
213 235 d.CancellationRequested(that.Cancel);
214 236 return d;
215 237 }
216 238
217 239 public static IPromise Then(this IPromise that, Action success, Action<Exception> error) {
218 240 return Then(that, success, error, null);
219 241 }
220 242
221 243 public static IPromise Then(this IPromise that, Action success) {
222 244 return Then(that, success, null, null);
223 245 }
224 246
225 247 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) {
226 248 Safe.ArgumentNotNull(that, "that");
227 249
228 250 var d = new FuncTask<T>(success, error, cancel, false);
229 251 that.On(d.Resolve, d.Reject, d.CancelOperation);
230 252 d.CancellationRequested(that.Cancel);
231 253 return d;
232 254 }
233 255
234 256 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) {
235 257 return Then(that, success, error, null);
236 258 }
237 259
238 260 public static IPromise<T> Then<T>(this IPromise that, Func<T> success) {
239 261 return Then(that, success, null, null);
240 262 }
241 263
242 264 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) {
243 265 Safe.ArgumentNotNull(that, "that");
244 266 var d = new FuncTask<T, T2>(success, error, cancel, false);
245 267 that.On(d.Resolve, d.Reject, d.CancelOperation);
246 268 d.CancellationRequested(that.Cancel);
247 269 return d;
248 270 }
249 271
250 272 public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error, Func<Exception, T> cancel) {
251 273 Safe.ArgumentNotNull(that, "that");
252 274 var d = new FuncTask<T, T>(
253 275 x => {
254 276 success(x);
255 277 return x;
256 278 },
257 279 error,
258 280 cancel,
259 281 false
260 282 );
261 283 that.On(d.Resolve, d.Reject, d.CancelOperation);
262 284 d.CancellationRequested(that.Cancel);
263 285 return d;
264 286 }
265 287
266 288 public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error) {
267 289 return Then(that, success, error, null);
268 290 }
269 291
270 292 public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success) {
271 293 return Then(that, success, null, null);
272 294 }
273 295
274 296 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) {
275 297 return Then(that, success, error, null);
276 298 }
277 299
278 300 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) {
279 301 return Then(that, success, null, null);
280 302 }
281 303
282 304 public static IPromise<T> Always<T>(this IPromise<T> that, Action handler) {
283 305 Func<Exception, T> errorOrCancel;
284 306 if (handler != null)
285 307 errorOrCancel = e => {
286 308 handler();
287 309 throw new PromiseTransientException(e);
288 310 };
289 311 else
290 312 errorOrCancel = null;
291 313
292 314 return Then(
293 315 that,
294 316 x => {
295 317 handler();
296 318 return x;
297 319 },
298 320 errorOrCancel,
299 321 errorOrCancel);
300 322 }
301 323
302 324 public static IPromise Always(this IPromise that, Action handler) {
303 325 Action<Exception> errorOrCancel;
304 326 if (handler != null)
305 327 errorOrCancel = e => {
306 328 handler();
307 329 throw new PromiseTransientException(e);
308 330 };
309 331 else
310 332 errorOrCancel = null;
311 333
312 334 return Then(
313 335 that,
314 336 handler,
315 337 errorOrCancel,
316 338 errorOrCancel);
317 339 }
318 340
319 341 public static IPromise Error(this IPromise that, Action<Exception> handler, bool handleCancellation) {
320 342 Action<Exception> errorOrCancel;
321 343 if (handler != null)
322 344 errorOrCancel = e => {
323 345 handler(e);
324 346 throw new PromiseTransientException(e);
325 347 };
326 348 else
327 349 errorOrCancel = null;
328 350
329 351 return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null);
330 352 }
331 353
332 354 public static IPromise Error(this IPromise that, Action<Exception> handler) {
333 355 return Error(that, handler, false);
334 356 }
335 357
336 358 public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler, bool handleCancellation) {
337 359 Func<Exception, T> errorOrCancel;
338 360 if (handler != null)
339 361 errorOrCancel = e => {
340 362 handler(e);
341 363 throw new PromiseTransientException(e);
342 364 };
343 365 else
344 366 errorOrCancel = null;
345 367
346 368 return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null);
347 369 }
348 370
349 371 public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler) {
350 372 return Error(that, handler, false);
351 373 }
352 374
353 375 #region chain traits
354 376 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) {
355 377 Safe.ArgumentNotNull(that, "that");
356 378
357 379 var d = new ActionChainTask(success, error, cancel, false);
358 380 that.On(d.Resolve, d.Reject, d.CancelOperation);
359 381 d.CancellationRequested(that.Cancel);
360 382 return d;
361 383 }
362 384
363 385 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error) {
364 386 return Chain(that, success, error, null);
365 387 }
366 388
367 389 public static IPromise Chain(this IPromise that, Func<IPromise> success) {
368 390 return Chain(that, success, null, null);
369 391 }
370 392
371 393 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) {
372 394 Safe.ArgumentNotNull(that, "that");
373 395
374 396 var d = new FuncChainTask<T>(success, error, cancel, false);
375 397 that.On(d.Resolve, d.Reject, d.CancelOperation);
376 398 if (success != null)
377 399 d.CancellationRequested(that.Cancel);
378 400 return d;
379 401 }
380 402
381 403 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) {
382 404 return Chain(that, success, error, null);
383 405 }
384 406
385 407 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) {
386 408 return Chain(that, success, null, null);
387 409 }
388 410
389 411 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) {
390 412 Safe.ArgumentNotNull(that, "that");
391 413 var d = new FuncChainTask<T, T2>(success, error, cancel, false);
392 414 that.On(d.Resolve, d.Reject, d.CancelOperation);
393 415 if (success != null)
394 416 d.CancellationRequested(that.Cancel);
395 417 return d;
396 418 }
397 419
398 420 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) {
399 421 return Chain(that, success, error, null);
400 422 }
401 423
402 424 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) {
403 425 return Chain(that, success, null, null);
404 426 }
405 427
406 428 #endregion
407 429
408 430
409 431 #if NET_4_5
410 432
411 433 public static PromiseAwaiter<T> GetAwaiter<T>(this IPromise<T> that) {
412 434 Safe.ArgumentNotNull(that, "that");
413 435
414 436 return new PromiseAwaiter<T>(that);
415 437 }
416 438
439 public static PromiseAwaiter GetAwaiter(this IPromise that) {
440 Safe.ArgumentNotNull(that, "that");
441
442 return new PromiseAwaiter(that);
443 }
444
417 445 #endif
418 446 }
419 447 }
420 448
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now