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 }