1 /**
2 License:
3 	Boost Software License - Version 1.0 - August 17th, 2003
4 
5 	Permission is hereby granted, free of charge, to any person or organization
6 	obtaining a copy of the software and accompanying documentation covered by
7 	this license (the "Software") to use, reproduce, display, distribute,
8 	execute, and transmit the Software, and to prepare derivative works of the
9 	Software, and to permit third-parties to whom the Software is furnished to
10 	do so, all subject to the following:
11 	
12 	The copyright notices in the Software and this entire statement, including
13 	the above license grant, this restriction and the following disclaimer,
14 	must be included in all copies of the Software, in whole or in part, and
15 	all derivative works of the Software, unless such copies or derivative
16 	works are solely in the form of machine-executable object code generated by
17 	a source language processor.
18 	
19 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 	DEALINGS IN THE SOFTWARE.
26 
27 Authors:
28 	aermicioi
29 **/
30 module aermicioi.aedi.container.type_based_container;
31 
32 import aermicioi.aedi.container.container;
33 import aermicioi.aedi.storage.alias_aware;
34 import aermicioi.aedi.storage.decorator;
35 import aermicioi.aedi.storage.storage;
36 import aermicioi.aedi.storage.object_storage;
37 import aermicioi.aedi.exception.not_found_exception;
38 import aermicioi.aedi.factory.factory;
39 import aermicioi.util.traits;
40 
41 import std.algorithm;
42 import std.array;
43 import std.range;
44 import std.typecons;
45 import std.traits;
46 import std.meta;
47 import std.container.rbtree;
48 
49 /**
50 A decorating container that can provide for requested
51 base class or interface, one implementor of those. 
52 This decorated will inherit following interfaces if
53 and only if T also implements them:
54     $(OL
55         $(LI AliasAware!string)
56         $(LI FactoryLocator!ObjectFactory)
57     )
58 Decorated container must implement following interfaces:
59     $(OL
60         $(LI Container)
61         $(LI Storage!(ObjectFactory, string))
62         $(LI MutableDecorator!T)
63         $(LI Decorator!Container)
64     )
65 
66 Params:
67     T = The decorated that switchable decorated will decorate.
68 **/
69 template TypeBasedContainer(T) {
70     
71     /**
72     Set which the type based container will decorate for T container.
73     **/
74     alias InheritanceSet =
75         NoDuplicates!( 
76             Container,
77             Storage!(ObjectFactory, string),
78             MutableDecorator!T,
79             Decorator!Container,
80             Filter!(
81                 templateOr!(
82                     partialSuffixed!(
83                         isDerived,
84                         AliasAware!string
85                     ),
86                     partialSuffixed!(
87                         isDerived,
88                         FactoryLocator!ObjectFactory
89                     )
90                 ),
91                 InterfacesTuple!T   
92             )
93         );
94         
95     class TypeBasedContainer : InheritanceSet {
96         
97         private {
98             T decorated_;
99             
100             ObjectStorage!(RedBlackTree!(string), string) candidates; 
101         }
102         
103         public {
104             this() {
105                 this.candidates = new ObjectStorage!(RedBlackTree!string, string);
106             }
107             
108             @property {
109             	TypeBasedContainer decorated(T decorated) @safe nothrow {
110             		this.decorated_ = decorated;
111             	
112             		return this;
113             	}
114             	
115             	T decorated() @safe nothrow {
116             		return this.decorated_;
117             	}
118             }
119             
120              /**
121             Sets up the internal state of container.
122             
123             Sets up the internal state of container (Ex, for singleton container it will spawn all objects that locator contains).
124             **/
125             TypeBasedContainer instantiate() {
126                 this.decorated.instantiate();
127                 
128                 return this;
129             }
130             
131             /**
132     		Save an object factory in TypeBasedContainer by identity.
133     		
134     		Save an object factory in TypeBasedContainer by identity.
135     		The object factory, and subsequent objects are registered as candidates for use for
136     		any interface or base class they implement, when no suitable solution
137     		is found in decorated container.
138     		
139     		Params:
140     			identity = identity of object factory in TypeBasedContainer.
141     			factory = object factory which is to be saved in TypeBasedContainer.
142     			
143     		Return:
144     			TypeBasedContainer
145     		**/
146             TypeBasedContainer set(ObjectFactory factory, string identity) {
147                 decorated.set(factory, identity);
148                 
149                 ClassInfo info = cast(ClassInfo) factory.type;
150                 if (info !is null) {
151                     info.crawlClassInfo(
152                         delegate (TypeInfo_Class crawled) {
153                             if (!this.candidates.has(crawled.toString())) {
154                                 this.candidates.set(new RedBlackTree!string, crawled.toString());
155                             }
156                             
157                             this.candidates.get(crawled.toString()).insert(identity);
158                         }
159                     );
160                 }
161                 
162                 return this;
163             }
164             
165             /**
166             Remove an object from TypeBasedContainer with identity.
167             
168             Remove an object from TypeBasedContainer with identity. 
169             It will remove the candidate from any interface or base class it
170             does implement.
171             
172             Params:
173             	identity = the identity of object to be removed.
174             	
175         	Return:
176         		TypeBasedContainer
177             **/
178             TypeBasedContainer remove(string identity) {
179                 decorated.remove(identity);
180                 
181                 foreach (pair; this.candidates.contents.byKeyValue.array) {
182                     pair.value.removeKey(identity);
183                     
184                     if (pair.value.empty) {
185                         this.candidates.remove(pair.key);
186                     }
187                 }
188                 
189                 return this;
190             }
191             
192             /**
193     		Get an object that is associated with identity.
194     		
195     		Get an object that is associated with identity.
196     		If no object associated by identity in decorated container
197     		is found, a search for a solution in list of candidates is
198     		done, and if found, first candidate is used as substitution
199     		for serving the requested object.
200     		
201     		Params:
202     			identity = object identity in decorated.
203     			
204     		Throws:
205     			NotFoundException in case if no object was found.
206     		
207     		Returns:
208     			Object if it is available.
209     		**/
210             Object get(string identity) {
211                 if (this.decorated.has(identity)) {
212                     return this.decorated.get(identity);
213                 }
214                 
215                 if (this.candidates.has(identity)) {
216                     return
217                     this.decorated.get( 
218                         this.candidates.get(identity).front
219                     );
220                 }
221                 
222                 throw new NotFoundException("Object with id " ~ identity ~ " not found.");
223             }
224             
225             /**
226             Check if an object is present in TypeBasedContainer by identity.
227             
228             Check if an object is present in TypeBasedContainer by identity.
229             If no object is available in decorated container, a candidate
230             is searched through list of candidates, and if found, true
231             is returned.
232             
233             Params:
234             	identity = identity of object.
235             	
236         	Returns:
237         		bool true if an object by identity is present in TypeBasedContainer.
238             **/
239             bool has(in string identity) inout {
240                 if (this.decorated_.has(identity)) {
241                     return true;
242                 }
243                 
244                 return this.candidates.has(identity);
245             }
246             
247             static if (is(T : AliasAware!string)) {
248                 /**
249                 Alias a key to an alias_.
250                         
251                 Params:
252                 	identity = the originial identity which is to be aliased.
253                 	alias_ = the alias of identity.
254                 	
255         		Returns:
256         			this
257                 **/
258                 TypeBasedContainer link(string identity, string alias_) {
259                     this.decorated.link(identity, alias_);
260                     
261                     return this;
262                 }
263                 
264                 /**
265                 Removes alias.
266                 
267                 Params:
268                 	alias_ = alias to remove.
269         
270                 Returns:
271                     this
272                 	
273                 **/
274                 TypeBasedContainer unlink(string alias_) {
275                     this.decorated.unlink(alias_);
276                     
277                     return this;
278                 }
279                 
280                 /**
281                 Resolve an alias to original identity, if possible.
282                 
283                 Params:
284                 	alias_ = alias of original identity
285                 
286                 Returns:
287                 	Type the last identity in alias chain.
288                 
289                 **/
290                 const(string) resolve(in string alias_) const {
291                     return this.decorated_.resolve(alias_);
292                 }
293             }
294             
295             static if (is(T : FactoryLocator!ObjectFactory)) {
296                 /**
297                 Get factory for constructed data identified by identity.
298                 
299                 Get factory for constructed data identified by identity.
300                 Params:
301                 	identity = the identity of data that factory constructs.
302                 
303                 Throws:
304                 	NotFoundException when factory for it is not found.
305                 
306                 Returns:
307                 	ObjectFactory the factory for constructed data.
308                 **/
309                 ObjectFactory getFactory(string identity) {
310                     return this.decorated.getFactory(identity);
311                 }
312                 
313                 /**
314                 Get all factories available in decorated.
315                 
316                 Get all factories available in decorated.
317                 
318                 Returns:
319                 	InputRange!(Tuple!(ObjectFactory, string)) a tuple of factory => identity.
320                 **/
321                 InputRange!(Tuple!(ObjectFactory, string)) getFactories() {
322                     return this.decorated.getFactories();
323                 }
324             }
325         }
326         
327     }
328 }
329 
330 private {
331     void crawlClassInfo(TypeInfo_Class class_, void delegate (TypeInfo_Class) dg) {
332         dg(class_);
333         
334         foreach (iface; class_.interfaces) {
335             crawlClassInfo(iface.classinfo, dg);
336         }
337         
338         if (class_.base !is null) {
339             crawlClassInfo(class_.base, dg);
340         }
341     }
342     
343     
344 }