#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Globalization;
using System.Reflection;
using Common.Logging;
using Spring.Core;
using Spring.Util;
#endregion
namespace Spring.Objects.Support{
/// <summary>
/// Describes an <see cref="Spring.Objects.IEventHandlerValue"/> implementation
/// that autowires events to handler methods.
/// </summary>
/// <author>Rick Evans</author>
public class AutoWiringEventHandlerValue : AbstractEventHandlerValue
{
#region Constants
private const string EventNamePlaceHolder = "${event}";
private const string DefaultMethodPrefix = "On";
private const string DefaultMethodName = DefaultMethodPrefix + EventNamePlaceHolder;
private static readonly ILog log
= LogManager.GetLogger(typeof (AutoWiringEventHandlerValue));
#endregion
#region Constructor (s) / Destructor
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Objects.Support.AutoWiringEventHandlerValue"/> class.
/// </summary>
public AutoWiringEventHandlerValue()
{
MethodName = DefaultMethodName;
}
#endregion
#region Properties
/// <summary>
/// The name of the method that is going to handle the event.
/// </summary>
public override string MethodName
{
get { return base.MethodName; }
set { base.MethodName = StringUtils.HasText(value) ? value.Trim() : DefaultMethodName; }
}
#endregion
#region Methods
/// <summary>
/// Wires up the specified handler to the named event on the supplied event source.
/// </summary>
/// <param name="source">
/// The object (an object instance, a <see cref="System.Type"/>, etc)
/// exposing the named event.
/// </param>
/// <param name="handler">
/// The handler for the event (an object instance, a <see cref="System.Type"/>,
/// etc).
/// </param>
public override void Wire(object source, object handler)
{
AssertUtils.ArgumentNotNull(source, "source");
AssertUtils.ArgumentNotNull(handler, "handler");
// simply delegate so that after wiring, state is wiped clean...
AutoWirer wirer = new AutoWirer(source, EventName, handler, MethodName);
wirer.Wire();
}
#endregion
#region Inner Class : AutoWirer
/// <summary>
/// Performs the matching up of handler methods to one or more source events.
/// </summary>
/// <remarks>
/// <p>
/// This class merely marshals the matching of handler methods to the events exposed
/// by an event source, and then delegates to a concrete
/// <see cref="Spring.Objects.IEventHandlerValue"/> implementation (such as
/// <see cref="Spring.Objects.Support.InstanceEventHandlerValue"/> or
/// <see cref="Spring.Objects.Support.StaticEventHandlerValue"/>) to do the heavy lifting of
/// actually wiring a handler method to an event.
/// </p>
/// <p>
/// Note : the order in which handler's are wired up to events is non-deterministic.
/// </p>
/// </remarks>
private sealed class AutoWirer
{
#region Constructor (s) / Destructor
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Objects.Support.AutoWiringEventHandlerValue.AutoWirer"/> class.
/// </summary>
/// <param name="source">
/// The object exposing the event (s) being wired up.
/// </param>
/// <param name="eventName">
/// The name of the event that is being wired up.
/// </param>
/// <param name="handler">
/// The object exposing the method (s) being wired to the event.
/// </param>
/// <param name="methodName">
/// The name of the method that is going to handle the event.
/// </param>
public AutoWirer(
object source, string eventName, object handler, string methodName)
{
Source = source;
EventName = eventName;
Handler = handler;
MethodName = methodName;
}
#endregion
#region Methods
/// <summary>
/// Wires up events on the source to methods exposed on the handler.
/// </summary>
public void Wire()
{
Type sourceType = ReflectionUtils.TypeOfOrType(Source);
// create the criteria for the event search...
ICriteria criteria = new RegularExpressionEventNameCriteria(EventName);
// and grab the events that satisfy the criteria...
BindingFlags eventFlags = BindingFlags.Instance | BindingFlags.Static
| BindingFlags.Public;
MemberInfo[] events = sourceType.FindMembers(
MemberTypes.Event,
eventFlags,
new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria),
criteria);
// and for each event that satisfied the criteria...
foreach (EventInfo evt in events)
{
WireEvent(evt);
}
}
/// <summary>
/// Wires up the supplied event to any handler methods that match the event
/// signature.
/// </summary>
/// <param name="theEvent">The event being wired up.</param>
private void WireEvent(EventInfo theEvent)
{
// grab some info (such as the delegate's method signature) about the event
DelegateInfo eventDelegate = new DelegateInfo(theEvent);
// if the method name needs to be customised on a per event basis, do so
string customMethodName = GetMethodNameCustomisedForEvent(theEvent.Name);
// create the criteria for the handler method search...
ComposedCriteria methodCriteria = new ComposedCriteria();
// a candidate handlers method name must match the custom method name
methodCriteria.Add(new RegularExpressionMethodNameCriteria(customMethodName));
// the return Type of a candidate handlers method must be the same as the return type of the event
methodCriteria.Add(new MethodReturnTypeCriteria(eventDelegate.GetReturnType()));
// a candidate handlers method parameters must match the event's parameters
methodCriteria.Add(new MethodParametersCriteria(eventDelegate.GetParameterTypes()));
// and grab the methods that satisfy the criteria...
BindingFlags methodFlags = BindingFlags.Instance | BindingFlags.Static
| BindingFlags.NonPublic | BindingFlags.Public;
MemberInfo[] methods = HandlerType.FindMembers(
MemberTypes.Method,
methodFlags,
new MemberFilter(new CriteriaMemberFilter().FilterMemberByCriteria),
methodCriteria);
// and for each method that satisfied the criteria...
foreach (MethodInfo method in methods)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format(
CultureInfo.InvariantCulture,
"Wiring up this method '{0}' to this event '{1}'",
method.Name,
theEvent.Name));
}
#endregion
IEventHandlerValue myHandler = method.IsStatic ?
(IEventHandlerValue) new StaticEventHandlerValue() :
(IEventHandlerValue) new InstanceEventHandlerValue();
myHandler.EventName = theEvent.Name;
myHandler.MethodName = method.Name;
myHandler.Wire(Source, Handler);
}
}
/// <summary>
/// Only replaces the <b>first</b> occurrence of the placeholder.
/// </summary>
/// <param name="eventName">The event whose name is going to be used.</param>
/// <returns>
/// The method name customised for the name of the supplied event.
/// </returns>
private string GetMethodNameCustomisedForEvent(string eventName)
{
string methodName = MethodName;
if (MethodName.IndexOf(EventNamePlaceHolder) >= 0)
{
methodName = MethodName.Replace(EventNamePlaceHolder, eventName);
}
return methodName;
}
#endregion
#region Properties
/// <summary>
/// The object exposing the event (s) being wired up.
/// </summary>
private object Source
{
get { return _source; }
set { _source = value; }
}
/// <summary>
/// The object exposing the method (s) being wired to an event source.
/// </summary>
private object Handler
{
get { return _handler; }
set { _handler = value; }
}
/// <summary>
/// The <see cref="Type"/> of the object that is handling any events.
/// </summary>
private Type HandlerType
{
get
{
if (_handlerType == null)
{
_handlerType = ReflectionUtils.TypeOfOrType(Handler);
}
return _handlerType;
}
}
/// <summary>
/// The name of the method that is going to handle the event.
/// </summary>
private string MethodName
{
get { return _methodName; }
set { _methodName = value; }
}
/// <summary>
/// The name of the event that is being wired up.
/// </summary>
private string EventName
{
get { return _eventName; }
set { _eventName = value; }
}
#endregion
#region Fields
private object _source;
private object _handler;
private string _methodName;
private string _eventName;
private Type _handlerType;
#endregion
}
#endregion
}
}
|