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.aedi.util.traits; 40 import aermicioi.aedi.util.range : inheritance; 41 42 import std.algorithm; 43 import std.array; 44 import std.range; 45 import std.traits; 46 import std.meta; 47 48 /** 49 A decorating container that can provide for requested 50 base class or interface, one implementor of those. 51 This decorated will inherit following interfaces if 52 and only if T also implements them: 53 $(OL 54 $(LI AliasAware!string) 55 $(LI FactoryLocator!ObjectFactory) 56 ) 57 Decorated container must implement following interfaces: 58 $(OL 59 $(LI Container) 60 $(LI Storage!(ObjectFactory, string)) 61 $(LI MutableDecorator!T) 62 $(LI Decorator!Container) 63 ) 64 65 Params: 66 T = The decorated that switchable decorated will decorate. 67 **/ 68 template TypeBasedContainer(T) { 69 70 /** 71 Set which the type based container will decorate for T container. 72 **/ 73 alias InheritanceSet = 74 NoDuplicates!( 75 Container, 76 Storage!(ObjectFactory, string), 77 Decorator!Container, 78 Filter!( 79 templateOr!( 80 partialSuffixed!( 81 isDerived, 82 AliasAware!string 83 ), 84 partialSuffixed!( 85 isDerived, 86 FactoryLocator!ObjectFactory 87 ) 88 ), 89 InterfacesTuple!T 90 ) 91 ); 92 93 @safe class TypeBasedContainer : InheritanceSet { 94 95 private { 96 string[][TypeInfo] entries; 97 } 98 99 public { 100 mixin MutableDecoratorMixin!T; 101 102 import aermicioi.aedi.container.container : ContainerMixin; 103 mixin ContainerMixin!(typeof(this)); 104 105 /** 106 Save an object factory in TypeBasedContainer by identity. 107 108 Save an object factory in TypeBasedContainer by identity. 109 The object factory, and subsequent objects are registered as candidates for use for 110 any interface or base class they implement, when no suitable solution 111 is found in decorated container. 112 113 Params: 114 identity = identity of object factory in TypeBasedContainer. 115 factory = object factory which is to be saved in TypeBasedContainer. 116 117 Return: 118 TypeBasedContainer 119 **/ 120 TypeBasedContainer set(ObjectFactory factory, string identity) { 121 decorated.set(factory, identity); 122 123 ClassInfo info = cast(ClassInfo) factory.type; 124 if (info !is null) { 125 foreach (TypeInfo inherited; info.inheritance) { 126 if ( 127 (inherited !in this.entries) || 128 !this.entries[inherited].canFind(identity) 129 ) { 130 this.entries[inherited] ~= identity; 131 } 132 } 133 } 134 135 return this; 136 } 137 138 /** 139 Remove an object from TypeBasedContainer with identity. 140 141 Remove an object from TypeBasedContainer with identity. 142 It will remove the candidate from any interface or base class it 143 does implement. 144 145 Params: 146 identity = the identity of object to be removed. 147 148 Return: 149 TypeBasedContainer 150 **/ 151 TypeBasedContainer remove(string identity) @trusted { 152 decorated.remove(identity); 153 154 foreach (type, candidates; this.entries) { 155 if (candidates.canFind(identity)) { 156 this.entries[type] = candidates.filter!(entry => entry != identity).array; 157 } 158 } 159 160 return this; 161 } 162 163 /** 164 Get an object that is associated with identity. 165 166 Get an object that is associated with identity. 167 If no object associated by identity in decorated container 168 is found, a search for a solution in list of candidates is 169 done, and if found, first candidate is used as substitution 170 for serving the requested object. 171 172 Params: 173 identity = object identity in decorated. 174 175 Throws: 176 NotFoundException in case if no object was found. 177 178 Returns: 179 Object if it is available. 180 **/ 181 Object get(string identity) { 182 if (this.decorated.has(identity)) { 183 return this.decorated.get(identity); 184 } 185 186 foreach (type, candidates; this.entries) { 187 if (identity == type.toString) { 188 return this.decorated.get(candidates.front); 189 } 190 } 191 192 throw new NotFoundException("Component ${identity} not found.", identity); 193 } 194 195 /** 196 Check if an object is present in TypeBasedContainer by identity. 197 198 Check if an object is present in TypeBasedContainer by identity. 199 If no object is available in decorated container, a candidate 200 is searched through list of candidates, and if found, true 201 is returned. 202 203 Params: 204 identity = identity of object. 205 206 Returns: 207 bool true if an object by identity is present in TypeBasedContainer. 208 **/ 209 bool has(in string identity) inout { 210 if (this.decorated.has(identity)) { 211 return true; 212 } 213 214 foreach (type, candidates; this.entries) { 215 if (identity == type.toString) { 216 return !candidates.empty; 217 } 218 } 219 220 return false; 221 } 222 223 static if (is(T : AliasAware!string)) { 224 mixin AliasAwareMixin!T; 225 } 226 227 static if (is(T : FactoryLocator!ObjectFactory)) { 228 229 mixin FactoryLocatorMixin!(typeof(this)); 230 } 231 } 232 233 } 234 }