171 lines
5.9 KiB
C#
171 lines
5.9 KiB
C#
namespace Caliburn.Micro.Core {
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
/// <summary>
|
|
/// Enables loosely-coupled publication of and subscription to events.
|
|
/// </summary>
|
|
public class EventAggregator : IEventAggregator {
|
|
static readonly ILog Log = LogManager.GetLog(typeof(EventAggregator));
|
|
readonly List<Handler> handlers = new List<Handler>();
|
|
|
|
/// <summary>
|
|
/// Processing of handler results on publication thread.
|
|
/// </summary>
|
|
public static Action<object, object> HandlerResultProcessing = (target, result) => { };
|
|
|
|
/// <summary>
|
|
/// Searches the subscribed handlers to check if we have a handler for
|
|
/// the message type supplied.
|
|
/// </summary>
|
|
/// <param name="messageType">The message type to check with</param>
|
|
/// <returns>True if any handler is found, false if not.</returns>
|
|
public bool HandlerExistsFor(Type messageType) {
|
|
return handlers.Any(handler => handler.Handles(messageType) & !handler.IsDead);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" />
|
|
/// </summary>
|
|
/// <param name = "subscriber">The instance to subscribe for event publication.</param>
|
|
public virtual void Subscribe(object subscriber) {
|
|
if (subscriber == null) {
|
|
throw new ArgumentNullException("subscriber");
|
|
}
|
|
lock(handlers) {
|
|
if (handlers.Any(x => x.Matches(subscriber))) {
|
|
return;
|
|
}
|
|
|
|
handlers.Add(new Handler(subscriber));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsubscribes the instance from all events.
|
|
/// </summary>
|
|
/// <param name = "subscriber">The instance to unsubscribe.</param>
|
|
public virtual void Unsubscribe(object subscriber) {
|
|
if (subscriber == null) {
|
|
throw new ArgumentNullException("subscriber");
|
|
}
|
|
lock(handlers) {
|
|
var found = handlers.FirstOrDefault(x => x.Matches(subscriber));
|
|
|
|
if (found != null) {
|
|
handlers.Remove(found);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual bool IsHandlerDead(object subscriber)
|
|
{
|
|
if (subscriber == null)
|
|
{
|
|
throw new ArgumentNullException("subscriber");
|
|
}
|
|
lock (handlers)
|
|
{
|
|
foreach (var @handler in handlers)
|
|
if (@handler.IsDead)
|
|
handlers.Remove(@handler);
|
|
|
|
if (handlers.Any(x => x.Matches(subscriber)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Publishes a message.
|
|
/// </summary>
|
|
/// <param name = "message">The message instance.</param>
|
|
/// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param>
|
|
public virtual void Publish(object message, Action<System.Action> marshal) {
|
|
if (message == null){
|
|
throw new ArgumentNullException("message");
|
|
}
|
|
if (marshal == null) {
|
|
throw new ArgumentNullException("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) {
|
|
dead.Apply(x => handlers.Remove(x));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
class Handler {
|
|
readonly object reference;
|
|
readonly Dictionary<Type, MethodInfo> supportedHandlers = new Dictionary<Type, MethodInfo>();
|
|
|
|
public bool IsDead {
|
|
//get { return reference.Target == null; }
|
|
get { return reference == null; }
|
|
}
|
|
|
|
public Handler(object handler) {
|
|
//reference = new WeakReference(handler);
|
|
reference = 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", new Type[] { type });
|
|
|
|
if (method != null) {
|
|
supportedHandlers[type] = method;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Matches(object instance) {
|
|
return reference == instance;
|
|
}
|
|
|
|
public bool Handle(Type messageType, object message) {
|
|
//var target = reference.Target;
|
|
var target = reference;
|
|
if (target == null) {
|
|
Log.Info("Subscriber missing.");
|
|
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;
|
|
}
|
|
|
|
public bool Handles(Type messageType) {
|
|
return supportedHandlers.Any(pair => pair.Key.IsAssignableFrom(messageType));
|
|
}
|
|
}
|
|
}
|
|
}
|