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.proxy_container;
31 
32 import aermicioi.aedi.container.container;
33 import aermicioi.aedi.storage.storage;
34 import aermicioi.aedi.storage.object_storage;
35 import aermicioi.aedi.factory.proxy_factory;
36 import aermicioi.aedi.exception.not_found_exception;
37 import aermicioi.aedi.storage.locator;
38 import aermicioi.aedi.storage.decorator;
39 import aermicioi.aedi.storage.alias_aware;
40 import aermicioi.aedi.factory.factory;
41 import std.range;
42 import std.meta;
43 import std.traits;
44 import aermicioi.aedi.util.traits;
45 
46 /**
47 TODO: Add description of what this is and why it was designed as such.
48 **/
49 @safe interface ProxyContainer : Container, Storage!(ProxyObjectFactory, string),
50     Decorator!(Locator!())
51 {
52 
53 }
54 
55 /**
56 Templated switchable container.
57 
58 Templated switchable container. This container will
59 decorate another container, and add switching logic
60 to it. Depending in which state (on/off) the switching
61 container is. It will instantiate if the container is on,
62 and not if container is in off mode. This container will
63 inherit following interfaces only and only if the
64 T also implements them:
65     $(OL
66         $(LI Storage!(ObjectFactory, string))
67         $(LI Container)
68         $(LI AliasAware!string)
69     )
70 
71 Params:
72     T = The container that switchable container will decorate.
73 
74 **/
75 template ProxyContainerImpl(T)
76 {
77     /**
78     Set which the switchable container will decorate for T. By default
79     Locator!() and Switchable is included.
80     **/
81     alias InheritanceSet =
82         NoDuplicates!(
83             Filter!(
84                 templateOr!(
85                     partialSuffixed!(
86                         isDerived,
87                         Storage!(ObjectFactory,
88                         string)
89                     ),
90                     partialSuffixed!(
91                         isDerived,
92                         AliasAware!string
93                     ),
94                     partialSuffixed!(
95                         isDerived,
96                         FactoryLocator!ObjectFactory
97                     )
98                 ),
99                 InterfacesTuple!T
100             ),
101             ProxyContainer,
102             Decorator!Container
103         );
104 
105     /**
106     Templated proxy container.
107     **/
108     @safe class ProxyContainerImpl : InheritanceSet
109     {
110         private
111         {
112             ObjectStorage!(ProxyObjectFactory, string) proxyFactories;
113             ObjectStorage!(Object, string) proxies;
114         }
115 
116         public
117         {
118 
119             /**
120              * Default constructor for ProxyContainerImpl
121             **/
122             this() {
123 
124                 this.proxyFactories = new ObjectStorage!(ProxyObjectFactory, string);
125                 this.proxies = new ObjectStorage!(Object, string);
126             }
127 
128             mixin MutableDecoratorMixin!T;
129 
130             /**
131             * Set object factory
132             *
133             * Params:
134             * 	factory = factory for a object that is to be managed by prototype container.
135             *   identity = identity by which a factory is identified
136             * Returns:
137             * 	typeof(this)
138             **/
139             ProxyContainerImpl set(ProxyObjectFactory factory, string identity)
140             {
141                 this.proxyFactories.set(factory, identity);
142 
143                 return this;
144             }
145 
146             static if (is(T : Container))
147             {
148 
149                 /**
150                 Prepare container to be used.
151 
152                 Prepare container to be used.
153 
154                 Returns:
155                 	ProxyContainer decorating container
156                 **/
157                 ProxyContainerImpl instantiate()
158                 {
159                     decorated.instantiate();
160 
161                     return this;
162                 }
163 
164                 /**
165                 Destruct all managed components.
166 
167                 Destruct all managed components. The method denotes the end of container lifetime, and therefore destruction of all managed components
168                 by it.
169                 **/
170                 ProxyContainerImpl terminate() {
171 
172                     foreach (pair; this.proxies.contents.byKeyValue) {
173                         this.proxyFactories.get(pair.key).destruct(pair.value);
174                     }
175 
176                     (() @trusted scope => this.proxies.contents.clear)();
177 
178                     return this;
179                 }
180             }
181 
182             static if (is(T : Storage!(ObjectFactory, string)))
183             {
184                 /**
185         		Set factory in container by identity.
186 
187         		Params:
188         			identity = identity of factory.
189         			element = factory that is to be saved in container.
190 
191         		Return:
192         			ProxyContainer decorating container.
193         		**/
194                 ProxyContainerImpl set(ObjectFactory element, string identity)
195                 {
196                     decorated.set(element, identity);
197 
198                     return this;
199                 }
200 
201                 /**
202                 Remove factory from container with identity.
203 
204                 Remove factory from container with identity.
205 
206                 Params:
207                 	identity = the identity of factory to be removed.
208 
209             	Return:
210             		ProxyContainer decorating container
211                 **/
212                 Storage!(ObjectFactory, string) remove(string identity)
213                 {
214                     if (this.proxies.has(identity)) {
215                         auto temporary = this.proxies.get(identity);
216                         this.proxyFactories.get(identity).destruct(temporary);
217                         this.proxies.remove(identity);
218                     }
219 
220                     this.decorated.remove(identity);
221                     this.proxyFactories.remove(identity);
222 
223                     return this;
224                 }
225 
226                 /**
227                 ditto
228                 **/
229                 Storage!(ProxyObjectFactory, string) remove(string identity)
230                 {
231                     (cast(Storage!(ObjectFactory, string)) this).remove(identity);
232 
233                     return this;
234                 }
235             }
236             else
237             {
238                 /**
239                 Remove factory from container with identity.
240 
241                 Remove factory from container with identity.
242 
243                 Params:
244                 	identity = the identity of factory to be removed.
245 
246             	Return:
247             		ProxyContainer decorating container
248                 **/
249                 ProxyContainerImpl remove(string identity)
250                 {
251                     if (this.proxies.has(identity)) {
252                         auto temporary = this.proxies.get(identity);
253                         this.proxyFactories.get(identity).destruct(temporary);
254                         this.proxies.remove(identity);
255                     }
256 
257                     this.proxyFactories.remove(identity);
258 
259                     return this;
260                 }
261             }
262 
263             static if (is(T : AliasAware!string))
264             {
265                 /**
266                 Alias identity to an alias_.
267 
268                 Params:
269                 	identity = originial identity which is to be aliased.
270                 	alias_ = alias of identity.
271 
272         		Returns:
273         			ProxyContainer decorating container
274                 **/
275                 ProxyContainerImpl link(string identity, string alias_)
276                 {
277                     this.decorated.link(identity, alias_);
278                     this.proxyFactories.link(identity, alias_);
279 
280                     return this;
281                 }
282 
283                 /**
284                 Removes alias.
285 
286                 Params:
287                 	alias_ = alias to remove.
288 
289                 Returns:
290                     ProxyContainer decorating container
291                 **/
292                 ProxyContainerImpl unlink(string alias_)
293                 {
294                     this.decorated.unlink(alias_);
295                     this.proxyFactories.unlink(alias_);
296 
297                     return this;
298                 }
299 
300                 /**
301                 Resolve an alias to original identity, if possible.
302 
303                 Params:
304                 	alias_ = alias of original identity
305 
306                 Returns:
307                 	const(string) the last identity in alias chain if container is enabled, or alias_ when not.
308 
309                 **/
310                 const(string) resolve(in string alias_) const
311                 {
312                     return this.proxyFactories.resolve(alias_);
313                 }
314             }
315 
316             static if (is(T : FactoryLocator!ObjectFactory))
317             {
318                 mixin FactoryLocatorMixin!(typeof(this));
319             }
320 
321             /**
322     		Get object that is associated with identity.
323 
324     		Params:
325     			identity = the object identity.
326 
327     		Throws:
328     			NotFoundException in case if the object wasn't found or container is not enabled.
329 
330     		Returns:
331     			Object if it is available.
332     		**/
333             Object get(string identity)
334             {
335                 this.proxies.set(proxyFactories.get(identity).factory(), identity);
336 
337                 return this.proxies.get(identity);
338             }
339 
340             /**
341             Check if object is present in ProxyContainer by key identity.
342 
343             Note:
344             	This check should be done for elements that locator actually contains, and
345             	not in chained locator (when locator is also a DelegatingLocator) for example.
346             Params:
347             	identity = identity of object.
348 
349         	Returns:
350         		bool true if container is enabled and has object by identity.
351             **/
352             bool has(in string identity) inout
353             {
354                 return proxyFactories.has(identity);
355             }
356         }
357     }
358 }