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