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.type_based_container; 31 32 import aermicioi.aedi.container.container; 33 import aermicioi.aedi.storage.alias_aware; 34 import aermicioi.aedi.storage.decorator; 35 import aermicioi.aedi.storage.storage; 36 import aermicioi.aedi.storage.object_storage; 37 import aermicioi.aedi.exception.not_found_exception; 38 import aermicioi.aedi.factory.factory; 39 import aermicioi.util.traits; 40 41 import std.algorithm; 42 import std.array; 43 import std.range; 44 import std.typecons; 45 import std.traits; 46 import std.meta; 47 import std.container.rbtree; 48 49 /** 50 A decorating container that can provide for requested 51 base class or interface, one implementor of those. 52 This decorated will inherit following interfaces if 53 and only if T also implements them: 54 $(OL 55 $(LI AliasAware!string) 56 $(LI FactoryLocator!ObjectFactory) 57 ) 58 Decorated container must implement following interfaces: 59 $(OL 60 $(LI Container) 61 $(LI Storage!(ObjectFactory, string)) 62 $(LI MutableDecorator!T) 63 $(LI Decorator!Container) 64 ) 65 66 Params: 67 T = The decorated that switchable decorated will decorate. 68 **/ 69 template TypeBasedContainer(T) { 70 71 /** 72 Set which the type based container will decorate for T container. 73 **/ 74 alias InheritanceSet = 75 NoDuplicates!( 76 Container, 77 Storage!(ObjectFactory, string), 78 MutableDecorator!T, 79 Decorator!Container, 80 Filter!( 81 templateOr!( 82 partialSuffixed!( 83 isDerived, 84 AliasAware!string 85 ), 86 partialSuffixed!( 87 isDerived, 88 FactoryLocator!ObjectFactory 89 ) 90 ), 91 InterfacesTuple!T 92 ) 93 ); 94 95 class TypeBasedContainer : InheritanceSet { 96 97 private { 98 T decorated_; 99 100 ObjectStorage!(RedBlackTree!(string), string) candidates; 101 } 102 103 public { 104 this() { 105 this.candidates = new ObjectStorage!(RedBlackTree!string, string); 106 } 107 108 @property { 109 TypeBasedContainer decorated(T decorated) @safe nothrow { 110 this.decorated_ = decorated; 111 112 return this; 113 } 114 115 T decorated() @safe nothrow { 116 return this.decorated_; 117 } 118 } 119 120 /** 121 Sets up the internal state of container. 122 123 Sets up the internal state of container (Ex, for singleton container it will spawn all objects that locator contains). 124 **/ 125 TypeBasedContainer instantiate() { 126 this.decorated.instantiate(); 127 128 return this; 129 } 130 131 /** 132 Save an object factory in TypeBasedContainer by identity. 133 134 Save an object factory in TypeBasedContainer by identity. 135 The object factory, and subsequent objects are registered as candidates for use for 136 any interface or base class they implement, when no suitable solution 137 is found in decorated container. 138 139 Params: 140 identity = identity of object factory in TypeBasedContainer. 141 factory = object factory which is to be saved in TypeBasedContainer. 142 143 Return: 144 TypeBasedContainer 145 **/ 146 TypeBasedContainer set(ObjectFactory factory, string identity) { 147 decorated.set(factory, identity); 148 149 ClassInfo info = cast(ClassInfo) factory.type; 150 if (info !is null) { 151 info.crawlClassInfo( 152 delegate (TypeInfo_Class crawled) { 153 if (!this.candidates.has(crawled.toString())) { 154 this.candidates.set(new RedBlackTree!string, crawled.toString()); 155 } 156 157 this.candidates.get(crawled.toString()).insert(identity); 158 } 159 ); 160 } 161 162 return this; 163 } 164 165 /** 166 Remove an object from TypeBasedContainer with identity. 167 168 Remove an object from TypeBasedContainer with identity. 169 It will remove the candidate from any interface or base class it 170 does implement. 171 172 Params: 173 identity = the identity of object to be removed. 174 175 Return: 176 TypeBasedContainer 177 **/ 178 TypeBasedContainer remove(string identity) { 179 decorated.remove(identity); 180 181 foreach (pair; this.candidates.contents.byKeyValue.array) { 182 pair.value.removeKey(identity); 183 184 if (pair.value.empty) { 185 this.candidates.remove(pair.key); 186 } 187 } 188 189 return this; 190 } 191 192 /** 193 Get an object that is associated with identity. 194 195 Get an object that is associated with identity. 196 If no object associated by identity in decorated container 197 is found, a search for a solution in list of candidates is 198 done, and if found, first candidate is used as substitution 199 for serving the requested object. 200 201 Params: 202 identity = object identity in decorated. 203 204 Throws: 205 NotFoundException in case if no object was found. 206 207 Returns: 208 Object if it is available. 209 **/ 210 Object get(string identity) { 211 if (this.decorated.has(identity)) { 212 return this.decorated.get(identity); 213 } 214 215 if (this.candidates.has(identity)) { 216 return 217 this.decorated.get( 218 this.candidates.get(identity).front 219 ); 220 } 221 222 throw new NotFoundException("Object with id " ~ identity ~ " not found."); 223 } 224 225 /** 226 Check if an object is present in TypeBasedContainer by identity. 227 228 Check if an object is present in TypeBasedContainer by identity. 229 If no object is available in decorated container, a candidate 230 is searched through list of candidates, and if found, true 231 is returned. 232 233 Params: 234 identity = identity of object. 235 236 Returns: 237 bool true if an object by identity is present in TypeBasedContainer. 238 **/ 239 bool has(in string identity) inout { 240 if (this.decorated_.has(identity)) { 241 return true; 242 } 243 244 return this.candidates.has(identity); 245 } 246 247 static if (is(T : AliasAware!string)) { 248 /** 249 Alias a key to an alias_. 250 251 Params: 252 identity = the originial identity which is to be aliased. 253 alias_ = the alias of identity. 254 255 Returns: 256 this 257 **/ 258 TypeBasedContainer link(string identity, string alias_) { 259 this.decorated.link(identity, alias_); 260 261 return this; 262 } 263 264 /** 265 Removes alias. 266 267 Params: 268 alias_ = alias to remove. 269 270 Returns: 271 this 272 273 **/ 274 TypeBasedContainer unlink(string alias_) { 275 this.decorated.unlink(alias_); 276 277 return this; 278 } 279 280 /** 281 Resolve an alias to original identity, if possible. 282 283 Params: 284 alias_ = alias of original identity 285 286 Returns: 287 Type the last identity in alias chain. 288 289 **/ 290 const(string) resolve(in string alias_) const { 291 return this.decorated_.resolve(alias_); 292 } 293 } 294 295 static if (is(T : FactoryLocator!ObjectFactory)) { 296 /** 297 Get factory for constructed data identified by identity. 298 299 Get factory for constructed data identified by identity. 300 Params: 301 identity = the identity of data that factory constructs. 302 303 Throws: 304 NotFoundException when factory for it is not found. 305 306 Returns: 307 ObjectFactory the factory for constructed data. 308 **/ 309 ObjectFactory getFactory(string identity) { 310 return this.decorated.getFactory(identity); 311 } 312 313 /** 314 Get all factories available in decorated. 315 316 Get all factories available in decorated. 317 318 Returns: 319 InputRange!(Tuple!(ObjectFactory, string)) a tuple of factory => identity. 320 **/ 321 InputRange!(Tuple!(ObjectFactory, string)) getFactories() { 322 return this.decorated.getFactories(); 323 } 324 } 325 } 326 327 } 328 } 329 330 private { 331 void crawlClassInfo(TypeInfo_Class class_, void delegate (TypeInfo_Class) dg) { 332 dg(class_); 333 334 foreach (iface; class_.interfaces) { 335 crawlClassInfo(iface.classinfo, dg); 336 } 337 338 if (class_.base !is null) { 339 crawlClassInfo(class_.base, dg); 340 } 341 } 342 343 344 }