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.allocator_aware : AllocatorAwareMixin, AllocatorAwareDecoratorMixin;
37 import aermicioi.aedi.storage.locator;
38 import aermicioi.aedi.factory.generic_factory;
39 import aermicioi.aedi.storage.decorator;
40 import aermicioi.aedi.storage.locator_aware;
41 import aermicioi.aedi.exception.invalid_cast_exception;
42 import aermicioi.aedi.exception.di_exception;
43 import aermicioi.aedi.util.typecons : Subscribable, Optional, optional;
44 
45 alias ObjectFactoryDecorator = Decorator!ObjectFactory;
46 
47 /**
48 A base class for generic factory decorators that channels calls
49 to decorated generic factory.
50 **/
51 @safe abstract class DecoratableGenericFactory(T) : GenericFactory!T, MutableDecorator!(GenericFactory!T) {
52 
53 	mixin AllocatorAwareDecoratorMixin!(typeof(this));
54 	mixin DestructDecoratorMixin!(typeof(this));
55 	mixin LocatorAwareDecoratorMixin!(typeof(this));
56 	mixin MutableDecoratorMixin!(GenericFactory!T);
57 	mixin InstanceFactoryAwareDecoratorMixin!T;
58 	mixin InstanceDestructorAwareDecoratorMixin!T;
59 	mixin PropertyConfigurersAwareDecoratorMixin!T;
60 
61     public {
62 		/**
63 		Instantiates something of type T.
64 
65 		Returns:
66 			T instantiated component of type T.
67 		**/
68         T factory() @safe {
69             return this.decorated.factory();
70         }
71 
72 		/**
73 		Get the type info of T that is created.
74 
75 		Returns:
76 			TypeInfo object of created object.
77 		**/
78         TypeInfo type() @safe nothrow const {
79             return this.decorated.type();
80         }
81     }
82 }
83 
84 /**
85 An object that can be tagged with some information.
86 **/
87 @safe interface Taggable(T) {
88     import std.range.interfaces : InputRange;
89     public {
90 
91         /**
92         Tag object with some information
93 
94         Params:
95         	tag = information that object should be tagged with.
96 
97         Returns:
98         	this
99         **/
100         Taggable!T tag(T tag);
101 
102         /**
103         Remove tagged information from object.
104 
105         Params:
106         	tag = tagged information that should be removed
107 
108         Returns:
109         	this
110         **/
111         Taggable!T untag(T tag);
112 
113         /**
114         Get all tagged information from this object.
115 
116         Returns:
117         	T[] a list of tags.
118         **/
119         T[] tags();
120     }
121 }
122 
123 /**
124 Decorates a factory with tagging functionality.
125 **/
126 @safe class TaggableFactoryDecorator(T, Z) : Factory!T, Taggable!Z, Decorator!(Factory!T) {
127 
128     mixin AllocatorAwareDecoratorMixin!(typeof(this));
129     mixin DestructDecoratorMixin!(typeof(this));
130 
131     private {
132         Z[] tags_;
133     }
134 
135     public {
136         @property {
137 
138 			mixin MutableDecoratorMixin!(Factory!T);
139 
140 			/**
141 				Set tags
142 
143 				Params:
144 					tags = set all tags for this factory
145 				Returns:
146 					typeof(this)
147 			**/
148             TaggableFactoryDecorator tags(Z[] tags) @safe nothrow {
149             	this.tags_ = tags;
150 
151             	return this;
152             }
153 
154 			/**
155 			Get all tagged information from this object.
156 
157 			Returns:
158 				T[] a list of tags.
159 			**/
160             Z[] tags() @safe nothrow {
161             	return this.tags_;
162             }
163 
164 			/**
165 				Set locator
166 
167 				Params:
168 					locator = locator used to search for created object dependencies
169 				Returns:
170 					typeof(this)
171 			**/
172             TaggableFactoryDecorator locator(Locator!() locator) {
173             	this.decorated.locator = locator;
174 
175             	return this;
176             }
177 
178 			/**
179 			Get the type info of T that is created.
180 
181 			Returns:
182 				TypeInfo object of created object.
183 			**/
184             TypeInfo type() @safe nothrow const {
185                 return this.decorated.type;
186             }
187         }
188 
189 		/**
190         Tag object with some information
191 
192         Params:
193         	tag = information that object should be tagged with.
194 
195         Returns:
196         	this
197         **/
198         TaggableFactoryDecorator tag(Z tag) {
199             this.tags_ ~= tag;
200 
201             return this;
202         }
203 
204 		/**
205         Remove tagged information from object.
206 
207         Params:
208         	tag = tagged information that should be removed
209 
210         Returns:
211         	this
212         **/
213         TaggableFactoryDecorator untag(Z tag) {
214             import std.algorithm : filter;
215             import std.array : array;
216 
217             this.tags_ = this.tags_.filter!(t => t != tag).array;
218 
219             return this;
220         }
221 
222 		/**
223 		Instantiates something of type T.
224 
225 		Returns:
226 			T instantiated component of type T.
227 		**/
228         T factory() @safe {
229             return this.decorated.factory;
230         }
231     }
232 }
233 
234 /**
235 Interface for object that can provide some location in
236 code/file that is associated with some kind of registration event
237 **/
238 @safe interface RegistrationLocation {
239     public {
240         @property {
241 
242         	/**
243         	Get file in which registration occurred.
244 
245         	Returns:
246             	string file in which a registration occured
247         	**/
248         	string file() @safe nothrow;
249 
250         	/**
251         	Get line in file on which registration occurred.
252 
253         	Returns:
254             	size_t line in file on which registration occurred.
255         	**/
256         	size_t line() @safe nothrow;
257         }
258     }
259 }
260 
261 /**
262 A decorating factory, that adds component registration information
263 when decorated factory threws some kind of exception.
264 **/
265 @safe class RegistrationAwareDecoratingFactory(T) : Factory!T, MutableDecorator!(Factory!T), RegistrationLocation {
266 
267 	mixin AllocatorAwareDecoratorMixin!(typeof(this));
268 
269 	private {
270 
271         string file_;
272         size_t line_;
273     }
274 
275     public {
276 
277         @property {
278 
279             /**
280         	Set file in which registration occurred.
281 
282         	Params:
283             	file = file in which a registration occured
284 
285         	Returns:
286             	this
287         	**/
288         	RegistrationAwareDecoratingFactory!T file(string file) @safe nothrow {
289         		this.file_ = file;
290 
291         		return this;
292         	}
293 
294         	/**
295         	Get file in which registration occurred.
296 
297         	Returns:
298             	string file in which a registration occured
299         	**/
300         	string file() @safe nothrow {
301         		return this.file_;
302         	}
303 
304         	/**
305         	Set line in file on which registration occurred.
306 
307         	Params:
308             	line = line in file on which registration occurred.
309 
310         	Returns:
311             	this
312         	**/
313         	RegistrationAwareDecoratingFactory!T line(size_t line) @safe nothrow {
314         		this.line_ = line;
315 
316         		return this;
317         	}
318 
319         	/**
320         	Get line in file on which registration occurred.
321 
322         	Returns:
323             	size_t line in file on which registration occurred.
324         	**/
325         	size_t line() @safe nothrow {
326         		return this.line_;
327         	}
328 
329         	mixin MutableDecoratorMixin!(Factory!T);
330 
331         	/**
332     		Get the type info of T that is created.
333 
334     		Returns:
335     			TypeInfo object of created object.
336     		**/
337         	TypeInfo type() @safe nothrow const {
338         	    return this.decorated.type;
339         	}
340 
341         	/**
342     		Set a locator for depedencies.
343 
344     		Params:
345     			locator = the locator that is set to oject.
346 
347     		Returns:
348     			this
349     		**/
350         	RegistrationAwareDecoratingFactory!T locator(Locator!() locator) {
351         	    this.decorated.locator = locator;
352 
353         	    return this;
354         	}
355         }
356 
357         /**
358 		Instantiates component of type T.
359 
360 		Instantiates component of type T. In case of some thrown exception,
361 		it will chain any thrown exception with an exception that has information
362         about location where component was registered.
363 
364 		Throws:
365     		AediException when another exception occurred during construction
366 
367 		Returns:
368 			T instantiated component of type T.
369 		**/
370         T factory() @safe {
371 
372             try {
373                 return this.decorated.factory;
374             } catch (Exception e) {
375                 import std.conv : to, text;
376                 throw new AediException(
377 					text(
378 						"An error occured during instantiation of component registered in file: ",
379 						this.file,
380 						":",
381 						this.line
382 					),
383 					null,
384                     this.file,
385                     this.line,
386                     e
387                 );
388             }
389         }
390 
391 		/**
392 		Destructs a component of type T.
393 
394 		Params:
395 			component = component that is to be destroyed.
396 		**/
397 		void destruct(ref T component) @safe {
398 
399 			try {
400 
401 				this.decorated.destruct(component);
402 			} catch (Exception e) {
403 				import std.conv : to, text;
404 
405 				throw new AediException(
406 					text(
407 						"An error occured during destruction of component registered in file: ",
408 						this.file,
409 						":",
410 						this.line
411 					),
412 					null,
413                     this.file,
414                     this.line,
415                     e
416                 );
417 			}
418 		}
419     }
420 }