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