using System.Collections.Immutable; 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 ImmutableDictionary> callbacks; private ImmutableDictionary _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)) { try { //handle in try/catch the emit ImmutableList callbacksLocal = this.callbacks[eventString]; foreach (var fn in callbacksLocal) { fn.Call(args); } } catch { } } 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] = ImmutableList.Empty; this.callbacks = this.callbacks.Add(eventString, ImmutableList.Empty); } ImmutableList callbacksLocal = this.callbacks[eventString]; callbacksLocal = callbacksLocal.Add(fn); //this.callbacks[eventString] = callbacksLocal; this.callbacks = this.callbacks.Remove(eventString).Add(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 = _onceCallbacks.Add(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 = ImmutableDictionary.Create>(); _onceCallbacks = ImmutableDictionary.Create(); return this; } /// /// Removes all listeners of the specified event. /// /// an event name /// a reference to this object. public Emitter Off(string eventString) { try { ImmutableList 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) { callbacks = callbacks.Remove(eventString); foreach (var listener in retrievedValue) { _onceCallbacks.Remove(listener); } } } 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)) { ImmutableList callbacksLocal = this.callbacks[eventString]; IListener offListener; _onceCallbacks.TryGetValue(fn, out offListener); _onceCallbacks = _onceCallbacks.Remove(fn); if (callbacksLocal.Count > 0 && callbacksLocal.Contains(offListener ?? fn)) { callbacksLocal = callbacksLocal.Remove(offListener ?? fn); this.callbacks = this.callbacks.Remove(eventString); this.callbacks = this.callbacks.Add(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 ImmutableList Listeners(string eventString) { if (this.callbacks.ContainsKey(eventString)) { ImmutableList callbacksLocal = this.callbacks[eventString]; return callbacksLocal ?? ImmutableList.Empty; } return ImmutableList.Empty; } /// /// 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; } } }