/*
Author: Marc Clifton
Copyright (c) 2004, Knowledge Automation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
and author notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
and author notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
Neither the name of the Knowledge Automation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Diagnostics;
/// Knowledge Automation Event Pool
namespace KAEventPool
{
/// <summary>
/// A defensive event firing mechanism.
/// Juval Lowy's and Eric Gunnerson's C# Best Practices
/// download from: http://www.only4gurus.com/v2/preview.asp?ID=3183
/// </summary>
public class EventsHelper
{
/// <summary>
/// Invoke all handlers attached to the multicast delegate, ensuring
/// that the delegate exists, and iterating through the delegate collection
/// so that event delegates with unassigned delegate handlers don't cause
/// exceptions.
/// </summary>
/// <param name="del"></param>
/// <param name="args"></param>
public static void Fire(Delegate del, params object[] args)
{
if (del != null)
{
Delegate[] delegates = del.GetInvocationList();
foreach(Delegate sink in delegates)
{
try
{
sink.DynamicInvoke(args);
}
catch(Exception e)
{
Trace.WriteLine(e.Message);
throw(e);
}
}
}
}
}
/// <summary>
/// EventPool simplifies the management of many different delegates, their associated events,
/// and event handlers. Events are abstracted by associating the event handler with a tag.
/// Events are invoked by referring to the tag, and multicast delegates are used so that multiple
/// handlers can be associated with the same event.
/// </summary>
public class EventPool
{
/// <summary>
/// The multicast delegate definition. Custom arguments should be derived from EventArgs.
/// </summary>
protected delegate void MulticastDelegate(object sender, EventArgs e);
/// <summary>
/// A one-to-one mapping of event tags to delegates managed by the event pool.
/// </summary>
protected Hashtable eventTagToDelegateMap;
/// <summary>
/// Constructor.
/// </summary>
public EventPool()
{
eventTagToDelegateMap=new Hashtable();
}
/// <summary>
/// Publish the events that this pool will manage.
/// </summary>
/// <param name="tags">A list of tags that can be associated with event handlers.</param>
virtual public void Publish(params object[] tags)
{
foreach(object tag in tags)
{
eventTagToDelegateMap.Add(tag, null);
}
}
/// <summary>
/// Subscribe to an event with validation that the event has been published.
/// </summary>
/// <param name="tag">The event tag.</param>
/// <param name="eventHandler">The event handler.</param>
virtual public void Subscribe(object tag, EventHandler eventHandler)
{
Subscribe(tag, eventHandler, false);
}
/// <summary>
/// Subscribe to an event of the specified tag.
/// </summary>
/// <param name="tag">The tag that to be used when invoking the associated event handlers.</param>
/// <param name="eventHandler">An EventHandler that receives the event, of the form "void (object sender, EventArgs args)"</param>
/// <param name="loose">if false (the default), verifies that the event has been published. If true, skips verification.</param>
virtual public void Subscribe(object tag, EventHandler eventHandler, bool loose)
{
// do we verify that the handler exists?
if (!loose)
{
if (!eventTagToDelegateMap.Contains(tag))
{
Trace.WriteLine("Event Pool does not contain an entry for: "+tag.ToString());
}
}
// create the delegate for this event handler
MulticastDelegate newDelegate=new MulticastDelegate(eventHandler);
// if this is a new tag...
if (!eventTagToDelegateMap.Contains(tag))
{
// ...add a new tag to the map.
eventTagToDelegateMap.Add(tag, newDelegate);
}
else
{
// ...otherwise, combine the new delegate with the delegates for existing mapping.
MulticastDelegate dlgt=(MulticastDelegate)eventTagToDelegateMap[tag];
// if the tag has associated delegates already...
if (dlgt != null)
{
// ... combine the new delegate with the existing ones.
dlgt=(MulticastDelegate)Delegate.Combine(dlgt, newDelegate);
// delegates are data structures, which are therefore passed by value.
eventTagToDelegateMap[tag]=dlgt;
}
else
{
// ... otherwise just assign the new delegate.
eventTagToDelegateMap[tag]=newDelegate;
}
}
}
/// <summary>
/// Fire the events associated with the tag.
/// </summary>
/// <param name="tag">The event tag.</param>
virtual public void FireEvent(object tag)
{
FireEvent(tag, null, null);
}
/// <summary>
/// Fire the events associated with the tag, passing the supplied arguments.
/// </summary>
/// <param name="tag">The event tag.</param>
/// <param name="args">The event arguments.</param>
virtual public void FireEvent(object tag, EventArgs args)
{
FireEvent(tag, null, args);
}
/// <summary>
/// Fire the events associated with the tag, passing the supplied sender.
/// </summary>
/// <param name="tag">The event tag.</param>
/// <param name="sender">The originator of the event.</param>
virtual public void FireEvent(object tag, object sender)
{
FireEvent(tag, sender, null);
}
/// <summary>
/// Fire the events associated with the tag, passing the supplied sender and event arguments.
/// </summary>
/// <param name="tag">The event tag.</param>
/// <param name="sender">The originator of the event.</param>
/// <param name="args">The event arguments.</param>
virtual public void FireEvent(object tag, object sender, EventArgs args)
{
Trace.WriteLine("EventPool Fire: "+tag.ToString());
try
{
MulticastDelegate dlgt=(MulticastDelegate)eventTagToDelegateMap[tag];
EventsHelper.Fire(dlgt, sender, args);
}
catch(Exception e)
{
Trace.WriteLine("Can't fire event tag: "+tag+" "+e.Message);
}
}
/// <summary>
/// Fire all events in the pool.
/// </summary>
virtual public void FireAll()
{
FireAll(null, null);
}
/// <summary>
/// Fire all events in the pool, passing the supplied sender.
/// </summary>
/// <param name="sender">The originator of the event.</param>
virtual public void FireAll(object sender)
{
FireAll(sender, null);
}
/// <summary>
/// Fire all events in the pool, passing the supplied arguments.
/// </summary>
/// <param name="args">The event arguments.</param>
virtual public void FireAll(EventArgs args)
{
FireAll(null, args);
}
/// <summary>
/// Fire all events in the pool, passing the supplied sender and event arguments.
/// </summary>
/// <param name="sender">The originator of the event.</param>
/// <param name="args">The event arguments.</param>
virtual public void FireAll(object sender, EventArgs args)
{
foreach (DictionaryEntry entry in eventTagToDelegateMap)
{
Trace.WriteLine("EventPool FireAll: "+entry.Key.ToString());
MulticastDelegate dlgt=entry.Value as MulticastDelegate;
EventsHelper.Fire(dlgt, sender, args);
}
}
}
}