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.locator : LocatorMixin;
36 import aermicioi.aedi.storage.alias_aware;
37 import aermicioi.aedi.storage.storage;
38 import aermicioi.aedi.factory.factory;
39 import aermicioi.aedi.exception.not_found_exception;
40 import aermicioi.aedi.util.traits;
41 import aermicioi.aedi.util.typecons : Subscribable, SubscribableMixin, Optional, optional;
42 import std.meta;
43 import std.traits;
44 
45 import std.range.interfaces;
46 
47 /**
48 Denotes pointcuts at which events for instantiation will be run.
49 **/
50 enum ContainerInstantiationEventType
51 {
52 
53     /**
54     Event emitted before beggining of instantiation process
55     **/
56     pre,
57 
58     /**
59     Event emitted after end of instantiation process
60     **/
61     post,
62 }
63 
64 /**
65 Denotes pointcuts at which events for termination will be run.
66 **/
67 enum ContainerTerminationEventType
68 {
69     /**
70     Event emitted before termination of container started.
71     **/
72     pre,
73 
74     /**
75     Event emitted after termination of container ended.
76     **/
77     post
78 }
79 
80 /**
81 Denotes store and removal of component factories.
82 **/
83 enum ContainerFactoryEventType
84 {
85     /**
86     Event emitted before component is set in decorated container.
87     **/
88     set,
89 
90     /**
91     Event emitted after component is removed from decorated container.
92     **/
93     remove
94 }
95 
96 /**
97 Denotes pointcuts at which element is accessed.
98 **/
99 enum ContainerAccessEventType
100 {
101     /**
102     Event emitted before component is accessed in decorated container.
103     **/
104     pre,
105 
106     /**
107     Event emitted after component is accessed in decorated container.
108     **/
109     post
110 }
111 
112 /**
113 Denotes pointcuts at which events for checking for component will be run.
114 **/
115 enum ContainerCheckEventType
116 {
117     /**
118     Event emitted before component is checked in decorated container.
119     **/
120     pre,
121 
122     /**
123     Event emitted after component is checked in decorated container.
124     **/
125     post
126 }
127 
128 /**
129 Decorating container that adds a set of events at different actions during lifetime of a container
130 to which subscribers can subscribe to. This decorated will
131 inherit following interfaces only and only if the
132 T also implements them:
133   $(OL
134       $(LI Storage!(ObjectFactory, string))
135       $(LI Container)
136       $(LI AliasAware!string)
137   )
138 Decorated container must implement following interfaces:
139     $(OL
140         $(LI Container)
141         $(LI MutableDecorator!T)
142         $(LI Subscribable!ContainerInstantiationEventType)
143         $(LI Decorator!Container)
144     )
145 
146 Params:
147     T = The decorated that switchable decorated will decorate.
148 **/
149 template SubscribableContainer(T)
150 {
151 
152     /**
153     Set which the switchable container will decorate for T. By default
154     Locator!() and Subscribable!ContainerInstantiationEventType is included.
155     **/
156     alias InheritanceSet = NoDuplicates!(Filter!(
157         templateOr!(
158             partialSuffixed!(
159                 isDerived,
160                 Storage!(ObjectFactory, string)
161             ),
162             partialSuffixed!(
163                 isDerived,
164                 AliasAware!string
165             ),
166             partialSuffixed!(
167                 isDerived,
168                 FactoryLocator!ObjectFactory
169             )
170         ),
171         InterfacesTuple!T),
172         Container,
173         Subscribable!(ContainerInstantiationEventType, void delegate () @safe),
174         Subscribable!(ContainerInstantiationEventType, void delegate(SubscribableContainer!T container) @safe),
175         Subscribable!(ContainerTerminationEventType, void delegate() @safe),
176         Subscribable!(ContainerTerminationEventType, void delegate(SubscribableContainer!T container) @safe),
177         Subscribable!(ContainerFactoryEventType, void delegate(ObjectFactory factory, string) @safe),
178         Subscribable!(ContainerAccessEventType, void delegate (Optional!Object component, Optional!string key) @safe),
179         Subscribable!(ContainerCheckEventType, void delegate(Optional!bool existence, Optional!string key) @safe),
180         Decorator!Container
181     );
182 
183     @safe class SubscribableContainer : InheritanceSet
184     {
185         mixin MutableDecoratorMixin!T;
186 
187         mixin SubscribableMixin!(ContainerInstantiationEventType, void delegate() @safe) InstantiationSubscribers;
188         mixin SubscribableMixin!(ContainerInstantiationEventType, void delegate(SubscribableContainer!T) @safe) InstantiationSubscribersWithContainerRef;
189 
190         mixin SubscribableMixin!(ContainerTerminationEventType, void delegate() @safe) TerminationSubscribers;
191         mixin SubscribableMixin!(ContainerTerminationEventType, void delegate (SubscribableContainer!T) @safe) TerminationSubscribersWithContainerRef;
192 
193         mixin SubscribableMixin!(ContainerFactoryEventType, void delegate(ObjectFactory factory, string) @safe) FactorySubscribers;
194 
195         mixin SubscribableMixin!(ContainerAccessEventType, void delegate (Optional!Object, Optional!string) @safe) ContainerAccessSubscribers;
196         mixin SubscribableMixin!(ContainerCheckEventType, void delegate (Optional!bool, Optional!string) @safe) ContainerCheckSubscribers;
197 
198         public
199         {
200             alias subscribe = InstantiationSubscribers.subscribe;
201             alias subscribe = InstantiationSubscribersWithContainerRef.subscribe;
202 
203             alias subscribe = TerminationSubscribers.subscribe;
204             alias subscribe = TerminationSubscribersWithContainerRef.subscribe;
205 
206             alias subscribe = FactorySubscribers.subscribe;
207 
208             alias subscribe = ContainerAccessSubscribers.subscribe;
209             alias subscribe = ContainerCheckSubscribers.subscribe;
210 
211             /**
212             Sets up the internal state of container.
213 
214             Sets up the internal state of container (Ex, for singleton container it will spawn all objects that locator contains).
215             **/
216             SubscribableContainer instantiate()
217             {
218                 InstantiationSubscribers.invoke(ContainerInstantiationEventType.pre);
219                 InstantiationSubscribersWithContainerRef.invoke(ContainerInstantiationEventType.pre, this);
220                 this.decorated.instantiate();
221                 InstantiationSubscribersWithContainerRef.invoke(ContainerInstantiationEventType.post, this);
222                 InstantiationSubscribers.invoke(ContainerInstantiationEventType.post);
223 
224                 return this;
225             }
226 
227             /**
228             Destruct all managed components.
229 
230             Destruct all managed components. The method denotes the end of container lifetime, and therefore destruction of all managed components
231             by it.
232             **/
233             Container terminate() {
234                 TerminationSubscribers.invoke(ContainerTerminationEventType.pre);
235                 TerminationSubscribersWithContainerRef.invoke(ContainerTerminationEventType.pre, this);
236                 this.decorated.terminate();
237                 TerminationSubscribersWithContainerRef.invoke(ContainerTerminationEventType.post, this);
238                 TerminationSubscribers.invoke(ContainerTerminationEventType.post);
239 
240                 return this;
241             }
242 
243             /**
244             Get component from decorated container.
245 
246             Params:
247                 key = identity of component
248             Returns:
249             Object
250             **/
251             Object get(string key)
252             {
253                 ContainerAccessSubscribers.invoke(ContainerAccessEventType.pre, optional!Object, key.optional);
254                 Object object = this.decorated.get(key);
255                 ContainerAccessSubscribers.invoke(ContainerAccessEventType.post, object.optional, key.optional);
256 
257                 return object;
258             }
259 
260             /**
261             Check if component exists in container.
262 
263             Params:
264                 key = identity of component.
265             Returns:
266                 bool true if it exists.
267             **/
268             bool has(in string key) inout
269             {
270                 ContainerCheckSubscribers.invoke(ContainerCheckEventType.pre, optional!bool, key[].optional);
271                 bool result = this.decorated.has(key);
272                 ContainerCheckSubscribers.invoke(ContainerCheckEventType.post, result.optional, key[].optional);
273 
274                 return result;
275             }
276 
277             static if (is(T : Storage!(ObjectFactory, string)))
278             {
279                 /**
280                 Set component in decorated by identity.
281 
282                 Params:
283                     identity = identity of factory.
284                     element = factory that is to be saved in decorated.
285 
286                 Return:
287                     SwitchableContainer!T decorating decorated.
288                 **/
289                 typeof(this) set(ObjectFactory element, string identity) {
290                     FactorySubscribers.invoke(ContainerFactoryEventType.set, element, identity);
291                     this.decorated.set(element, identity);
292 
293                     return this;
294                 }
295 
296                 /**
297                 Remove factory from decorated with identity.
298 
299                 Remove factory from decorated with identity.
300 
301                 Params:
302                     identity = the identity of factory to be removed.
303 
304                 Return:
305                     SwitchableContainer!T decorating decorated
306                 **/
307                 typeof(this) remove(string identity) {
308                     decorated.remove(identity);
309 
310                     FactorySubscribers.invoke(ContainerFactoryEventType.remove, null, identity);
311                     return this;
312                 }
313             }
314 
315             static if (is(T : AliasAware!string))
316             {
317                 mixin AliasAwareMixin!(typeof(this));
318             }
319 
320             static if (is(T : FactoryLocator!ObjectFactory))
321             {
322                 mixin FactoryLocatorMixin!(typeof(this));
323             }
324         }
325     }
326 }