##// END OF EJS Templates
Prerelease version of RunnableComponent...
cin -
r251:7c7e9ad6fe4a v3
parent child
Show More
@@ -0,0 +1,14
1 namespace Implab.Messaging {
2 public interface ISession {
3 /// <summary>
4 /// Starts message consumers, call this method after all adapters are ready
5 /// </summary>
6 void Start();
7
8 /// <summary>
9 /// Stops message consumers
10 /// </summary>
11 void Stop();
12
13 }
14 } No newline at end of file
@@ -0,0 +1,70
1 using System;
2 using System.Threading.Tasks;
3
4 namespace Implab {
5 public static class TaskHelpers {
6
7 public static async Task Then(this Task that, Action fulfilled, Action<Exception> rejected) {
8 Safe.ArgumentNotNull(that, nameof(that));
9 if (rejected != null) {
10 try {
11 await that;
12 } catch (Exception e) {
13 rejected(e);
14 return;
15 }
16 } else {
17 await that;
18 }
19
20 if (fulfilled != null)
21 fulfilled();
22 }
23
24 public static async Task Then(this Task that, Action fulfilled) {
25 Safe.ArgumentNotNull(that, nameof(that));
26 await that;
27 if (fulfilled != null)
28 fulfilled();
29 }
30
31 public static async Task Then(this Task that, Func<Task> fulfilled) {
32 Safe.ArgumentNotNull(that, nameof(that));
33 await that;
34 if (fulfilled != null)
35 await fulfilled();
36 }
37
38 public static async Task Finally(this Task that, Action handler) {
39 Safe.ArgumentNotNull(that, nameof(that));
40 try {
41 await that;
42 } finally {
43 if (handler != null)
44 handler();
45 }
46 }
47
48 public static async void Then(this Task that, IResolvable next) {
49 try {
50 await that;
51 } catch (Exception e) {
52 next.Reject(e);
53 return;
54 }
55 next.Resolve();
56 }
57
58 public static async void Then<T>(this Task<T> that, IResolvable<T> next) {
59 T result;
60 try {
61 result = await that;
62 } catch (Exception e) {
63 next.Reject(e);
64 return;
65 }
66 next.Resolve(result);
67 }
68
69 }
70 } No newline at end of file
@@ -1,17 +1,29
1 1 using System;
2 using System.Diagnostics;
2 3 using System.Threading;
3 4 using Xunit;
4 5
5 6 namespace Implab.Test
6 7 {
7 8 public class UnitTest1
8 9 {
9 10 [Fact]
10 11 public void Test1()
11 12 {
12 using(var cts = new CancellationTokenSource(1000)) {
13 PromiseHelper.Sleep(10000, cts.Token).Join();
14 }
13 var listener = new TextWriterTraceListener(Console.Out);
14 var source = new TraceSource("Custom",SourceLevels.ActivityTracing);
15
16 source.Listeners.Add(listener);
17
18 Trace.Listeners.Add(listener);
19 Trace.WriteLine("Hello!");
20 Trace.CorrelationManager.StartLogicalOperation();
21 Trace.WriteLine("Inner");
22 foreach(var x in Trace.CorrelationManager.LogicalOperationStack)
23 Trace.WriteLine($"-{x}");
24 source.TraceEvent(TraceEventType.Information, 1, "source event");
25 source.TraceData(TraceEventType.Start, 1, DateTime.Now);
26 Trace.CorrelationManager.StopLogicalOperation();
15 27 }
16 28 }
17 29 }
@@ -1,348 +1,348
1 1 using Implab;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Linq;
5 5 using System.Diagnostics;
6 6 using System.IO;
7 7 using System.CodeDom.Compiler;
8 8 using System.CodeDom;
9 9
10 10 namespace Implab.Automaton {
11 11 public class DFATable : IDFATableBuilder {
12 12 int m_stateCount;
13 13 int m_symbolCount;
14 14 int m_initialState;
15 15
16 16 readonly HashSet<int> m_finalStates = new HashSet<int>();
17 17 readonly HashSet<AutomatonTransition> m_transitions = new HashSet<AutomatonTransition>();
18 18
19 19
20 20 #region IDFADefinition implementation
21 21
22 22 public bool IsFinalState(int s) {
23 Safe.ArgumentInRange(s, 0, m_stateCount, "s");
23 Safe.ArgumentInRange(s >= 0 && s < m_stateCount, nameof(s));
24 24
25 25 return m_finalStates.Contains(s);
26 26 }
27 27
28 28 public IEnumerable<int> FinalStates {
29 29 get {
30 30 return m_finalStates;
31 31 }
32 32 }
33 33
34 34 public int StateCount {
35 35 get { return m_stateCount; }
36 36 }
37 37
38 38 public int AlphabetSize {
39 39 get { return m_symbolCount; }
40 40 }
41 41
42 42 public int InitialState {
43 43 get { return m_initialState; }
44 44 }
45 45
46 46 #endregion
47 47
48 48 public void SetInitialState(int s) {
49 Safe.ArgumentAssert(s >= 0, "s");
49 Safe.ArgumentInRange(s >= 0, nameof(s));
50 50 m_stateCount = Math.Max(m_stateCount, s + 1);
51 51 m_initialState = s;
52 52 }
53 53
54 54 public void MarkFinalState(int state) {
55 55 m_stateCount = Math.Max(m_stateCount, state + 1);
56 56 m_finalStates.Add(state);
57 57 }
58 58
59 59 public void Add(AutomatonTransition item) {
60 Safe.ArgumentAssert(item.s1 >= 0, "item");
61 Safe.ArgumentAssert(item.s2 >= 0, "item");
62 Safe.ArgumentAssert(item.edge >= 0, "item");
60 Safe.ArgumentAssert(item.s1 >= 0, nameof(item));
61 Safe.ArgumentAssert(item.s2 >= 0, nameof(item));
62 Safe.ArgumentAssert(item.edge >= 0, nameof(item));
63 63
64 64 m_stateCount = Math.Max(m_stateCount, Math.Max(item.s1, item.s2) + 1);
65 65 m_symbolCount = Math.Max(m_symbolCount, item.edge + 1);
66 66
67 67 m_transitions.Add(item);
68 68 }
69 69
70 70 public void Clear() {
71 71 m_stateCount = 0;
72 72 m_symbolCount = 0;
73 73 m_finalStates.Clear();
74 74 m_transitions.Clear();
75 75 }
76 76
77 77 public bool Contains(AutomatonTransition item) {
78 78 return m_transitions.Contains(item);
79 79 }
80 80
81 81 public void CopyTo(AutomatonTransition[] array, int arrayIndex) {
82 82 m_transitions.CopyTo(array, arrayIndex);
83 83 }
84 84
85 85 public bool Remove(AutomatonTransition item) {
86 86 return m_transitions.Remove(item);
87 87 }
88 88
89 89 public int Count {
90 90 get {
91 91 return m_transitions.Count;
92 92 }
93 93 }
94 94
95 95 public bool IsReadOnly {
96 96 get {
97 97 return false;
98 98 }
99 99 }
100 100
101 101 public IEnumerator<AutomatonTransition> GetEnumerator() {
102 102 return m_transitions.GetEnumerator();
103 103 }
104 104
105 105 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
106 106 return GetEnumerator();
107 107 }
108 108
109 109 public void AddSymbol(int symbol) {
110 110 Safe.ArgumentAssert(symbol >= 0, "symbol");
111 111 m_symbolCount = Math.Max(symbol + 1, m_symbolCount);
112 112 }
113 113
114 114 public int[,] CreateTransitionTable() {
115 115 var table = new int[StateCount,AlphabetSize];
116 116
117 117 for (int i = 0; i < StateCount; i++)
118 118 for (int j = 0; j < AlphabetSize; j++)
119 119 table[i, j] = AutomatonConst.UnreachableState;
120 120
121 121 foreach (var t in this)
122 122 table[t.s1,t.edge] = (byte)t.s2;
123 123
124 124 return table;
125 125 }
126 126
127 127 public bool[] CreateFinalStateTable() {
128 128 var table = new bool[StateCount];
129 129
130 130 foreach (var s in FinalStates)
131 131 table[s] = true;
132 132
133 133 return table;
134 134 }
135 135
136 136 /// <summary>Π€ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅Ρ‚ мноТСства ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Ρ… состояний ΠΏΠ΅Ρ€Π΅Π΄ Π½Π°Ρ‡Π°Π»ΠΎΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° ΠΌΠΈΠ½ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ.</summary>
137 137 /// <remarks>
138 138 /// Π’ процСссС построСния минимального Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚Π° трСбуСтся Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ мноТСство состояний,
139 139 /// Π½Π° Π΄Π²Π° подмноТСства - ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Π΅ состояния ΠΈ всС ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅, послС Ρ‡Π΅Π³ΠΎ эти подмноТСства
140 140 /// Π±ΡƒΠ΄ΡƒΡ‚ Ρ€Π΅Π·Π΄Π΅Π»Π΅Π½Ρ‹ Π½Π° Π±ΠΎΠ»Π΅Π΅ ΠΌΠ΅Π»ΠΊΠΈΠ΅. Иногда трСбуСтся Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ различия ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Ρ… сосотяний,
141 141 /// для этого Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ†ΡŽ Ρ„ΡƒΠΊΠ½Ρ†ΠΈΡŽ, для получСния мноТСств ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Ρ… состояний.
142 142 /// </remarks>
143 143 /// <returns>The final states.</returns>
144 144 protected virtual IEnumerable<HashSet<int>> SplitFinalStates(IEnumerable<int> states) {
145 145 return new [] { new HashSet<int>(states) };
146 146 }
147 147
148 148 protected void Optimize(
149 149 IDFATableBuilder optimalDFA,
150 150 IDictionary<int,int> alphabetMap,
151 151 IDictionary<int,int> stateMap
152 152 ) {
153 153 Safe.ArgumentNotNull(optimalDFA, "dfa");
154 154 Safe.ArgumentNotNull(alphabetMap, "alphabetMap");
155 155 Safe.ArgumentNotNull(stateMap, "stateMap");
156 156
157 157
158 158 var setComparer = new CustomEqualityComparer<HashSet<int>>(
159 159 (x, y) => x.SetEquals(y),
160 160 s => s.Sum(x => x.GetHashCode())
161 161 );
162 162
163 163 var optimalStates = new HashSet<HashSet<int>>(setComparer);
164 164 var queue = new HashSet<HashSet<int>>(setComparer);
165 165
166 166 optimalStates.Add(new HashSet<int>(FinalStates));
167 167
168 168 var state = new HashSet<int>(
169 169 Enumerable
170 170 .Range(0, m_stateCount)
171 171 .Where(i => !m_finalStates.Contains(i))
172 172 );
173 173
174 174 optimalStates.Add(state);
175 175 queue.Add(state);
176 176
177 177 var rmap = m_transitions
178 178 .GroupBy(t => t.s2)
179 179 .ToDictionary(
180 180 g => g.Key, // s2
181 181 g => g.ToLookup(t => t.edge, t => t.s1)//.ToDictionary(p => p.Key)
182 182 );
183 183
184 184 while (queue.Count > 0) {
185 185 var stateA = queue.First();
186 186 queue.Remove(stateA);
187 187
188 188 for (int c = 0; c < m_symbolCount; c++) {
189 189 var stateX = new HashSet<int>();
190 190 foreach(var a in stateA.Where(rmap.ContainsKey))
191 191 stateX.UnionWith(rmap[a][c]); // all states from wich the symbol 'c' leads to the state 'a'
192 192
193 193 var tmp = optimalStates.ToArray();
194 194 foreach (var stateY in tmp) {
195 195 var stateR1 = new HashSet<int>(stateY);
196 196 var stateR2 = new HashSet<int>(stateY);
197 197
198 198 stateR1.IntersectWith(stateX);
199 199 stateR2.ExceptWith(stateX);
200 200
201 201 if (stateR1.Count > 0 && stateR2.Count > 0) {
202 202
203 203
204 204 optimalStates.Remove(stateY);
205 205 optimalStates.Add(stateR1);
206 206 optimalStates.Add(stateR2);
207 207
208 208 if (queue.Contains(stateY)) {
209 209 queue.Remove(stateY);
210 210 queue.Add(stateR1);
211 211 queue.Add(stateR2);
212 212 } else {
213 213 queue.Add(stateR1.Count <= stateR2.Count ? stateR1 : stateR2);
214 214 }
215 215 }
216 216 }
217 217 }
218 218 }
219 219
220 220 // Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ€Π°Π·Π±ΠΈΠ²Π°Π΅ΠΌ ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Π΅ состояния
221 221 foreach (var final in optimalStates.Where(s => s.Overlaps(m_finalStates)).ToArray()) {
222 222 optimalStates.Remove(final);
223 223 foreach (var split in SplitFinalStates(final))
224 224 optimalStates.Add(split);
225 225 }
226 226
227 227
228 228 // ΠΊΠ°Ρ€Ρ‚Π° получСния ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ состояния ΠΏΠΎ ΡΠΎΠΎΡ‚Π²Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅ΠΌΡƒ Π΅ΠΌΡƒ простому ΡΠΎΡΡ‚ΠΎΡΠ½ΠΈΡŽ
229 229 var nextState = 0;
230 230 foreach (var item in optimalStates) {
231 231 var id = nextState++;
232 232 foreach (var s in item)
233 233 stateMap[s] = id;
234 234 }
235 235
236 236 // ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Π°Π»Ρ„Π°Π²ΠΈΡ‚
237 237 // Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ символы Π½Π΅ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠΌΡ‹, Ссли Move(s,a1) == Move(s,a2), для любого s
238 238 // для этого ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ кластСризации, сначала
239 239 // считаСм, Ρ‡Ρ‚ΠΎ всС символы Π½Π΅ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠΌΡ‹
240 240
241 241 var minClasses = new HashSet<HashSet<int>>(setComparer);
242 242 var alphaQueue = new Queue<HashSet<int>>();
243 243 alphaQueue.Enqueue(new HashSet<int>(Enumerable.Range(0,AlphabetSize)));
244 244
245 245 // для всСх состояний, Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ класс Π½Π° Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠΌΠΎΡΡ‚ΡŒ,
246 246 // Ρ‚.Π΅. символы Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠΌΡ‹, Ссли ΠΎΠ½ΠΈ приводят ΠΊ Ρ€Π°Π·Π½Ρ‹ΠΌ состояниям
247 247 for (int s = 0 ; s < optimalStates.Count; s++) {
248 248 var newQueue = new Queue<HashSet<int>>();
249 249
250 250 foreach (var A in alphaQueue) {
251 251 // классы ΠΈΠ· ΠΎΠ΄Π½ΠΎΠ³ΠΎ символа Π΄Π΅Π»ΠΈΡ‚ΡŒ бСсполСзно, ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΈΠΌ ΠΈΡ… сразу Π²
252 252 // Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠΉ Π°Π»Ρ„Π°Π²ΠΈΡ‚
253 253 if (A.Count == 1) {
254 254 minClasses.Add(A);
255 255 continue;
256 256 }
257 257
258 258 // Ρ€Π°Π·Π»ΠΈΡ‡Π°Π΅ΠΌ классы символов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ пСрСводят Π² Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹Π΅ состояния
259 259 // optimalState -> alphaClass
260 260 var classes = new Dictionary<int, HashSet<int>>();
261 261
262 262 foreach (var term in A) {
263 263 // ΠΈΡ‰Π΅ΠΌ всС ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Ρ‹ класса ΠΏΠΎ символу term
264 264 var s2 = m_transitions.Where(t => stateMap[t.s1] == s && t.edge == term).Select(t => stateMap[t.s2]).DefaultIfEmpty(-1).First();
265 265
266 266 HashSet<int> a2;
267 267 if (!classes.TryGetValue(s2, out a2)) {
268 268 a2 = new HashSet<int>();
269 269 newQueue.Enqueue(a2);
270 270 classes[s2] = a2;
271 271 }
272 272 a2.Add(term);
273 273 }
274 274 }
275 275
276 276 if (newQueue.Count == 0)
277 277 break;
278 278 alphaQueue = newQueue;
279 279 }
280 280
281 281 // послС окончания Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ останутся ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹Π΅ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠΌΡ‹Π΅ классы
282 282 // Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… символов
283 283 foreach (var A in alphaQueue)
284 284 minClasses.Add(A);
285 285
286 286 // построСниС отобраТСния Π°Π»Ρ„Π°Π²ΠΈΡ‚ΠΎΠ² Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… символов.
287 287 // ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ символ DFAConst.UNCLASSIFIED_INPUT ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ
288 288 // ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Ρ‚ΠΎΠ³Π΄Π° сохраним ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ класс,
289 289 // содСрТащий этот символ Π½Π° Ρ‚ΠΎΠΌΠΆΠ΅ мСстС.
290 290
291 291 var nextCls = 0;
292 292 foreach (var item in minClasses) {
293 293 if (nextCls == AutomatonConst.UnclassifiedInput)
294 294 nextCls++;
295 295
296 296 // сохраняСм DFAConst.UNCLASSIFIED_INPUT
297 297 var cls = item.Contains(AutomatonConst.UnclassifiedInput) ? AutomatonConst.UnclassifiedInput : nextCls++;
298 298 optimalDFA.AddSymbol(cls);
299 299
300 300 foreach (var a in item)
301 301 alphabetMap[a] = cls;
302 302 }
303 303
304 304 // построСниС Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚Π°
305 305 optimalDFA.SetInitialState(stateMap[m_initialState]);
306 306
307 307 foreach (var sf in m_finalStates.Select(s => stateMap[s]).Distinct())
308 308 optimalDFA.MarkFinalState(sf);
309 309
310 310 foreach (var t in m_transitions.Select(t => new AutomatonTransition(stateMap[t.s1],stateMap[t.s2],alphabetMap[t.edge])).Distinct())
311 311 optimalDFA.Add(t);
312 312 }
313 313
314 314 /*protected string PrintDFA<TInput, TState>(IAlphabet<TInput> inputAlphabet, IAlphabet<TState> stateAlphabet) {
315 315 Safe.ArgumentNotNull(inputAlphabet, "inputAlphabet");
316 316 Safe.ArgumentNotNull(stateAlphabet, "stateAlphabet");
317 317
318 318 var data = new List<string>();
319 319
320 320 data.Add("digraph dfa {");
321 321
322 322 foreach (var final in m_finalStates)
323 323 data.Add(String.Format("{0} [shape=box];",String.Join("", stateAlphabet.GetSymbols(final))));
324 324
325 325 foreach (var t in m_transitions)
326 326 data.Add(String.Format(
327 327 "{0} -> {2} [label={1}];",
328 328 String.Join("", stateAlphabet.GetSymbols(t.s1)),
329 329 ToLiteral(ToLiteral(String.Join("", t.edge == AutomatonConst.UnclassifiedInput ? new [] { "@" } : inputAlphabet.GetSymbols(t.edge).Select(x => x.ToString())))),
330 330 String.Join("", stateAlphabet.GetSymbols(t.s2))
331 331 ));
332 332 data.Add("}");
333 333 return String.Join("\n", data);
334 334 }
335 335
336 336 static string ToLiteral(string input)
337 337 {
338 338 using (var writer = new StringWriter())
339 339 {
340 340 using (var provider = CodeDomProvider.CreateProvider("CSharp"))
341 341 {
342 342 provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
343 343 return writer.ToString();
344 344 }
345 345 }
346 346 }*/
347 347 }
348 348 }
@@ -1,24 +1,26
1 1 namespace Implab.Components {
2 2
3 3 public enum ExecutionState {
4 4 Undefined = 0,
5 5
6 6 Created,
7 7
8 8 Initializing,
9 9
10 10 Ready,
11 11
12 12 Starting,
13 13
14 14 Running,
15 15
16 16 Stopping,
17 17
18 Stopped,
19
18 20 Failed,
19 21
20 22 Disposed,
21 23
22 24 Last = Disposed
23 25 }
24 26 } No newline at end of file
@@ -1,21 +1,28
1 1 using System;
2 2
3 3 namespace Implab.Components {
4 4 /// <summary>
5 5 /// Initializable components are created and initialized in two steps, first we have create the component,
6 6 /// then we have to complete it's creation by calling an <see cref="Initialize()"/> method. All parameters needed
7 7 /// to complete the initialization must be passed before the calling <see cref="Initialize()"/>
8 8 /// </summary>
9 9 public interface IInitializable {
10 10 /// <summary>
11 11 /// Completes initialization.
12 12 /// </summary>
13 13 /// <remarks>
14 /// <para>
14 15 /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but
15 16 /// they can be called from this method. This method is also usefull when we constructing a complex grpah
16 17 /// of components where cyclic references may take place.
18 /// </para>
19 /// <para>
20 /// In asyncronous patterns <see cref="Initialize()"/> can be called
21 /// to start initialization and the <see cref="IRunnable.Completion"/>
22 /// property can be used to track operation completion.
23 /// </para>
17 24 /// </remarks>
18 25 void Initialize();
19 26 }
20 27 }
21 28
@@ -1,29 +1,59
1 1 using System;
2 2 using System.Threading;
3 3 using System.Threading.Tasks;
4 4
5 5 namespace Implab.Components {
6 6 /// <summary>
7 7 /// Interface for the component which performs a long running task.
8 8 /// </summary>
9 public interface IRunnable : IDisposable {
9 /// <remarks>
10 /// The access to the runnable component should be sequential, the
11 /// componet should support asynchronous completion of the initiated
12 /// operation but operations itself must be initiated sequentially.
13 /// </remarks>
14 public interface IRunnable {
10 15 /// <summary>
11 16 /// Starts this instance
12 17 /// </summary>
18 /// <remarks>
19 /// This operation is cancellable and it's expected to move to
20 /// the failed state or just ignore the cancellation request,
21 /// </remarks>
13 22 void Start(CancellationToken ct);
14 23
15 24 /// <summary>
16 /// Stops this instance and releases all resources, after the instance is stopped it is moved to Disposed state and can't be reused.
25 /// Stops this instance and releases all resources, after the
26 /// instance is stopped it is moved to Disposed state and
27 /// can't be reused.
17 28 /// </summary>
29 /// <remarks>
30 /// If the componet was in the starting state the pending operation
31 /// will be requested to cancel. The stop operatin will be
32 /// performed only if the component in the running state.
33 /// </remarks>
18 34 void Stop(CancellationToken ct);
19 35
20 Task<ExecutionState> Completion { get; }
36 /// <summary>
37 /// The result of the last started operation. This property reflects
38 /// only the result of the last started operation and therefore should
39 /// change only if a new operation is initiated.
40 /// </summary>
41 Task Completion { get; }
21 42
43 /// <summary>
44 /// Current state of the componenet
45 /// </summary>
22 46 ExecutionState State { get; }
23 47
48 /// <summary>
49 /// Event to monitor the state of the component.
50 /// </summary>
24 51 event EventHandler<StateChangeEventArgs> StateChanged;
25 52
53 /// <summary>
54 /// The last error
55 /// </summary>
26 56 Exception LastError { get; }
27 57 }
28 58 }
29 59
@@ -1,75 +1,75
1 1 using Implab.Parallels;
2 2 using System;
3 3 using System.Threading;
4 4
5 5 namespace Implab.Components {
6 6 /// <summary>
7 7 /// The base class for creating object pools.
8 8 /// </summary>
9 9 /// <remarks>
10 10 /// <para>The objects pool is offers frequently requested objects to be reused, this gives
11 11 /// a gool speed improvement for the 'heavy' objects. To avoid memory overhead the pool uses
12 12 /// weak references allowing CG to do it's work. If there are no free objects in the pool
13 13 /// they are created on demand. </para>
14 14 /// <para>
15 15 /// Implementors need to defined a <see cref="CreateInstance()"/> method
16 16 /// </para>
17 17 /// <para>The instances of this class are thred-safe.</para>
18 18 /// </remarks>
19 19 public abstract class ObjectPool<T> where T : class {
20 20 readonly AsyncQueue<WeakReference> m_queue = new AsyncQueue<WeakReference>();
21 21 readonly int m_size;
22 22 int m_count = 0;
23 23
24 24 protected ObjectPool() : this(Environment.ProcessorCount+1) {
25 25
26 26 }
27 27
28 28 protected ObjectPool(int size) {
29 Safe.ArgumentInRange(size,1,size,"size");
29 Safe.ArgumentInRange(size > 0, nameof(size));
30 30
31 31 m_size = size;
32 32 }
33 33
34 34 /// <summary>
35 35 /// Creates the instance if there are no free ones in the pool.
36 36 /// </summary>
37 37 /// <returns>The new instance.</returns>
38 38 protected abstract T CreateInstance();
39 39
40 40 /// <summary>
41 41 /// Cleanups the instance.
42 42 /// </summary>
43 43 /// <param name="instance">The instance to cleanup and prepare it for the next use.</param>
44 44 protected virtual void CleanupInstance(T instance) {
45 45 }
46 46
47 47 /// <summary>
48 48 /// Allocate free instance from the pool or reates a new one.
49 49 /// </summary>
50 50 public T Allocate() {
51 51 WeakReference reference;
52 52 while (m_queue.TryDequeue(out reference)) {
53 53 Interlocked.Decrement(ref m_count);
54 54 object instance = reference.Target;
55 55 if (instance == null)
56 56 continue;
57 57 return (T)instance;
58 58 }
59 59 return CreateInstance();
60 60 }
61 61
62 62 /// <summary>
63 63 /// Release the specified instance and returns it to the pool of free instances.
64 64 /// </summary>
65 65 /// <param name="instance">The instance to return to the pool.</param>
66 66 /// <remarks>Before the instance is returned to the pool the <see cref="CleanupInstance(T)"/> is called.</remarks>
67 67 public void Release(T instance) {
68 68 if (m_count < m_size && instance != null) {
69 69 Interlocked.Increment(ref m_count);
70 70 CleanupInstance(instance);
71 71 m_queue.Enqueue(new WeakReference(instance));
72 72 }
73 73 }
74 74 }
75 75 }
@@ -1,57 +1,273
1 1 using System;
2 using System.Diagnostics;
2 3 using System.Threading;
3 4 using System.Threading.Tasks;
4 5
5 namespace Implab.Components
6 {
7 public class RunnableComponent : IRunnable {
6 namespace Implab.Components {
7 /// <summary>
8 /// Base class for implementing components which support start and stop operations,
9 /// such components may represent running services.
10 /// </summary>
11 /// <remarks>
12 /// This class provides a basic lifecycle from the creation to the
13 /// termination of the component.
14 /// </remarks>
15 public class RunnableComponent : IRunnable, IInitializable, IDisposable {
16
17 /// <summary>
18 /// This class bound <see cref="CancellationTokenSource"/> lifetime to the task,
19 /// when the task completes the associated token source will be disposed.
20 /// </summary>
21 class AsyncOperationDescriptor {
22
23 public static AsyncOperationDescriptor None { get; } = new AsyncOperationDescriptor();
24
25 readonly CancellationTokenSource m_cts;
26
27 bool m_done;
28
29 public CancellationToken Token {
30 get { return m_cts == null ? CancellationToken.None : m_cts.Token; }
31 }
32
33 public Task Task { get; private set; }
34
35 private AsyncOperationDescriptor(Task task, CancellationTokenSource cts) {
36 m_cts = cts;
37 Task = Chain(task);
38 }
39
40 private AsyncOperationDescriptor() {
41 Task = Task.CompletedTask;
42 }
8 43
44 public void Cancel() {
45 if (m_cts != null) {
46 lock (m_cts) {
47 if (!m_done)
48 m_cts.Cancel();
49 }
50 }
51 }
52
53 void Done() {
54 if (m_cts != null) {
55 lock (m_cts) {
56 m_done = true;
57 m_cts.Dispose();
58 }
59 } else {
60 m_done = true;
61 }
62 }
63
64 async Task Chain(Task other) {
65 try {
66 await other;
67 } finally {
68 Done();
69 }
70 }
71
72 public static AsyncOperationDescriptor Create(Func<CancellationToken, Task> factory, CancellationToken ct) {
73 var cts = ct.CanBeCanceled ?
74 CancellationTokenSource.CreateLinkedTokenSource(ct) :
75 new CancellationTokenSource();
76
77 return new AsyncOperationDescriptor(factory(cts.Token), cts);
78 }
79
80 }
81
82 // this lock is used to synchronize state flow of the component during
83 // completions or the operations.
9 84 readonly object m_lock = new object();
10 85
11 CancellationTokenSource m_cts;
86 // current operation cookie, used to check wheather a call to
87 // MoveSuccess/MoveFailed method belongs to the current
88 // operation, if cookies didn't match ignore completion result.
89 object m_cookie;
12 90
13 public Task<ExecutionState> Completion {
14 get;
15 private set;
91 AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None;
92
93 ExecutionState m_state;
94
95
96 protected RunnableComponent(bool initialized) {
97 State = initialized ? ExecutionState.Ready : ExecutionState.Created;
16 98 }
17 99
18 public ExecutionState State => throw new NotImplementedException();
100 public Task Completion {
101 get { return m_current.Task; }
102 }
19 103
20 public Exception LastError => throw new NotImplementedException();
104 public ExecutionState State {
105 get { return m_state; }
106 private set {
107 if (m_state != value) {
108 m_state = value;
109 StateChanged.DispatchEvent(this, new StateChangeEventArgs {
110 State = value,
111 LastError = LastError
112 });
113 }
114 }
115 }
116
117 public Exception LastError { get; private set; }
21 118
22 119 public event EventHandler<StateChangeEventArgs> StateChanged;
23 120
121 /// <summary>
122 /// Releases all resources used by the current component regardless of its
123 /// execution state.
124 /// </summary>
125 /// <remarks>
126 /// Calling to this method may result unexpedted results if the component
127 /// isn't in the stopped state. Call this method after the component is
128 /// stopped if needed or if the component is in the failed state.
129 /// </remarks>
24 130 public void Dispose() {
25 lock(m_lock) {
131 bool dispose = false;
132 if (dispose) {
26 133 Dispose(true);
27 134 GC.SuppressFinalize(this);
28 135 }
29 136 }
30 137
138 ~RunnableComponent() {
139 Dispose(false);
140 }
141
142 /// <summary>
143 /// Releases all resources used by the current component regardless of its
144 /// execution state.
145 /// </summary>
146 /// <param name="disposing">Indicates that the component is disposed
147 /// during a normal disposing or during GC.</param>
31 148 protected virtual void Dispose(bool disposing) {
32 if (disposing) {
33 Safe.Dispose(m_cts);
34 }
149 }
150
151 public void Initialize() {
152 var cookie = new object();
153 if (MoveInitialize(cookie))
154 ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie);
155 }
156
157 /// <summary>
158 /// This method is used for initialization during a component creation.
159 /// </summary>
160 /// <param name="ct">A cancellation token for this operation</param>
161 /// <remarks>
162 /// This method should be used for short and mostly syncronous operations,
163 /// other operations which require time to run shoud be placed in
164 /// <see cref="StartInternal(CancellationToken)"/> method.
165 /// </remarks>
166 protected virtual Task InitializeInternalAsync(CancellationToken ct) {
167 return Task.CompletedTask;
35 168 }
36 169
37 170 public void Start(CancellationToken ct) {
38 lock(m_lock) {
39 switch (State)
40 {
41
42 default:
43 throw new InvalidOperationException();
171 var cookie = new object();
172 if (MoveStart(cookie))
173 ScheduleTask(StartInternal, ct, cookie);
174 }
175
176 protected virtual Task StartInternal(CancellationToken ct) {
177 return Task.CompletedTask;
178 }
179
180 public void Stop(CancellationToken ct) {
181 var cookie = new object();
182 if (MoveStop(cookie))
183 ScheduleTask(StopAsync, ct, cookie);
184 }
185
186 async Task StopAsync(CancellationToken ct) {
187 m_current.Cancel();
188 await Completion;
189
190 ct.ThrowIfCancellationRequested();
191
192 await StopInternalAsync(ct);
193 }
194
195 protected virtual Task StopInternalAsync(CancellationToken ct) {
196 return Task.CompletedTask;
197 }
198
199
200 #region state management
201
202 bool MoveInitialize(object cookie) {
203 lock (m_lock) {
204 if (State != ExecutionState.Created)
205 return false;
206 State = ExecutionState.Initializing;
207 m_cookie = cookie;
208 return true;
209 }
210 }
211
212 bool MoveStart(object cookie) {
213 lock (m_lock) {
214 if (State != ExecutionState.Ready)
215 return false;
216 State = ExecutionState.Starting;
217 m_cookie = cookie;
218 return true;
219 }
220 }
221
222 bool MoveStop(object cookie) {
223 lock (m_lock) {
224 if (State != ExecutionState.Starting && State != ExecutionState.Running)
225 return false;
226 State = ExecutionState.Stopping;
227 m_cookie = cookie;
228 return true;
229 }
230 }
231
232 void MoveSuccess(object cookie) {
233 lock (m_lock) {
234 if (m_cookie != cookie)
235 return;
236 switch (State) {
237 case ExecutionState.Initializing:
238 State = ExecutionState.Ready;
239 break;
240 case ExecutionState.Starting:
241 State = ExecutionState.Running;
242 break;
243 case ExecutionState.Stopping:
244 State = ExecutionState.Stopped;
245 break;
44 246 }
45 247 }
46 248 }
47 249
48 public void Stop(CancellationToken ct) {
49 throw new NotImplementedException();
250 void MoveFailed(Exception err, object cookie) {
251 lock (m_lock) {
252 if (m_cookie != cookie)
253 return;
254 LastError = err;
255 State = ExecutionState.Failed;
256 }
50 257 }
51 258
52 protected virtual Task StartImpl(CancellationToken ct) {
259
53 260
54 return Task.CompletedTask;
261 protected async void ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) {
262 try {
263 m_current = AsyncOperationDescriptor.Create(next, ct);
264 await m_current.Task;
265 MoveSuccess(cookie);
266 } catch (Exception e) {
267 MoveFailed(e, cookie);
268 }
55 269 }
270
271 #endregion
56 272 }
57 273 } No newline at end of file
@@ -1,53 +1,64
1 1 using System;
2 2 using System.Diagnostics;
3 using System.Threading.Tasks;
3 4
4 5 namespace Implab {
5 6 /// <summary>
6 7 /// This class is responsible for the promise resolution, dispatching and chaining
7 8 /// </summary>
8 9 public class Deferred : IResolvable {
9 10
10 11 readonly Promise m_promise;
11 12 internal Deferred() {
12 13 m_promise = new Promise();
13 14 }
14 15
15 16 internal Deferred(Promise promise, IDispatcher dispatcher) {
16 17 Debug.Assert(promise != null);
17 18 m_promise = promise;
18 19 }
19 20
20 21 public IPromise Promise {
21 22 get { return m_promise; }
22 23 }
23 24
24 25 public void Cancel() {
25 26 Reject(new OperationCanceledException());
26 27 }
27 28
28 29 public virtual void Reject(Exception error) {
29 30 if (error is PromiseTransientException)
30 31 error = ((PromiseTransientException)error).InnerException;
31 32
32 33 m_promise.RejectPromise(error);
33 34 }
34 35
35 36 public virtual void Resolve() {
36 37 m_promise.ResolvePromise();
37 38 }
38 39
39 40 public virtual void Resolve(IPromise thenable) {
40 41 if (thenable == null)
41 42 Reject(new Exception("The promise or task are expected"));
42 43 if (thenable == m_promise)
43 44 Reject(new Exception("The promise cannot be resolved with oneself"));
44 45
45 46 try {
46 47 thenable.Then(this);
47 48 } catch (Exception err) {
48 49 Reject(err);
49 50 }
50 51 }
51 52
53 public virtual void Resolve(Task thenable) {
54 if (thenable == null)
55 Reject(new Exception("The promise or task are expected"));
56 try {
57 thenable.Then(this);
58 } catch(Exception err) {
59 Reject(err);
60 }
61 }
62
52 63 }
53 64 } No newline at end of file
@@ -1,49 +1,61
1 1 using System;
2 2 using System.Diagnostics;
3 using System.Threading.Tasks;
3 4
4 5 namespace Implab {
5 6 public class Deferred<T> : IResolvable<T> {
6 7 readonly Promise<T> m_promise;
7 8
8 9 internal Deferred() {
9 10 m_promise = new Promise<T>();
10 11 }
11 12
12 13 protected Deferred(Promise<T> promise) {
13 14 Debug.Assert(promise != null);
14 15 m_promise = promise;
15 16 }
16 17
17 18 public IPromise<T> Promise {
18 19 get { return m_promise; }
19 20 }
20 21
21 22 public void Cancel() {
22 23 Reject(new OperationCanceledException());
23 24 }
24 25
25 26 public virtual void Reject(Exception error) {
26 27 if (error is PromiseTransientException)
27 28 error = ((PromiseTransientException)error).InnerException;
28 29
29 30 m_promise.RejectPromise(error);
30 31 }
31 32
32 33 public virtual void Resolve(T value) {
33 34 m_promise.ResolvePromise(value);
34 35 }
35 36
36 37 public virtual void Resolve(IPromise<T> thenable) {
37 38 if (thenable == null)
38 39 Reject(new Exception("The promise or task are expected"));
39 40 if (thenable == m_promise)
40 41 Reject(new Exception("The promise cannot be resolved with oneself"));
41 42
42 43 try {
43 44 thenable.Then(this);
44 45 } catch (Exception err) {
45 46 Reject(err);
46 47 }
47 48 }
49
50 public virtual void Resolve(Task<T> thenable) {
51 if (thenable == null)
52 Reject(new Exception("The promise or task are expected"));
53
54 try {
55 thenable.Then(this);
56 } catch (Exception err) {
57 Reject(err);
58 }
59 }
48 60 }
49 61 } No newline at end of file
@@ -1,76 +1,70
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Threading.Tasks;
6 6
7 7 namespace Implab.Formats.Json {
8 8 public class JsonStringScanner : JsonScanner {
9 9 const int _defaultBuffer = 64;
10 10
11 11 readonly string m_data;
12 12 int m_offset;
13 13
14 14 JsonStringScanner(string data, char[] buffer, int pos, int length, int offset) : base(buffer, pos, length) {
15 15 m_data = data;
16 16 m_offset = offset;
17 17 }
18 18
19 19 protected override int Read(char[] buffer, int offset, int size) {
20 20 if (m_data == null)
21 21 return 0;
22 22 if (m_offset >= m_data.Length)
23 23 return 0;
24 24
25 25 var count = Math.Min(size, m_data.Length - m_offset);
26 26
27 27 m_data.CopyTo(m_offset, buffer, offset, count);
28 28 m_offset += count;
29 29
30 30 return count;
31 31 }
32 32
33 33 public static JsonStringScanner Create(string data) {
34 34 Safe.ArgumentNotNull(data, nameof(data));
35 35
36 36 if (data.Length <= _defaultBuffer)
37 37 return new JsonStringScanner(null, data.ToCharArray(), 0, data.Length, data.Length);
38 38
39 39 var buffer = new char[_defaultBuffer];
40 40 data.CopyTo(0, buffer, 0, _defaultBuffer);
41 41 return new JsonStringScanner(data, buffer, 0, _defaultBuffer, _defaultBuffer);
42 42 }
43 43
44 44 public static JsonStringScanner Create(string data, int offset, int length) {
45 45 Safe.ArgumentNotNull(data, nameof(data));
46 Safe.ArgumentGreaterThan(offset, 0, nameof(offset));
47 Safe.ArgumentGreaterThan(length, 0, nameof(length));
48
49 if (offset + length > data.Length)
50 throw new ArgumentOutOfRangeException("Specified offset and length are out of the string bounds");
46 Safe.ArgumentInRange(offset >= 0 && offset < data.Length , nameof(offset));
47 Safe.ArgumentInRange(length >= 0 && offset + length <= data.Length, nameof(length));
51 48
52 49 if (length <= _defaultBuffer) {
53 50 var buffer = new char[length];
54 51 data.CopyTo(offset, buffer, 0, length);
55 52
56 53 return new JsonStringScanner(null, buffer, 0, length, length);
57 54 } else {
58 55 var buffer = new char[_defaultBuffer];
59 56 data.CopyTo(offset, buffer, 0, _defaultBuffer);
60 57 return new JsonStringScanner(data, buffer, 0, _defaultBuffer, offset + _defaultBuffer);
61 58 }
62 59 }
63 60
64 61 public static JsonStringScanner Create(char[] data, int offset, int length) {
65 62 Safe.ArgumentNotNull(data, nameof(data));
66 Safe.ArgumentGreaterThan(offset, 0, nameof(offset));
67 Safe.ArgumentGreaterThan(length, 0, nameof(length));
68
69 if (offset + length > data.Length)
70 throw new ArgumentOutOfRangeException("Specified offset and length are out of the array bounds");
63 Safe.ArgumentInRange(offset >= 0 && offset < data.Length , nameof(offset));
64 Safe.ArgumentInRange(length >= 0 && offset + length <= data.Length, nameof(length));
71 65
72 66 return new JsonStringScanner(null, data, offset, offset + length, offset + length);
73 67
74 68 }
75 69 }
76 70 }
@@ -1,10 +1,12
1 1 using System.Threading;
2 2 using System.Threading.Tasks;
3 3
4 4 namespace Implab.Messaging {
5 5 public interface IConsumer<T> {
6 Task<T> Receive(CancellationToken ct);
6 T Receive(CancellationToken ct);
7
8 Task<T> ReceiveAsync(CancellationToken ct);
7 9
8 10 bool TryReceive(out T message);
9 11 }
10 12 } No newline at end of file
@@ -1,11 +1,15
1 1 using System.Collections.Generic;
2 2 using System.Threading;
3 3 using System.Threading.Tasks;
4 4
5 5 namespace Implab.Messaging {
6 6 public interface IProducer<T> {
7 void PostMessage(T message, CancellationToken ct);
8
7 9 Task PostMessageAsync(T message, CancellationToken ct);
8 10
11 void PostMessages(IEnumerable<T> messages, CancellationToken ct);
12
9 13 Task PostMessagesAsync(IEnumerable<T> messages, CancellationToken ct);
10 14 }
11 15 } No newline at end of file
@@ -1,101 +1,101
1 1 using System;
2 2 using System.Threading;
3 3
4 4 namespace Implab.Parallels {
5 5 public class BlockingQueue<T> : AsyncQueue<T> {
6 6 readonly object m_lock = new object();
7 7
8 8 public void EnqueuePulse(T value) {
9 9 base.Enqueue(value);
10 10 lock (m_lock)
11 11 Monitor.Pulse(m_lock);
12 12 }
13 13
14 14 public void EnqueueRangePulse(T[] data, int offset, int length) {
15 15 base.EnqueueRange(data, offset, length);
16 16 if (length > 1)
17 17 lock (m_lock)
18 18 Monitor.PulseAll(m_lock);
19 19 else
20 20 lock (m_lock)
21 21 Monitor.Pulse(m_lock);
22 22 }
23 23
24 24 public T GetItem(int timeout) {
25 25 T item;
26 26
27 27 if (!TryDequeue(out item)) {
28 28 var t1 = Environment.TickCount;
29 29 var dt = timeout;
30 30
31 31 lock (m_lock) {
32 32 while (!TryDequeue(out item)) {
33 33 if (!Monitor.Wait(m_lock, dt))
34 34 throw new TimeoutException();
35 35 if (timeout >= 0) {
36 36 dt = timeout - Environment.TickCount + t1;
37 37 if (dt < 0)
38 38 throw new TimeoutException();
39 39 }
40 40 }
41 41 }
42 42 }
43 43 return item;
44 44 }
45 45
46 46 public T GetItem() {
47 47 T item;
48 48 if (!TryDequeue(out item))
49 49 lock (m_lock) {
50 50 while (!TryDequeue(out item))
51 51 Monitor.Wait(m_lock);
52 52 }
53 53 return item;
54 54 }
55 55
56 56 public T[] GetRange(int max, int timeout) {
57 Safe.ArgumentInRange(max, 1, Int32.MaxValue, "max");
57 Safe.ArgumentInRange(max > 0 , nameof(max));
58 58
59 59 var buffer = new T[max];
60 60 int actual;
61 61 if (!TryDequeueRange(buffer, 0, max, out actual)) {
62 62 var t1 = Environment.TickCount;
63 63 var dt = timeout;
64 64
65 65 lock (m_lock) {
66 66 while (!TryDequeueRange(buffer, 0, max, out actual)) {
67 67
68 68 if (!Monitor.Wait(m_lock, dt))
69 69 throw new TimeoutException();
70 70
71 71 if (timeout >= 0) {
72 72 dt = timeout - Environment.TickCount + t1;
73 73 if (dt < 0)
74 74 throw new TimeoutException();
75 75 }
76 76 }
77 77 }
78 78 }
79 79
80 80 var data = new T[actual];
81 81 Array.Copy(buffer, data, actual);
82 82 return data;
83 83 }
84 84
85 85 public T[] GetRange(int max) {
86 Safe.ArgumentInRange(max, 1, Int32.MaxValue, "max");
86 Safe.ArgumentInRange(max > 0, nameof(max));
87 87
88 88 var buffer = new T[max];
89 89 int actual;
90 90 if (!TryDequeueRange(buffer, 0, max, out actual))
91 91 lock (m_lock)
92 92 while (!TryDequeueRange(buffer, 0, max, out actual))
93 93 Monitor.Wait(m_lock);
94 94
95 95 var data = new T[actual];
96 96 Array.Copy(buffer, data, actual);
97 97 return data;
98 98 }
99 99 }
100 100 }
101 101
@@ -1,164 +1,171
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using System.Diagnostics;
7 7 using System.Collections;
8 8 using System.Runtime.CompilerServices;
9 using System.Threading.Tasks;
9 10
10 11 #if NET_4_5
11 12 using System.Threading.Tasks;
12 13 #endif
13 14
14 15 namespace Implab
15 16 {
16 17 public static class Safe
17 18 {
18 19 [MethodImpl(MethodImplOptions.AggressiveInlining)]
19 20 public static void ArgumentAssert(bool condition, string paramName) {
20 21 if (!condition)
21 22 throw new ArgumentException("The parameter is invalid", paramName);
22 23 }
23 24
24 25 [MethodImpl(MethodImplOptions.AggressiveInlining)]
25 26 public static void ArgumentMatch(string value, string paramName, Regex rx) {
26 27 if (rx == null)
27 28 throw new ArgumentNullException("rx");
28 29 if (!rx.IsMatch(value))
29 30 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), paramName);
30 31 }
31 32
32 33 [MethodImpl(MethodImplOptions.AggressiveInlining)]
33 34 public static void ArgumentNotEmpty(string value, string paramName) {
34 35 if (String.IsNullOrEmpty(value))
35 36 throw new ArgumentException("The parameter can't be empty", paramName);
36 37 }
37 38
38 39 [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 40 public static void ArgumentNotEmpty<T>(T[] value, string paramName) {
40 41 if (value == null || value.Length == 0)
41 42 throw new ArgumentException("The array must be not emty", paramName);
42 43 }
43 44
44 45 [MethodImpl(MethodImplOptions.AggressiveInlining)]
45 46 public static void ArgumentNotNull(object value, string paramName) {
46 47 if (value == null)
47 48 throw new ArgumentNullException(paramName);
48 49 }
49 50
50 51 [MethodImpl(MethodImplOptions.AggressiveInlining)]
51 internal static void ArgumentGreaterThan(int value, int min, string paramName) {
52 internal static void ArgumentGreaterEqThan(int value, int min, string paramName) {
52 53 if (value < min)
53 54 throw new ArgumentOutOfRangeException(paramName);
54 55 }
55 56
56 57 [MethodImpl(MethodImplOptions.AggressiveInlining)]
57 public static void ArgumentInRange(int value, int min, int max, string paramName) {
58 if (value < min || value > max)
58 public static void ArgumentInRange(bool condition, string paramName) {
59 if (!condition)
59 60 throw new ArgumentOutOfRangeException(paramName);
60 61 }
61 62
62 63 [MethodImpl(MethodImplOptions.AggressiveInlining)]
63 64 public static void ArgumentOfType(object value, Type type, string paramName) {
64 65 if (!type.IsInstanceOfType(value))
65 66 throw new ArgumentException(String.Format("The parameter must be of type {0}", type), paramName);
66 67 }
67 68
68 69 public static void Dispose(params IDisposable[] objects) {
69 70 foreach (var d in objects)
70 71 if (d != null)
71 72 d.Dispose();
72 73 }
73 74
74 75 public static void Dispose(params object[] objects) {
75 76 foreach (var obj in objects) {
76 77 var d = obj as IDisposable;
77 78 if (d != null)
78 79 d.Dispose();
79 80 }
80 81 }
81 82
82 83 public static void DisposeCollection(IEnumerable<IDisposable> objects) {
83 84 foreach (var d in objects)
84 85 Dispose(d);
85 86 }
86 87
87 88 public static void DisposeCollection(IEnumerable objects) {
88 89 foreach (var d in objects)
89 90 Dispose(d);
90 91 }
91 92
92 93 public static void Dispose(object obj) {
93 94 if (obj is IDisposable)
94 95 Dispose((IDisposable)obj);
95 96
96 97 }
97 98
98 99 [DebuggerStepThrough]
99 100 public static void DispatchEvent<T>(this EventHandler<T> handler, object sender, T args) {
100 101 if (handler != null)
101 102 handler(sender, args);
102 103 }
103 104
104 105 [DebuggerStepThrough]
105 106 public static void DispatchEvent(this EventHandler handler, object sender, EventArgs args) {
106 107 if (handler != null)
107 108 handler(sender, args);
108 109 }
109 110
110 111 [DebuggerStepThrough]
111 112 public static IPromise<T> Run<T>(Func<T> action) {
112 113 ArgumentNotNull(action, "action");
113 114
114 115 try {
115 116 return Promise.Resolve(action());
116 117 } catch (Exception err) {
117 118 return Promise.Reject<T>(err);
118 119 }
119 120 }
120 121
121 122 [DebuggerStepThrough]
122 123 public static IPromise Run(Action action) {
123 124 ArgumentNotNull(action, "action");
124 125
125 126 try {
126 127 action();
127 128 return Promise.Resolve();
128 129 } catch (Exception err) {
129 130 return Promise.Reject(err);
130 131 }
131 132 }
132 133
133 134 [DebuggerStepThrough]
134 135 public static IPromise Run(Func<IPromise> action) {
135 136 ArgumentNotNull(action, "action");
136 137
137 138 try {
138 139 return action() ?? Promise.Reject(new Exception("The action returned null"));
139 140 } catch (Exception err) {
140 141 return Promise.Reject(err);
141 142 }
142 143 }
143 144
144 145 public static void NoWait(IPromise promise) {
145 146 }
146 147
148 public static void NoWait(Task promise) {
149 }
150
151 public static void NoWait<T>(Task<T> promise) {
152 }
153
147 154 [DebuggerStepThrough]
148 155 public static IPromise<T> Run<T>(Func<IPromise<T>> action) {
149 156 ArgumentNotNull(action, "action");
150 157
151 158 try {
152 159 return action() ?? Promise.Reject<T>(new Exception("The action returned null"));
153 160 } catch (Exception err) {
154 161 return Promise.Reject<T>(err);
155 162 }
156 163 }
157 164
158 165 #if NET_4_5
159 166 public static void NoWait(Task t) {
160 167 }
161 168 #endif
162 169
163 170 }
164 171 }
@@ -1,610 +1,610
1 1 using Implab.Formats.Json;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Globalization;
5 5 using System.Linq;
6 6 using System.Xml;
7 7
8 8 namespace Implab.Xml {
9 9 public class JsonXmlReader : XmlReader {
10 10 struct JsonContext {
11 11 public string localName;
12 12 public bool skip;
13 13 }
14 14
15 15 JsonReader m_parser;
16 16 JsonXmlReaderOptions m_options;
17 17 JsonXmlReaderPosition m_position = JsonXmlReaderPosition.Initial;
18 18 XmlNameTable m_nameTable;
19 19
20 20 readonly string m_jsonRootName;
21 21 readonly string m_jsonNamespace;
22 22 readonly string m_jsonPrefix;
23 23 readonly bool m_jsonFlattenArrays;
24 24 readonly string m_jsonArrayItemName;
25 25
26 26 string m_jsonLocalName;
27 27 string m_jsonValueName;
28 28 bool m_jsonSkip; // indicates wheather to generate closing tag for objects or arrays
29 29
30 30 readonly Stack<JsonContext> m_jsonNameStack = new Stack<JsonContext>();
31 31
32 32 XmlQualifiedName m_elementQName;
33 33 string m_elementPrefix;
34 34 int m_elementDepth;
35 35 bool m_elementIsEmpty;
36 36
37 37 XmlQualifiedName m_qName;
38 38 string m_prefix;
39 39 int m_xmlDepth;
40 40
41 41 XmlSimpleAttribute[] m_attributes;
42 42 string m_value;
43 43 bool m_isEmpty;
44 44
45 45 XmlNodeType m_nodeType = XmlNodeType.None;
46 46
47 47 bool m_isAttribute; // indicates that we are reading attribute nodes
48 48 int m_currentAttribute;
49 49 bool m_currentAttributeRead;
50 50
51 51
52 52 XmlNameContext m_context;
53 53
54 54 readonly string m_xmlnsPrefix;
55 55 readonly string m_xmlnsNamespace;
56 56 readonly string m_xsiPrefix;
57 57 readonly string m_xsiNamespace;
58 58
59 59
60 60 public JsonXmlReader(JsonReader parser, JsonXmlReaderOptions options) {
61 61 Safe.ArgumentNotNull(parser, nameof(parser));
62 62 m_parser = parser;
63 63
64 64 m_options = options ?? new JsonXmlReaderOptions();
65 65
66 66 m_jsonFlattenArrays = m_options.FlattenArrays;
67 67 m_nameTable = m_options.NameTable ?? new NameTable();
68 68
69 69 m_jsonRootName = m_nameTable.Add(string.IsNullOrEmpty(m_options.RootName) ? "data" : m_options.RootName);
70 70 m_jsonArrayItemName = m_nameTable.Add(string.IsNullOrEmpty(m_options.ArrayItemName) ? "item" : m_options.ArrayItemName);
71 71 m_jsonNamespace = m_nameTable.Add(m_options.NamespaceUri ?? string.Empty);
72 72 m_jsonPrefix = m_nameTable.Add(m_options.NodesPrefix ?? string.Empty);
73 73 m_xmlnsPrefix = m_nameTable.Add(XmlNameContext.XmlnsPrefix);
74 74 m_xmlnsNamespace = m_nameTable.Add(XmlNameContext.XmlnsNamespace);
75 75 m_xsiPrefix = m_nameTable.Add(XmlNameContext.XsiPrefix);
76 76 m_xsiNamespace = m_nameTable.Add(XmlNameContext.XsiNamespace);
77 77
78 78 // TODO validate m_jsonRootName, m_jsonArrayItemName
79 79
80 80 m_context = new XmlNameContext(null, 0);
81 81 }
82 82
83 83 public override int AttributeCount {
84 84 get {
85 85 return m_attributes == null ? 0 : m_attributes.Length;
86 86 }
87 87 }
88 88
89 89 public override string BaseURI {
90 90 get {
91 91 return string.Empty;
92 92 }
93 93 }
94 94
95 95 public override int Depth {
96 96 get {
97 97 return m_xmlDepth;
98 98 }
99 99 }
100 100
101 101 public override bool EOF {
102 102 get {
103 103 return m_position == JsonXmlReaderPosition.Eof;
104 104 }
105 105 }
106 106
107 107 public override bool IsEmptyElement {
108 108 get { return m_isEmpty; }
109 109 }
110 110
111 111
112 112 public override string LocalName {
113 113 get {
114 114 return m_qName.Name;
115 115 }
116 116 }
117 117
118 118 public override string NamespaceURI {
119 119 get {
120 120 return m_qName.Namespace;
121 121 }
122 122 }
123 123
124 124 public override XmlNameTable NameTable {
125 125 get {
126 126 return m_nameTable;
127 127 }
128 128 }
129 129
130 130 public override XmlNodeType NodeType {
131 131 get {
132 132 return m_nodeType;
133 133 }
134 134 }
135 135
136 136 public override string Prefix {
137 137 get {
138 138 return m_prefix;
139 139 }
140 140 }
141 141
142 142 public override ReadState ReadState {
143 143 get {
144 144 switch (m_position) {
145 145 case JsonXmlReaderPosition.Initial:
146 146 return ReadState.Initial;
147 147 case JsonXmlReaderPosition.Eof:
148 148 return ReadState.EndOfFile;
149 149 case JsonXmlReaderPosition.Closed:
150 150 return ReadState.Closed;
151 151 case JsonXmlReaderPosition.Error:
152 152 return ReadState.Error;
153 153 default:
154 154 return ReadState.Interactive;
155 155 };
156 156 }
157 157 }
158 158
159 159 public override string Value {
160 160 get {
161 161 return m_value;
162 162 }
163 163 }
164 164
165 165 public override string GetAttribute(int i) {
166 Safe.ArgumentInRange(i, 0, AttributeCount - 1, nameof(i));
166 Safe.ArgumentInRange(i >= 0 && i < AttributeCount, nameof(i));
167 167 return m_attributes[i].Value;
168 168 }
169 169
170 170 public override string GetAttribute(string name) {
171 171 if (m_attributes == null)
172 172 return null;
173 173 var qName = m_context.Resolve(name);
174 174 var attr = Array.Find(m_attributes, x => x.QName == qName);
175 175 var value = attr?.Value;
176 176 return value == string.Empty ? null : value;
177 177 }
178 178
179 179 public override string GetAttribute(string name, string namespaceURI) {
180 180 if (m_attributes == null)
181 181 return null;
182 182 var qName = new XmlQualifiedName(name, namespaceURI);
183 183 var attr = Array.Find(m_attributes, x => x.QName == qName);
184 184 var value = attr?.Value;
185 185 return value == string.Empty ? null : value;
186 186 }
187 187
188 188 public override string LookupNamespace(string prefix) {
189 189 return m_context.ResolvePrefix(prefix);
190 190 }
191 191
192 192 public override bool MoveToAttribute(string name) {
193 193 if (m_attributes == null || m_attributes.Length == 0)
194 194 return false;
195 195
196 196 var qName = m_context.Resolve(name);
197 197 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
198 198 if (index >= 0) {
199 199 MoveToAttributeImpl(index);
200 200 return true;
201 201 }
202 202 return false;
203 203 }
204 204
205 205 public override bool MoveToAttribute(string name, string ns) {
206 206 if (m_attributes == null || m_attributes.Length == 0)
207 207 return false;
208 208
209 209 var qName = m_context.Resolve(name);
210 210 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
211 211 if (index >= 0) {
212 212 MoveToAttributeImpl(index);
213 213 return true;
214 214 }
215 215 return false;
216 216 }
217 217
218 218 void MoveToAttributeImpl(int i) {
219 219 if (!m_isAttribute) {
220 220 m_elementQName = m_qName;
221 221 m_elementDepth = m_xmlDepth;
222 222 m_elementPrefix = m_prefix;
223 223 m_elementIsEmpty = m_isEmpty;
224 224 m_isAttribute = true;
225 225 }
226 226
227 227 var attr = m_attributes[i];
228 228
229 229
230 230 m_currentAttribute = i;
231 231 m_currentAttributeRead = false;
232 232 m_nodeType = XmlNodeType.Attribute;
233 233
234 234 m_xmlDepth = m_elementDepth + 1;
235 235 m_qName = attr.QName;
236 236 m_value = attr.Value;
237 237 m_prefix = attr.Prefix;
238 238 }
239 239
240 240 public override bool MoveToElement() {
241 241 if (m_isAttribute) {
242 242 m_value = null;
243 243 m_nodeType = XmlNodeType.Element;
244 244 m_xmlDepth = m_elementDepth;
245 245 m_prefix = m_elementPrefix;
246 246 m_qName = m_elementQName;
247 247 m_isEmpty = m_elementIsEmpty;
248 248 m_isAttribute = false;
249 249 return true;
250 250 }
251 251 return false;
252 252 }
253 253
254 254 public override bool MoveToFirstAttribute() {
255 255 if (m_attributes != null && m_attributes.Length > 0) {
256 256 MoveToAttributeImpl(0);
257 257 return true;
258 258 }
259 259 return false;
260 260 }
261 261
262 262 public override bool MoveToNextAttribute() {
263 263 if (m_isAttribute) {
264 264 var next = m_currentAttribute + 1;
265 265 if (next < AttributeCount) {
266 266 MoveToAttributeImpl(next);
267 267 return true;
268 268 }
269 269 return false;
270 270 } else {
271 271 return MoveToFirstAttribute();
272 272 }
273 273
274 274 }
275 275
276 276 public override bool ReadAttributeValue() {
277 277 if (!m_isAttribute || m_currentAttributeRead)
278 278 return false;
279 279
280 280 ValueNode(m_attributes[m_currentAttribute].Value);
281 281 m_currentAttributeRead = true;
282 282 return true;
283 283 }
284 284
285 285 public override void ResolveEntity() {
286 286 /* do nothing */
287 287 }
288 288
289 289 /// <summary>
290 290 /// Determines do we need to increase depth after the current node
291 291 /// </summary>
292 292 /// <returns></returns>
293 293 public bool IsSibling() {
294 294 switch (m_nodeType) {
295 295 case XmlNodeType.None: // start document
296 296 case XmlNodeType.Attribute: // after attribute only it's content can be iterated with ReadAttributeValue method
297 297 return false;
298 298 case XmlNodeType.Element:
299 299 // if the elemnt is empty the next element will be it's sibling
300 300 return m_isEmpty;
301 301 default:
302 302 return true;
303 303 }
304 304 }
305 305
306 306 void ValueNode(string value) {
307 307 if (!IsSibling()) // the node is nested
308 308 m_xmlDepth++;
309 309
310 310 m_qName = XmlQualifiedName.Empty;
311 311 m_nodeType = XmlNodeType.Text;
312 312 m_prefix = string.Empty;
313 313 m_value = value;
314 314 m_isEmpty = false;
315 315 m_attributes = null;
316 316 }
317 317
318 318 void ElementNode(string name, string ns, XmlSimpleAttribute[] attrs, bool empty) {
319 319 if (!IsSibling()) // the node is nested
320 320 m_xmlDepth++;
321 321
322 322 var context = m_context;
323 323 List<XmlSimpleAttribute> definedAttrs = null;
324 324
325 325 // define new namespaces
326 326 if (attrs != null) {
327 327 foreach (var attr in attrs) {
328 328 if (attr.QName.Name == "xmlns") {
329 329 if (context == m_context)
330 330 context = new XmlNameContext(m_context, m_xmlDepth);
331 331 context.DefinePrefix(attr.Value, string.Empty);
332 332 } else if (attr.Prefix == m_xmlnsPrefix) {
333 333 if (context == m_context)
334 334 context = new XmlNameContext(m_context, m_xmlDepth);
335 335 context.DefinePrefix(attr.Value, attr.QName.Name);
336 336 } else {
337 337 string attrPrefix;
338 338 if (string.IsNullOrEmpty(attr.QName.Namespace))
339 339 continue;
340 340
341 341 // auto-define prefixes
342 342 if (!context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
343 343 // new namespace prefix added
344 344 attrPrefix = context.CreateNamespacePrefix(attr.QName.Namespace);
345 345 attr.Prefix = attrPrefix;
346 346
347 347 if (definedAttrs == null)
348 348 definedAttrs = new List<XmlSimpleAttribute>();
349 349
350 350 definedAttrs.Add(new XmlSimpleAttribute(attrPrefix, m_xmlnsNamespace, m_xmlnsPrefix, attr.QName.Namespace));
351 351 }
352 352 }
353 353 }
354 354 }
355 355
356 356 string p;
357 357 // auto-define prefixes
358 358 if (!context.LookupNamespacePrefix(ns, out p)) {
359 359 if (context == m_context)
360 360 context = new XmlNameContext(m_context, m_xmlDepth);
361 361 p = context.CreateNamespacePrefix(ns);
362 362 if (definedAttrs == null)
363 363 definedAttrs = new List<XmlSimpleAttribute>();
364 364
365 365 definedAttrs.Add(new XmlSimpleAttribute(p, m_xmlnsNamespace, m_xmlnsPrefix, ns));
366 366 }
367 367
368 368 if (definedAttrs != null) {
369 369 if (attrs != null)
370 370 definedAttrs.AddRange(attrs);
371 371 attrs = definedAttrs.ToArray();
372 372 }
373 373
374 374 if (!empty)
375 375 m_context = context;
376 376
377 377 m_nodeType = XmlNodeType.Element;
378 378 m_qName = new XmlQualifiedName(name, ns);
379 379 m_prefix = p;
380 380 m_value = null;
381 381 m_isEmpty = empty;
382 382 m_attributes = attrs;
383 383 }
384 384
385 385 void EndElementNode(string name, string ns) {
386 386 if (IsSibling()) {
387 387 // closing the element which has children
388 388 m_xmlDepth--;
389 389 }
390 390
391 391 string p;
392 392 if (!m_context.LookupNamespacePrefix(ns, out p))
393 393 throw new Exception($"Failed to lookup namespace '{ns}'");
394 394
395 395 if (m_context.Depth == m_xmlDepth)
396 396 m_context = m_context.ParentContext;
397 397
398 398 m_nodeType = XmlNodeType.EndElement;
399 399 m_prefix = p;
400 400 m_qName = new XmlQualifiedName(name, ns);
401 401 m_value = null;
402 402 m_attributes = null;
403 403 m_isEmpty = false;
404 404 }
405 405
406 406 void XmlDeclaration() {
407 407 if (!IsSibling()) // the node is nested
408 408 m_xmlDepth++;
409 409 m_nodeType = XmlNodeType.XmlDeclaration;
410 410 m_qName = new XmlQualifiedName("xml");
411 411 m_value = "version='1.0'";
412 412 m_prefix = string.Empty;
413 413 m_attributes = null;
414 414 m_isEmpty = false;
415 415 }
416 416
417 417 public override bool Read() {
418 418 try {
419 419 string elementName;
420 420 XmlSimpleAttribute[] elementAttrs = null;
421 421 MoveToElement();
422 422
423 423 switch (m_position) {
424 424 case JsonXmlReaderPosition.Initial:
425 425 m_jsonLocalName = m_jsonRootName;
426 426 m_jsonSkip = false;
427 427 XmlDeclaration();
428 428 m_position = JsonXmlReaderPosition.Declaration;
429 429 return true;
430 430 case JsonXmlReaderPosition.Declaration:
431 431 elementAttrs = new[] {
432 432 new XmlSimpleAttribute(m_xsiPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_xsiNamespace),
433 433 string.IsNullOrEmpty(m_jsonPrefix) ?
434 434 new XmlSimpleAttribute(m_xmlnsPrefix, string.Empty, string.Empty, m_jsonNamespace) :
435 435 new XmlSimpleAttribute(m_jsonPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_jsonNamespace)
436 436 };
437 437 break;
438 438 case JsonXmlReaderPosition.ValueElement:
439 439 if (!m_isEmpty) {
440 440 if (m_parser.ElementValue != null && !m_parser.ElementValue.Equals(string.Empty))
441 441 ValueNode(m_parser.ElementValue);
442 442 else
443 443 goto case JsonXmlReaderPosition.ValueContent;
444 444 m_position = JsonXmlReaderPosition.ValueContent;
445 445 return true;
446 446 } else {
447 447 m_position = JsonXmlReaderPosition.ValueEndElement;
448 448 break;
449 449 }
450 450 case JsonXmlReaderPosition.ValueContent:
451 451 EndElementNode(m_jsonValueName, m_jsonNamespace);
452 452 m_position = JsonXmlReaderPosition.ValueEndElement;
453 453 return true;
454 454 case JsonXmlReaderPosition.Eof:
455 455 case JsonXmlReaderPosition.Closed:
456 456 case JsonXmlReaderPosition.Error:
457 457 return false;
458 458 }
459 459
460 460 while (m_parser.Read()) {
461 461 var jsonName = m_nameTable.Add(m_parser.ElementName);
462 462
463 463 switch (m_parser.ElementType) {
464 464 case JsonElementType.BeginObject:
465 465 if (!EnterJsonObject(jsonName, out elementName))
466 466 continue;
467 467
468 468 m_position = JsonXmlReaderPosition.BeginObject;
469 469 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
470 470 break;
471 471 case JsonElementType.EndObject:
472 472 if (!LeaveJsonScope(out elementName))
473 473 continue;
474 474
475 475 m_position = JsonXmlReaderPosition.EndObject;
476 476 EndElementNode(elementName, m_jsonNamespace);
477 477 break;
478 478 case JsonElementType.BeginArray:
479 479 if (!EnterJsonArray(jsonName, out elementName))
480 480 continue;
481 481
482 482 m_position = JsonXmlReaderPosition.BeginArray;
483 483 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
484 484 break;
485 485 case JsonElementType.EndArray:
486 486 if (!LeaveJsonScope(out elementName))
487 487 continue;
488 488
489 489 m_position = JsonXmlReaderPosition.EndArray;
490 490 EndElementNode(elementName, m_jsonNamespace);
491 491 break;
492 492 case JsonElementType.Value:
493 493 if (!VisitJsonValue(jsonName, out m_jsonValueName))
494 494 continue;
495 495
496 496 m_position = JsonXmlReaderPosition.ValueElement;
497 497 if (m_parser.ElementValue == null)
498 498 // generate empty element with xsi:nil="true" attribute
499 499 ElementNode(
500 500 m_jsonValueName,
501 501 m_jsonNamespace,
502 502 new[] {
503 503 new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, "true")
504 504 },
505 505 true
506 506 );
507 507 else
508 508 ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue.Equals(string.Empty));
509 509 break;
510 510 default:
511 511 throw new Exception($"Unexpected JSON element {m_parser.ElementType}: {m_parser.ElementName}");
512 512 }
513 513 return true;
514 514 }
515 515
516 516 m_position = JsonXmlReaderPosition.Eof;
517 517 return false;
518 518 } catch {
519 519 m_position = JsonXmlReaderPosition.Error;
520 520 throw;
521 521 }
522 522 }
523 523
524 524 void SaveJsonName() {
525 525 m_jsonNameStack.Push(new JsonContext {
526 526 skip = m_jsonSkip,
527 527 localName = m_jsonLocalName
528 528 });
529 529
530 530 }
531 531
532 532 bool EnterJsonObject(string name, out string elementName) {
533 533 SaveJsonName();
534 534 m_jsonSkip = false;
535 535
536 536 if (string.IsNullOrEmpty(name)) {
537 537 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
538 538 m_jsonLocalName = m_jsonArrayItemName;
539 539 } else {
540 540 m_jsonLocalName = name;
541 541 }
542 542
543 543 elementName = m_jsonLocalName;
544 544 return true;
545 545 }
546 546
547 547 /// <summary>
548 548 /// Called when JSON parser visits BeginArray ('[') element.
549 549 /// </summary>
550 550 /// <param name="name">Optional property name if the array is the member of an object</param>
551 551 /// <returns>true if element should be emited, false otherwise</returns>
552 552 bool EnterJsonArray(string name, out string elementName) {
553 553 SaveJsonName();
554 554
555 555 if (string.IsNullOrEmpty(name)) {
556 556 // m_jsonNameStack.Count == 1 means the root node
557 557 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
558 558 m_jsonLocalName = m_jsonArrayItemName;
559 559
560 560 m_jsonSkip = false; // we should not flatten arrays inside arrays or in the document root
561 561 } else {
562 562 m_jsonLocalName = name;
563 563 m_jsonSkip = m_jsonFlattenArrays;
564 564 }
565 565 elementName = m_jsonLocalName;
566 566
567 567 return !m_jsonSkip;
568 568 }
569 569
570 570 bool VisitJsonValue(string name, out string elementName) {
571 571 if (string.IsNullOrEmpty(name)) {
572 572 // m_jsonNameStack.Count == 0 means that JSON document consists from simple value
573 573 elementName = (m_jsonNameStack.Count == 0 || m_jsonFlattenArrays) ? m_jsonLocalName : m_jsonArrayItemName;
574 574 } else {
575 575 elementName = name;
576 576 }
577 577 return true;
578 578 }
579 579
580 580 bool LeaveJsonScope(out string elementName) {
581 581 elementName = m_jsonLocalName;
582 582 var skip = m_jsonSkip;
583 583
584 584 var prev = m_jsonNameStack.Pop();
585 585 m_jsonLocalName = prev.localName;
586 586 m_jsonSkip = prev.skip;
587 587
588 588 return !skip;
589 589 }
590 590
591 591 public override string ToString() {
592 592 switch (NodeType) {
593 593 case XmlNodeType.Element:
594 594 return $"<{Name} {string.Join(" ", (m_attributes ?? new XmlSimpleAttribute[0]).Select(x => $"{x.Prefix}{(string.IsNullOrEmpty(x.Prefix) ? "" : ":")}{x.QName.Name}='{x.Value}'"))} {(IsEmptyElement ? "/" : "")}>";
595 595 case XmlNodeType.Attribute:
596 596 return $"@{Name}";
597 597 case XmlNodeType.Text:
598 598 return $"{Value}";
599 599 case XmlNodeType.CDATA:
600 600 return $"<![CDATA[{Value}]]>";
601 601 case XmlNodeType.EntityReference:
602 602 return $"&{Name};";
603 603 case XmlNodeType.EndElement:
604 604 return $"</{Name}>";
605 605 default:
606 606 return $".{NodeType} {Name} {Value}";
607 607 }
608 608 }
609 609 }
610 610 }
@@ -1,51 +1,65
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.IO;
4 4 using System.Linq;
5 5 using System.Text;
6 6 using System.Threading.Tasks;
7 7 using System.Xml;
8 8 using System.Xml.Linq;
9 9
10 10 namespace Implab.Xml {
11 11 public static class SerializationHelpers {
12 12 public static string SerializeAsString<T>(T obj) {
13 13 return SerializersPool<T>.Instance.SerializeAsString(obj);
14 14 }
15 15
16 16 public static void Serialize<T>(XmlWriter writer, T obj) {
17 17 SerializersPool<T>.Instance.Serialize(writer, obj);
18 18 }
19 19
20 20 public static XmlDocument SerializeAsXmlDocument<T>(T obj) {
21 21 var doc = new XmlDocument();
22 22 using (var writer = doc.CreateNavigator().AppendChild()) {
23 23 SerializersPool<T>.Instance.Serialize(writer, obj);
24 24 }
25 25 return doc;
26 26 }
27 27
28 28 public static XDocument SerializeAsXDocument<T>(T obj) {
29 29 var doc = new XDocument();
30 30 using (var writer = doc.CreateWriter()) {
31 31 SerializersPool<T>.Instance.Serialize(writer, obj);
32 32 }
33 33 return doc;
34 34 }
35 35
36 36 public static void SerializeToFile<T>(string file, T obj) {
37 37 using (var writer = File.CreateText(file))
38 38 SerializersPool<T>.Instance.Serialize(writer, obj);
39 39 }
40 40
41 public static void SerializeToElementChild<T>(XmlElement element, T obj) {
42 using(var writer = element.CreateNavigator().AppendChild())
43 SerializersPool<T>.Instance.Serialize(writer, obj);
44 }
45
46 public static T Deserialize<T>(XmlReader reader) {
47 return SerializersPool<T>.Instance.Deserialize(reader);
48 }
49
50 public static T DeserializeFromFile<T>(string file) {
51 using(var reader = XmlReader.Create(File.OpenText(file)))
52 return Deserialize<T>(reader);
53 }
54
41 55 public static T DeserializeFromString<T>(string data) {
42 56 return SerializersPool<T>.Instance.DeserializeFromString(data);
43 57 }
44 58
45 59 public static T DeserializeFromXmlNode<T>(XmlNode node) {
46 60 Safe.ArgumentNotNull(node, nameof(node));
47 61 using (var reader = node.CreateNavigator().ReadSubtree())
48 62 return SerializersPool<T>.Instance.Deserialize(reader);
49 63 }
50 64 }
51 65 }
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