1 /**
2 This module implements annotation based configuration of containers.
3 
4 License:
5 	Boost Software License - Version 1.0 - August 17th, 2003
6     
7     Permission is hereby granted, free of charge, to any person or organization
8     obtaining a copy of the software and accompanying documentation covered by
9     this license (the "Software") to use, reproduce, display, distribute,
10     execute, and transmit the Software, and to prepare derivative works of the
11     Software, and to permit third-parties to whom the Software is furnished to
12     do so, all subject to the following:
13     
14     The copyright notices in the Software and this entire statement, including
15     the above license grant, this restriction and the following disclaimer,
16     must be included in all copies of the Software, in whole or in part, and
17     all derivative works of the Software, unless such copies or derivative
18     works are solely in the form of machine-executable object code generated by
19     a source language processor.
20     
21     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27     DEALINGS IN THE SOFTWARE.
28 
29 Authors:
30 	Alexandru Ermicioi
31 **/
32 module aermicioi.aedi.configurer.annotation.annotation;
33 
34 public import aermicioi.aedi.factory.reference : lref, anonymous;
35 
36 import aermicioi.aedi.storage.locator;
37 import aermicioi.aedi.storage.storage;
38 import aermicioi.aedi.storage.wrapper;
39 import aermicioi.aedi.container.container;
40 import aermicioi.aedi.factory.factory;
41 import aermicioi.aedi.factory.reference;
42 import aermicioi.aedi.factory.generic_factory;
43 import aermicioi.aedi.factory.proxy_factory;
44 import aermicioi.aedi.exception;
45 import aermicioi.util.traits;
46 
47 import std.traits;
48 import std.meta;
49 import std.typecons;
50 import std.conv : to;
51 import std.algorithm;
52 
53 /**
54 Annotation used to denote an aggregate that should be stored into an container.
55 **/
56 struct ComponentAnnotation {
57     
58     /**
59     Constructs a factory for aggregate of type T
60     
61     Params:
62     	T = the aggregate type
63     	locator = locator used to extract needed dependencies for T
64     	
65     Returns:
66     	GenericFactory!T for objects
67     	GenericFactory!(Wrapper!T) for structs
68     **/
69     GenericFactory!T factory(T)(Locator!() locator) {
70         return new GenericFactoryImpl!(T)(locator);
71     }
72 }
73 
74 /**
75 ditto
76 **/
77 alias component = ComponentAnnotation;
78 
79 /**
80 Annotation used to mark a constructor to be used for aggregate instantiation.
81 
82 Params:
83     Args = tuple of argument types for arguments to be passed into a constructor.
84 **/
85 struct ConstructorAnnotation(Args...) {
86     Tuple!Args args;
87     
88     /**
89     Constructor accepting a list of arguments, that will be passed to constructor.
90     
91     Params:
92     	args = arguments passed to aggregate's constructor
93     **/
94     this(Args args) {
95         this.args = args;
96     }
97     
98     /**
99     Constructs a constructor based factory for aggregate of type T
100     
101     Params:
102     	T = the aggregate type
103     	locator = locator used to extract needed dependencies for T
104     	
105     Returns:
106     	InstanceFactory!T for objects
107     	InstanceFactory!(Wrapper!T) for structs
108     **/
109     InstanceFactory!T factoryContainer(T, string property)(Locator!() locator) {
110         auto constructor = new ConstructorBasedFactory!(T, Args)(args.expand);
111         constructor.locator = locator;
112         
113         return constructor;
114     }
115 }
116 
117 /**
118 ditto
119 **/
120 auto constructor(Args...)(Args args) {
121     return ConstructorAnnotation!Args(args);
122 }
123 
124 /**
125 Annotation used to mark a member to be called or set (in case of fields), with args passed to setter.
126 
127 Note: if an overloaded method is annotated with Setter, the method from overload set that matches argument list in Setter annotation 
128 will be called.
129 
130 Params:
131     Args = the argument types of arguments passed to method
132 **/
133 struct SetterAnnotation(Args...) {
134     Tuple!Args args;
135     
136     /**
137     Constructor accepting a list of arguments, that will be passed to method, or set to a field.
138     
139     Params:
140     	args = arguments passed to aggregate's constructor
141     **/
142     this(Args args) {
143         this.args = args;
144     }
145     
146     /**
147     Constructs a configurer that will call or set a member for aggregate of type T.
148     
149     Constructs a configurer that will call or set a member for aggregate of type T.
150     In case when member is a method, it will be called with passed arguments.
151     If method is an overload set, the method that matches argument list will be called.
152     In case when member is a field, it will be set to first argument from Args list.
153     
154     Params:
155     	T = the aggregate type
156     	method = the member which setter will call or set.
157     	locator = locator used to extract needed dependencies for T
158     	
159     Returns:
160     	PropertyConfigurer!T for objects
161     	PropertyConfigurer!(Wrapper!T) for structs
162     **/
163     PropertyConfigurer!T factoryConfigurer(T, string method)(Locator!() locator)
164         if (
165             !isField!(T, method)
166         ) {
167         mixin assertObjectMethodCompatible!(T, method, Args);
168         
169         auto method = new MethodConfigurer!(T, method, Args)(args.expand);
170         method.locator = locator;
171         
172         return method;
173     }
174         
175     /**
176     ditto
177     **/
178     PropertyConfigurer!T factoryConfigurer(T, string method)(Locator!() locator)
179         if (
180             isField!(T, method) &&
181             (Args.length == 1)
182         ) {
183         mixin assertFieldCompatible!(T, method, Args);
184         
185         auto method = new FieldConfigurer!(T, method, Args[0])(args[0]);
186         method.locator = locator;
187         
188         return method;
189     }
190 }
191 
192 /**
193 ditto
194 **/
195 auto setter(Args...)(Args args) {
196     return SetterAnnotation!Args(args);
197 }
198 
199 /**
200 Annotation that specifies a delegate to be used to instantiate aggregate.
201 
202 Params:
203 	Z = the type of aggregate that will be returned by the delegate
204 	Args = type tuple of args that can be passed to delegate.
205 **/
206 struct CallbackFactoryAnnotation(Z, Dg, Args...)
207     if ((is(Dg == Z delegate (Locator!(), Args)) || is(Dg == Z function (Locator!(), Args)))) {
208     Tuple!Args args;
209     Dg dg;
210     
211     /**
212     Constructor accepting a factory delegate, and it's arguments.
213     
214     Params:
215     	dg = delegate that will factory an aggregate
216     	args = list of arguments passed to delegate.
217     **/
218     this(Dg dg, ref Args args) {
219         this.dg = dg;
220         this.args = tuple(args);
221     }
222     
223     /**
224     Constructs a factory that uses delegate to instantiate an aggregate of type T.
225     
226     Params:
227     	T = the aggregate type
228     	locator = locator used to extract needed dependencies for T, it is also passed to delegate as first argument.
229     	
230     Returns:
231     	InstanceFactory!T for objects
232     	InstanceFactory!(Wrapper!T) for structs
233     **/
234     InstanceFactory!T factoryContainer(T, string p = "")(Locator!() locator)
235         if (is(Z : T)) {
236         auto callback = new CallbackFactory!(T, Dg, Args)(dg, args.expand);
237         callback.locator = locator;
238         
239         return callback;
240     }
241 }
242 
243 /**
244 ditto
245 **/
246 auto fact(T, Args...)(T delegate(Locator!(), Args) dg, Args args) {
247     return CallbackFactoryAnnotation!(T, T delegate(Locator!(), Args), Args)(dg, args);
248 }
249 
250 /**
251 ditto
252 **/
253 auto fact(T, Args...)(T function(Locator!(), Args) dg, Args args) {
254     return CallbackFactoryAnnotation!(T, T function(Locator!(), Args), Args)(dg, args);
255 }
256 
257 /**
258 Annotation that specifies a delegate to be used to configure aggregate somehow.
259 
260 Params:
261 	Z = the type of aggregate that will be returned by the delegate
262 	Args = type tuple of args that can be passed to delegate.
263 **/
264 struct CallbackConfigurerAnnotation(Z, Dg, Args...) 
265     if (
266         is(Dg == void delegate (Locator!(), Z, Args)) || 
267         is(Dg == void function (Locator!(), Z, Args)) ||
268         is(Dg == void delegate (Locator!(), ref Z, Args)) || 
269         is(Dg == void function (Locator!(), ref Z, Args))
270     ){
271     Tuple!Args args;
272     Dg dg;
273     
274     /**
275     Constructor accepting a configurer delegate, and it's arguments.
276     
277     Params:
278     	dg = delegate that will be used to configure an aggregate
279     	args = list of arguments passed to delegate.
280     **/
281     this(Dg dg, ref Args args) {
282         this.dg = dg;
283         this.args = tuple(args);
284     }
285     
286     /**
287     Constructs a configurer that uses delegate to configure an aggregate of type T.
288     
289     Params:
290     	T = the aggregate type
291     	locator = locator that can be used by delegate to extract some custom data.
292     	
293     Returns:
294     	PropertyConfigurer!T for objects
295     	PropertyConfigurer!(Wrapper!T) for structs
296     **/
297     PropertyConfigurer!T factoryConfigurer(T, string p = "")(Locator!() locator)
298         if (is(T : Z)) {
299         auto callback = new CallbackConfigurer!(T, Dg, Args)(dg, args.expand);
300         callback.locator = locator;
301         
302         return callback;
303     }
304 }
305 
306 /**
307 ditto
308 **/
309 auto callback(T, Args...)(void delegate (Locator!(), ref T, Args) dg, Args args) {
310     return CallbackConfigurerAnnotation!(T, void delegate (Locator!(), ref T, Args), Args)(dg, args);
311 }
312 
313 /**
314 ditto
315 **/
316 auto callback(T, Args...)(void function (Locator!(), ref T, Args) dg, Args args) {
317     return CallbackConfigurerAnnotation!(T, void function (Locator!(), ref T, Args), Args)(dg, args);
318 }
319 
320 /**
321 ditto
322 **/
323 auto callback(T, Args...)(void delegate (Locator!(), T, Args) dg, Args args) {
324     return CallbackConfigurerAnnotation!(T, void delegate (Locator!(), T, Args), Args)(dg, args);
325 }
326 
327 /**
328 ditto
329 **/
330 auto callback(T, Args...)(void function (Locator!(), T, Args) dg, Args args) {
331     return CallbackConfigurerAnnotation!(T, void function (Locator!(), T, Args), Args)(dg, args);
332 }
333 
334 /**
335 Annotation used to mark constructor or method for auto wiring.
336 
337 Marking a method/constructor with autowired annotation will make container to call it with arguments fetched from
338 locator by types of them.
339 
340 Note: even if a method/constructor from an overloaded set is marked with autowired annotation, the first method from overload set
341 will be used. Due to that autowired annotation is recommended to use on methods/constrcutors that are not overloaded.
342 
343 **/
344 struct AutowiredAnnotation {
345     PropertyConfigurer!T factoryConfigurer(T, string method)(Locator!() locator)
346         if (
347             !isField!(T, method) &&
348             isSomeFunction!(getMember!(T, method))
349         ) {
350         
351         alias params = Parameters!(__traits(getOverloads, T, method)[0]);
352         auto references = tuple(staticMap!(toLref, params));
353         
354         auto method = new MethodConfigurer!(T, method, staticMap!(toLrefType, params))(references.expand);
355         method.locator = locator;
356         
357         return method;
358     }
359         
360     PropertyConfigurer!T factoryConfigurer(T, string property)(Locator!() locator)
361         if (
362             isField!(T, property)
363         ) {
364         
365         alias paramType = typeof(getMember!(T, property));
366         
367         pragma(msg, T, ".", property);
368         auto lref = toLref!paramType;
369         auto method = new FieldConfigurer!(T, property, toLrefType!paramType)(lref);
370         method.locator = locator;
371         
372         return method;
373     }
374         
375     InstanceFactory!T factoryContainer(T, string property)(Locator!() locator) {
376         alias params = Parameters!(__traits(getOverloads, T, "__ctor")[0]);
377         auto references = tuple(staticMap!(toLref, params));
378         
379         auto method = new ConstructorBasedFactory!(T, staticMap!(toLrefType, params))(references.expand);
380         method.locator = locator;
381         
382         return method;
383     }
384 }
385 
386 /**
387 ditto
388 **/
389 alias autowired = AutowiredAnnotation;
390 
391 /**
392 An annotation used to provide custom identity for an object in container.
393 **/
394 struct QualifierAnnotation {
395     string id;
396 }
397 
398 /**
399 An annotation used to provide custom identity for an object in container.
400 
401 This function is a convenince function to automatically infer required types for underlying annotation.
402 
403 Params:
404     id = identity of object in container
405 **/
406 auto qualifier(string id) {
407     return QualifierAnnotation(id);
408 }
409 
410 /**
411 An annotation used to provide custom identity for an object in container by some interface.
412 
413 This function is a convenince function to automatically infer required types for underlying annotation.
414 
415 Params:
416     I = identity of object in container
417 **/
418 auto qualifier(I)() {
419     return QualifierAnnotation(name!I);
420 }
421 
422 /**
423 When objects are registered into an aggregate container, this annotation marks in which sub-container it is required to store.
424 **/
425 struct ContainedAnnotation {
426     string id;
427 }
428 
429 /**
430 When objects are registered into an aggregate container, this annotation marks in which sub-container it is required to store.
431 
432 This function is a convenince function to automatically infer required types for underlying annotation.
433 
434 Params:
435     id = identity of container where to store the object.
436 **/
437 auto contained(string id) {
438     return ContainedAnnotation(id);
439 }