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