1 /**
2 Contains primitives related to reference resolving during construction of component
3 (objects, structs, basic types, etc.).
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 	aermicioi
32 **/
33 module aermicioi.aedi.factory.reference;
34 
35 import aermicioi.aedi.exception.invalid_cast_exception;
36 import aermicioi.aedi.factory.factory;
37 import aermicioi.aedi.storage.locator;
38 import std.traits;
39 import std.conv : text;
40 
41 /**
42 Represents a reference that some component is dependent on it.
43 
44 Represents a reference that some component is dependent on it.
45 It will resolve itself to the referenced component, that is
46 subclass of Object, or component that is encapsulated in Wrapper object.
47 **/
48 @safe interface RuntimeReference {
49 
50     /**
51     Resolve the reference, to referenced component.
52 
53     Resolve the reference, to referenced component.
54 
55     Params:
56     	locator = an optional source of component used to resolve reference
57 
58     Returns:
59     	Object the actual object, or component that is wrapped in Wrapper object.
60     **/
61     Object get(Locator!() locator);
62 }
63 
64 /**
65 Represents a reference that is located in locator.
66 
67 Represents a reference that is located in locator.
68 It uses referenced component's identity in locator to
69 find it and serve.
70 **/
71 @safe class LocatorReference : RuntimeReference {
72     private {
73         string identity_;
74     }
75 
76     public {
77         /**
78         Constructor for LocatorReference
79 
80         Params:
81             id = identity of component that is referenced
82         **/
83         this(string id) {
84             this.identity = id;
85         }
86 
87         @property {
88 
89             /**
90             Set the identity of referenced component.
91 
92             Set the identity of referenced component.
93             Description
94 
95             Params:
96             	identity = the identity of referenced component.
97 
98             Returns:
99             	this
100             **/
101         	LocatorReference identity(string identity) @safe nothrow {
102         		this.identity_ = identity;
103 
104         		return this;
105         	}
106 
107         	/**
108         	Get the identity of referenced component.
109 
110         	Get the identity of referenced component.
111 
112         	Returns:
113         		string the identity of referenced component
114         	**/
115         	string identity() @safe nothrow {
116         		return this.identity_;
117         	}
118         }
119 
120         /**
121         Resolve the reference, to referenced component.
122 
123         Resolve the reference, to referenced component.
124 
125         Params:
126             locator = an optional source of components used to resolve reference
127 
128         Returns:
129             Object the actual object, or component that is wrapped in Wrapper object.
130         **/
131         Object get(Locator!() locator) {
132             return locator.get(this.identity);
133         }
134 
135         override string toString() const {
136             return text("IdRef(", identity_, ")");
137         }
138     }
139 }
140 
141 /**
142 ditto
143 **/
144 @safe auto lref(string id) {
145     return new LocatorReference(id);
146 }
147 
148 /**
149 ditto
150 **/
151 @safe auto lref(string name)() {
152     return name.lref;
153 }
154 
155 /**
156 Reference to a component stored in a locator by it's type.
157 **/
158 @safe class TypeLocatorReference(T) : RuntimeReference {
159 
160     public {
161         /**
162         Resolve the reference, to referenced component.
163 
164         Resolve the reference, to referenced component.
165 
166         Params:
167             locator = an optional source of components used to resolve reference
168 
169         Returns:
170             Object the actual object, or component that is wrapped in Wrapper object.
171         **/
172         Object get(Locator!() locator) {
173             auto type = typeid(T);
174 
175             if (locator.has(type.toString())) {
176 
177                 return locator.get(type.toString());
178             } else {
179 
180                 return locator.get(fullyQualifiedName!T);
181             }
182         }
183 
184         override string toString() {
185             return text("TypeRef(", typeid(T), ")");
186         }
187     }
188 }
189 
190 /**
191 ditto
192 **/
193 @safe auto lref(T)() {
194     return new TypeLocatorReference!T;
195 }
196 
197 /**
198 Represents a reference to component yet to be constructed.
199 
200 Represents a reference to component yet to be constructed.
201 It will instantiate the referenced component using an object
202 factory, and will serve it to requestor.
203 **/
204 @safe class AnonymousFactoryReference : RuntimeReference {
205 
206     private {
207         ObjectFactory factory_;
208     }
209 
210     public {
211         @property {
212             /**
213             Set factory
214 
215             Params:
216                 factory = factory used by anonymous reference to create component
217             Returns:
218                 typeof(this)
219             **/
220         	AnonymousFactoryReference factory(ObjectFactory factory) @safe nothrow {
221         		this.factory_ = factory;
222 
223         		return this;
224         	}
225 
226             /**
227             Get factory
228 
229             Returns:
230                 ObjectFactory
231             **/
232         	ObjectFactory factory() @safe nothrow {
233         		return this.factory_;
234         	}
235         }
236 
237         /**
238         Resolve the reference, to referenced component.
239 
240         Resolve the reference, to referenced component.
241 
242         Params:
243             locator = an optional source of components used to resolve reference
244 
245         Returns:
246             Object the actual object, or component that is wrapped in Wrapper object.
247         **/
248         Object get(Locator!() locator) {
249             this.factory.locator = locator;
250             scope(exit) this.factory.locator = null;
251 
252             return this.factory.factory;
253         }
254 
255         override string toString() {
256             return text("AnonRef(", factory.type, ")");
257         }
258     }
259 }
260 
261 /**
262 ditto
263 **/
264 @safe auto anonymous(T : Factory!X, X)(T factory) {
265     import aermicioi.aedi.factory.wrapping_factory : WrappingFactory;
266     return anonymous(new WrappingFactory!T(factory));
267 }
268 
269 /**
270 ditto
271 **/
272 @safe auto anonymous(ObjectFactory factory) {
273     auto anonymous = new AnonymousFactoryReference();
274     anonymous.factory = factory;
275 
276     return anonymous;
277 }
278 
279 /**
280 Reference that defaults to alternate component in case that original one is not fetchable from container
281 
282 Params:
283     original = original reference to a component that is attempted to be fetched.
284     alternate = reference to alternate component that is meant to substitute original component in case of some failure.
285 Throws:
286 
287 Returns:
288     AlternateReference
289 **/
290 @safe AlternateReference alternate(RuntimeReference original, RuntimeReference alternate) {
291     AlternateReference reference = new AlternateReference();
292 
293     reference.original = original;
294     reference.alternative = alternate;
295 
296     return reference;
297 }
298 
299 /**
300 ditto
301 **/
302 @safe class AlternateReference : RuntimeReference {
303     private {
304         RuntimeReference original_;
305         RuntimeReference alternative_;
306     }
307 
308     public {
309         /**
310             Set original
311 
312             Params:
313                 original = primary reference used to fetch dependency
314             Returns:
315                 typeof(this)
316         **/
317         typeof(this) original(RuntimeReference original) @safe nothrow pure {
318             this.original_ = original;
319 
320             return this;
321         }
322 
323         /**
324             Get original
325 
326             Returns:
327                 RuntimeReference
328         **/
329         RuntimeReference original() @safe nothrow pure {
330             return this.original_;
331         }
332 
333         /**
334             Set alternative
335 
336             Params:
337                 alternative = the second reference used when first throws exception
338 
339             Returns:
340                 typeof(this)
341         **/
342         typeof(this) alternative(RuntimeReference alternative) @safe nothrow pure {
343             this.alternative_ = alternative;
344 
345             return this;
346         }
347 
348         /**
349             Get alternative
350 
351             Returns:
352                 RuntimeReference
353         **/
354         RuntimeReference alternative() @safe nothrow pure {
355             return this.alternative_;
356         }
357 
358         /**
359         Resolve the reference, to referenced component.
360 
361         Resolve the reference, to referenced component.
362 
363         Params:
364             locator = an optional source of components used to resolve reference
365 
366         Returns:
367             Object the actual object, or component that is wrapped in Wrapper object.
368         **/
369         Object get(Locator!() locator) {
370             import aermicioi.aedi.exception.not_found_exception : NotFoundException;
371 
372             try {
373 
374                 return this.original.get(locator);
375             } catch (NotFoundException e) {
376 
377                 return this.alternative.get(locator);
378             }
379         }
380 
381         override string toString() @trusted {
382             return text("OptRef(", this.original, ", ", this.alternative, ")");
383         }
384     }
385 }
386 
387 /**
388 Create a reference with type enforcement.
389 
390 The resulting reference will check for returned object to be compliant
391 with specified T type, otherwise a not found exception is thrown.
392 
393 Params:
394     reference = reference to be enforced with expected type
395     T = expected type returned from container
396 
397 Returns:
398     TypeEnforcedRuntimeReference!T enforced reference with type.
399 **/
400 auto typeEnforcedRef(T)(RuntimeReference reference) {
401     return new TypeEnforcedRuntimeReference!T(reference);
402 }
403 
404 /**
405 ditto
406 **/
407 @safe class TypeEnforcedRuntimeReference(T) : RuntimeReference {
408     private {
409         RuntimeReference reference;
410     }
411 
412     public {
413         /**
414         Constructor for enforced type reference accepting reference to be enforced.
415 
416         Params:
417             reference = reference to enforce with type
418         **/
419         this(RuntimeReference reference)
420         in (reference !is null, "Expected a reference, not null value") {
421             this.reference = reference;
422         }
423 
424         /**
425         Resolve the reference, to referenced component.
426 
427         Resolve the reference, to referenced component.
428 
429         Params:
430             locator = an optional source of components used to resolve reference
431 
432         Returns:
433             Object the actual object, or component that is wrapped in Wrapper object.
434         **/
435         Object get(Locator!() locator) @trusted {
436             import aermicioi.aedi.exception.not_found_exception : NotFoundException;
437             import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException;
438             import aermicioi.aedi.storage.wrapper : unwrap;
439 
440             Object value = this.reference.get(locator);
441 
442             try {
443                 cast(void) value.unwrap!T;
444             } catch (InvalidCastException exception) {
445 
446                 throw new NotFoundException(text(
447                     "The component was found using ", this.reference, " however it wasn't of expected type ", typeid(T), " but of ", value, "."
448                 ), null, exception);
449             }
450 
451             return value;
452         }
453 
454         override string toString() @trusted {
455             return text("TypeEnfRef!(", typeid(T), ")(", this.reference, ")");
456         }
457     }
458 }
459 
460 /**
461 Resolve a reference, and attempt to convert to component of type T.
462 
463 See:
464     aermicioi.aedi.storage.wrapper : unwrap for downcasting semantics.
465 
466 Params:
467 	T = the expected type of resolved component.
468 	locator = optional source of components for resolving reference
469 
470 Throws:
471 	InvalidCastException when resolved component is not of expected type.
472 
473 Returns:
474 	T referenced object
475 	Wrapper!T referenced component that is not of Object subclass.
476 **/
477 @trusted auto resolve(T)(RuntimeReference reference, Locator!() locator) {
478     import aermicioi.aedi.storage.wrapper : unwrap;
479     return reference.get(locator).unwrap!T;
480 }
481 
482 /**
483 ditto
484 **/
485 @trusted auto ref Z resolve(T, Z)(auto ref Z reference, Locator!() locator)
486     if (!is(Z : RuntimeReference)) {
487     return reference;
488 }
489 
490 /**
491 Alias to fullyQualifiedName from std.traits, for shorter notation.
492 **/
493 template name(alias T)
494     if (is(typeof(T))) {
495     alias name = fullyQualifiedName!(typeof(T));
496 }
497 
498 /**
499 ditto
500 **/
501 template name(T) {
502     alias name = fullyQualifiedName!T;
503 }
504 
505 RuntimeReference withDefault(T)(RuntimeReference reference, T defaults) {
506     import aermicioi.aedi.factory.generic_factory : genericFactory, ValueInstanceFactory;
507     auto factory = genericFactory!T(null);
508 
509     factory.setInstanceFactory(new ValueInstanceFactory!T(defaults));
510     return reference.alternate(factory.anonymous);
511 }
512 
513 auto transformToReference(string reference, string symbol) {
514     string delegate (string) toTypeGen = (s) => "toType!(" ~ s ~ ")";
515     string delegate (string, string) typeEnforcedRefGen = (t, s) => "typeEnforcedRef!(" ~ toTypeGen(t) ~ ")(" ~ s ~ ")";
516     string delegate (string) identifierGen = (s) => "__traits(identifier, " ~ s ~ ")";
517     string delegate (string) lrefGen = (s) => s ~ ".lref";
518     string delegate (string) typeLrefGen = (s) => "lref!(" ~ s ~ ")";
519     string delegate (string, string) alternateGen = (f, s) => f ~ ".alternate(" ~ s ~ ")";
520     return "
521         import aermicioi.aedi.util.traits : toType;
522         static if (is(typeof(" ~ identifierGen(symbol) ~ "))) {
523             " ~ reference ~ " = " ~ alternateGen(typeEnforcedRefGen(symbol, lrefGen(identifierGen(symbol))), typeEnforcedRefGen(symbol, typeLrefGen(toTypeGen(symbol)))) ~ ";
524         }
525 
526         if (" ~ reference ~ " is null) {
527             " ~ reference ~ " = " ~ typeLrefGen(toTypeGen(symbol)) ~ ";
528         }
529 
530         static if (is(typeof(((" ~ symbol ~ " arg) => arg[0])()))) {
531             import aermicioi.aedi.factory.reference : withDefault;
532             " ~ reference ~ " = " ~ reference ~ ".withDefault(((" ~ symbol ~ " arg) => arg[0])());
533         }
534     ";
535 }
536 
537 auto makeFunctionParameterReferences(alias FunctionType, alias transformer = transformToReference)() {
538     static if (is(FunctionTypeOf!FunctionType params == __parameters)) {
539         import std.meta : Repeat;
540         import std.conv : to;
541         import aermicioi.aedi.util.typecons : tuple;
542         import aermicioi.aedi.util.traits : toType;
543 
544         Repeat!(params.length, RuntimeReference) references;
545 
546         static foreach (index, reference; references) {{
547             mixin(transformer("references[" ~ index.to!string ~ "]", "params[" ~ index.to!string ~ ".." ~ (index + 1).to!string ~ "]"));
548         }}
549 
550         return tuple(references);
551     }
552 }