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 }