1 module glued.application.di.initializers;
2 
3 import std.algorithm;
4 import std.meta;
5 import std.traits;
6 
7 import glued.annotations;
8 import glued.logging;
9 
10 import glued.application.di.annotations;
11 import glued.application.di.resolveCall;
12 
13 import dejector;
14 
15 private template queryForField(T, string name)
16     if (is(T == class))
17 { 
18     alias annotation = getAnnotation!(__traits(getMember, T, name), Autowire); 
19     static if (is(annotation.Query == DefaultQuery))
20     { 
21         alias queryForField = typeof(__traits(getMember, T, name)); 
22     } 
23     else 
24     { 
25         alias queryForField = annotation.Query; 
26     } 
27 } 
28 
29 private template queryForProperty(T, string name, size_t overloadIdx)
30     if (is(T == class))
31 { 
32     alias annotation = getAnnotation!(__traits(getOverloads, T, name)[overloadIdx], Autowire); 
33     static if (is(annotation.Query == DefaultQuery))
34     { 
35         alias queryForProperty = Parameters!(__traits(getOverloads, T, name)[overloadIdx])[0]; 
36     } 
37     else 
38     { 
39         alias queryForProperty = annotation.Query; 
40     } 
41 } 
42 
43 /**
44  * Injects dependencies to existing instance.
45  *
46  * Finds every public field or property setter (single-param method with @property
47  * attribute that returns void) that is annotated with Autowire.
48  */
49 class InstanceInitializer(T, bool checkNulls): Initializer 
50     if (is(T == class))
51 {
52     mixin CreateLogger;
53     private Dejector injector;
54     private Logger log;
55 
56     this(Dejector injector)
57     {
58         this.injector = injector;
59         log = Logger(injector.get!(LogSink));
60     }
61 
62     override void initialize(Object o)
63     {
64         log.debug_.emit("Instance initialization for ", &o, " of type ", fullyQualifiedName!T);
65         T t = (cast(T)o);
66         log.debug_.emit("Field injection for ", &o, " of type ", fullyQualifiedName!T);
67         fieldInjection(t);
68         propertyInjection(t);
69         //postConstructInjection(t);
70         log.debug_.emit("Instance initialization for ", &o, " of type ", fullyQualifiedName!T, " finished");
71     }
72 
73     private void fieldInjection(T t)
74     {
75         enum isPublic(string name) = __traits(getProtection, __traits(getMember, T, name)) == "public";
76         enum isAutowired(string name) = hasOneAnnotation!(__traits(getMember, T, name), Autowire);
77         auto hasNullValue(string name)()
78         {
79             return __traits(getMember, t, name) is null;
80         }
81         
82         static foreach (i, name; FieldNameTuple!T)
83         {
84             static if (isPublic!name && isAutowired!name)
85             {
86                 mixin("import "~moduleName!(queryForField!(T, name))~";");
87                 static if (checkNulls) 
88                 {
89                     if (hasNullValue!name()) 
90                     {
91                         log.debug_.emit("Injecting field ", name, " with query ", fullyQualifiedName!(queryForField!(T, name)), " after null check");
92                         __traits(getMember, t, name) = this.injector.get!(queryForField!(T, name));
93                         log.debug_.emit("Injected field ", name, " after null check");
94                     } 
95                     else 
96                     {
97                         log.debug_.emit("Field ", name, " didn't pass null check (value=", __traits(getMember, t, name), ")");
98                     }
99                 } 
100                 else 
101                 {
102                     log.debug_.emit("Injecting field ", name, " with query ", fullyQualifiedName!(queryForField!(T, name)), " with no null check");
103                     __traits(getMember, t, name) = this.injector.get!(queryForField!(T, name));
104                     log.debug_.emit("Injected field ", name, " with no null check");
105                 }
106             }
107         }
108     }
109     
110     private void propertyInjection(T t){
111         enum isPublic(string name, size_t overloadIdx) = __traits(getProtection, __traits(getOverloads, T, name)[overloadIdx]) == "public";
112         enum isAutowired(string name, size_t overloadIdx) = hasOneAnnotation!(__traits(getOverloads, T, name)[overloadIdx], Autowire);
113         enum isMarkedAsProperty(string name, size_t overloadIdx) = hasFunctionAttributes!(__traits(getOverloads, T, name)[overloadIdx], "@property");
114         enum isVoidMethod(string name, size_t overloadIdx) = is(ReturnType!(__traits(getOverloads, T, name)[overloadIdx]) == void);
115         enum hasOneParam(string name, size_t overloadIdx) = Parameters!(__traits(getOverloads, T, name)[overloadIdx]).length == 1;
116         
117         static foreach (i, name; __traits(allMembers, T))
118         {
119             static foreach (j, _ignored; __traits(getOverloads, T, name))
120             {
121                 static if (isPublic!(name, j) && 
122                             isAutowired!(name, j) && 
123                             isMarkedAsProperty!(name, j) &&
124                             isVoidMethod!(name, j) &&
125                             hasOneParam!(name, j))
126                 {
127                     log.debug_.emit("Injecting property ", name, " with query ", fullyQualifiedName!(queryForProperty!(T, name, j)));
128                      __traits(getOverloads, t, name)[j](injector.get!(queryForProperty!(T, name, j)));
129                     log.debug_.emit("Injecting property ", name);
130                 }
131             }
132         }
133     }
134 }
135 
136 /**
137  * Initializes freshly created instance (for which constructor wasn't called yet).
138  * Besides standard injection methods performs constructor injection.
139  * 
140  * If aggregate has many constructors, up to one of them can be annotated
141  * with Constructor - that one will be called.
142  * If there is only one constructor it will be called by default. To turn this
143  * behaviour off annotate that constructor with DontInject.
144  */
145 class ComponentSeedInitializer(T): InstanceInitializer!(T, false) 
146     if (is(T == class))
147 {
148     mixin CreateLogger;
149     private Dejector injector;
150     private Logger log;
151 
152     this(Dejector injector)
153     {
154         super(injector);
155         this.injector = injector;
156         log = Logger(injector.get!(LogSink));
157     }
158 
159     override void initialize(Object o)
160     {
161         log.debug_.emit("Seed initialization for ", &o, " of type ", fullyQualifiedName!T);
162         T t = (cast(T)o);
163         log.debug_.emit("Constructor injection for ", &o, " of type ", fullyQualifiedName!T);
164         constructorInjection(t);
165         super.initialize(o);
166         log.debug_.emit("Seed initialization for ", &o, " of type ", fullyQualifiedName!T, " finished");
167     }
168 
169     private void constructorInjection(T t)
170     {
171         enum isPublic(alias ctor) = __traits(getProtection, ctor) == "public";
172         enum isAnnotatedForInjection(alias ctor) = hasOneAnnotation!(ctor, Constructor);
173         enum isAnnotatedForIgnoring(alias ctor) = hasOneAnnotation!(ctor, DontInject);
174         
175         static if (hasMember!(T, "__ctor")) 
176         {
177             static if (__traits(getOverloads, T, "__ctor").length == 1 && 
178                         isPublic!(__traits(getOverloads, T, "__ctor")[0]))
179             {
180                 static if(isAnnotatedForIgnoring!(__traits(getOverloads, t, "__ctor")[0]))
181                 {
182                     log.debug_.emit("Sole constructor for seed ", &t, " of type ", fullyQualifiedName!T, " is ignored");
183                 }
184                 else
185                 {
186                     log.debug_.emit("Calling default constructor for seed ", &t, " of type ", fullyQualifiedName!T);
187                     resolveCall(this.injector, &__traits(getOverloads, t, "__ctor")[0]);
188                     log.debug_.emit("Called default constructor for instance", &t);
189                 }
190             }
191             else 
192             {
193                 bool ctorCalled = false;
194                 static foreach (i, ctor; __traits(getOverloads, T, "__ctor"))
195                 {
196                     static if (isPublic!ctor && isAnnotatedForInjection!ctor)
197                     {
198                         assert(!ctorCalled);
199                         log.debug_.emit("Calling constructor for seed ", &t, " of type ", fullyQualifiedName!T);
200                         resolveCall!(__traits(getOverloads, T, "__ctor")[i])(this.injector, &__traits(getOverloads, t, "__ctor")[i]);
201                         log.debug_.emit("Called constructor for instance", &t);
202                         ctorCalled = true;
203                     }
204                 }
205                 if (!ctorCalled)
206                     log.debug_.emit("Couldn't find any constructor for seed ", &t, " of type ", fullyQualifiedName!T);
207             }
208         }
209         else
210         {
211             log.debug_.emit("Couldn't find any constructor for seed ", &t, " of type ", fullyQualifiedName!T);
212         }
213     }
214 }