2016-08-17 20:44:36 +02:00
/ * *
* This module contains instance factory facilities
*
* Authors :
* Mike Bierlee , m . bierlee @lostmoment.com
2018-01-01 15:54:32 +01:00
* Copyright : 2014 - 2018 Mike Bierlee
2016-08-17 20:44:36 +02:00
* License :
* This software is licensed under the terms of the MIT license .
* The full terms of the license can be found in the LICENSE file .
* /
module poodinis.factory ;
2016-08-22 23:10:29 +02:00
import poodinis.container ;
2017-08-13 18:21:03 +02:00
import poodinis.imports ;
2016-08-22 23:10:29 +02:00
2016-08-17 20:44:36 +02:00
import std.typecons ;
import std.exception ;
2016-08-22 23:10:29 +02:00
import std.traits ;
2016-09-03 18:51:01 +02:00
import std.string ;
2016-12-06 23:17:38 +01:00
import std.stdio ;
2016-08-17 20:44:36 +02:00
alias CreatesSingleton = Flag ! "CreatesSingleton" ;
alias InstanceFactoryMethod = Object delegate ( ) ;
class InstanceCreationException : Exception {
2018-01-01 15:54:32 +01:00
this ( string message , string file = __FILE__ , size_t line = __LINE__ ) {
super ( message , file , line ) ;
}
2016-08-17 20:44:36 +02:00
}
2016-08-17 21:56:52 +02:00
struct InstanceFactoryParameters {
2018-01-01 15:54:32 +01:00
TypeInfo_Class instanceType ;
CreatesSingleton createsSingleton = CreatesSingleton . yes ;
Object existingInstance ;
InstanceFactoryMethod factoryMethod ;
2016-08-17 21:56:52 +02:00
}
2016-08-17 20:44:36 +02:00
class InstanceFactory {
2018-01-01 15:54:32 +01:00
private Object instance = null ;
private InstanceFactoryParameters _factoryParameters ;
this ( ) {
factoryParameters = InstanceFactoryParameters ( ) ;
}
public @property void factoryParameters ( InstanceFactoryParameters factoryParameters ) {
if ( factoryParameters . factoryMethod is null ) {
factoryParameters . factoryMethod = & this . createInstance ;
}
if ( factoryParameters . existingInstance ! is null ) {
factoryParameters . createsSingleton = CreatesSingleton . yes ;
this . instance = factoryParameters . existingInstance ;
}
_factoryParameters = factoryParameters ;
}
public @property InstanceFactoryParameters factoryParameters ( ) {
return _factoryParameters ;
}
public Object getInstance ( ) {
if ( _factoryParameters . createsSingleton & & instance ! is null ) {
debug ( poodinisVerbose ) {
printDebugUseExistingInstance ( ) ;
}
return instance ;
}
debug ( poodinisVerbose ) {
printDebugCreateNewInstance ( ) ;
}
instance = _factoryParameters . factoryMethod ( ) ;
return instance ;
}
private void printDebugUseExistingInstance ( ) {
if ( _factoryParameters . instanceType ! is null ) {
writeln ( format ( "DEBUG: Existing instance returned of type %s" , _factoryParameters . instanceType . toString ( ) ) ) ;
} else {
writeln ( "DEBUG: Existing instance returned from custom factory method" ) ;
}
}
private void printDebugCreateNewInstance ( ) {
if ( _factoryParameters . instanceType ! is null ) {
writeln ( format ( "DEBUG: Creating new instance of type %s" , _factoryParameters . instanceType . toString ( ) ) ) ;
} else {
writeln ( "DEBUG: Creating new instance from custom factory method" ) ;
}
}
protected Object createInstance ( ) {
enforce ! InstanceCreationException ( _factoryParameters . instanceType , "Instance type is not defined, cannot create instance without knowing its type." ) ;
return _factoryParameters . instanceType . create ( ) ;
}
2016-08-17 20:44:36 +02:00
}
2016-08-22 23:10:29 +02:00
class ConstructorInjectingInstanceFactory ( InstanceType ) : InstanceFactory {
2018-01-01 15:54:32 +01:00
private shared DependencyContainer container ;
private bool isBeingInjected = false ;
this ( shared DependencyContainer container ) {
this . container = container ;
}
private static string createArgumentList ( Params . . . ) ( ) {
string argumentList = "" ;
foreach ( param ; Params ) {
if ( argumentList . length > 0 ) {
argumentList ~ = "," ;
}
argumentList ~ = "container.resolve!" ~ param . stringof ;
}
return argumentList ;
}
private static string createImportList ( Params . . . ) ( ) {
string importList = "" ;
foreach ( param ; Params ) {
importList ~ = createImportsString ! param ;
}
return importList ;
}
private static bool parametersAreValid ( Params . . . ) ( ) {
bool isValid = true ;
foreach ( param ; Params ) {
if ( isBuiltinType ! param ) {
isValid = false ;
break ;
}
}
return isValid ;
}
protected override Object createInstance ( ) {
enforce ! InstanceCreationException ( container , "A dependency container is not defined. Cannot perform constructor injection without one." ) ;
enforce ! InstanceCreationException ( ! isBeingInjected , format ( "%s is already being created and injected; possible circular dependencies in constructors?" , InstanceType . stringof ) ) ;
Object instance = null ;
static if ( __traits ( compiles , __traits ( getOverloads , InstanceType , `__ctor` ) ) ) {
foreach ( ctor ; __traits ( getOverloads , InstanceType , `__ctor` ) ) {
static if ( parametersAreValid ! ( Parameters ! ctor ) ) {
isBeingInjected = true ;
mixin ( createImportsString ! InstanceType
~ createImportList ! ( Parameters ! ctor ) ~ `
instance = new ` ~ fullyQualifiedName!InstanceType ~ ` ( ` ~ createArgumentList!(Parameters!ctor) ~ ` ) ;
` ) ;
isBeingInjected = false ;
break ;
}
}
}
if ( instance is null ) {
instance = typeid ( InstanceType ) . create ( ) ;
}
enforce ! InstanceCreationException ( instance ! is null , "Unable to create instance of type" ~ InstanceType . stringof ~ ", does it have injectable constructors?" ) ;
return instance ;
}
2016-08-22 23:10:29 +02:00
}