1 /**
2 
3 License:
4 	Boost Software License - Version 1.0 - August 17th, 2003
5     
6     Permission is hereby granted, free of charge, to any person or organization
7     obtaining a copy of the software and accompanying documentation covered by
8     this license (the "Software") to use, reproduce, display, distribute,
9     execute, and transmit the Software, and to prepare derivative works of the
10     Software, and to permit third-parties to whom the Software is furnished to
11     do so, all subject to the following:
12     
13     The copyright notices in the Software and this entire statement, including
14     the above license grant, this restriction and the following disclaimer,
15     must be included in all copies of the Software, in whole or in part, and
16     all derivative works of the Software, unless such copies or derivative
17     works are solely in the form of machine-executable object code generated by
18     a source language processor.
19     
20     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26     DEALINGS IN THE SOFTWARE.
27 
28 Authors:
29 	Alexandru Ermicioi
30 **/
31 module aermicioi.aedi.factory.generic_factory;
32 
33 import aermicioi.aedi.factory.factory;
34 import aermicioi.aedi.factory.reference;
35 
36 import aermicioi.aedi.storage.locator;
37 import aermicioi.aedi.storage.locator_aware;
38 import aermicioi.aedi.exception;
39 import aermicioi.aedi.storage.wrapper;
40 import aermicioi.util.traits;
41 
42 import std.typecons;
43 import std.traits;
44 import std.meta;
45 import std.conv : to;
46 
47 /**
48 A property configurer, has the purpose to modify data of type T according to some logic encapsulated in it.
49 
50 **/
51 interface PropertyConfigurer(T) {
52     
53     public {
54         
55         /**
56         Accepts a reference to an object that is to be configured by the configurer.
57         
58         Params:
59         	object = An object of type T, that will be configured
60         **/
61         void configure(ref T object);
62     }
63 }
64 
65 /**
66 An instance factory, instantiates data of type T.
67 **/
68 interface InstanceFactory(T) {
69     
70     public {
71         
72         /**
73         Create a new instance of object of type T.
74         **/
75         T factory();
76     }
77 }
78 
79 /**
80 A generic factory, is a factory that instantiates data of type T using InstanceFactory and a list of PropertyConfigurers.
81 
82 A generic factory, is a factory that instantiates data of type T using InstanceFactory and a list of PropertyConfigurers.
83 It can optionally provide a Locator!() object to InstanceFactory and PropertyConfigurer to be used as a source of data. 
84 **/
85 interface GenericFactory(T) : Factory!T {
86     
87     public {
88         
89         @property {
90             
91             /**
92             Sets the constructor of new object.
93             
94             Params:
95             	factory = a factory of objects of type T.
96         	
97         	Returns:
98     			The GenericFactoryInstance
99             **/
100             GenericFactory!T setInstanceFactory(InstanceFactory!T factory);
101             
102             /**
103             Get the GenericFactory locator.
104             
105             Returns:
106             	Locator!() the locator that should be used by underlying constructor or property configurer.
107             **/
108             Locator!() locator();
109         }
110         
111         /**
112         Adds an configurer to the GenericFactory.
113         
114         Params:
115         	configurer = a configurer that will be invoked after factory of an object.
116         	
117     	Returns:
118     		The GenericFactoryInstance
119         **/
120         GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer);
121     }
122 }
123 
124 /**
125 A concrete implementation of GenericFactory interface.
126 **/
127 class GenericFactoryImpl(T) : GenericFactory!T, LocatorAware!() {
128     
129     private {
130         Locator!() locator_;
131         
132         InstanceFactory!T factory_;
133         
134         PropertyConfigurer!T[] configurers;
135     }
136     
137     public {
138         
139         this(Locator!() locator) {
140             this.locator = locator;
141             
142             static if (hasDefaultCtor!T) {
143                 
144                 this.setInstanceFactory(
145                     new DefaultConstructorBasedFactory!T
146                 );
147             }
148         }
149         
150         T factory() {
151             T instance;
152             
153             if (this.factory_ !is null) {
154                 instance = this.factory_.factory;
155             } else {
156                 
157                 throw new AediException("Failed to construct object due to no constructor");
158             }
159 
160             foreach (key, configurer; this.configurers) {
161                 configurer.configure(instance);
162             }
163             
164             return instance;
165         }
166         
167         @property {
168             
169             GenericFactory!T setInstanceFactory(InstanceFactory!T factory) {
170                 this.factory_ = factory;
171                 
172                 return this;
173             }
174             
175             GenericFactoryImpl!T locator(Locator!() locator) {
176                 this.locator_ = locator;
177                 return this;
178             }
179             
180             Locator!() locator() {
181                 return this.locator_;
182             }
183             
184             /**
185     		Get the type info of object that is created.
186     		
187     		Returns:
188     			TypeInfo object of created object.
189     		**/
190     		TypeInfo type() {
191     		    return typeid(T);
192     		}
193         }
194             
195         GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) {
196             
197             this.configurers ~= configurer;
198             
199             return this;
200         }
201     }
202     
203 }
204 
205 /**
206 ParameterHolder Stores a set of Args for futher usage in it's subclasses.
207 
208 Params:
209     Args = a type tuple of args that ParameterHolder can hold.
210 **/
211 abstract class ParameterHolder(Args...) : LocatorAware!() {
212     
213     protected {
214         Tuple!Args args_;
215         Locator!() locator_;
216     }
217     
218     public {
219         
220         static if (Args.length > 0) {
221             ParameterHolder args(ref Args args) @safe nothrow {
222             	this.args_ = tuple(args);
223             
224             	return this;
225             }
226         }
227         
228         Tuple!Args args() @safe nothrow {
229         	return this.args_;
230         } 
231     
232         @property {
233             /**
234     		Sets the locator that will be used by configurer to fetch object referenced in argument list.
235     		
236     		Params:
237     			locator = the (service) locator that will be used to fetch required objects.
238     		
239     		Returns:
240     			The ParameterHolder instance.
241     		**/
242             ParameterHolder!Args locator(Locator!() locator)  {
243             	this.locator_ = locator;
244             
245             	return this;
246             }
247             
248             Locator!() locator() @safe nothrow {
249             	return this.locator_;
250             }
251         }
252     }
253 }
254 
255 /**
256 Calls aggregate's method with a set of arguments.
257 
258 Encapsulates a call to aggregate's method, with a set of arguments.
259 The algorithm that calls aggregate's method, will automatically replace
260 references from args list with data extracted from container, casted to 
261 type that is extracted from method's signature.
262 
263 Params:
264     T = the aggregate type
265     property = method that will be called
266     Args = type tuple of args that method can be called with.
267 **/
268 class MethodConfigurer(T, string property, Args...) : ParameterHolder!Args, PropertyConfigurer!T
269 	if (
270 	    isMethodCompatible!(T, property, Args)
271     ) {
272     
273     public {
274         
275         this(ref Args args) {
276             this.args(args);
277         }
278         
279         /**
280         See PropertyConfigurer interface
281         
282         Throws:
283             InvalidCastException when extracted data by reference, is not of type expected by argument
284             of aggregate's method
285         **/
286         void configure(ref T obj) {
287             
288             try {
289                 
290                 alias ArgTuple = Parameters!(Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, property))[0]);
291                 Tuple!ArgTuple parameters;
292                 
293                 foreach (index, ref parameter; parameters) {
294                     
295                     parameter = args[index].resolve!(typeof(parameter))(this.locator);
296                 }
297                 __traits(getMember, obj, property)(parameters.expand);
298             } catch (Exception e) {
299                 throw new PropertyConfigurerException("Error occurred during call of " ~ name!T ~ "." ~ property, e);
300             }
301         }
302     }
303 }
304     
305 /**
306 Invoke aggregate's method with supplied args.
307 
308 Configures aggregate's factory to call specified method with passed args.
309 The function will check if the arguments passed to it are compatible with at 
310 least one method from possible overload set.
311 The args list can contain references to other objects in locator as well, though
312 no type compatibility checks will be performed at compile time.
313 
314 Params:
315 	factory = the factory which will be configured to invoke method.
316 	args = the arguments that will be used to invoke method on the new object.
317 	
318 Returns:
319 	GenericFactory!T.
320 **/
321 auto methodConfigurer(string property, T, Args...)(Locator!() locator, auto ref Args args) 
322     if (!isField!(T, property)) {
323     mixin assertObjectMethodCompatible!(T, property, Args);
324     
325     auto propertySetter = new MethodConfigurer!(T, property, Args)(args);
326     propertySetter.locator = locator;
327     
328     return propertySetter;
329 }
330 
331 /**
332 Sets aggregate's field to a value.
333 
334 Encapsulates logic that sets aggregates field to a certain value.
335 If argument that is contained by configurer is a reference, it will be automatically
336 replaced with value extracted from locator, and set to aggregate's field.
337 **/
338 class FieldConfigurer(T, string property, Arg) : ParameterHolder!Arg, PropertyConfigurer!T
339 	if (
340 	    isFieldCompatible!(T, property, Arg)
341     ) {
342     
343     public {
344         
345         this(ref Arg arg) {
346             this.args(arg);
347         }
348         
349         /**
350         See PropertyConfigurer interface
351         
352         Throws:
353             InvalidCastException when extracted data by reference, is not of type expected by argument
354             of aggregate's field
355         **/
356         void configure(ref T obj) {
357             
358             try {
359             
360                 __traits(getMember, obj, property) = args[0].resolve!(typeof(__traits(getMember, obj, property)))(this.locator);
361             } catch (Exception e) {
362                 
363                 throw new PropertyConfigurerException("Error occurred during set of " ~ name!T ~ "." ~ property, e);
364             }
365         }
366     }
367 }
368     
369 /**
370 Set aggregate's public field to passed arg.
371 
372 Configures aggregate's factory to set specified field to passed arg.
373 The function will check if passed argument is type compatible with specified field.
374 The argument can be a reference as well. In case of argument being reference to another data
375 in container, no type compatiblity checking will be done.
376 
377 Params
378     factory = the factory which will be configured to set property.
379 	arg = the value of property to be set, or reference to data in container.
380 
381 Returns:
382 	GenericFactory!T.
383 **/
384 auto fieldConfigurer(string property, T, Arg)(Locator!() locator, auto ref Arg arg)
385     if (isField!(T, property)) {
386     mixin assertFieldCompatible!(T, property, Arg);
387     
388     auto propertySetter = new FieldConfigurer!(T, property, Arg)(arg);
389     propertySetter.locator = locator;
390     
391     return propertySetter;
392 }
393 
394 /**
395 Instantiates an aggregate using it's constructor with no arguments.
396 **/
397 class DefaultConstructorBasedFactory(T) : InstanceFactory!T 
398     if (
399         hasDefaultCtor!T
400     ) {
401     
402     public {
403         
404         T factory() {
405             
406             try {
407                 static if (is(T : Object)) {
408                     return new T();
409                 } else {
410                     return T.init;
411                 }
412             } catch (Exception e) {
413                 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T, e);
414             }
415         }
416     }
417 }
418     
419 /**
420 Instantiates aggregate using it's constructor with args.
421 
422 Encapsulates construction of aggregate using a constructor, with args.
423 Arguments from argument list that are references, are automatically 
424 replaced with data extracted from locator.
425 
426 Params:
427     T = aggregate type
428     Args = type tuple of args that are passed to T's constructor
429 **/
430 class ConstructorBasedFactory(T, Args...) : ParameterHolder!Args, InstanceFactory!T
431 	if (
432 	    isObjectConstructorCompatible!(T, Args)
433 	) {
434     
435     public {
436         
437         this(ref Args args) {
438             this.args(args);
439         }
440         
441         /**
442         See InstanceFactory interface
443         
444         Throws:
445             InvalidCastException when extracted data by reference, is not of type expected by argument
446             of aggregate's constructor
447         **/
448         T factory() {
449             
450             try {
451                 
452                 alias ConstructorArgs = Parameters!(Filter!(partialSuffixed!(isArgumentListCompatible, Args), __traits(getOverloads, T, "__ctor"))[0]);
453                 
454                 Tuple!ConstructorArgs parameters;
455                 
456                 foreach (index, ref parameter; parameters) {
457                     parameter = this.args[index].resolve!(typeof(parameter))(this.locator);
458                 }
459                 
460                 static if (is(T : Object)) {
461                     return new T(parameters.expand);
462                 } else {
463                     return T(parameters.expand);
464                 }
465             } catch (Exception e) {
466                 
467                 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T, e);
468             }
469         }
470     }
471 }
472 	
473 /**
474 Construct aggregate using args.
475 
476 Constructs aggregate using args, that are passed to function.
477 The function will attempt to find at least one construct that 
478 can accept passed argument list. If it fails, compiler will
479 produce error, with respective problems.
480 The argument list can contain beside simple values, references
481 to other data in locator. Arguments that are references to other data
482 won't be type checked.
483 
484 Params:
485 	factory = the factory which will call constructor with passed arguments.
486 	args = a list of arguments that will be passed to constructor.
487 	
488 Returns:
489 	GenericFactory!T.
490 **/
491 
492 auto constructorBasedFactory(T, Args...)(Locator!() locator, auto ref Args args) {
493     mixin assertObjectConstructorCompatible!(T, Args);
494     auto constructor = new ConstructorBasedFactory!(T, Args)(args);
495     
496     constructor.locator = locator;
497     
498     return constructor;
499 }
500 	
501 /**
502 Instantiates an aggregate using a method from other aggregate (factory method pattern).
503 
504 Encapsulates construction of aggregate using factory method.
505 Arguments that are references, will be replaced with data extracted
506 from locator, and passed to factory's method.
507 In case when method is not static member, the algorithm will use 
508 an instantiaton of factory passed to it, or extracted from locator
509 if a reference is passed.
510 
511 Params:
512     T = factory that is used to instantiate aggregate using it's method
513     method = the name of method used to instantiate aggregate
514     W = the factory T, or a LocatorReference to the factory.
515     Args = type tuple of arguments passed to factory.
516 **/
517 class FactoryMethodBasedFactory(T, string method, W, Args...) : ParameterHolder!Args, InstanceFactory!(ReturnType!(getCompatibleOverload!(T, method, Args)))
518     if (
519         (is(W : LocatorReference) || is(W : T)) &&
520         isMethodCompatible!(T, method, Args) &&
521         isAggregateType!(ReturnType!(getCompatibleOverload!(T, method, Args)))
522     ) {
523         
524     static if (!__traits(isStaticFunction, getCompatibleOverload!(T, method, Args))) {
525         
526         private {
527             W fact;
528         }
529         
530         public {
531             this(ref W fact, ref Args args) {
532                 this.args(args);
533                 this.fact = fact;
534             }
535         }
536     } else {
537         
538         this(ref Args args) {
539             this.args(args);
540         }
541     }
542         
543     private {
544         
545         alias Z = ReturnType!(getCompatibleOverload!(T, method, Args));
546     }
547     
548     public {
549         /**
550         See InstanceFactory interface
551         
552         Throws:
553             InvalidCastException when extracted data by reference, is not of type expected by argument
554             factory's method, or when factory is referenced, and the object referenced in locator is not
555             of factory's type T.
556         **/
557         Z factory() {
558             
559             try {
560                 
561                 alias FactoryMethodParameters = Parameters!(Filter!(partialSuffixed!(isArgumentListCompatible, Args), __traits(getOverloads, T, method))[0]);
562                 
563                 Tuple!FactoryMethodParameters parameters;
564                 
565                 foreach (index, ref parameter; parameters) {
566                     parameter = this.args[index].resolve!(typeof(parameter))(this.locator);
567                 }
568                 
569                 static if (!__traits(isStaticFunction, getCompatibleOverload!(T, method, Args))) {
570     
571                     return __traits(getMember, this.fact.resolve!(T)(this.locator), method)(parameters.expand);
572                 } else {
573     
574                     return __traits(getMember, T, method)(parameters.expand);
575                 }
576             } catch (Exception e) {
577                 
578                 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T ~ " using factory method of " ~ name!T ~ "." ~ method, e);
579             }
580         }
581     }
582 }
583     
584 /**
585 Invoke T's method to create data of type X.
586 
587 Configures aggregate's factory to call method of factoryMethod with args,
588 in order to create data of type X.
589 In case when method is not a static member, the function requires to
590 pass a instance of factoryMethod or a reference to it.
591 The algorithm will check for args compatiblity with parameters of 
592 factory method. No type check is done for arguments that are references
593 at compile time.
594 
595 Params:
596     factory = aggregate's factory that is configured to call factoryMethod methods to spawn aggregate
597     factoryMethod = instance of factory method that will be used to instantiate aggregate
598     args = a list of arguments passed to factory method
599     T = type of factoryMethod
600     method = the method that is called from T to instantiate aggregate
601     W = either LocatorReference or T
602     X = the return type of T.method member
603 **/
604 auto factoryMethodBasedFactory(T, string method, W, Args...)(Locator!() locator, auto ref W factoryMethod, auto ref Args args)
605     if (
606         isNonStaticMethodCompatible!(T, method, Args) &&
607         (is(W : T) || is(W : LocatorReference))
608     ) {
609         
610     auto constructor = new FactoryMethodBasedFactory!(T, method, W, Args)(factoryMethod, args);
611     
612     constructor.locator = locator;
613     
614     return constructor;
615 }
616 
617 /**
618 ditto
619 **/
620 auto factoryMethodBasedFactory(T, string method, Args...)(Locator!() locator, auto ref Args args)
621     if (
622         isStaticMethodCompatible!(T, method, Args)
623     ) {
624     auto constructor = new FactoryMethodBasedFactory!(T, method, T, Args)(args);
625     
626     constructor.locator = locator;
627     
628     return constructor;
629 }
630     
631 /**
632 Instantiates data of type T using a delegate or function.
633 
634 Encapsulates data's construction logic using a delegate.
635 The algorithm uses a delegate to create required data, 
636 with a set of Args that are passed to delegate, and a locator
637 for dependency fetching. 
638 
639 Params:
640     T = the constructed aggregate
641     Args = type tuple of arguments passed to delegate for aggregate's construction. 
642 **/
643 class CallbackFactory(T, Dg, Args...) : ParameterHolder!Args, InstanceFactory!T
644     if ((is(Dg == T delegate (Locator!(), Args)) || is(Dg == T function (Locator!(), Args)))) {
645     
646     private {
647         Dg dg;
648     }
649     
650     public {
651         this(Dg dg, ref Args args) {
652             this.dg = dg;
653             this.args(args);
654         }
655         
656         /**
657         See InstanceFactory interface
658         **/
659         T factory() {
660             try {
661                 
662                 return this.dg(this.locator_, args.expand);
663             } catch (Exception e) {
664                 
665                 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T ~ " using callback factory", e);
666             }
667         }
668     }
669 }
670     
671 /**
672 Construct aggregate using a delegate.
673 
674 Constructs aggregate using a delegate, and a list of arguments passed to delegate.
675 
676 Params:
677 	factory = the factory which will use delegate to construct aggregate.
678 	dg = the delegate that is responsible for creating aggregate, given a list of arguments.
679 	args = the arguments that will be used by delegate to construct aggregate.
680 	
681 Returns:
682 	GenericFactory!T.
683 **/
684 auto callbackFactory(T, Args...)(Locator!() locator, T delegate(Locator!(), Args) dg, auto ref Args args) {
685     auto constr = new CallbackFactory!(T, T delegate(Locator!(), Args), Args)(dg, args);
686     constr.locator = locator;
687     return constr;
688 }
689     
690 /**
691 ditto
692 **/
693 auto callbackFactory(T, Args...)(Locator!() locator, T function(Locator!(), Args) dg, auto ref Args args) {
694     auto constr = new CallbackFactory!(T, T function(Locator!(), Args), Args)(dg, args);
695     constr.locator = locator;
696     return constr;
697 }
698 
699 /**
700 Configures/modifies data of type T with help of a delegate or function.
701 
702 Encapsulates data configuration logic using a delegate.
703 The algorithm calls delegate, with a locator, a set of Args,
704 and configured data, in order to modify the data
705 somehow.
706 
707 Note:
708     If data is not a reference type it is recommended to pass it by reference
709     in order to avoid receiving of a copy and not original one in delegate.
710 Params:
711     T = the aggregate
712     Args = type tuple of arguments used by delegate for customization.
713 **/
714 class CallbackConfigurer(T, Dg, Args...) : ParameterHolder!Args, PropertyConfigurer!T 
715     if (
716         is(Dg == void delegate (Locator!(), T, Args)) || 
717         is(Dg == void function (Locator!(), T, Args)) ||
718         is(Dg == void delegate (Locator!(), ref T, Args)) || 
719         is(Dg == void function (Locator!(), ref T, Args))
720     ) {
721     
722     private {
723         Dg dg;
724     }
725     
726     /**
727     See InstanceFactory interface
728     **/
729     public {
730         this(Dg dg, ref Args args) {
731             this.dg = dg;
732             this.args(args);
733         }
734         
735         void configure(ref T object) {
736             
737             try {
738             
739                 return this.dg(this.locator_, object, args.expand);
740             } catch (Exception e) {
741             	throw new PropertyConfigurerException("Error occurred during callback configuration of " ~ name!T, e);
742             }
743         }
744     }
745 }
746     
747 /**
748 Call dg on an aggregate that is in configuration phase.
749 
750 Call dg on aggregate to perform some modifications, using args as input.
751 
752 Params:
753     factory = factory which will call dg with args.
754     dg = delegate that will perform some modifications on aggregate using passed args.
755     args = a list of arguments passed to dg.
756     
757 Returns:
758     GenericFactory!T
759 **/
760 auto callbackConfigurer(T, Args...)(Locator!() locator, void delegate(Locator!(), T, Args) dg, auto ref Args args) {
761     auto constr = new CallbackConfigurer!(T, void delegate(Locator!(), T, Args), Args)(dg, args);
762     constr.locator = locator;
763     return constr;
764 }
765     
766 /**
767 ditto
768 **/
769 auto callbackConfigurer(T, Args...)(Locator!() locator, void function(Locator!(), T, Args) dg, auto ref Args args) {
770     auto constr = new CallbackConfigurer!(T, void function(Locator!(), T, Args), Args)(dg, args);
771     constr.locator = locator;
772     return constr;
773 }
774 
775 /**
776 ditto
777 **/
778 auto callbackConfigurer(T, Args...)(Locator!() locator, void delegate(Locator!(), ref T, Args) dg, auto ref Args args) {
779     auto constr = new CallbackConfigurer!(T, void delegate(Locator!(), ref T, Args), Args)(dg, args);
780     constr.locator = locator;
781     return constr;
782 }
783     
784 /**
785 ditto
786 **/
787 auto callbackConfigurer(T, Args...)(Locator!() locator, void function(Locator!(), ref T, Args) dg, auto ref Args args) {
788     auto constr = new CallbackConfigurer!(T, void function(Locator!(), ref T, Args), Args)(dg, args);
789     constr.locator = locator;
790     return constr;
791 }
792 
793 /**
794 Instantiates data of type T using another factory, and configures it with help of it's own property configurers.
795 **/
796 class ParentAwareGenericFactory(T) : GenericFactory!T {
797     
798     private {
799         Factory!T fact_;
800         Locator!() locator_;
801         PropertyConfigurer!T[] configurers;
802     }
803     
804     public {
805         @property {
806         	ParentAwareGenericFactory fact(Factory!T fact) @safe nothrow {
807         		this.fact_ = fact;
808         	
809         		return this;
810         	}
811         	
812         	Factory!T fact() @safe nothrow {
813         		return this.fact_;
814         	}
815         	
816         	ParentAwareGenericFactory locator(Locator!() locator) @safe nothrow {
817         		this.locator_ = locator;
818         	
819         		return this;
820         	}
821         	
822         	Locator!() locator() @safe nothrow {
823         		return this.locator_;
824         	}
825         	
826         	TypeInfo type() @safe nothrow {
827         		return typeid(T);
828         	}
829         }
830         
831         T factory() {
832             T result = this.fact.factory;
833             
834             foreach (configurer; this.configurers) {
835                 configurer.configure(result);
836             }
837             
838             return result;
839         }
840         
841         ParentAwareGenericFactory!T setInstanceFactory(InstanceFactory!T factory) {
842             throw new AediException("Setting constructor for an object with parent is not supported yet.");
843         }
844         
845         ParentAwareGenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) {
846             this.configurers ~= configurer;
847             
848             return this;
849         }
850     }
851 }
852 
853 GenericFactory!T genericFactory(T)(Locator!() locator) {
854     return new GenericFactoryImpl!T(locator);
855 }
856 
857 /**
858 An check if the argument list passed to ConstructorBasedFactory or MethodConfigurer is compatible with signature of underlying
859 method or constructor.
860 
861 Note:
862 	For now it checks if the lengths are equal. For future it should also check if types are compatible.
863 **/
864 template isArgumentListCompatible(alias func, ArgTuple...) 
865 	if (isSomeFunction!func) {
866     bool isArgumentListCompatible() {
867         alias FuncParams = Parameters!func;
868         alias Required = Filter!(partialSuffixed!(isValueOfType, void), ParameterDefaults!func);
869        
870         static if ((ArgTuple.length < Required.length) || (ArgTuple.length > FuncParams.length)) {
871           
872             return false;
873         } else {
874             
875             bool result = true;
876             foreach (index, Argument; ArgTuple) {
877           
878                 static if (!is(Argument : LocatorReference) && !isImplicitlyConvertible!(Argument, FuncParams[index])) {
879           
880                     result = false;
881                     break;
882                 } 
883             }
884             
885             return result;
886         }
887     }
888 }
889 	
890 mixin template assertFieldCompatible(T, string field, Arg) {
891     import aermicioi.util.traits;
892     import std.traits;
893     import std.meta;
894     
895     static assert(isField!(T, field), name!T ~ "'s " ~ field ~ " member is not a field");
896     static assert(isProtection!(T, field, "public"), name!T ~ "'s " ~ field ~ " is not public and therefore cannot be accessed.");
897     static assert(is(Arg : LocatorReference) ? true : isImplicitlyConvertible!(Arg, typeof(getMember!(T, field))), name!T ~"'s " ~ field ~ " type " ~ name!(typeof(getMember!(T, field))) ~ " doesn't match with passed arguments type " ~ name!Arg);
898 }
899 
900 enum bool isFieldCompatible(T, string field, Arg) = 
901     isField!(T, field) &&
902     isProtection!(T, field, "public") &&
903     is(Arg : LocatorReference) ? true : isImplicitlyConvertible!(Arg, typeof(getMember!(T, field)));
904 
905 mixin template assertObjectConstructorCompatible(T, Args...) {
906     import aermicioi.util.traits;
907     import std.traits;
908     import std.meta;
909     
910     static assert(hasMember!(T, "__ctor"), name!T ~ " doesn't have any constructor to call.");
911     static assert(isProtection!(T, "__ctor", "public"), name!T ~ "'s constructor is not public.");
912     static assert(isSomeFunction!(__traits(getMember, T, "__ctor")), name!T ~ "'s constructor is not a function, probably a template.");
913     static assert(variadicFunctionStyle!(__traits(getMember, T, "__ctor")) == Variadic.no, name!T ~ "'s constructor is a variadic function. Only non-variadic constructors are supported.");
914     static assert(Filter!(partialSuffixed!(isArgumentListCompatible, Args), __traits(getOverloads, T, "__ctor")).length == 1, "None, or multiple overloads found for " ~ name!T ~ "'s constructor with passed arguments.");
915 }
916 
917 enum bool isObjectConstructorCompatible(T, Args...) = isMethodCompatible!(T, "__ctor", Args);
918 
919 mixin template assertObjectMethodCompatible(T, string method, Args...) {
920     import std.range : only;
921     import std.array : array;
922     import aermicioi.util.traits;
923     import std.traits;
924     import std.meta;
925     
926     static assert(hasMember!(T, method), name!T ~ "'s method " ~ method ~ " not found.");
927     static assert(isProtection!(T, method, "public"), name!T ~ "'s method " ~ method ~ " is not public");
928     static assert(isSomeFunction!(__traits(getMember, T, method)), name!T ~ "'s member " ~ method ~ " is not a function, probably a field, or a template.");
929     static assert(variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no, name!T ~ "'s method " ~ method ~ "is variadic function. Only non-variadic methods are supported.");
930     static assert(Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method)).length == 1, name!T ~ "'s " ~ method ~ " doesn't have overload matching passed arguments (" ~ only(staticMap!(name, Args)).joiner(", ").array ~ "), or has several overloads that match.");
931 }
932 
933 enum bool isObjectMethodCompatible(T, string method, Args...) = isMethodCompatible!(T, method, Args);
934 
935 
936 template isMethodCompatible(T, string method, Args...) {
937     enum bool isMethodCompatible = 
938             hasMember!(T, method) &&
939             isProtection!(T, method, "public") &&
940             isSomeFunction!(__traits(getMember, T, method)) &&
941             (variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no) &&
942             (Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method)).length == 1);
943 }
944 
945 template getCompatibleOverload(T, string method, Args...)
946     if (isObjectMethodCompatible!(T, method, Args)) {
947     
948     alias getCompatibleOverload = Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method))[0];
949 }
950 
951 alias isStaticMethodCompatible = templateAnd!(
952     isMethodCompatible, 
953     chain!(
954         isStaticFunction, 
955         getCompatibleOverload
956     )
957 );
958 
959 alias isNonStaticMethodCompatible = templateAnd!(
960     isMethodCompatible, 
961     chain!(
962         templateNot!isStaticFunction, 
963         getCompatibleOverload
964     )
965 );
966 
967 alias isNonStaticMethodCompatibleAndReturnTypeOf(X) = templateAnd!(
968     isMethodCompatible, 
969     chain!(
970         templateNot!isStaticFunction, 
971         getCompatibleOverload
972     ),
973     partialSuffixed!(
974         chain!(
975             partialPrefixed!(
976                 get,
977                 0
978             ),
979             getCompatibleOverload
980         ),
981         X
982     )
983 );
984 
985 private {
986     template isValueOfType(alias value, Type) {
987         enum bool isValueOfType = is(typeof(value) == Type);
988     }
989     
990     template isValueOfType(Value, Type) {
991         enum bool isValueOfType = is(Value == Type);
992     }
993     
994     enum bool isStruct(T) = is(T == struct);
995     
996     alias hasDefaultCtor = 
997         partialSuffixed!(
998             templateOr!(
999                 templateNot!hasMember,
1000                 chain!(
1001                     isStruct,
1002                     get!0
1003                 ),
1004                 templateAnd!(
1005                     partialSuffixed!(
1006                         isProtection,
1007                         "public"
1008                     ),
1009                     chain!(
1010                         partialPrefixed!(
1011                             anySatisfy,
1012                             eq!0
1013                         ),
1014                         partialPrefixed!(
1015                             staticMap,
1016                             arity
1017                         ),
1018                         chain!(
1019                             partialPrefixed!(
1020                                 Filter,
1021                                 partialSuffixed!(
1022                                     isProtection,
1023                                     "public"
1024                                 )
1025                             ),
1026                             getOverloads
1027                         )
1028                     )
1029                 )
1030             ),
1031             "__ctor"
1032         );
1033 }