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_property_reader.helper.help_decorating_container;
31 
32 import aermicioi.aedi_property_reader.helper.help_decorating_information_provider;
33 import aermicioi.aedi_property_reader.helper.help_decorating_exception;
34 import aermicioi.aedi_property_reader.helper.help_needing_exception;
35 import aermicioi.aedi.exception.not_found_exception;
36 import aermicioi.aedi.exception.di_exception;
37 import aermicioi.aedi.storage.storage;
38 import aermicioi.aedi.storage.locator;
39 import aermicioi.aedi.storage.alias_aware;
40 import aermicioi.aedi.storage.decorator;
41 import aermicioi.aedi.container.container;
42 import aermicioi.aedi.factory.factory;
43 import std.range.interfaces;
44 import std.typecons;
45 import std.algorithm;
46 
47 /**
48 An interface for class that can toggle on or off help information.
49 **/
50 interface HelpInformationToggable {
51     @property {
52         /**
53         Set on or off help information displaying.
54         
55         Params: 
56             helpEnabled = whether help info is enabled or not
57             
58         Returns:
59             typeof(this)
60         **/
61     	HelpInformationToggable helpEnabled(bool helpEnabled) @safe nothrow;
62     }
63 }
64 
65 /**
66 Templated help decorating container.
67 
68 Templated help decorating container. This decorated will
69 decorate a container, and add help information logic 
70 to it. Depending if help information is on or off, it
71 will display or not help information on stdout for end user. 
72 This decorated will inherit following interfaces only and only if the 
73 T also implements them:
74     $(UL
75         $(LI $(D_INLINECODE Storage!(ObjectFactory, string)))
76         $(LI $(D_INLINECODE Container))
77         $(LI $(D_INLINECODE AliasAware!string))
78         $(LI $(D_INLINECODE FactoryLocator!ObjectFactory))
79     )
80 
81 Following interface is a must for decorated container:
82 $(UL
83     $(LI $(D_INLINECODE Locator!())),
84 )
85 
86 Params:
87     T = The decorated that switchable decorated will decorate.
88    
89 **/
90 template HelpDecoratingContainer(T) {
91     import std.meta;
92     import std.traits;
93     import aermicioi.util.traits;
94     
95     /**
96     Set which the switchable decorated will decorate for T. By default
97     Locator!() and Switchable is included.
98     **/
99     alias InheritanceSet = NoDuplicates!(Filter!(
100         templateOr!(
101             partialSuffixed!(
102                 isDerived,
103                 Storage!(ObjectFactory, string)
104             ),
105             partialSuffixed!(
106                 isDerived,
107                 AliasAware!string
108             ),
109             partialSuffixed!(
110                 isDerived,
111                 FactoryLocator!ObjectFactory
112             ),
113             partialSuffixed!(
114                 isDerived,
115                 Container
116             )
117         ),
118         InterfacesTuple!T),
119         Locator!(),
120         MutableDecorator!T,
121         HelpInformationToggable,
122     );
123 
124     /**
125     Templated help decorating container.
126     **/
127     class HelpDecoratingContainer : InheritanceSet {
128         private {
129             T decorated_;
130             HelpInformationProvider[] providers_;
131             
132             bool helpEnabled_;
133         }
134         
135         public {
136             
137             /**
138             Set on or off help information displaying.
139             
140             Params: 
141                 helpEnabled = whether help info is enabled or not
142                 
143             Returns:
144                 typeof(this)
145             **/
146             HelpDecoratingContainer helpEnabled(bool helpEnabled) @safe nothrow {
147             	this.helpEnabled_ = helpEnabled;
148             
149             	return this;
150             }
151             
152             /**
153             Get helpEnabled
154             
155             Returns:
156                 bool
157             **/
158             bool helpEnabled() @safe nothrow {
159             	return this.helpEnabled_;
160             }
161             
162             /**
163             Set the decorated container
164             
165             Params:
166                 decorated = container to be decorated
167                 
168             Returns:
169                 HelpDecoratingContainer!T decorating container.
170             **/
171             HelpDecoratingContainer!T decorated(T decorated) @safe nothrow {
172             	this.decorated_ = decorated;
173             
174             	return this;
175             }
176             
177             
178             /**
179             Get the decorated container.
180             
181             Returns:
182             	inout(T) decorated container
183             **/
184             T decorated() @safe nothrow {
185             	return this.decorated_;
186             }
187             
188             /**
189             Add a help provider
190             
191             Params: 
192                 provider = help provider that supplies help text
193                 
194             Returns:
195                 typeof(this)
196             **/
197             HelpDecoratingContainer addHelpProvider(HelpInformationProvider provider) {
198             	this.providers_ ~= provider;
199             
200             	return this;
201             }
202             
203             /**
204             Remove a help provider from help decorating container
205             
206             Params: 
207                 key = index of provider to be removed
208                 
209             Returns:
210                 typeof(this)
211             **/
212             HelpDecoratingContainer removeHelpProvider(size_t key) {
213             	import std.algorithm;
214             	this.providers_ = this.providers_.remove(key);
215             
216             	return this;
217             }
218             
219             /**
220             Get help provider used by help decorating container
221             
222             Params: 
223                 key = index of fetched provider
224             Throws: 
225                 NotFoundException when no decorator is present
226             Returns:
227                 HelpInformationProvider
228             **/
229             HelpInformationProvider getHelpProvider(size_t key) {
230                 
231                 if (key >= this.providers_.length) {
232                     throw new NotFoundException("Help provider not found");
233                 }
234                 
235             	return this.providers_[key];
236             }
237             
238             static if (is(T : Container)) {
239                 
240                 /**
241                 Prepare decorated to be used.
242                 
243                 Prepare decorated to be used.
244 
245                 Returns:
246                 	HelpDecoratingContainer!T decorating container
247                 **/
248                 HelpDecoratingContainer instantiate() {
249                     try {
250                         decorated.instantiate();
251                     } catch (AediException e) {
252                         
253                         Throwable t = e;
254                         
255                         do {
256                             HelpDecoratingException signaler = cast(HelpDecoratingException) t;
257                             
258                             if (signaler !is null) {
259                                 if (this.helpEnabled) {
260                                     throw new HelpNeedingException(this.providers_, signaler.next);
261                                 }
262                             }
263                             
264                             t = t.next;
265                         } while (t !is null);
266                         
267                         throw e.next;
268                     }
269                     
270                     return this;
271                 }
272             }
273             
274             static if (is(T : Storage!(ObjectFactory, string))) {
275                 /**
276         		Set factory in decorated by identity.
277         		
278         		Params:
279         			identity = identity of factory.
280         			element = factory that is to be saved in decorated.
281         			
282         		Return:
283         			HelpDecoratingContainer!T decorating decorated.
284         		**/
285                 HelpDecoratingContainer!T set(ObjectFactory element, string identity) {
286                     decorated.set(element, identity);
287                     
288                     return this;
289                 }
290                 
291                 /**
292                 Remove factory from decorated with identity.
293                 
294                 Remove factory from decorated with identity. 
295                 
296                 Params:
297                 	identity = the identity of factory to be removed.
298                 	
299             	Return:
300             		HelpDecoratingContainer!T decorating decorated
301                 **/
302                 HelpDecoratingContainer!T remove(string identity) {
303                     decorated.remove(identity);
304                     
305                     return this;
306                 }
307             }
308             
309             static if (is(T : AliasAware!string)) {
310                 /**
311                 Alias identity to an alias_.
312                         
313                 Params:
314                 	identity = originial identity which is to be aliased.
315                 	alias_ = alias of identity.
316                 	
317         		Returns:
318         			HelpDecoratingContainer!T decorating decorated
319                 **/
320                 HelpDecoratingContainer!T link(string identity, string alias_) {
321                     decorated.link(identity, alias_);
322                     
323                     return this;
324                 }
325                 
326                 /**
327                 Removes alias.
328                 
329                 Params:
330                 	alias_ = alias to remove.
331         
332                 Returns:
333                     HelpDecoratingContainer!T decorating decorated
334                 **/
335                 HelpDecoratingContainer!T unlink(string alias_) {
336                     decorated.unlink(alias_);
337                     
338                     return this;
339                 }
340                 
341                 /**
342                 Resolve an alias to original identity, if possible.
343                 
344                 Params:
345                 	alias_ = alias of original identity
346                 
347                 Returns:
348                 	const(string) the last identity in alias chain if decorated is enabled, or alias_ when not.
349                 
350                 **/
351                 const(string) resolve(in string alias_) const {
352                     return decorated_.resolve(alias_);
353                 }
354             }
355             
356             static if (is(T : FactoryLocator!ObjectFactory)) {
357                 
358                 /**
359                 Get factory for constructed component identified by identity.
360                 
361                 Get factory for constructed component identified by identity.
362                 Params:
363                     identity = the identity of data that factory constructs.
364                 
365                 Throws:
366                     NotFoundException when factory for it is not found.
367                 
368                 Returns:
369                     T the factory for constructed data.
370                 **/
371                 ObjectFactory getFactory(string identity) {
372                     return this.decorated.getFactory(identity);
373                 }
374                 
375                 /**
376                 Get all factories available in container.
377                 
378                 Get all factories available in container.
379                 
380                 Returns:
381                     InputRange!(Tuple!(T, string)) a tuple of factory => identity.
382                 **/
383                 InputRange!(Tuple!(ObjectFactory, string)) getFactories() {
384                     return this.decorated.getFactories();
385                 }
386             }
387             
388             /**
389     		Get object that is associated with identity.
390     		
391     		Params:
392     			identity = the object identity.
393     			
394     		Throws:
395     			NotFoundException in case if the object wasn't found or decorated is not enabled.
396     		
397     		Returns:
398     			Object if it is available.
399     		**/
400             Object get(string identity) {
401                 
402                 try {
403                     
404                     return decorated.get(identity);
405                 } catch (AediException e) {
406                     
407                     Throwable t = e;
408                     
409                     do {
410                         HelpDecoratingException signaler = cast(HelpDecoratingException) t;
411                         
412                         if (signaler !is null) {
413                             if (this.helpEnabled) {
414                                 throw new HelpNeedingException(this.providers_, signaler.next);
415                             }
416                         }
417                         
418                         t = t.next;
419                     } while (t !is null);
420                     
421                     throw e.next;
422                 }
423             }
424             
425             /**
426             Check if object is present in HelpDecoratingContainer!T by key identity.
427             
428             Note:
429             	This check should be done for elements that locator actually contains, and
430             	not in chained locator (when locator is also a DelegatingLocator) for example.
431             Params:
432             	identity = identity of object.
433             	
434         	Returns:
435         		bool true if decorated is enabled and has object by identity.
436             **/
437             bool has(in string identity) inout {
438                 return decorated_.has(identity);
439             }
440         }
441     }
442 }