1 /**
2 Contains factories that are used for decorational purposes like tagging with additional
3  information, or wrapping result of a factory in some container.
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.decorating_factory;
34 
35 import aermicioi.aedi.factory.factory;
36 import aermicioi.aedi.storage.locator;
37 import aermicioi.aedi.factory.generic_factory;
38 import aermicioi.aedi.storage.decorator;
39 import aermicioi.aedi.exception.di_exception;
40 
41 alias ObjectFactoryDecorator = Decorator!ObjectFactory;
42 
43 /**
44 A base class for generic factory decorators that channels calls
45 to decorated generic factory.
46 **/
47 abstract class DecoratableGenericFactory(T) : GenericFactory!T, MutableDecorator!(GenericFactory!T) {
48     
49     private {
50         Locator!() locator_;
51         GenericFactory!T decorated_;
52     }
53     
54     public {
55         @property {
56         	
57 			/**
58 				Set locator
59 				
60 				Params: 
61 					locator = locator used to search for created object dependencies
62 				Returns:
63 					typeof(this)
64 			**/
65         	DecoratableGenericFactory!T locator(Locator!() locator) @safe nothrow {
66         		this.locator_ = locator;
67         	
68         		return this;
69         	}
70         	
71 			/**
72 				Get locator
73 				
74 				Returns:
75 					Locator!()
76 			**/
77         	Locator!() locator() {
78         		return this.locator_;
79         	}
80         	
81 			/**
82             Set the decorated object for decorator.
83             
84             Params:
85                 decorated = decorated data
86             
87             Returns:
88             	this
89             **/
90         	DecoratableGenericFactory!T decorated(GenericFactory!T decorated) @safe nothrow {
91         		this.decorated_ = decorated;
92         	
93         		return this;
94         	}
95         	
96 			/**
97             Get the decorated object.
98             
99             Returns:
100             	GenericFactory!T decorated object
101             **/
102         	GenericFactory!T decorated() @safe nothrow {
103         		return this.decorated_;
104         	}
105         	
106 			/**
107             Sets the constructor of new object.
108             
109             Params:
110             	factory = a factory of objects of type T.
111         	
112         	Returns:
113     			The GenericFactoryInstance
114             **/
115         	GenericFactory!T setInstanceFactory(InstanceFactory!T factory) {
116         	    this.decorated.setInstanceFactory(factory);
117         	    
118         	    return this;
119         	}
120         }
121         
122 		/**
123 		Instantiates something of type T.
124 		
125 		Returns:
126 			T instantiated data of type T.
127 		**/
128         T factory() {
129             return this.decorated.factory();
130         }
131         
132 		/**
133 		Get the type info of T that is created.
134 		
135 		Returns:
136 			TypeInfo object of created object.
137 		**/
138         TypeInfo type() {
139             return this.decorated.type();
140         }
141         
142 		/**
143         Adds an configurer to the GenericFactory.
144         
145         Params:
146         	configurer = a configurer that will be invoked after factory of an object.
147         	
148     	Returns:
149     		The GenericFactoryInstance
150         **/
151         GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) {
152             this.decorated.addPropertyConfigurer(configurer);
153             
154             return this;
155         }
156     }
157 }
158 
159 /**
160 An object that can be tagged with some information.
161 **/
162 interface Taggable(T) {
163     import std.range.interfaces : InputRange;
164     public {
165         
166         /**
167         Tag object with some information
168         
169         Params:
170         	tag = information that object should be tagged with.
171         
172         Returns:
173         	this
174         **/
175         Taggable!T tag(T tag);
176         
177         /**
178         Remove tagged information from object.
179         
180         Params:
181         	tag = tagged information that should be removed
182         
183         Returns:
184         	this
185         **/
186         Taggable!T untag(T tag);
187         
188         /**
189         Get all tagged information from this object.
190         
191         Returns:
192         	T[] a list of tags.
193         **/
194         T[] tags();
195     }
196 }
197 
198 /**
199 Decorates a factory with tagging functionality.
200 **/
201 class TaggableFactoryDecorator(T, Z) : Factory!T, Taggable!Z, Decorator!(Factory!T) {
202     
203     private {
204         Factory!T decorated_;
205         
206         Z[] tags_;
207     }
208     
209     public {
210         @property {
211 
212 			/**
213             Set the decorated object for decorator.
214             
215             Params:
216                 decorated = decorated data
217             
218             Returns:
219             	this
220             **/
221         	TaggableFactoryDecorator!(T, Z) decorated(Factory!T decorated) @safe nothrow {
222             	this.decorated_ = decorated;
223             
224             	return this;
225             }
226             
227 			/**
228             Get the decorated object.
229             
230             Returns:
231             	GenericFactory!T decorated object
232             **/
233             Factory!T decorated() @safe nothrow {
234             	return this.decorated_;
235             }
236             
237 			/**
238 				Set tags
239 				
240 				Params: 
241 					tags = set all tags for this factory
242 				Returns:
243 					typeof(this)
244 			**/
245             TaggableFactoryDecorator tags(Z[] tags) @safe nothrow {
246             	this.tags_ = tags;
247             
248             	return this;
249             }
250             
251 			/**
252 			Get all tagged information from this object.
253 			
254 			Returns:
255 				T[] a list of tags.
256 			**/
257             Z[] tags() @safe nothrow {
258             	return this.tags_;
259             }
260             
261 			/**
262 				Set locator
263 				
264 				Params: 
265 					locator = locator used to search for created object dependencies
266 				Returns:
267 					typeof(this)
268 			**/
269             TaggableFactoryDecorator locator(Locator!() locator) {
270             	this.decorated.locator = locator;
271             
272             	return this;
273             }
274             
275 			/**
276 			Get the type info of T that is created.
277 			
278 			Returns:
279 				TypeInfo object of created object.
280 			**/
281             TypeInfo type() {
282                 return this.decorated.type;
283             }
284         }
285         
286 		/**
287         Tag object with some information
288         
289         Params:
290         	tag = information that object should be tagged with.
291         
292         Returns:
293         	this
294         **/
295         TaggableFactoryDecorator tag(Z tag) {
296             this.tags_ ~= tag;
297             
298             return this;
299         }
300         
301 		/**
302         Remove tagged information from object.
303         
304         Params:
305         	tag = tagged information that should be removed
306         
307         Returns:
308         	this
309         **/
310         TaggableFactoryDecorator untag(Z tag) {
311             import std.algorithm : filter;
312             import std.array : array;
313             
314             this.tags_ = this.tags_.filter!(t => t != tag).array;
315             
316             return this;
317         }
318         
319 		/**
320 		Instantiates something of type T.
321 		
322 		Returns:
323 			T instantiated data of type T.
324 		**/
325         T factory() {
326             return this.decorated.factory;
327         }
328     }
329 }
330 
331 /**
332 Interface for object that can provide some location in 
333 code/file that is associated with some kind of registration event
334 **/
335 interface RegistrationLocation {
336     public {
337         @property {
338         	
339         	/**
340         	Get file in which registration occurred.
341         	
342         	Returns:
343             	string file in which a registration occured
344         	**/
345         	string file() @safe nothrow;
346         	
347         	/**
348         	Get line in file on which registration occurred.
349         	
350         	Returns:
351             	size_t line in file on which registration occurred.
352         	**/
353         	size_t line() @safe nothrow;
354         }
355     }
356 }
357 
358 /**
359 A decorating factory, that adds component registration information
360 when decorated factory threws some kind of exception.
361 **/
362 class RegistrationAwareDecoratingFactory(T) : Factory!T, MutableDecorator!(Factory!T), RegistrationLocation {
363     
364     private {
365         
366         Factory!T decorated_;
367         
368         string file_;
369         size_t line_;
370     }
371     
372     public {
373         
374         @property {
375             
376             /**
377         	Set file in which registration occurred.
378         	
379         	Params:
380             	file = file in which a registration occured
381         	
382         	Returns:
383             	this
384         	**/
385         	RegistrationAwareDecoratingFactory!T file(string file) @safe nothrow {
386         		this.file_ = file;
387         	
388         		return this;
389         	}
390         	
391         	/**
392         	Get file in which registration occurred.
393         	
394         	Returns:
395             	string file in which a registration occured
396         	**/
397         	string file() @safe nothrow {
398         		return this.file_;
399         	}
400         	
401         	/**
402         	Set line in file on which registration occurred.
403         	
404         	Params:
405             	line = line in file on which registration occurred.
406         	
407         	Returns:
408             	this
409         	**/
410         	RegistrationAwareDecoratingFactory!T line(size_t line) @safe nothrow {
411         		this.line_ = line;
412         	
413         		return this;
414         	}
415         	
416         	/**
417         	Get line in file on which registration occurred.
418         	
419         	Returns:
420             	size_t line in file on which registration occurred.
421         	**/
422         	size_t line() @safe nothrow {
423         		return this.line_;
424         	}
425         	
426         	/**
427             Set the decorated factory for decorator.
428             
429             Params:
430                 decorated = decorated factory
431             
432             Returns:
433             	this
434             **/
435         	RegistrationAwareDecoratingFactory!T decorated(Factory!T decorated) @safe nothrow {
436         		this.decorated_ = decorated;
437         	
438         		return this;
439         	}
440         	
441         	/**
442             Get the decorated object.
443             
444             Returns:
445             	Factory!T decorated factory
446             **/
447         	Factory!T decorated() @safe nothrow {
448         		return this.decorated_;
449         	}
450         	
451         	/**
452     		Get the type info of T that is created.
453     		
454     		Returns:
455     			TypeInfo object of created object.
456     		**/
457         	TypeInfo type() {
458         	    return this.decorated.type;
459         	}
460         	
461         	/**
462     		Set a locator for depedencies.
463     		
464     		Params:
465     			locator = the locator that is set to oject.
466     		
467     		Returns:
468     			this
469     		**/
470         	RegistrationAwareDecoratingFactory!T locator(Locator!() locator) {
471         	    this.decorated.locator = locator;
472         	    
473         	    return this;
474         	}
475         }
476         
477         /**
478 		Instantiates component of type T.
479 		
480 		Instantiates component of type T. In case of some thrown exception, 
481 		it will chain any thrown exception with an exception that has information
482         about location where component was registered.
483 		
484 		Throws:
485     		AediException when another exception occurred during construction
486 		
487 		Returns:
488 			T instantiated data of type T.
489 		**/
490         T factory() {
491             
492             try {
493                 return this.decorated.factory;
494             } catch (Exception e) {
495                 import std.conv;
496                 throw new AediException(
497                     "An error occured during instantiation of component registered in file: " ~ 
498                     this.file ~ 
499                     " at line " ~
500                     this.line.to!string,
501                     this.file,
502                     this.line,
503                     e
504                 );
505             }
506         }
507     }
508 }