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 ;
5+ using System . Diagnostics . CodeAnalysis ;
6+ using System . Globalization ;
57 using System ;
68 using System . Collections . Concurrent ;
79 using System . Diagnostics ;
810 using System . Runtime . CompilerServices ;
9- using System . Text . Json ;
1011 using System . Threading . Tasks ;
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 ;
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+ private static readonly ConcurrentDictionary < string , ConcurrentDictionary < string , PropertyGetter > > AllPropertyGetters = new ( ) ;
41+
2742 private readonly object objLock = new object ( ) ;
2843
2944 public virtual int Id
3045 {
31- get
32- {
33- return - 1 ;
34- }
46+ get => - 1 ;
3547
3648 // ReSharper disable once ValueParameterNotUsed
3749 protected set
3850 {
3951 }
4052 }
4153
42- protected abstract SocketEventNameTypes SocketEventNameType { get ; }
54+ protected abstract SocketTaskEventNameTypes SocketTaskEventNameType { get ; }
55+ protected virtual SocketTaskMessageNameTypes SocketTaskMessageNameType => SocketTaskMessageNameTypes . NoDashUpperFirst ;
56+ protected virtual SocketEventNameTypes SocketEventNameType => SocketEventNameTypes . DashedLower ;
4357
4458 protected ApiBase ( )
4559 {
4660 this . objectName = this . GetType ( ) . Name . LowerFirst ( ) ;
61+ propertyGetters = AllPropertyGetters . GetOrAdd ( objectName , _ => new ConcurrentDictionary < string , PropertyGetter > ( ) ) ;
4762 }
4863
4964 protected void CallMethod0 ( [ CallerMemberName ] string callerName = null )
@@ -98,15 +113,15 @@ protected void CallMethod3(object val1, object val2, object val3, [CallerMemberN
98113 }
99114 }
100115
101- protected Task < T > GetPropertyAsync < T > ( [ CallerMemberName ] string callerName = null )
116+ protected Task < T > GetPropertyAsync < T > ( object arg = null , [ CallerMemberName ] string callerName = null )
102117 {
103118 Debug . Assert ( callerName != null , nameof ( callerName ) + " != null" ) ;
104119
105120 lock ( this . objLock )
106121 {
107122 return this . propertyGetters . GetOrAdd ( callerName , _ =>
108123 {
109- var getter = new PropertyGetter < T > ( this , callerName , PropertyTimeout ) ;
124+ var getter = new PropertyGetter < T > ( this , callerName , PropertyTimeout , arg ) ;
110125
111126 getter . Task < T > ( ) . ContinueWith ( _ =>
112127 {
@@ -120,6 +135,98 @@ protected Task<T> GetPropertyAsync<T>([CallerMemberName] string callerName = nul
120135 } ) . Task < T > ( ) ;
121136 }
122137 }
138+
139+ protected void AddEvent ( Action value , int ? id = null , [ CallerMemberName ] string callerName = null )
140+ {
141+ Debug . Assert ( callerName != null , nameof ( callerName ) + " != null" ) ;
142+ var eventName = EventName ( callerName ) ;
143+
144+ var eventKey = EventKey ( eventName , id ) ;
145+
146+ lock ( objLock )
147+ {
148+ var container = eventContainers . GetOrAdd ( eventKey , _ =>
149+ {
150+ var container = new EventContainer ( ) ;
151+ BridgeConnector . Socket . On ( eventKey , container . OnEventAction ) ;
152+ BridgeConnector . Socket . Emit ( $ "register-{ eventName } ", id ) ;
153+ return container ;
154+ } ) ;
155+
156+ container . Register ( value ) ;
157+ }
158+ }
159+
160+ protected void RemoveEvent ( Action value , int ? id = null , [ CallerMemberName ] string callerName = null )
161+ {
162+ Debug . Assert ( callerName != null , nameof ( callerName ) + " != null" ) ;
163+ var eventName = EventName ( callerName ) ;
164+ var eventKey = EventKey ( eventName , id ) ;
165+
166+ lock ( objLock )
167+ {
168+ if ( eventContainers . TryGetValue ( eventKey , out var container ) && ! container . Unregister ( value ) )
169+ {
170+ BridgeConnector . Socket . Off ( eventKey ) ;
171+ eventContainers . TryRemove ( eventKey , out _ ) ;
172+ }
173+ }
174+ }
175+
176+ protected void AddEvent < T > ( Action < T > value , int ? id = null , [ CallerMemberName ] string callerName = null )
177+ {
178+ Debug . Assert ( callerName != null , nameof ( callerName ) + " != null" ) ;
179+
180+ var eventName = EventName ( callerName ) ;
181+ var eventKey = EventKey ( eventName , id ) ;
182+
183+ lock ( objLock )
184+ {
185+ var container = eventContainers . GetOrAdd ( eventKey , _ =>
186+ {
187+ var container = new EventContainer ( ) ;
188+ BridgeConnector . Socket . On < T > ( eventKey , container . OnEventActionT ) ;
189+ BridgeConnector . Socket . Emit ( $ "register-{ eventName } ", id ) ;
190+ return container ;
191+ } ) ;
192+
193+ container . Register ( value ) ;
194+ }
195+ }
196+
197+ protected void RemoveEvent < T > ( Action < T > value , int ? id = null , [ CallerMemberName ] string callerName = null )
198+ {
199+ Debug . Assert ( callerName != null , nameof ( callerName ) + " != null" ) ;
200+ var eventName = EventName ( callerName ) ;
201+ var eventKey = EventKey ( eventName , id ) ;
202+
203+ lock ( objLock )
204+ {
205+ if ( eventContainers . TryGetValue ( eventKey , out var container ) && ! container . Unregister ( value ) )
206+ {
207+ BridgeConnector . Socket . Off ( eventKey ) ;
208+ eventContainers . TryRemove ( eventKey , out _ ) ;
209+ }
210+ }
211+ }
212+
213+ private string EventName ( string callerName )
214+ {
215+ switch ( SocketEventNameType )
216+ {
217+ case SocketEventNameTypes . DashedLower :
218+ return $ "{ objectName } -{ callerName . ToDashedEventName ( ) } ";
219+ case SocketEventNameTypes . CamelCase :
220+ return $ "{ objectName } -{ callerName . ToCamelCaseEventName ( ) } ";
221+ default :
222+ throw new ArgumentOutOfRangeException ( ) ;
223+ }
224+ }
225+
226+ private string EventKey ( string eventName , int ? id )
227+ {
228+ return string . Format ( CultureInfo . InvariantCulture , "{0}{1:D}" , eventName , id ) ;
229+ }
123230
124231 internal abstract class PropertyGetter
125232 {
@@ -131,26 +238,37 @@ internal class PropertyGetter<T> : PropertyGetter
131238 private readonly Task < T > tcsTask ;
132239 private TaskCompletionSource < T > tcs ;
133240
134- public PropertyGetter ( ApiBase apiBase , string callerName , int timeoutMs )
241+ public PropertyGetter ( ApiBase apiBase , string callerName , int timeoutMs , object arg = null )
135242 {
136243 this . tcs = new TaskCompletionSource < T > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
137244 this . tcsTask = this . tcs . Task ;
138245
139246 string eventName ;
247+ string messageName ;
140248
141- switch ( apiBase . SocketEventNameType )
249+ switch ( apiBase . SocketTaskEventNameType )
142250 {
143- case SocketEventNameTypes . DashesLowerFirst :
251+ case SocketTaskEventNameTypes . DashesLowerFirst :
144252 eventName = apiBase . propertyEventNames . GetOrAdd ( callerName , s => $ "{ apiBase . objectName } -{ s . StripAsync ( ) . LowerFirst ( ) } -completed") ;
145253 break ;
146- case SocketEventNameTypes . NoDashUpperFirst :
254+ case SocketTaskEventNameTypes . NoDashUpperFirst :
147255 eventName = apiBase . propertyEventNames . GetOrAdd ( callerName , s => $ "{ apiBase . objectName } { s . StripAsync ( ) } Completed") ;
148256 break ;
149257 default :
150258 throw new ArgumentOutOfRangeException ( ) ;
151259 }
152-
153- var messageName = apiBase . propertyMessageNames . GetOrAdd ( callerName , s => apiBase . objectName + s . StripAsync ( ) ) ;
260+
261+ switch ( apiBase . SocketTaskMessageNameType )
262+ {
263+ case SocketTaskMessageNameTypes . DashesLowerFirst :
264+ messageName = apiBase . propertyMessageNames . GetOrAdd ( callerName , s => $ "{ apiBase . objectName } -{ s . StripAsync ( ) . LowerFirst ( ) } ") ;
265+ break ;
266+ case SocketTaskMessageNameTypes . NoDashUpperFirst :
267+ messageName = apiBase . propertyMessageNames . GetOrAdd ( callerName , s => apiBase . objectName + s . StripAsync ( ) ) ;
268+ break ;
269+ default :
270+ throw new ArgumentOutOfRangeException ( ) ;
271+ }
154272
155273 BridgeConnector . Socket . Once < T > ( eventName , ( result ) =>
156274 {
@@ -171,14 +289,14 @@ public PropertyGetter(ApiBase apiBase, string callerName, int timeoutMs)
171289 }
172290 }
173291 } ) ;
174-
175- if ( apiBase . Id >= 0 )
292+
293+ if ( arg != null )
176294 {
177- BridgeConnector . Socket . Emit ( messageName , apiBase . Id ) ;
295+ _ = apiBase . Id >= 0 ? BridgeConnector . Socket . Emit ( messageName , apiBase . Id , arg ) : BridgeConnector . Socket . Emit ( messageName , arg ) ;
178296 }
179297 else
180298 {
181- BridgeConnector . Socket . Emit ( messageName ) ;
299+ _ = apiBase . Id >= 0 ? BridgeConnector . Socket . Emit ( messageName , apiBase . Id ) : BridgeConnector . Socket . Emit ( messageName ) ;
182300 }
183301
184302 System . Threading . Tasks . Task . Delay ( PropertyTimeout ) . ContinueWith ( _ =>
@@ -203,5 +321,53 @@ public override Task<T1> Task<T1>()
203321 return this . tcsTask as Task < T1 > ;
204322 }
205323 }
324+
325+ [ SuppressMessage ( "ReSharper" , "InconsistentlySynchronizedField" ) ]
326+ private class EventContainer
327+ {
328+ private Action eventAction ;
329+ private Delegate eventActionT ;
330+
331+ private Action < T > GetEventActionT < T > ( )
332+ {
333+ return ( Action < T > ) eventActionT ;
334+ }
335+
336+ private void SetEventActionT < T > ( Action < T > actionT )
337+ {
338+ eventActionT = actionT ;
339+ }
340+
341+ public void OnEventAction ( ) => eventAction ? . Invoke ( ) ;
342+
343+ public void OnEventActionT < T > ( T p ) => GetEventActionT < T > ( ) ? . Invoke ( p ) ;
344+
345+ public void Register ( Action receiver )
346+ {
347+ eventAction += receiver ;
348+ }
349+
350+ public void Register < T > ( Action < T > receiver )
351+ {
352+ var actionT = GetEventActionT < T > ( ) ;
353+ actionT += receiver ;
354+ SetEventActionT ( actionT ) ;
355+ }
356+
357+ public bool Unregister ( Action receiver )
358+ {
359+ eventAction -= receiver ;
360+ return this . eventAction != null ;
361+ }
362+
363+ public bool Unregister < T > ( Action < T > receiver )
364+ {
365+ var actionT = GetEventActionT < T > ( ) ;
366+ actionT -= receiver ;
367+ SetEventActionT ( actionT ) ;
368+
369+ return actionT != null ;
370+ }
371+ }
206372 }
207373}
0 commit comments