1 /**
2 This module provides fluent api based configuration of components with custom 
3 configuration errors.
4 
5 License:
6 	Boost Software License - Version 1.0 - August 17th, 2003
7     
8     Permission is hereby granted, free of charge, to any person or organization
9     obtaining a copy of the software and accompanying documentation covered by
10     this license (the "Software") to use, reproduce, display, distribute,
11     execute, and transmit the Software, and to prepare derivative works of the
12     Software, and to permit third-parties to whom the Software is furnished to
13     do so, all subject to the following:
14     
15     The copyright notices in the Software and this entire statement, including
16     the above license grant, this restriction and the following disclaimer,
17     must be included in all copies of the Software, in whole or in part, and
18     all derivative works of the Software, unless such copies or derivative
19     works are solely in the form of machine-executable object code generated by
20     a source language processor.
21     
22     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28     DEALINGS IN THE SOFTWARE.
29 
30 Authors:
31 	Alexandru Ermicioi
32 **/
33 module aermicioi.aedi.configurer.register.factory_configurer;
34 
35 public import aermicioi.aedi.factory.reference : lref, anonymous;
36 
37 import aermicioi.aedi.configurer.register.generic_factory_metadata_decorator;
38 import aermicioi.aedi.storage.storage;
39 import aermicioi.aedi.storage.locator;
40 import aermicioi.aedi.factory;
41 import aermicioi.aedi.storage.decorator;
42 import aermicioi.aedi.container.proxy_container;
43 import aermicioi.aedi.container.container;
44 import aermicioi.util.traits;
45 import aermicioi.aedi.exception;
46 
47 import std.traits;
48 import std.meta;
49 
50 /**
51 Construct aggregate using args.
52 
53 Constructs aggregate using args, that are passed to function.
54 The function will attempt to find at least one construct that 
55 can accept passed argument list. If it fails, compiler will
56 produce error, with respective problems.
57 The argument list can contain beside simple values, references
58 to other data in locator. Arguments that are references to other data
59 won't be type checked.
60 
61 Params:
62 	factory = the factory which will call constructor with passed arguments.
63 	args = a list of arguments that will be passed to constructor.
64 	
65 Returns:
66 	MetadataDecoratedGenericFactory!T.
67 **/
68 
69 auto construct(T, Args...)(MetadataDecoratedGenericFactory!T factory, auto ref Args args) {
70     factory.setInstanceFactory(constructorBasedFactory!T(factory.locator, args));
71     
72     return factory;
73 }
74 
75 /**
76 Invoke T's method to create aggregate of type X.
77 
78 Configures aggregate's factory to call method of factoryMethod with args,
79 in order to create aggregate of type X.
80 In case when method is not a static member, the function requires to
81 pass a instance of factoryMethod or a reference to it.
82 The algorithm will check for args compatiblity with parameters of 
83 factory method. No type check is done for arguments that are references
84 at compile time.
85 
86 Params:
87     factory = aggregate's factory that is configured to call factoryMethod methods to spawn aggregate
88     factoryMethod = instance of factory method that will be used to instantiate aggregate
89     args = a list of arguments passed to factory method
90     T = type of factoryMethod
91     method = the method that is called from T to instantiate aggregate
92     W = either LocatorReference or T
93     X = the return type of T.method member
94 **/
95 MetadataDecoratedGenericFactory!(X) factoryMethod(T, string method, X, W, Args...)(MetadataDecoratedGenericFactory!(X) factory, auto ref W factoryMethod, auto ref Args args)
96     if (
97         isNonStaticMethodCompatible!(T, method, Args) &&
98         (is(W : T) || is(W : LocatorReference))
99     ) {
100     factory.setInstanceFactory(factoryMethodBasedFactory!(T, method)(factory.locator, factoryMethod, args));
101     
102     return factory;
103 }
104 
105 /**
106 ditto
107 **/
108 MetadataDecoratedGenericFactory!(X) factoryMethod(T, string method, X, Args...)(MetadataDecoratedGenericFactory!(X) factory, auto ref Args args)
109     if (
110         isStaticMethodCompatible!(T, method, Args)
111     ) {
112 
113     factory.setInstanceFactory(factoryMethodBasedFactory!(T, method)(factory.locator, args));    
114     return factory;
115 }
116 
117 /**
118 Invoke aggregate's method with supplied args.
119 
120 Configures aggregate's factory to call specified method with passed args.
121 The function will check if the arguments passed to it are compatible with at 
122 least one method from possible overload set.
123 The args list can contain references to other objects in locator as well, though
124 no type compatibility checks will be performed at compile time.
125 
126 Params:
127 	factory = the factory which will be configured to invoke method.
128 	args = the arguments that will be used to invoke method on the new object.
129 	
130 Returns:
131 	MetadataDecoratedGenericFactory!T.
132 **/
133 auto set(string property, T, Args...)(MetadataDecoratedGenericFactory!T factory, auto ref Args args) 
134     if (!isField!(T, property)) {
135     mixin assertObjectMethodCompatible!(T, property, Args);
136     
137     factory.addPropertyConfigurer(methodConfigurer!(property, T)(factory.locator, args));
138     
139     return factory;
140 }
141 
142 /**
143 Set aggregate's public field to passed arg.
144 
145 Configures aggregate's factory to set specified field to passed arg.
146 The function will check if passed argument is type compatible with specified field.
147 The argument can be a reference as well. In case of argument being reference to another data
148 in container, no type compatiblity checking will be done.
149 
150 Params
151     factory = the factory which will be configured to set property.
152 	arg = the value of property to be set, or reference to data in container.
153 
154 Returns:
155 	MetadataDecoratedGenericFactory!T.
156 **/
157 auto set(string property, T, Arg)(MetadataDecoratedGenericFactory!T factory, auto ref Arg arg)
158     if (isField!(T, property)) {
159     mixin assertFieldCompatible!(T, property, Arg);
160     
161     factory.addPropertyConfigurer(fieldConfigurer!(property, T)(factory.locator, arg));
162     
163     return factory;
164 }
165     
166 /**
167 Construct aggregate using a delegate.
168 
169 Constructs aggregate using a delegate, and a list of arguments passed to delegate.
170 
171 Params:
172 	factory = the factory which will use delegate to construct aggregate.
173 	dg = the delegate that is responsible for creating aggregate, given a list of arguments.
174 	args = the arguments that will be used by delegate to construct aggregate.
175 	
176 Returns:
177 	MetadataDecoratedGenericFactory!T.
178 **/
179 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, T delegate(Locator!(), Args) dg, auto ref Args args) {
180     factory.setInstanceFactory(callbackFactory!T(factory.locator, dg, args));
181 
182     return factory;
183 }
184     
185 /**
186 ditto
187 **/
188 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, T function(Locator!(), Args) dg, auto ref Args args) {
189     factory.setInstanceFactory(callbackFactory!T(factory.locator, dg, args));
190     
191     return factory;
192 }
193 
194 /**
195 Call dg on an aggregate that is in configuration phase.
196 
197 Call dg on aggregate to perform some modifications, using args as input.
198 
199 Params:
200     factory = factory which will call dg with args.
201     dg = delegate that will perform some modifications on aggregate using passed args.
202     args = a list of arguments passed to dg.
203     
204 Returns:
205     MetadataDecoratedGenericFactory!T
206 **/
207 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void delegate(Locator!(), T, Args) dg, auto ref Args args) {
208     factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args));
209     
210     return factory;
211 }
212     
213 /**
214 ditto
215 **/
216 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void function(Locator!(), T, Args) dg, auto ref Args args) {
217     factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args));
218     
219     return factory;
220 }
221 
222 /**
223 ditto
224 **/
225 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void delegate(Locator!(), ref T, Args) dg, auto ref Args args) {
226     factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args));
227     
228     return factory;
229 }
230     
231 /**
232 ditto
233 **/
234 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void function(Locator!(), ref T, Args) dg, auto ref Args args) {
235     factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args));
236     
237     return factory;
238 }
239 
240 /**
241 Autowire a constructor, field or a method.
242 
243 Autowire a constructor, field or a method.
244 A constructor is autowired only when no member is passed as argument.
245 When a member is passed as argument, it will be called with
246 a list of references (where args are identified by their type FQN) in
247 case when member is a function, or it will set the member to the 
248 value that is located in container by it's type FQN.
249 Note: In case of constructors as well as methods that are overloaded,
250 the first constructor or method from overload set is selected to be autowired.
251 
252 Params:
253     T = the aggregate type
254     member = field or method of aggregate T
255     factory = MetadataDecoratedGenericFactory where to inject the constructor or method configurer
256     
257 Returns:
258     MetadataDecoratedGenericFactory!T
259 **/
260 auto autowire(T)(MetadataDecoratedGenericFactory!T factory) 
261     if (getMembersWithProtection!(T, "__ctor", "public").length > 0) {
262     return factory.construct!(T)(staticMap!(toLref, Parameters!(getMembersWithProtection!(T, "__ctor", "public")[0])));
263 }
264 
265 /**
266 ditto
267 **/
268 auto autowire(string member, T)(MetadataDecoratedGenericFactory!T factory) 
269     if (getMembersWithProtection!(T, member, "public").length > 0) {
270     return factory.set!(member)(staticMap!(toLref, Parameters!(getMembersWithProtection!(T, member, "public")[0])));
271 }
272     
273 /**
274 ditto
275 **/
276 auto autowire(string member, T)(MetadataDecoratedGenericFactory!T factory) 
277     if (isField!(T, member)) {
278     return factory.set!(member)(lref!(typeof(getMember!(T, member))));
279 }
280 
281 /**
282 Move constructed data from one container to another one.
283 
284 Move constructed data from one container to another one.
285 Description
286 
287 Params:
288 	factory = factory for constructed data
289 	storage = new location for factory
290 
291 Returns:
292 	factory
293 **/
294 auto container(T)(MetadataDecoratedGenericFactory!T factory, Storage!(ObjectFactory, string) storage) {
295     if (factory.storage !is null) {
296         factory.storage.remove(factory.identity);
297     }
298     
299     factory.storage = storage;
300     factory.storage.set(factory.wrapper, factory.identity);
301     
302     return factory;
303 }
304 
305 /**
306 ditto
307 **/
308 auto container(T)(MetadataDecoratedGenericFactory!T factory, string storageId) {
309     import std.algorithm;
310     
311     auto storage = factory.locator.locate!(Storage!(ObjectFactory, string))(storageId);
312     
313     factory.storageIdentity = storageId;
314     return factory.container(storage);
315 }
316 
317 /**
318 Tag constructed data with some information.
319 
320 Tag constructed data with some information.
321 Description
322 
323 Params:
324 	factory = factory for constructed data
325 	tag = tag with which to tag factory.
326 
327 Returns:
328 	factory
329 **/
330 auto tag(T, Z)(MetadataDecoratedGenericFactory!T factory, auto ref Z tag) {
331     
332     auto taggable = findDecorator!(Taggable!Z, ObjectFactoryDecorator)(factory.wrapper);
333     
334     if (taggable is null) {
335         auto taggableDecorator = new TaggableFactoryDecorator!(Object, Z);
336         taggableDecorator.decorated = factory.wrapper;
337         factory.wrapper = taggableDecorator;
338         
339         taggable = taggableDecorator;
340         factory.storage.set(factory.wrapper, factory.identity);
341     }
342     
343     taggable.tag(tag);
344     
345     return factory;
346 }
347 
348 //Due to BUG 17177, minimal usage is not possible.
349 //Mark constructed object to be provided through a proxy instead of directly doing so.
350 //
351 //Mark constructed object to be provided through a proxy instead of directly doing so.
352 //Object will be proxied only in case when the storage where it is stored support 
353 //storing of proxy object factories.
354 //
355 //Params:
356 //	factory = factory for constructed object
357 //
358 //Returns:
359 //	factory
360 //
361 //auto proxy(T)(MetadataDecoratedGenericFactory!T factory) {
362 //    import aermicioi.aedi.factory.proxy_factory;
363 //    import aermicioi.aedi.container.proxy_container;
364 //    
365 //    auto proxyAware = cast(ProxyContainer) factory.storage;
366 //    if (proxyAware !is null) {
367 //        proxyAware.set(
368 //            new ProxyObjectWrappingFactory!T(
369 //                new ProxyFactory!T(factory.identity, proxyAware.decorated)
370 //            ),
371 //            factory.identity,
372 //        );
373 //    }
374 //    
375 //    return factory;
376 //}
377 
378 /**
379 Find a decorator in decorator chain that implements Needle type.
380 
381 Find a decorator in decorator chain that implements Needle type.
382 
383 Params:
384 	Needle = the type searched decorator should implement
385 	Haystack = type of the chain of decorators through which to traverse
386 	decorated = top of decorator chain.
387 
388 Returns:
389 	Decorator or null if not found.
390 **/
391 Needle findDecorator(Needle, Haystack : Decorator!Z, Z, T)(T decorated) {
392     
393     Haystack decorator = cast(Haystack) decorated;
394     Needle needle = cast(Needle) decorated;
395     
396     while ((needle is null) && (decorator !is null)) {
397         decorator = cast(Haystack) decorator.decorated;
398         needle = cast(Needle) decorator;
399     }
400     
401     return needle;
402 }