using System; using System.Reflection; using Chernobyl.Collections.Generic.Event; using Chernobyl.Event; using Chernobyl.Utility; namespace Chernobyl.Dependency { /// /// Used to mark an instance that needs to be retrieved from an /// instance. When this /// instance has it's /// /// method invoked, it will take the instance from the /// provided but only if that instance exists. Otherwise the property will /// set the instance when it receives that instance. /// [AttributeUsage(AttributeTargets.Property)] public class InjectAttribute : ServiceAttribute { /// /// Initializes a new instance of the /// class. /// public InjectAttribute() { } /// /// Initializes a new instance of the /// class. /// /// The location of the service within the /// hierarchy or an empty array if the service /// is at the root of the hierarchy. This array /// represents the "path" that must be taken through the hierarchy of /// to get to the location where the service /// specified by resides. Each /// must implement the /// interface. An example of what the service path might look like /// (using '/' as separators): /// /IEventEnumerable/IGraphicsServices/ITextureServices /// In this example, the service represented by /// is located in the ITextureServices. That /// is located within IGraphicsServices which /// is located at the root . public InjectAttribute(params Type[] location) : base(location) { } /// /// Retrieves the service from the instance /// and passes it to the provided. /// /// The instance that allows access to the other /// services and provides adding or removing of specific services. /// The instance that contained a code element that /// was attributed with a instance. /// The code element that was attributed /// with a and whose service needs to be /// added into the . public override void Resolve(IEventCollection services, object instance, ICustomAttributeProvider attributeProvider) { services.ThrowIfNull("services"); if (attributeProvider != null) { _property = (PropertyInfo)attributeProvider; SetValue = SetValue ?? _property.SetValue; Type = Type ?? _property.PropertyType; } if (SetValue == null) throw new Exception("A method to set the code element was not " + "found. You must either provide a non-null " + typeof(ICustomAttributeProvider).Name + " value to the " + "Resolve(" + typeof(IEventEnumerable).Name + ", object, " + typeof(ICustomAttributeProvider).Name + ") method or you must " + "set the InjectAttribute.SetValue property explicitly."); if (Type == null) throw new Exception("The type to inject was not found. found. " + "You must either provide a non-null " + typeof(ICustomAttributeProvider).Name + " value to the " + "Resolve(" + typeof(IEventEnumerable).Name + ", object, " + typeof(ICustomAttributeProvider).Name + ") method or you must " + "set the InjectAttribute.Type property explicitly."); _instance = instance; // We need to move Type nextServiceType = Type; if (Location.Length != 0) nextServiceType = Location[0]; services.OfType(nextServiceType, ServiceRetriever); } /// /// The method that sets the value on the property. By default, this is /// set to the /// method of the passed into /// /// as the . The first parameter /// is the instance on which the /// belongs. The second parameter is the service being set. The third /// parameter is the index values for index properties. /// public Action SetValue { get; set; } /// /// The method that is invoked when the service of the type specified by /// is resolved and can be used. /// /// The service that was requested. void ServiceRetriever(object service) { if (_currentLocationIndex == Location.Length) SetValue(_instance, service, null); else { IEventEnumerable services = (IEventEnumerable)service; // If we we've reached the last Type in the location path // then we are at the IEventEnumerable that contains the type that we // need. In that case, we'll just request the Type rather than // the next IEventEnumerable instance in the location path. ++_currentLocationIndex; Type nextServiceType = Type; if(_currentLocationIndex < Location.Length) nextServiceType = Location[_currentLocationIndex]; services.OfType(nextServiceType).First(ServiceRetriever); } } /// /// The index into the array /// that points to the we are currently processing /// in . /// int _currentLocationIndex; /// /// The property that was attributed with this attribute. /// PropertyInfo _property; /// /// The instance that whose type was attributed with this attribute. /// object _instance; } }