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 interface Subscribable(T) {
47     
48     public {
49         
50         Subscribable subscribe(
51             T type,
52             void delegate() subscriber
53         );
54     }
55 }
56 
57 enum ContainerInstantiationEventType {
58     pre,
59     post,
60 }
61 
62 template SubscribableContainer(T) {
63     
64     /**
65     Set which the switchable container will decorate for T. By default
66     Locator!() and Subscribable!ContainerInstantiationEventType is included.
67     **/
68     alias InheritanceSet = AliasSeq!(Filter!(
69         templateOr!(
70             partialSuffixed!(
71                 isDerived,
72                 Storage!(ObjectFactory, string)
73             ),
74             partialSuffixed!(
75                 isDerived,
76                 AliasAware!string
77             ),
78             partialSuffixed!(
79                 isDerived,
80                 FactoryLocator!ObjectFactory
81             )
82         ),
83         InterfacesTuple!T),
84         Container,
85         Subscribable!ContainerInstantiationEventType,
86         MutableDecorator!T
87     );
88     
89     class SubscribableContainer : InheritanceSet {
90         private {
91             void delegate()[][ContainerInstantiationEventType] subscribers;
92             T decorated_;
93         }
94         
95         public {
96             
97             this() {
98                 subscribers[ContainerInstantiationEventType.pre] = null;
99                 subscribers[ContainerInstantiationEventType.post] = null;
100             }
101             
102             @property {
103             	SubscribableContainer decorated(T container) @safe nothrow {
104             		this.decorated_ = container;
105             	
106             		return this;
107             	}
108             	
109             	T decorated() @safe nothrow {
110             		return this.decorated_;
111             	}
112             }
113             
114             Object get(string key) {
115                 return this.decorated.get(key);
116             }
117             
118             bool has(in string key) inout {
119                 return this.decorated_.has(key);
120             }
121             
122             SubscribableContainer subscribe(
123                 ContainerInstantiationEventType event,
124                 void delegate() subscriber
125             ) {
126                 this.subscribers[event] ~= subscriber;
127                 
128                 return this;
129             }
130             
131             SubscribableContainer instantiate() {
132                 foreach (preProcessor; this.subscribers[ContainerInstantiationEventType.pre]) {
133                     preProcessor();
134                 }
135                 
136                 this.decorated.instantiate();
137                 
138                 foreach (postProcessor; this.subscribers[ContainerInstantiationEventType.post]) {
139                     postProcessor();
140                 }
141                 
142                 return this;
143             }
144             
145             static if (is(T : Storage!(ObjectFactory, string))) {
146 
147                 SubscribableContainer!T set(ObjectFactory element, string identity) {
148                     decorated.set(element, identity);
149                     
150                     return this;
151                 }
152                 
153                 SubscribableContainer!T remove(string identity) {
154                     decorated.remove(identity);
155                     
156                     return this;
157                 }
158             }
159             
160             static if (is(T : AliasAware!string)) {
161                 SubscribableContainer!T link(string identity, string alias_) {
162                     decorated.link(identity, alias_);
163                     
164                     return this;
165                 }
166                 
167                 SubscribableContainer!T unlink(string alias_) {
168                     decorated.unlink(alias_);
169                     
170                     return this;
171                 }
172                 
173                 const(string) resolve(in string alias_) const {
174                     return decorated_.resolve(alias_);
175                 }
176             }
177             
178             static if (is(T : FactoryLocator!ObjectFactory)) {
179                 
180                 ObjectFactory getFactory(string identity) {
181                     return this.decorated.getFactory(identity);
182                 }
183                 
184                 InputRange!(Tuple!(ObjectFactory, string)) getFactories() {
185                     return this.decorated.getFactories();
186                 }
187             }
188         }
189     }
190 }
191