PageRenderTime 104ms CodeModel.GetById 60ms app.highlight 6ms RepoModel.GetById 34ms app.codeStats 0ms

/src/LinFu.IoC/Configuration/Loaders/ImplementsAttributeLoader.cs

http://github.com/philiplaureano/LinFu
C# | 140 lines | 86 code | 14 blank | 40 comment | 9 complexity | 6a362929dae60586bebe7961312588e4 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.IO;
  4using System.Linq;
  5using System.Reflection;
  6using LinFu.IoC.Configuration.Interfaces;
  7using LinFu.IoC.Factories;
  8using LinFu.IoC.Interfaces;
  9
 10namespace LinFu.IoC.Configuration.Loaders
 11{
 12    /// <summary>
 13    ///     A loader class that scans a type for <see cref="ImplementsAttribute" />
 14    ///     attribute declarations and creates a factory for each corresponding
 15    ///     attribute instance.
 16    /// </summary>
 17    /// <seealso cref="IFactory" />
 18    public class ImplementsAttributeLoader : ITypeLoader
 19    {
 20        /// <summary>
 21        ///     Converts a given <see cref="System.Type" /> into
 22        ///     a set of <see cref="Action{IServiceContainer}" /> instances so that
 23        ///     the <see cref="IContainer" /> instance can be loaded
 24        ///     with the given factories.
 25        /// </summary>
 26        /// <param name="sourceType">The input type from which one or more factories will be created.</param>
 27        /// <returns>A set of <see cref="Action{IServiceContainer}" /> instances. This cannot be null.</returns>
 28        public IEnumerable<Action<IServiceContainer>> Load(Type sourceType)
 29        {
 30            // Extract the Implements attribute from the source type
 31            ICustomAttributeProvider provider = sourceType;
 32            var attributes = provider.GetCustomAttributes(typeof(ImplementsAttribute), false);
 33            var attributeList = attributes.Cast<ImplementsAttribute>().ToList();
 34
 35            var results = new List<Action<IServiceContainer>>();
 36            IFactory singletonFactory = null;
 37            foreach (var attribute in attributeList)
 38            {
 39                var serviceName = attribute.ServiceName;
 40                var serviceType = attribute.ServiceType;
 41                var lifeCycle = attribute.LifecycleType;
 42
 43                var currentFactory = CreateFactory(serviceType, sourceType, lifeCycle);
 44                if (currentFactory == null)
 45                    continue;
 46
 47                // If this type is implemented as a factory singleton,
 48                // it only needs to be implemented once
 49                if (lifeCycle == LifecycleType.Singleton)
 50                {
 51                    if (singletonFactory == null)
 52                        singletonFactory = currentFactory;
 53                    else
 54                        currentFactory = singletonFactory;
 55                }
 56
 57                results.Add(container =>
 58                    container.AddFactory(serviceName, serviceType, currentFactory));
 59            }
 60
 61            return results;
 62        }
 63
 64        /// <summary>
 65        ///     Determines whether or not the current <paramref name="sourceType" />
 66        ///     can be loaded.
 67        /// </summary>
 68        /// <param name="sourceType">The source type currently being loaded.</param>
 69        /// <returns>Returns <c>true</c> if the type is a class type; otherwise, it returns <c>false</c>.</returns>
 70        public bool CanLoad(Type sourceType)
 71        {
 72            try
 73            {
 74                return sourceType.IsClass;
 75            }
 76            catch (TypeInitializationException)
 77            {
 78                // Ignore the error
 79                return false;
 80            }
 81            catch (FileNotFoundException)
 82            {
 83                // Ignore the error
 84                return false;
 85            }
 86        }
 87
 88
 89        /// <summary>
 90        ///     Creates a factory instance that can create instaces of the given
 91        ///     <paramref name="serviceType" />  using the <paramref name="implementingType" />
 92        ///     as the implementation.
 93        /// </summary>
 94        /// <param name="serviceType">The service being implemented.</param>
 95        /// <param name="implementingType">The actual type that will implement the service.</param>
 96        /// <param name="lifecycle">The <see cref="LifecycleType" /> that determines the lifetime of each instance being created.</param>
 97        /// <returns>A valid <see cref="IFactory" /> instance.</returns>
 98        private static IFactory CreateFactory(Type serviceType, Type implementingType, LifecycleType lifecycle)
 99        {
100            // HACK: Use a lazy factory since the actualy IFactoryBuilder instance won't
101            // be available until runtime
102            Func<IFactoryRequest, object> factoryMethod =
103                request =>
104                {
105                    var currentContainer = request.Container;
106                    var arguments = request.Arguments;
107                    var builder = currentContainer.GetService<IFactoryBuilder>();
108
109                    // HACK: If the service type is a type definition and
110                    // the implementing type is a type definition,
111                    // assume that the service type has the same number of
112                    // generic arguments as the implementing type
113                    var actualServiceType = serviceType;
114                    var actualImplementingType = implementingType;
115                    if (serviceType.IsGenericTypeDefinition && implementingType.IsGenericTypeDefinition &&
116                        serviceType.GetGenericArguments().Count() == implementingType.GetGenericArguments().Count())
117                    {
118                        var typeArguments = request.ServiceType.GetGenericArguments();
119                        actualServiceType = serviceType.MakeGenericType(typeArguments);
120                        actualImplementingType = implementingType.MakeGenericType(typeArguments);
121                    }
122
123                    var actualFactory = builder.CreateFactory(actualServiceType, actualImplementingType,
124                        lifecycle);
125
126                    var factoryRequest = new FactoryRequest
127                    {
128                        ServiceType = serviceType,
129                        ServiceName = request.ServiceName,
130                        Arguments = arguments,
131                        Container = currentContainer
132                    };
133
134                    return actualFactory.CreateInstance(factoryRequest);
135                };
136
137            return new FunctorFactory(factoryMethod);
138        }
139    }
140}