using System.Collections.Concurrent; using System.Collections.Generic; using Quobject.EngineIoClientDotNet.Modules; using System; namespace Quobject.EngineIoClientDotNet.ComponentEmitter { /// /// The event emitter which is ported from the JavaScript module. /// https://github.com/component/emitter /// public class Emitter { private ConcurrentDictionary> callbacks; private ConcurrentDictionary _onceCallbacks; public Emitter() { this.Off(); } /// /// Executes each of listeners with the given args. /// /// an event name. /// /// a reference to this object. public virtual Emitter Emit(string eventString, params object[] args) { //var log = LogManager.GetLogger(Global.CallerName()); //log.Info("Emitter emit event = " + eventString); if (this.callbacks.ContainsKey(eventString)) { List callbacksLocal = this.callbacks[eventString]; var listCopy = new List(); listCopy.AddRange(callbacksLocal); foreach (var fn in listCopy) { fn.Call(args); } } return this; } /// /// Listens on the event. /// /// event name /// /// a reference to this object public Emitter On(string eventString, IListener fn) { if (!this.callbacks.ContainsKey(eventString)) { //this.callbacks[eventString] = List(); this.callbacks.TryAdd(eventString,new List()); } List callbacksLocal = this.callbacks[eventString]; callbacksLocal.Add(fn); //this.callbacks[eventString] = callbacksLocal; List outref; this.callbacks.TryRemove(eventString, out outref); this.callbacks.TryAdd(eventString, callbacksLocal); return this; } /// /// Listens on the event. /// /// event name /// /// a reference to this object public Emitter On(string eventString, Action fn) { var listener = new ListenerImpl(fn); return this.On(eventString, listener); } /// /// Listens on the event. /// /// event name /// /// a reference to this object public Emitter On(string eventString, Action fn) { var listener = new ListenerImpl(fn); return this.On(eventString, listener); } /// /// Adds a one time listener for the event. /// /// an event name. /// /// a reference to this object public Emitter Once(string eventString, IListener fn) { var on = new OnceListener(eventString, fn, this); _onceCallbacks.TryAdd(fn, on); this.On(eventString, on); return this; } /// /// Adds a one time listener for the event. /// /// an event name. /// /// a reference to this object public Emitter Once(string eventString, Action fn) { var listener = new ListenerImpl(fn); return this.Once(eventString, listener); } /// /// Removes all registered listeners. /// /// a reference to this object. public Emitter Off() { callbacks = new ConcurrentDictionary>(); _onceCallbacks = new ConcurrentDictionary(); return this; } /// /// Removes all listeners of the specified event. /// /// an event name /// a reference to this object. public Emitter Off(string eventString) { try { List retrievedValue; if (!callbacks.TryGetValue(eventString, out retrievedValue)) { var log = LogManager.GetLogger(Global.CallerName()); log.Info(string.Format("Emitter.Off Could not remove {0}", eventString)); } if (retrievedValue != null) { List outref; IListener ilistenerOutRef; callbacks.TryRemove(eventString, out outref); foreach (var listener in retrievedValue) { _onceCallbacks.TryRemove(listener, out ilistenerOutRef); } } } catch (Exception) { this.Off(); } return this; } /// /// Removes the listener /// /// an event name /// /// a reference to this object. public Emitter Off(string eventString, IListener fn) { try { if (this.callbacks.ContainsKey(eventString)) { List callbacksLocal = this.callbacks[eventString]; IListener offListener; //_onceCallbacks.TryGetValue(fn,out offListener); _onceCallbacks.TryRemove(fn, out offListener); if (callbacksLocal.Count > 0 && callbacksLocal.Contains(offListener ?? fn)) { callbacksLocal.Remove(offListener ?? fn); List outref; this.callbacks.TryRemove(eventString, out outref); this.callbacks.TryAdd(eventString, callbacksLocal); } } } catch (Exception) { this.Off(); } return this; } /// /// Returns a list of listeners for the specified event. /// /// an event name. /// a reference to this object public List Listeners(string eventString) { if (this.callbacks.ContainsKey(eventString)) { List callbacksLocal = this.callbacks[eventString]; return callbacksLocal ?? new List(); } return new List(); } /// /// Check if this emitter has listeners for the specified event. /// /// an event name /// bool public bool HasListeners(string eventString) { return this.Listeners(eventString).Count > 0; } } public interface IListener: System.IComparable { int GetId(); void Call(params object[] args); } public class ListenerImpl : IListener { private static int id_counter = 0; private int Id; private readonly Action fn1; private readonly Action fn; public ListenerImpl(Action fn) { this.fn = fn; this.Id = id_counter++; } public ListenerImpl(Action fn) { this.fn1 = fn; this.Id = id_counter++; } public void Call(params object[] args) { if (fn != null) { var arg = args.Length > 0 ? args[0] : null; fn(arg); } else { fn1(); } } public int CompareTo(IListener other) { return this.GetId().CompareTo(other.GetId()); } public int GetId() { return Id; } } public class OnceListener : IListener { private static int id_counter = 0; private int Id; private readonly string _eventString; private readonly IListener _fn; private readonly Emitter _emitter; public OnceListener(string eventString, IListener fn, Emitter emitter) { this._eventString = eventString; this._fn = fn; this._emitter = emitter; Id = id_counter++; } void IListener.Call(params object[] args) { _emitter.Off(_eventString, this); _fn.Call(args); } public int CompareTo(IListener other) { return this.GetId().CompareTo(other.GetId()); } public int GetId() { return Id; } } }