Skip to content

Commit 9275055

Browse files
agraciosoftworkz
authored andcommitted
refactoring events API
1 parent 8542dd0 commit 9275055

20 files changed

Lines changed: 605 additions & 556 deletions

src/ElectronNET.API/API/ApiBase.cs

Lines changed: 191 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,57 @@
1-
namespace ElectronNET.API
1+
// ReSharper disable InconsistentNaming
2+
namespace ElectronNET.API
23
{
3-
using ElectronNET.API.Serialization;
4-
using ElectronNET.Common;
4+
using Common;
55
using System;
66
using System.Collections.Concurrent;
77
using System.Diagnostics;
88
using System.Runtime.CompilerServices;
9-
using System.Text.Json;
109
using System.Threading.Tasks;
10+
using System.Diagnostics.CodeAnalysis;
11+
using System.Globalization;
1112

1213
public abstract class ApiBase
1314
{
14-
protected enum SocketEventNameTypes
15+
protected enum SocketTaskEventNameTypes
16+
{
17+
DashesLowerFirst,
18+
NoDashUpperFirst
19+
}
20+
protected enum SocketTaskMessageNameTypes
1521
{
1622
DashesLowerFirst,
17-
NoDashUpperFirst,
23+
NoDashUpperFirst
1824
}
1925

20-
internal const int PropertyTimeout = 1000;
26+
protected enum SocketEventNameTypes
27+
{
28+
DashedLower,
29+
CamelCase,
30+
}
31+
32+
private const int PropertyTimeout = 1000;
2133

2234
private readonly string objectName;
23-
private readonly ConcurrentDictionary<string, PropertyGetter> propertyGetters = new ConcurrentDictionary<string, PropertyGetter>();
24-
private readonly ConcurrentDictionary<string, string> propertyEventNames = new ConcurrentDictionary<string, string>();
25-
private readonly ConcurrentDictionary<string, string> propertyMessageNames = new ConcurrentDictionary<string, string>();
26-
private readonly ConcurrentDictionary<string, string> methodMessageNames = new ConcurrentDictionary<string, string>();
35+
private readonly ConcurrentDictionary<string, PropertyGetter> propertyGetters = new();
36+
private readonly ConcurrentDictionary<string, string> propertyEventNames = new();
37+
private readonly ConcurrentDictionary<string, string> propertyMessageNames = new();
38+
private readonly ConcurrentDictionary<string, string> methodMessageNames = new();
39+
private static readonly ConcurrentDictionary<string, EventContainer> eventContainers = new();
40+
2741
private readonly object objLock = new object();
2842

2943
public virtual int Id
3044
{
31-
get
32-
{
33-
return -1;
34-
}
45+
get => -1;
3546

3647
// ReSharper disable once ValueParameterNotUsed
3748
protected set
3849
{
3950
}
4051
}
4152

53+
protected abstract SocketTaskEventNameTypes SocketTaskEventNameType { get; }
54+
protected virtual SocketTaskMessageNameTypes SocketTaskMessageNameType => SocketTaskMessageNameTypes.NoDashUpperFirst;
4255
protected abstract SocketEventNameTypes SocketEventNameType { get; }
4356

4457
protected ApiBase()
@@ -98,15 +111,15 @@ protected void CallMethod3(object val1, object val2, object val3, [CallerMemberN
98111
}
99112
}
100113

101-
protected Task<T> GetPropertyAsync<T>([CallerMemberName] string callerName = null)
114+
protected Task<T> GetPropertyAsync<T>(object arg = null, [CallerMemberName] string callerName = null)
102115
{
103116
Debug.Assert(callerName != null, nameof(callerName) + " != null");
104117

105118
lock (this.objLock)
106119
{
107120
return this.propertyGetters.GetOrAdd(callerName, _ =>
108121
{
109-
var getter = new PropertyGetter<T>(this, callerName, PropertyTimeout);
122+
var getter = new PropertyGetter<T>(this, callerName, PropertyTimeout, arg);
110123

111124
getter.Task<T>().ContinueWith(_ =>
112125
{
@@ -120,6 +133,98 @@ protected Task<T> GetPropertyAsync<T>([CallerMemberName] string callerName = nul
120133
}).Task<T>();
121134
}
122135
}
136+
137+
protected void AddEvent(Action value, int? id = null, [CallerMemberName] string callerName = null)
138+
{
139+
Debug.Assert(callerName != null, nameof(callerName) + " != null");
140+
var eventName = EventName(callerName);
141+
142+
var eventKey = EventKey(eventName, id);
143+
144+
lock (objLock)
145+
{
146+
var container = eventContainers.GetOrAdd(eventKey, _ =>
147+
{
148+
var container = new EventContainer();
149+
BridgeConnector.Socket.On(eventKey, container.OnEventAction);
150+
BridgeConnector.Socket.Emit($"register-{eventName}", id);
151+
return container;
152+
});
153+
154+
container.Register(value);
155+
}
156+
}
157+
158+
protected void RemoveEvent(Action value, int? id = null, [CallerMemberName] string callerName = null)
159+
{
160+
Debug.Assert(callerName != null, nameof(callerName) + " != null");
161+
var eventName = EventName(callerName);
162+
var eventKey = EventKey(eventName, id);
163+
164+
lock (objLock)
165+
{
166+
if (eventContainers.TryGetValue(eventKey, out var container) && !container.Unregister(value))
167+
{
168+
BridgeConnector.Socket.Off(eventKey);
169+
eventContainers.TryRemove(eventKey, out _);
170+
}
171+
}
172+
}
173+
174+
protected void AddEvent<T>(Action<T> value, int? id = null, [CallerMemberName] string callerName = null)
175+
{
176+
Debug.Assert(callerName != null, nameof(callerName) + " != null");
177+
178+
var eventName = EventName(callerName);
179+
var eventKey = EventKey(eventName, id);
180+
181+
lock (objLock)
182+
{
183+
var container = eventContainers.GetOrAdd(eventKey, _ =>
184+
{
185+
var container = new EventContainer();
186+
BridgeConnector.Socket.On<T>(eventKey, container.OnEventActionT);
187+
BridgeConnector.Socket.Emit($"register-{eventName}", id);
188+
return container;
189+
});
190+
191+
container.Register(value);
192+
}
193+
}
194+
195+
protected void RemoveEvent<T>(Action<T> value, int? id = null, [CallerMemberName] string callerName = null)
196+
{
197+
Debug.Assert(callerName != null, nameof(callerName) + " != null");
198+
var eventName = EventName(callerName);
199+
var eventKey = EventKey(eventName, id);
200+
201+
lock (objLock)
202+
{
203+
if (eventContainers.TryGetValue(eventKey, out var container) && !container.Unregister(value))
204+
{
205+
BridgeConnector.Socket.Off(eventKey);
206+
eventContainers.TryRemove(eventKey, out _);
207+
}
208+
}
209+
}
210+
211+
private string EventName(string callerName)
212+
{
213+
switch (SocketEventNameType)
214+
{
215+
case SocketEventNameTypes.DashedLower:
216+
return $"{objectName}-{callerName.ToDashedEventName()}";
217+
case SocketEventNameTypes.CamelCase:
218+
return $"{objectName}-{callerName.ToCamelCaseEventName()}";
219+
default:
220+
throw new ArgumentOutOfRangeException();
221+
}
222+
}
223+
224+
private string EventKey(string eventName, int? id)
225+
{
226+
return string.Format(CultureInfo.InvariantCulture, "{0}{1:D}", eventName, id);
227+
}
123228

124229
internal abstract class PropertyGetter
125230
{
@@ -131,26 +236,37 @@ internal class PropertyGetter<T> : PropertyGetter
131236
private readonly Task<T> tcsTask;
132237
private TaskCompletionSource<T> tcs;
133238

134-
public PropertyGetter(ApiBase apiBase, string callerName, int timeoutMs)
239+
public PropertyGetter(ApiBase apiBase, string callerName, int timeoutMs, object arg = null)
135240
{
136241
this.tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
137242
this.tcsTask = this.tcs.Task;
138243

139244
string eventName;
245+
string messageName;
140246

141-
switch (apiBase.SocketEventNameType)
247+
switch (apiBase.SocketTaskEventNameType)
142248
{
143-
case SocketEventNameTypes.DashesLowerFirst:
249+
case SocketTaskEventNameTypes.DashesLowerFirst:
144250
eventName = apiBase.propertyEventNames.GetOrAdd(callerName, s => $"{apiBase.objectName}-{s.StripAsync().LowerFirst()}-completed");
145251
break;
146-
case SocketEventNameTypes.NoDashUpperFirst:
252+
case SocketTaskEventNameTypes.NoDashUpperFirst:
147253
eventName = apiBase.propertyEventNames.GetOrAdd(callerName, s => $"{apiBase.objectName}{s.StripAsync()}Completed");
148254
break;
149255
default:
150256
throw new ArgumentOutOfRangeException();
151257
}
152-
153-
var messageName = apiBase.propertyMessageNames.GetOrAdd(callerName, s => apiBase.objectName + s.StripAsync());
258+
259+
switch (apiBase.SocketTaskMessageNameType)
260+
{
261+
case SocketTaskMessageNameTypes.DashesLowerFirst:
262+
messageName = apiBase.propertyMessageNames.GetOrAdd(callerName, s => $"{apiBase.objectName}-{s.StripAsync().LowerFirst()}");
263+
break;
264+
case SocketTaskMessageNameTypes.NoDashUpperFirst:
265+
messageName = apiBase.propertyMessageNames.GetOrAdd(callerName, s => apiBase.objectName + s.StripAsync());
266+
break;
267+
default:
268+
throw new ArgumentOutOfRangeException();
269+
}
154270

155271
BridgeConnector.Socket.Once<T>(eventName, (result) =>
156272
{
@@ -171,14 +287,14 @@ public PropertyGetter(ApiBase apiBase, string callerName, int timeoutMs)
171287
}
172288
}
173289
});
174-
175-
if (apiBase.Id >= 0)
290+
291+
if (arg != null)
176292
{
177-
BridgeConnector.Socket.Emit(messageName, apiBase.Id);
293+
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id, arg) : BridgeConnector.Socket.Emit(messageName, arg);
178294
}
179295
else
180296
{
181-
BridgeConnector.Socket.Emit(messageName);
297+
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id) : BridgeConnector.Socket.Emit(messageName);
182298
}
183299

184300
System.Threading.Tasks.Task.Delay(PropertyTimeout).ContinueWith(_ =>
@@ -203,5 +319,53 @@ public override Task<T1> Task<T1>()
203319
return this.tcsTask as Task<T1>;
204320
}
205321
}
322+
323+
[SuppressMessage("ReSharper", "InconsistentlySynchronizedField")]
324+
private class EventContainer
325+
{
326+
private Action eventAction;
327+
private Delegate eventActionT;
328+
329+
private Action<T> GetEventActionT<T>()
330+
{
331+
return (Action<T>)eventActionT;
332+
}
333+
334+
private void SetEventActionT<T>(Action<T> actionT)
335+
{
336+
eventActionT = actionT;
337+
}
338+
339+
public void OnEventAction() => eventAction?.Invoke();
340+
341+
public void OnEventActionT<T>(T p) => GetEventActionT<T>()?.Invoke(p);
342+
343+
public void Register(Action receiver)
344+
{
345+
eventAction += receiver;
346+
}
347+
348+
public void Register<T>(Action<T> receiver)
349+
{
350+
var actionT = GetEventActionT<T>();
351+
actionT += receiver;
352+
SetEventActionT(actionT);
353+
}
354+
355+
public bool Unregister(Action receiver)
356+
{
357+
eventAction -= receiver;
358+
return this.eventAction != null;
359+
}
360+
361+
public bool Unregister<T>(Action<T> receiver)
362+
{
363+
var actionT = GetEventActionT<T>();
364+
actionT -= receiver;
365+
SetEventActionT(actionT);
366+
367+
return actionT != null;
368+
}
369+
}
206370
}
207371
}

0 commit comments

Comments
 (0)