PageRenderTime 10ms CodeModel.GetById 1ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 1ms

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

http://github.com/philiplaureano/LinFu
C# | 216 lines | 126 code | 29 blank | 61 comment | 15 complexity | 8aaeb8cb8e343a9705c55b09b96120d0 MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.IO;
  4using System.Linq;
  5using LinFu.IoC.Configuration.Injectors;
  6using LinFu.IoC.Factories;
  7using LinFu.IoC.Interfaces;
  8
  9namespace LinFu.IoC.Configuration
 10{
 11    /// <summary>
 12    ///     A class that injects custom <see cref="IFactory" /> and <see cref="IFactory{T}" />
 13    ///     instances into an <see cref="IServiceContainer" /> instance.
 14    /// </summary>
 15    public class FactoryAttributeLoader : ITypeLoader
 16    {
 17        /// <summary>
 18        ///     Loads an <see cref="IFactory" /> and <see cref="IFactory{T}" /> instance
 19        ///     into a <see cref="IServiceContainer" /> instance using the given
 20        ///     <paramref name="sourceType" />.
 21        /// </summary>
 22        /// <param name="sourceType">The input type from which one or more factories will be created.</param>
 23        /// <returns>A set of <see cref="Action{T}" /> instances. This cannot be null.</returns>
 24        public IEnumerable<Action<IServiceContainer>> Load(Type sourceType)
 25        {
 26            if (sourceType == null)
 27                throw new ArgumentNullException("sourceType");
 28
 29            // Extract the factory attributes from the current type
 30            var attributes = sourceType.GetCustomAttributes(typeof(FactoryAttribute), false);
 31            var attributeList = attributes.Cast<FactoryAttribute>()
 32                .Where(f => f != null).ToList();
 33
 34
 35            // The target type must have at least one
 36            // factory attribute
 37            if (attributeList.Count == 0)
 38                return new Action<IServiceContainer>[0];
 39
 40
 41            // Make sure the factory is created only once            
 42            Func<IFactoryRequest, object> getFactoryInstance = request =>
 43            {
 44                var container = request.Container;
 45                return container.AutoCreateInternal(sourceType);
 46            };
 47
 48
 49            return GetResults(sourceType, attributeList, getFactoryInstance);
 50        }
 51
 52        /// <summary>
 53        ///     Determines whether or not the current <paramref name="sourceType" />
 54        ///     can be loaded.
 55        /// </summary>
 56        /// <param name="sourceType">The source type currently being loaded.</param>
 57        /// <returns>Returns <c>true</c> if the type is a class type; otherwise, it returns <c>false</c>.</returns>
 58        public bool CanLoad(Type sourceType)
 59        {
 60            try
 61            {
 62                return sourceType.IsClass;
 63            }
 64            catch (TypeInitializationException)
 65            {
 66                // Ignore the error
 67                return false;
 68            }
 69            catch (FileNotFoundException)
 70            {
 71                // Ignore the error
 72                return false;
 73            }
 74        }
 75
 76
 77        /// <summary>
 78        ///     Instantiates the <see cref="IFactory" /> instances associated with the <paramref name="sourceType" /> and
 79        ///     adds those factories to the target container upon initialization.
 80        /// </summary>
 81        /// <param name="sourceType">The <see cref="System.Type" /> currently being inspected.</param>
 82        /// <param name="attributeList">
 83        ///     The list of <see cref="FactoryAttribute" /> instances currently declared on on the source
 84        ///     type.
 85        /// </param>
 86        /// <param name="getFactoryInstance">The functor that will be responsible for generating the factory instance.</param>
 87        /// <returns>A list of actions that will add the factories to the target container.</returns>
 88        private static IEnumerable<Action<IServiceContainer>> GetResults(Type sourceType,
 89            IEnumerable<FactoryAttribute> attributeList,
 90            Func<IFactoryRequest, object>
 91                getFactoryInstance)
 92        {
 93            var results = new List<Action<IServiceContainer>>();
 94            // The factory instance must implement either
 95            // IFactory or IFactory<T>
 96            var factoryInterfaces = from t in sourceType.GetInterfaces()
 97                where t.IsGenericType &&
 98                      t.GetGenericTypeDefinition() == typeof(IFactory<>)
 99                select t;
100
101            if (!typeof(IFactory).IsAssignableFrom(sourceType) && factoryInterfaces.Count() == 0)
102            {
103                var message =
104                    string.Format(
105                        "The factory type '{0}' must implement either the IFactory interface or the IFactory<T> interface.",
106                        sourceType.AssemblyQualifiedName);
107                throw new ArgumentException(message, "sourceType");
108            }
109
110            var implementedInterfaces = new HashSet<Type>(factoryInterfaces);
111            Func<Type, Func<IFactoryRequest, object>, IFactory> createFactory =
112                (currentServiceType, getFactory) =>
113                {
114                    // Determine if the factory implements
115                    // the generic IFactory<T> instance
116                    // and use that instance if possible
117
118                    Func<IFactoryRequest, IFactory> getStronglyTypedFactory =
119                        request =>
120                        {
121                            var result = getFactory(request);
122                            // If the object is IFactory then we can just return it.
123                            if (result is IFactory)
124                                return (IFactory) result;
125
126                            // Check to see if the object is IFactory<T>, if so we need to adapt it to 
127                            // IFactory.
128                            var genericType = typeof(IFactory<>).MakeGenericType(currentServiceType);
129                            if (!genericType.IsInstanceOfType(result)) return null;
130
131                            // Adapt IFactory<T> to IFactory.
132                            var adapterType = typeof(FactoryAdapter<>).MakeGenericType(currentServiceType);
133                            var adapter = (IFactory) Activator.CreateInstance(adapterType, result);
134                            return adapter;
135                        };
136
137                    return GetFactory(currentServiceType, getStronglyTypedFactory, implementedInterfaces);
138                };
139
140            // Build the list of services that this factory can implement
141            var servicesToImplement = from f in attributeList
142                let serviceName = f.ServiceName
143                let serviceType = f.ServiceType
144                let argumentTypes = f.ArgumentTypes ?? new Type[0]
145                let factory = createFactory(serviceType, getFactoryInstance)
146                where factory != null
147                select new
148                {
149                    ServiceName = serviceName,
150                    ServiceType = serviceType,
151                    ArgumentTypes = argumentTypes,
152                    FactoryInstance = factory
153                };
154
155
156            foreach (var currentService in servicesToImplement)
157            {
158                var serviceName = currentService.ServiceName;
159                var serviceType = currentService.ServiceType;
160                var argumentTypes = currentService.ArgumentTypes;
161                var factory = currentService.FactoryInstance;
162
163                // HACK: Unnamed custom factories should be able to
164                // intercept every request for the given service type
165                if (serviceName == null)
166                {
167                    var injector = new CustomFactoryInjector(serviceType, factory);
168                    results.Add(container => container.PreProcessors.Add(injector));
169                }
170
171                // Add each service to the container on initialization
172                results.Add(container => container.AddFactory(serviceName, serviceType, argumentTypes, factory));
173            }
174
175            return results;
176        }
177
178        /// <summary>
179        ///     Instantiates the given factory using the <paramref name="getStronglyTypedFactory">factory functor.</paramref>
180        /// </summary>
181        /// <param name="currentServiceType">The service type that will be created by the factory.</param>
182        /// <param name="getStronglyTypedFactory">The functor that will be responsible for creating the factory itself.</param>
183        /// <param name="implementedInterfaces">
184        ///     The list of <see cref="IFactory{T}" /> interfaces that are implemented by the
185        ///     source type.
186        /// </param>
187        /// <returns>A valid factory instance.</returns>
188        private static IFactory GetFactory(Type currentServiceType,
189            Func<IFactoryRequest, IFactory> getStronglyTypedFactory,
190            ICollection<Type> implementedInterfaces)
191        {
192            var genericType = typeof(IFactory<>).MakeGenericType(currentServiceType);
193
194            // Lazy-instantiate the factories so that they can be injected by the container
195            IFactory lazyFactory = new LazyFactory(getStronglyTypedFactory);
196
197            IFactory result;
198            if (implementedInterfaces.Contains(genericType))
199            {
200                // Convert the IFactory<T> instance down to an IFactory
201                // instance so that it can be used by the target container 
202                var lazyFactoryType = typeof(LazyFactory<>).MakeGenericType(currentServiceType);
203                var adapterType = typeof(FactoryAdapter<>).MakeGenericType(currentServiceType);
204
205                lazyFactory = (IFactory) Activator.CreateInstance(lazyFactoryType, getStronglyTypedFactory);
206                result = (IFactory) Activator.CreateInstance(adapterType, lazyFactory);
207
208                return result;
209            }
210
211            // Otherwise, use the untyped IFactory instance instead
212            result = lazyFactory;
213            return result;
214        }
215    }
216}