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