There are a number of ways that I would like to improve the code, but just never have had the time. When I get a few free moments, I will discuss some of the enhancements that I would like to put in the code eventually.
// for MethodImplAttrbute
using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
// for ISynchronizer
// --------------------------------------------------------------------
//
// This code is (C) Copyright 2005 Marc Adler
//
// --------------------------------------------------------------------
namespace Magmasystems.EventManager
{
  public delegate bool EventManagerEventHandler(object sender, string topicName, EventArgs e);
  public class EventManager
  {
    #region Delegates
    // Internal delegate used to help us fire async events
    private delegate void AsyncFire(Delegate del, object[] args);
    #endregion
    #region Variables
    // The singleton EventManager
    private static EventBroker _theEventManager = null;
    // A Dictionary of EventManagerEventInfo classes, indexed by the topic name
    private Hashtable _theEventDictionary = null;
    private Hashtable _theWildcardSubscribers = null;
    // Used to disabled and re-enable the firing of events
    private bool _isEnabled = true;
    #endregion
    #region Constructors and Static Instance
    protected EventBroker()
    {
    }
    // This is the way that the EventManager singleton is accessed
    static public EventManager Instance
    {
      get
      {
        if(_theEventManager == null)
        {
          _theEventManager = new EventBroker();
          _theEventManager._theEventDictionary = new Hashtable();
          _theEventManager._theWildcardSubscribers = new Hashtable();
          _theEventManager._isEnabled = true;
        }
        return _theEventManager;
      }
    }
    #endregion
    #region Properties
    private Hashtable Dictionary
    {
      get
      {
        // make sure that the dictionary and event manager are instantiated
        return _theEventDictionary;
      }
    }
    private Hashtable WildcardDictionary
    {
      get
      {
        // make sure that the dictionary and event manager are instantiated
        return _theWildcardSubscribers;
      }
    }
    public bool Enabled
    {
      get { return this._isEnabled; }
      set { this._isEnabled = value; }
    }
    #endregion
    #region General Methods
    static public void Register(object o)
    {
      Type type = o.GetType();
      MethodInfo[] methods = type.GetMethods();
      foreach (MethodInfo mi in methods)
      {
//        if(mi.DeclaringType != type)
//          continue;
        object[] oAttrs = mi.GetCustomAttributes(true);
        foreach (object oAttr in oAttrs)
        {
          EventManagerEventInfo evInfo;
          if(oAttr is EventSubscriberAttribute)
          {
            EventSubscriberAttribute evSubAttr = oAttr as EventSubscriberAttribute;
            evInfo = FindTopicEntry(evSubAttr.Topic, true);
            if(evInfo != null)
            {
              EventSubscriberInfo subInfo = evInfo.AddSubscriber(evSubAttr.Topic, o, evSubAttr.IsBackground, mi);
              if(subInfo.HasWildcards)
              {
                EventBroker.Instance._theWildcardSubscribers[evSubAttr.Topic] = evInfo;
              }
            }
          }
          else if(oAttr is EventPublisherAttribute)
          {
            EventPublisherAttribute evPubAttr = oAttr as EventPublisherAttribute;
            evInfo = FindTopicEntry(evPubAttr.Topic, true);
            if(evInfo != null)
            {
              evInfo.AddPublisher(evPubAttr.Topic, o);
            }
          }
        }
      }
    }
    static private EventManagerEventInfo FindTopicEntry(string topicName, bool createIfEmpty)
    {
      EventManagerEventInfo evInfo  ;
      topicName = topicName.ToUpper();
      object oEventInfo = EventBroker.Instance._theEventDictionary[topicName];
      if(oEventInfo == null)
      {
        if(createIfEmpty)
        {
          // Allocate a new entry
          evInfo = new EventManagerEventInfo();
          EventBroker.Instance.Dictionary[topicName] = evInfo;
        }
        else
        {
          evInfo = null;
        }
      }
      else
      {
        // Use the existing entry
        evInfo = oEventInfo as EventManagerEventInfo;
      }
      return evInfo;
    }
    #endregion
    #region Ways to Fire and Event
    [MethodImpl(MethodImplOptions.NoInlining)]
    static public void Fire(object sender, string topicName, EventArgs args)
    {
      if (EventBroker.Instance.Enabled == false)
        return;
      EventManagerEventInfo evInfo = FindTopicEntry(topicName, false);
      if(evInfo != null)
      {
        evInfo.Fire(sender, topicName, args);
      }
      foreach (object oKey in EventBroker.Instance._theWildcardSubscribers.Keys)
      {
        string keyName = oKey as String;
        Regex regExp = new Regex(keyName, RegexOptions.IgnoreCase);
        if(regExp.IsMatch(topicName))
        {
          evInfo = EventBroker.Instance._theWildcardSubscribers[oKey] as EventManagerEventInfo;
          evInfo.Fire(sender, topicName, args);
        }
      }
    }
    #endregion
    #region Inner Class for EventManagerEventInfo
    /// 
    /// EventManagerEventInfo
    /// This class represents information about a single event
    /// 
    public class EventManagerEventInfo
    {
      #region Variables
      // Multicast delegate of all subcribers to this event
      private event EventManagerEventHandler _eventHandler;
      // The list of classes that publish this event
      private ArrayList _publishers;
      // The list of classes that subscribe to this event
      private ArrayList _subscribers;
      #endregion
      #region Constructors
      public EventManagerEventInfo()
      {
        this._publishers = new ArrayList();
        this._subscribers = new ArrayList();
        this._eventHandler = null;
      }
      #endregion
      #region Events
      public event EventManagerEventHandler EventManagerEvent
      {
        add { this._eventHandler += value; }
        remove { this._eventHandler -= value; }
      }
      #endregion
      #region Properties
      public EventManagerEventHandler EventHandler
      {
        get { return this._eventHandler; }
      }
      public ArrayList Publishers
      {
        get { return this._publishers; }
      }
      public ArrayList Subscribers
      {
        get { return this._subscribers; }
      }
      #endregion
      #region Methods
      public EventSubscriberInfo AddSubscriber(string topicName, object objectRef, bool isBackground, MethodInfo mi)
      {
        EventSubscriberInfo subInfo = new EventSubscriberInfo(this, topicName, objectRef, isBackground, mi);
        this._eventHandler += new EventManagerEventHandler(subInfo.OnPublisherFired);
        this.Subscribers.Add(subInfo);
        return subInfo;
      }
      public EventPublisherInfo AddPublisher(string topicName, object objectRef)
      {
        EventPublisherInfo pubInfo = new EventPublisherInfo(this, topicName, objectRef);
        this.Publishers.Add(pubInfo);
        return pubInfo;
      }
      public void RemoveSubscriber(EventSubscriberInfo sub)
      {
        this.Subscribers.Remove(sub);
      }
      public void RemovePublisher(EventPublisherInfo pub)
      {
        this.Publishers.Remove(pub);
      }
      #endregion
      #region Event Firing
      public void Fire(object sender, string topicName, EventArgs args)
      {
        // This handles the event firing to the SubscriberInfo class. In turn, the
        // SubscriberInfo object will invoke the actual delegate. The SubscriberInfo
        // object will determine whether the delegate shoul dbe called synchronously
        // or asynchronously.
        if(this.EventHandler != null)
        {
          this.EventHandler(sender, topicName, args);
        }
      }
      #endregion
    }
    #endregion
    #region Inner class for EventPublisherInfo
    public class EventPublisherInfo
    {
      #region Variables
      private EventManagerEventInfo _evInfo;
      private WeakReference _objectRef;
      private string _topicName; // the upper-cased name
      private string _displayName; // used to generate context menus at runtime
      #endregion
      #region Constructors
      private EventPublisherInfo()
      {
        this._objectRef = null;
      }
      public EventPublisherInfo(EventManagerEventInfo evInfo, string topicName) : this()
      {
        this._evInfo = evInfo;
        this.DisplayName = topicName;
        this._topicName = topicName.ToUpper();
      }
      public EventPublisherInfo(EventManagerEventInfo evInfo, string topicName, object objectRef) : this(evInfo, topicName)
      {
        this._objectRef = new WeakReference(objectRef);
      }
      #endregion
      #region Properties
      public WeakReference Publisher
      {
        get { return (this._objectRef.IsAlive == true) ? this._objectRef : null; }
        set { this._objectRef = value; }
      }
      public string DisplayName
      {
        get { return this._displayName; }
        set { this._displayName = value; }
      }
      #endregion
    }
    #endregion
    #region Inner class for EventSubscriberInfo
    public class EventSubscriberInfo
    {
      #region Variables
      private EventManagerEventInfo _eventInfo; // ref back to the holding container
      private WeakReference _objectRef;
      private MethodInfo _methodInfo; // The method that the event firer should Invoke
      private bool _isBackground;
      private EventManagerEventHandler _delegateForAsync;
      private string _topicName; // we may have wildcards
      private bool _hasWildcards; // to help determine whether to use RegEx or not
      #endregion
      #region Constructors
      private EventSubscriberInfo()
      {
        this._objectRef = null;
        this._isBackground = false;
        this._hasWildcards = false;
      }
      public EventSubscriberInfo(EventManagerEventInfo evInfo, string topicName) : this()
      {
        this._eventInfo = evInfo;
        this.TopicName = topicName; // use the property so that formatting is done
      }
      public EventSubscriberInfo(EventManagerEventInfo evInfo, string topicName, object objectRef) : this(evInfo, topicName)
      {
        this._objectRef = new WeakReference(objectRef);
      }
      public EventSubscriberInfo(EventManagerEventInfo evInfo, string topicName, object objectRef, bool isBackground, MethodInfo mi) : this(evInfo, topicName, objectRef)
      {
        this._isBackground = isBackground;
        this._methodInfo = mi;
        if(isBackground)
        {
          this._delegateForAsync = (EventManagerEventHandler) Delegate.CreateDelegate(typeof (EventManagerEventHandler), objectRef, mi.Name);
        }
      }
      #endregion
      #region Properties
      public object Subscriber
      {
        get { return (this._objectRef.IsAlive) ? this._objectRef.Target : null; }
      }
      public bool IsBackground
      {
        get { return this._isBackground; }
        set { this._isBackground = value; }
      }
      public string TopicName
      {
        get { return this._topicName; }
        set { this._topicName = FormatTopicName(value); }
      }
      public bool HasWildcards
      {
        get { return this._hasWildcards; }
      }
      #endregion
      #region Methods
      private string FormatTopicName(string topic)
      {
        // We want to make things easy for matching publishers and sunscribers.
        // 1) We always use upper-case topic names.
        // 2) Replace the dots with slashes so that the dot is not taken as a regexp
        //    character by the pattern matcher.
        // 3) Let us know whether this topic has a wildcard in it so we know whether
        //    to use the slow regexp matcher or the faster Equals operator.
        if(topic.IndexOfAny(new char[] {'*'}) >= 0)
          this._hasWildcards = true;
        return topic.Replace('.', '/').ToUpper();
      }
      #endregion
      #region Event Firing
      /// 
      /// This gets called whenever the event manager fires an event.
      /// This does the hard work in event firing. It eventually calls
      /// the subscriber's delegate in order to process the event. it also determines
      /// whether the delegate should be called synchronously or asynchronously.
      /// 
      /// 
      /// 
      /// 
      /// 
      // 
      public bool OnPublisherFired(object sender, string topicName, EventArgs args)
      {
        // If the object that this subscriber is bound to has been garbage-collected, then
        // remove the subscriber from the EventInfo's subscriber list and return.
        if(this.Subscriber == null)
        {
          this._eventInfo.RemoveSubscriber(this);
          return true;
        }
        if(this.IsBackground)
        {
          AsyncFire asyncFire = new AsyncFire(InvokeDelegate);
          asyncFire.BeginInvoke(this._delegateForAsync, new object[] {sender, topicName, args}, new AsyncCallback(Cleanup), null);
        }
        else
        {
          object oRet = this._methodInfo.Invoke(this.Subscriber, new object[] {sender, topicName, args});
          if(oRet is Boolean)
            return (bool) oRet;
        }
        return true;
      }
      private void InvokeDelegate(Delegate del, object[] args)
      {
        ISynchronizeInvoke synchronizer = del.Target as ISynchronizeInvoke;
        if(synchronizer != null) // Requires thread affinity
        {
          if(synchronizer.InvokeRequired)
          {
            synchronizer.Invoke(del, args);
            return;
          }
        }
        // Not requiring thread afinity or invoke is not required
        del.DynamicInvoke(args);
      }
      private void Cleanup(IAsyncResult asyncResult)
      {
        asyncResult.AsyncWaitHandle.Close();
      }
      #endregion
    }
    #endregion
  }
}
©2006 Marc Adler - All Rights Reserved
 
 
No comments:
Post a Comment