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.configuration_context_factory;
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 component using args.
52 
53 Constructs component 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 	Z.
67 **/
68 
69 auto construct(Z : InstanceFactoryAware!T, T, Args...)(Z 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 component of type X.
77 
78 Configures component's factory to call method of factoryMethod with args,
79 in order to create component 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 = component's factory that is configured to call factoryMethod methods to spawn component
88     factoryMethod = instance of factory method that will be used to instantiate component
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 component
92     W = either LocatorReference or T
93     X = the return type of T.method member
94 **/
95 Z factoryMethod(T, string method, Z : InstanceFactoryAware!X, X, W, Args...)(Z factory, auto ref W factoryMethod, auto ref Args args)
96     if (
97         isNonStaticMethodCompatible!(T, method, Args) &&
98         (is(W : T) || is(W : RuntimeReference))
99     ) {
100     factory.setInstanceFactory(factoryMethodBasedFactory!(T, method)(factory.locator, factoryMethod, args));
101     
102     return factory;
103 }
104 
105 /**
106 ditto
107 **/
108 Z factoryMethod(T, string method, Z : InstanceFactoryAware!X, X, Args...)(Z 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 component's method with supplied args.
119 
120 Configures component'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 	Z.
132 **/
133 auto set(string property, Z : PropertyConfigurersAware!T, T, Args...)(Z 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 component's public field to passed arg.
144 
145 Configures component'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 	Z.
156 **/
157 auto set(string property, Z : PropertyConfigurersAware!T, T, Arg)(Z 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 component using a delegate.
168 
169 Constructs component using a delegate, and a list of arguments passed to delegate.
170 
171 Params:
172 	factory = the factory which will use delegate to construct component.
173 	dg = the delegate that is responsible for creating component, given a list of arguments.
174 	args = the arguments that will be used by delegate to construct component.
175 	
176 Returns:
177 	Z.
178 **/
179 auto callback(Z : InstanceFactoryAware!T, T, Args...)(Z 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(Z : InstanceFactoryAware!T, T, Args...)(Z 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 component that is in configuration phase.
196 
197 Call dg on component 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 component using passed args.
202     args = a list of arguments passed to dg.
203     
204 Returns:
205     Z
206 **/
207 auto callback(Z : PropertyConfigurersAware!T, T, Args...)(Z 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(Z : PropertyConfigurersAware!T, T, Args...)(Z 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(Z : PropertyConfigurersAware!T, T, Args...)(Z 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(Z : PropertyConfigurersAware!T, T, Args...)(Z 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 component type
254     member = field or method of component T
255     factory = ConfigurationContextFactory where to inject the constructor or method configurer
256     
257 Returns:
258     Z
259 **/
260 auto autowire(Z : InstanceFactoryAware!T, T)(Z factory) 
261     if (getMembersWithProtection!(T, "__ctor", "public").length > 0) {
262     return factory.construct(staticMap!(toLref, Parameters!(getMembersWithProtection!(T, "__ctor", "public")[0])));
263 }
264 
265 /**
266 ditto
267 **/
268 auto autowire(string member, Z : PropertyConfigurersAware!T, T)(Z 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, Z : PropertyConfigurersAware!T, T)(Z factory) 
277     if (isField!(T, member)) {
278     return factory.set!(member)(lref!(typeof(getMember!(T, member))));
279 }
280 
281 /**
282 Instantiates a component using a value as basis.
283 
284 Instantiates a component using a value as basis.
285 As a consequence, any reference based type will 
286 point to same content when it is instantiated 
287 multiple times.
288 
289 Params:
290     T = the component type
291     factory = ConfigurationContextFactory where to inject the constructor or method configurer
292     value = default value used to instantiate component
293 **/
294 auto value(Z : InstanceFactoryAware!T, T)(Z factory, auto ref T value) {
295     return factory.setInstanceFactory(new ValueInstanceFactory!T(value));
296 }
297 
298 /**
299 Instantiates a component using as basis some third party factory.
300 
301 Params:
302     T = the type of component that is to be configured
303     factory = factory that uses the parent factory for component instantiation
304     delegated = the factory used by factory to instantiate an object.
305 **/
306 auto parent(Z : InstanceFactoryAware!T, T, X : Factory!W, W : T)(Z factory, X delegated) {
307     return factory.setInstanceFactory(new DelegatingInstanceFactory!(T, W)(delegated));
308 }
309 
310 /**
311 Instantiates a component using as basis some third party factory.
312 
313 Instantiates a component using as basis some third party factory.
314 When a parent factory from a source of factories is used,
315 type checking of component returned by parent factory
316 is done at runtime, and will throw an exception if type
317 of parent factory mismatches the type of component that
318 uses respective factory.
319 
320 Params:
321     T = the type of component that is to be configured
322     factory = factory that uses the parent factory for component instantiation
323     source = source of factories from which parent factory is extracted
324     identity = identity of factory used by factory to instantiate component
325 **/
326 auto parent(Z : InstanceFactoryAware!T, T, X : FactoryLocator!ObjectFactory)(Z factory, X source, string identity) {
327     return factory.setInstanceFactory(
328         new DelegatingInstanceFactory!(T, T)(
329             new ClassUnwrappingFactory!T(
330                 source.getFactory(identity)
331             )
332         )
333     );
334 }
335 
336 /**
337 Move constructed data from one container to another one.
338 
339 Move constructed data from one container to another one.
340 Description
341 
342 Params:
343 	factory = factory for constructed data
344 	storage = new location for factory
345 
346 Returns:
347 	factory
348 **/
349 auto container(Z : ConfigurationContextFactory!T, T)(Z factory, Storage!(ObjectFactory, string) storage) {
350     if (factory.storage !is null) {
351         factory.storage.remove(factory.identity);
352     }
353     
354     factory.storage = storage;
355     factory.storage.set(factory.wrapper, factory.identity);
356     
357     return factory;
358 }
359 
360 /**
361 ditto
362 **/
363 auto container(Z : ConfigurationContextFactory!T, T)(Z factory, string storageId) {
364     import std.algorithm;
365     
366     auto storage = factory.locator.locate!(Storage!(ObjectFactory, string))(storageId);
367     
368     factory.storageIdentity = storageId;
369     return factory.container(storage);
370 }
371 
372 /**
373 Tag constructed data with some information.
374 
375 Tag constructed data with some information.
376 Description
377 
378 Params:
379 	factory = factory for constructed data
380 	tag = tag with which to tag factory.
381 
382 Returns:
383 	factory
384 **/
385 auto tag(W : ConfigurationContextFactory!T, T, Z)(W factory, auto ref Z tag) {
386     
387     auto taggable = findDecorator!(Taggable!Z, ObjectFactoryDecorator)(factory.wrapper);
388     
389     if (taggable is null) {
390         auto taggableDecorator = new TaggableFactoryDecorator!(Object, Z);
391         taggableDecorator.decorated = factory.wrapper;
392         factory.wrapper = taggableDecorator;
393         
394         taggable = taggableDecorator;
395         factory.storage.set(factory.wrapper, factory.identity);
396     }
397     
398     taggable.tag(tag);
399     
400     return factory;
401 }
402 
403 /**
404 Mark constructed object to be provided through a proxy instead of directly doing so.
405 Object will be proxied only in case when the storage where it is stored support 
406 storing of proxy object factories.
407 
408 Params:
409 	factory = factory for constructed object
410 
411 Returns:
412 	factory
413 **/
414 auto proxy(Z : ConfigurationContextFactory!T, T)(Z factory) {
415     import aermicioi.aedi.factory.proxy_factory;
416     import aermicioi.aedi.container.proxy_container;
417     
418     auto proxyAware = cast(ProxyContainer) factory.storage;
419     if (proxyAware !is null) {
420         proxyAware.set(
421             new ProxyObjectWrappingFactory!T(
422                 new ProxyFactory!T(factory.identity, proxyAware.decorated)
423             ),
424             factory.identity,
425         );
426     }
427     
428     return factory;
429 }