using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;
namespace AIStudio.Wpf.DiagramDesigner
{
#region WeakAction Inner Class
///
/// This class creates a weak delegate of form Action(Of Object)
///
public class WeakAction
{
#region Data
private readonly WeakReference _target;
private readonly Type _ownerType;
private readonly Type _actionType;
private readonly string _methodName;
#endregion
#region Public Properties/Methods
public WeakAction(object target, Type actionType, MethodBase mi)
{
if (target == null)
{
Debug.Assert(mi.IsStatic);
_ownerType = mi.DeclaringType;
}
else
_target = new WeakReference(target);
_methodName = mi.Name;
_actionType = actionType;
}
public Type ActionType
{
get { return _actionType; }
}
public bool HasBeenCollected
{
get
{
return (_ownerType == null && (_target == null || !_target.IsAlive));
}
}
public Delegate GetMethod()
{
if (_ownerType != null)
{
return Delegate.CreateDelegate(_actionType, _ownerType, _methodName);
}
if (_target != null && _target.IsAlive)
{
object target = _target.Target;
if (target != null)
return Delegate.CreateDelegate(_actionType, target, _methodName);
}
return null;
}
#endregion
}
#endregion
///
/// This class creates a simple Mediator which loosely connects different objects together.
/// The message handlers are organized using string-based message keys and are held in a WeakReference
/// collection.
///
public class Mediator
{
#region Data
static readonly Mediator instance = new Mediator();
static readonly object syncLock = new object();
private readonly Dictionary> _registeredHandlers =
new Dictionary>();
#endregion
#region Ctor
static Mediator()
{
}
private Mediator()
{
}
#endregion
#region Private Methods
///
/// Performs the actual registration of a target
///
/// Key to store in dictionary
/// Delegate type
/// Method
private void RegisterHandler(object key, Type actionType, Delegate handler)
{
var action = new WeakAction(handler.Target, actionType, handler.Method);
lock (_registeredHandlers)
{
List wr;
if (_registeredHandlers.TryGetValue(key, out wr))
{
if (wr.Count > 0)
{
WeakAction wa = wr[0];
if (wa.ActionType != actionType &&
!wa.ActionType.IsAssignableFrom(actionType))
throw new ArgumentException("Invalid key passed to RegisterHandler - existing handler has incompatible parameter type");
}
wr.Add(action);
}
else
{
wr = new List { action };
_registeredHandlers.Add(key, wr);
}
}
}
///
/// Performs the unregistration from a target
///
/// Key to store in dictionary
/// Delegate type
/// Method
private void UnregisterHandler(object key, Type actionType, Delegate handler)
{
lock (_registeredHandlers)
{
List wr;
if (_registeredHandlers.TryGetValue(key, out wr))
{
wr.RemoveAll(wa => handler == wa.GetMethod() && actionType == wa.ActionType);
if (wr.Count == 0)
_registeredHandlers.Remove(key);
}
}
}
///
/// This method broadcasts a message to all message targets for a given
/// message key and passes a parameter.
///
/// Message key
/// Message parameter
/// True/False if any handlers were invoked.
private bool NotifyColleagues(object key, object message)
{
List wr;
List wrCopy = new List();
lock (_registeredHandlers)
{
if (!_registeredHandlers.TryGetValue(key, out wr))
return false;
else
{
foreach (var weakRe in wr)
{
wrCopy.Add(weakRe);
}
}
}
foreach (var cb in wrCopy)
{
Delegate action = cb.GetMethod();
if (action != null)
action.DynamicInvoke(message);
}
lock (_registeredHandlers)
{
wr.RemoveAll(wa => wa.HasBeenCollected);
}
return true;
}
#endregion
#region Public Properties/Methods
///
/// Singleton Instance
///
public static Mediator Instance
{
get
{
return instance;
}
}
///
/// This registers a Type with the mediator. Any methods decorated with will be
/// registered as target method handlers for the given message key.
///
/// Object to register
public void Register(object view)
{
// Look at all instance/static methods on this object type.
foreach (var mi in view.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
// See if we have a target attribute - if so, register the method as a handler.
foreach (var att in mi.GetCustomAttributes(typeof(MediatorMessageSinkAttribute), true))
{
var mha = (MediatorMessageSinkAttribute)att;
var pi = mi.GetParameters();
if (pi.Length != 1)
throw new InvalidCastException("Cannot cast " + mi.Name + " to Action delegate type.");
Type actionType = typeof(Action<>).MakeGenericType(pi[0].ParameterType);
object key = (mha.MessageKey) ?? actionType;
if (mi.IsStatic)
RegisterHandler(key, actionType, Delegate.CreateDelegate(actionType, mi));
else
RegisterHandler(key, actionType, Delegate.CreateDelegate(actionType, view, mi.Name));
}
}
}
///
/// This method unregisters a type from the message mediator.
///
/// Object to unregister
public void Unregister(object view)
{
foreach (var mi in view.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
foreach (var att in mi.GetCustomAttributes(typeof(MediatorMessageSinkAttribute), true))
{
var mha = (MediatorMessageSinkAttribute)att;
var pi = mi.GetParameters();
if (pi.Length != 1)
throw new InvalidCastException("Cannot cast " + mi.Name + " to Action delegate type.");
Type actionType = typeof(Action<>).MakeGenericType(pi[0].ParameterType);
object key = (mha.MessageKey) ?? actionType;
if (mi.IsStatic)
UnregisterHandler(key, actionType, Delegate.CreateDelegate(actionType, mi));
else
UnregisterHandler(key, actionType, Delegate.CreateDelegate(actionType, view, mi.Name));
}
}
}
///
/// This registers a specific method as a message handler for a specific type.
///
/// Message key
/// Handler method
public void RegisterHandler(string key, Action handler)
{
RegisterHandler(key, handler.GetType(), handler);
}
///
/// This registers a specific method as a message handler for a specific type.
///
/// Handler method
public void RegisterHandler(Action handler)
{
RegisterHandler(typeof(Action), handler.GetType(), handler);
}
///
/// This unregisters a method as a handler.
///
/// Message key
/// Handler
public void UnregisterHandler(string key, Action handler)
{
UnregisterHandler(key, handler.GetType(), handler);
}
///
/// This unregisters a method as a handler for a specific type
///
/// Handler
public void UnregisterHandler(Action handler)
{
UnregisterHandler(typeof(Action), handler.GetType(), handler);
}
///
/// This method broadcasts a message to all message targets for a given
/// message key and passes a parameter.
///
/// Message key
/// Message parameter
/// True/False if any handlers were invoked.
public bool NotifyColleagues(string key, T message)
{
return NotifyColleagues((object)key, message);
}
///
/// This method broadcasts a message to all message targets for a given parameter type.
/// If a derived type is passed, any handlers for interfaces or base types will also be
/// invoked.
///
/// Message parameter
/// True/False if any handlers were invoked.
public bool NotifyColleagues(T message)
{
Type actionType = typeof(Action<>).MakeGenericType(typeof(T));
var keyList = from key in _registeredHandlers.Keys
where key is Type && ((Type)key).IsAssignableFrom(actionType)
select key;
bool rc = false;
foreach (var key in keyList)
rc |= NotifyColleagues(key, message);
return rc;
}
///
/// This method broadcasts a message to all message targets for a given
/// message key and passes a parameter. The message targets are all called
/// asynchronously and any resulting exceptions are ignored.
///
/// Message key
/// Message parameter
public void NotifyColleaguesAsync(string key, T message)
{
Func smaFunc = NotifyColleagues;
smaFunc.BeginInvoke(key, message, ia =>
{
try { smaFunc.EndInvoke(ia); }
catch { }
}, null);
}
///
/// This method broadcasts a message to all message targets for a given parameter type.
/// If a derived type is passed, any handlers for interfaces or base types will also be
/// invoked. The message targets are all called asynchronously and any resulting exceptions
/// are ignored.
///
/// Message parameter
public void NotifyColleaguesAsync(T message)
{
Func smaFunc = NotifyColleagues;
smaFunc.BeginInvoke(message, ia =>
{
try { smaFunc.EndInvoke(ia); }
catch { }
}, null);
}
#endregion
}
}