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