Sic04/FrameworkLocal/UIClient/Caliburn.Micro/Core/EventAggregator.cs

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));
}
}
}
}