using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
//From http://caliburnmicro.codeplex.com/
namespace NzbDrone.Common.Eventing
{
///
/// Enables loosely-coupled publication of and subscription to events.
///
public class EventAggregator : IEventAggregator
{
readonly List handlers = new List();
///
/// The default thread marshaller used for publication;
///
public static Action DefaultPublicationThreadMarshaller = action => action();
///
/// Processing of handler results on publication thread.
///
public static Action HandlerResultProcessing = (target, result) => { };
///
/// Initializes a new instance of the class.
///
public EventAggregator()
{
PublicationThreadMarshaller = DefaultPublicationThreadMarshaller;
}
///
/// Gets or sets the default publication thread marshaller.
///
///
/// The default publication thread marshaller.
///
public Action PublicationThreadMarshaller { get; set; }
///
/// Subscribes an instance to all events declared through implementations of
///
/// The instance to subscribe for event publication.
public virtual void Subscribe(object instance)
{
lock (handlers)
{
if (handlers.Any(x => x.Matches(instance)))
{
return;
}
handlers.Add(new Handler(instance));
}
}
///
/// Unsubscribes the instance from all events.
///
/// The instance to unsubscribe.
public virtual void Unsubscribe(object instance)
{
lock (handlers)
{
var found = handlers.FirstOrDefault(x => x.Matches(instance));
if (found != null)
{
handlers.Remove(found);
}
}
}
///
/// Publishes a message.
///
/// The message instance.
///
/// Does not marshall the the publication to any special thread by default.
///
public virtual void Publish(object message)
{
Publish(message, PublicationThreadMarshaller);
}
///
/// Publishes a message.
///
/// The message instance.
/// Allows the publisher to provide a custom thread marshaller for the message publication.
public virtual void Publish(object message, Action marshal)
{
Handler[] toNotify;
lock (handlers)
{
toNotify = handlers.ToArray();
}
marshal(() =>
{
var messageType = message.GetType();
var dead = toNotify
.Where(handler => !handler.Handle(messageType, message))
.ToList();
if (dead.Any())
{
lock (handlers)
{
foreach (var item in dead)
{
handlers.Remove(item);
}
}
}
});
}
class Handler
{
readonly WeakReference reference;
readonly Dictionary supportedHandlers = new Dictionary();
public Handler(object handler)
{
reference = new WeakReference(handler);
var interfaces = handler.GetType().GetInterfaces()
.Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsGenericType);
foreach (var @interface in interfaces)
{
var type = @interface.GetGenericArguments()[0];
var method = @interface.GetMethod("Handle");
supportedHandlers[type] = method;
}
}
public bool Matches(object instance)
{
return reference.Target == instance;
}
public bool Handle(Type messageType, object message)
{
var target = reference.Target;
if (target == null)
{
return false;
}
foreach (var pair in supportedHandlers)
{
if (pair.Key.IsAssignableFrom(messageType))
{
var result = pair.Value.Invoke(target, new[] { message });
if (result != null)
{
HandlerResultProcessing(target, result);
}
return true;
}
}
return true;
}
}
}
}