-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathEvent.cs
393 lines (360 loc) · 12.9 KB
/
Event.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Inversion.Process {
/// <summary>
/// Represents an event occuring in the system.
/// </summary>
/// <remarks>
/// Exactly what "event" means is application specific
/// and can range from imperative to reactive.
/// </remarks>
public class Event : IEvent, IEnumerable<KeyValuePair<string, string>> {
private readonly string _message;
private readonly IProcessContext _context;
private readonly IDictionary<string, string> _params;
private IData _object;
/// <summary>
/// Provides indexed access to the events parameters.
/// </summary>
/// <param name="key">The key of the parameter to look up.</param>
/// <returns>Returns the parameter indexed by the key.</returns>
public string this[string key] {
get { return this.Params.ContainsKey(key) ? this.Params[key] : null; }
}
/// <summary>
/// The simple message the event represents.
/// </summary>
/// <remarks>
/// Again, exactly what this means is application specific.
/// </remarks>
public string Message {
get {
return _message;
}
}
/// <summary>
/// The parameters for this event represented
/// as key-value pairs.
/// </summary>
public IDictionary<string, string> Params {
get {
return _params;
}
}
/// <summary>
/// The context upon which this event is being fired.
/// </summary>
/// <remarks>
/// And event always belongs to a context.
/// </remarks>
public IProcessContext Context {
get {
return _context;
}
}
/// <summary>
/// Any object that the event may be carrying.
/// </summary>
/// <remarks>
/// This is a dirty escape hatch, and
/// can even be used as a "return" value
/// for the event.
/// </remarks>
public IData Object {
get { return _object; }
set {
if (_object == null) {
_object = value;
} else {
throw new InvalidOperationException("You may only set the Object value of an event the once. Thereafter it is readonly.");
}
}
}
/// <summary>
/// Provides an abstract representation
/// of the objects data expressed as a JSON object.
/// </summary>
/// <remarks>
/// For this type the json object is created each time
/// it is accessed.
/// </remarks>
public JObject Data {
get { return this.ToJsonObject(); }
}
/// <summary>
/// Instantiates a new event bound to a context.
/// </summary>
/// <param name="context">The context to which the event is bound.</param>
/// <param name="message">The simple message the event represents.</param>
/// <param name="parameters">The parameters of the event.</param>
public Event(IProcessContext context, string message, IDictionary<string, string> parameters) : this(context, message, null, parameters) { }
/// <summary>
/// Instantiates a new event bound to a context.
/// </summary>
/// <param name="context">The context to which the event is bound.</param>
/// <param name="message">The simple message the event represents.</param>
/// <param name="obj">An object being carried by the event.</param>
/// <param name="parameters">The parameters of the event.</param>
public Event(IProcessContext context, string message, IData obj, IDictionary<string, string> parameters) {
_context = context;
_message = message;
_object = obj;
_params = (parameters == null) ? new Dictionary<string, string>() : new Dictionary<string, string>(parameters);
}
/// <summary>
/// Instantiates a new event bound to a context.
/// </summary>
/// <param name="context">The context to which the event is bound.</param>
/// <param name="message">The simple message the event represents.</param>
/// <param name="parms">
/// A sequnce of context parameter names that should be copied from the context
/// to this event.
/// </param>
public Event(IProcessContext context, string message, params string[] parms) : this(context, message, null, parms) { }
/// <summary>
/// Instantiates a new event bound to a context.
/// </summary>
/// <param name="context">The context to which the event is bound.</param>
/// <param name="message">The simple message the event represents.</param>
/// <param name="obj">An object being carried by the event.</param>
/// <param name="parms">
/// A sequnce of context parameter names that should be copied from the context
/// to this event.
/// </param>
public Event(IProcessContext context, string message, IData obj, params string[] parms) {
_context = context;
_message = message;
_object = obj;
_params = new Dictionary<string, string>();
foreach (string parm in parms) {
if (context.Params.ContainsKey(parm)) {
this.Add(parm, context.Params[parm]);
}
}
}
/// <summary>
/// Instantiates a new event as a copy of the event provided.
/// </summary>
/// <param name="ev">The event to copy for this new instance.</param>
public Event(IEvent ev) {
_context = ev.Context;
_message = ev.Message;
_object = ev.Object;
_params = new Dictionary<string, string>(ev.Params);
}
/// <summary>
/// Creates a clone of this event by copying
/// it into a new instance.
/// </summary>
/// <returns>The newly cloned event.</returns>
object ICloneable.Clone() {
return new Event(this);
}
/// <summary>
/// Creates a clone of this event by copying
/// it into a new instance.
/// </summary>
/// <returns>The newly cloned event.</returns>
public virtual Event Clone() {
return new Event(this);
}
/// <summary>
/// Adds a key-value pair as a parameter to the event.
/// </summary>
/// <param name="key">The key of the parameter.</param>
/// <param name="value">The value of the parameter.</param>
public void Add(string key, string value) {
this.Params.Add(key, value);
}
/// <summary>
/// Fires the event on the context to which it is bound.
/// </summary>
/// <returns>
/// Returns the event that has just been fired.
/// </returns>
public IEvent Fire() {
return this.Context.Fire(this);
}
/// <summary>
/// Determines whether or not the parameters
/// specified exist in the event.
/// </summary>
/// <param name="parms">The parameters to check for.</param>
/// <returns>Returns true if all the parameters exist; otherwise return false.</returns>
public bool HasParams(params string[] parms) {
return parms.Length > 0 && parms.All(parm => this.Params.ContainsKey(parm));
}
/// <summary>
/// Determines whether or not the parameters
/// specified exist in the event.
/// </summary>
/// <param name="parms">The parameters to check for.</param>
/// <returns>Returns true if all the parameters exist; otherwise return false.</returns>
public bool HasParams(IEnumerable<string> parms) {
return parms != null && parms.All(parm => this.Params.ContainsKey(parm));
}
/// <summary>
/// Determines whether or not all the key-value pairs
/// provided exist in the events parameters.
/// </summary>
/// <param name="match">The key-value pairs to check for.</param>
/// <returns>
/// Returns true if all the key-value pairs specified exists in the events
/// parameters; otherwise returns false.
/// </returns>
public bool HasParamValues(IEnumerable<KeyValuePair<string, string>> match) {
return match.All(entry => this.Params.Contains(entry));
}
/// <summary>
/// Determines whether or not each of the prameters specified
/// exist on the event, and creates an error for each one that
/// does not.
/// </summary>
/// <param name="parms">The paramter names to check for.</param>
/// <returns>
/// Returns true if each of the parameters exist on the event;
/// otherwise returns false.
/// </returns>
public bool HasRequiredParams(params string[] parms) {
bool has = parms.Length > 0;
foreach (string parm in parms) {
if (!this.Params.ContainsKey(parm)) {
has = false;
this.Context.Errors.CreateMessage("The parameter '{0}' is required and was not present.", parm);
}
}
return has;
}
/// <summary>
/// Creates a string representation of the event.
/// </summary>
/// <returns>Returns a new string representing the event.</returns>
public override string ToString() {
StringBuilder sb = new StringBuilder();
sb.AppendFormat("(event @message {0}\n", this.Message);
foreach (KeyValuePair<string, string> entry in this.Params) {
sb.AppendFormat(" ({0} -{1})\n", entry.Key, entry.Value);
}
sb.AppendLine(")");
return sb.ToString();
}
/// <summary>
/// Obtains an enumerator for the events parameters.
/// </summary>
/// <returns>Returns an enumerator suitable for iterating through the events parameters.</returns>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
return this.Params.GetEnumerator();
}
/// <summary>
/// Obtains an enumerator for the events parameters.
/// </summary>
/// <returns>Returns an enumerator suitable for iterating through the events parameters.</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return this.Params.GetEnumerator();
}
/// <summary>
/// Produces an xml representation of the model.
/// </summary>
/// <param name="xml">The writer to used to write the xml to. </param>
public virtual void ToXml(XmlWriter xml) {
xml.WriteStartElement("event");
xml.WriteAttributeString("message", this.Message);
xml.WriteStartElement("params");
foreach (KeyValuePair<string, string> entry in this.Params) {
xml.WriteStartElement("item");
xml.WriteAttributeString("name", entry.Key);
xml.WriteAttributeString("value", entry.Value);
xml.WriteEndElement();
}
xml.WriteEndElement();
xml.WriteEndElement();
}
/// <summary>
/// Produces a json respresentation of the model.
/// </summary>
/// <param name="json">The writer to use for producing json.</param>
public virtual void ToJson(JsonWriter json) {
json.WriteStartObject();
json.WritePropertyName("_type");
json.WriteValue("event");
json.WritePropertyName("message");
json.WriteValue(this.Message);
json.WritePropertyName("params");
json.WriteStartObject();
foreach (KeyValuePair<string, string> kvp in this.Params) {
json.WritePropertyName(kvp.Key);
json.WriteValue(kvp.Value);
}
json.WriteEndObject();
json.WriteEndObject();
}
/// <summary>
/// Creates a new event from an xml representation.
/// </summary>
/// <param name="context">The context to which the new event will be bound.</param>
/// <param name="xml">The xml representation of an event.</param>
/// <returns>Returns a new event.</returns>
public static Event FromXml(IProcessContext context, string xml) {
try {
XElement ev = XElement.Parse(xml);
if (ev.Name == "event") {
return new Event(
context,
ev.Attribute("message").Value,
ev.Elements().ToDictionary(el => el.Attribute("name").Value, el => el.Attribute("value").Value)
);
} else {
throw new ParseException("The expressed type of the json provided does not appear to be an event.");
}
} catch (Exception err) {
throw new ParseException("An unexpected error was encoutered parsing the provided json into an event object.", err);
}
}
/// <summary>
/// Creates a new event from an json representation.
/// </summary>
/// <param name="context">The context to which the new event will be bound.</param>
/// <param name="json">The json representation of an event.</param>
/// <returns>Returns a new event.</returns>
public static Event FromJson(IProcessContext context, string json) {
try {
JObject job = JObject.Parse(json);
if (job.Value<string>("_type") == "event") {
return new Event(
context,
job.Value<string>("message"),
job.Value<JObject>("params").Properties().ToDictionary(p => p.Name, p => p.Value.ToString())
);
} else {
throw new ParseException("The expressed type of the json provided does not appear to be an event.");
}
} catch (Exception err) {
throw new ParseException("An unexpected error was encoutered parsing the provided json into an event object.", err);
}
}
/// <summary>
/// An exception thrown when unable to parse the xml or json representations
/// for creating a new event.
/// </summary>
public class ParseException : InvalidOperationException {
/// <summary>
/// Instantiates a new parse exception with a human readable message.
/// </summary>
/// <param name="message">The human readable message for the exception.</param>
public ParseException(string message) : base(message) { }
/// <summary>
/// instantiates a new exception wrapping a provided inner exception,
/// with a human readable message.
/// </summary>
/// <param name="message">The human readable message for the exception.</param>
/// <param name="err">The inner exception to wrap.</param>
public ParseException(string message, Exception err) : base(message, err) { }
}
}
}