using System;
using System.Linq;
using System.Reflection;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.Dependency;
using Chernobyl.Event;
using Chernobyl.Reflection;
namespace Chernobyl.Attribution
{
///
/// An that is applied to methods. When executed,
/// instances of this type will execute the attributed method.
///
[AttributeUsage(AttributeTargets.Method)]
public class RunAttribute
: Attribute, IProcessAttribute
{
///
/// Performs the task requested on the
/// passed in.
///
/// The
/// this was applied to and that is to be
/// processed.
/// Thrown if the method is not static
/// or has a return value and the property
/// is not set to true.
public void Process(MethodInfo method)
{
// This type does not invoke non-static methods.
method.ThrowWhenStaticIs(false);
// This type does not do anything with the return value. Make sure
// the user knows this to prevent errors.
if (IgnoreReturnValue == false && method.ReturnType != typeof(void))
throw new MethodException(String.Format("Unable to run the static " +
"method. The {0} does not " +
"know what to do with the " +
"value returned by the method.",
typeof(RunAttribute)),
method.ToEnumerable());
// Grab the parameters as objects.
var parameterInfos = method.GetParameters();
var parameters = parameterInfos.Get().ToEventList().AsValues();
// Prepare method for invoking the method.
Func parametersReady = () => parameters.Count() == parameterInfos.Length;
Action invokeMethod = () => method.Invoke(null, parameters.ToArray());
// Listen for the parameters being added.
EventHandler> invokeOnce = null;
invokeOnce = (sender, args) =>
{
// If the numbers of parameters is now the amount expected,
// invoke the method.
if (parametersReady())
{
invokeMethod();
parameters.ItemsAdded -= invokeOnce;
}
};
// If the parameters are ready now, we can invoke them. Otherwise,
// wait for the parameters to be ready.
if (parametersReady())
invokeMethod();
else
parameters.ItemsAdded += invokeOnce;
}
///
/// False if an exception should be thrown when processed methods have
/// return values, true if otherwise. By default, this property is false.
///
public bool IgnoreReturnValue { get; set; }
}
}