1 /** 2 This module provides fluent api based configuration of components with custom 3 configuration errors. 4 5 License: 6 Boost Software License - Version 1.0 - August 17th, 2003 7 8 Permission is hereby granted, free of charge, to any person or organization 9 obtaining a copy of the software and accompanying documentation covered by 10 this license (the "Software") to use, reproduce, display, distribute, 11 execute, and transmit the Software, and to prepare derivative works of the 12 Software, and to permit third-parties to whom the Software is furnished to 13 do so, all subject to the following: 14 15 The copyright notices in the Software and this entire statement, including 16 the above license grant, this restriction and the following disclaimer, 17 must be included in all copies of the Software, in whole or in part, and 18 all derivative works of the Software, unless such copies or derivative 19 works are solely in the form of machine-executable object code generated by 20 a source language processor. 21 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 25 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 26 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 DEALINGS IN THE SOFTWARE. 29 30 Authors: 31 Alexandru Ermicioi 32 **/ 33 module aermicioi.aedi.configurer.register.factory_configurer; 34 35 public import aermicioi.aedi.factory.reference : lref, anonymous; 36 37 import aermicioi.aedi.configurer.register.generic_factory_metadata_decorator; 38 import aermicioi.aedi.storage.storage; 39 import aermicioi.aedi.storage.locator; 40 import aermicioi.aedi.factory; 41 import aermicioi.aedi.storage.decorator; 42 import aermicioi.aedi.container.proxy_container; 43 import aermicioi.aedi.container.container; 44 import aermicioi.util.traits; 45 import aermicioi.aedi.exception; 46 47 import std.traits; 48 import std.meta; 49 50 /** 51 Construct aggregate using args. 52 53 Constructs aggregate using args, that are passed to function. 54 The function will attempt to find at least one construct that 55 can accept passed argument list. If it fails, compiler will 56 produce error, with respective problems. 57 The argument list can contain beside simple values, references 58 to other data in locator. Arguments that are references to other data 59 won't be type checked. 60 61 Params: 62 factory = the factory which will call constructor with passed arguments. 63 args = a list of arguments that will be passed to constructor. 64 65 Returns: 66 MetadataDecoratedGenericFactory!T. 67 **/ 68 69 auto construct(T, Args...)(MetadataDecoratedGenericFactory!T factory, auto ref Args args) { 70 factory.setInstanceFactory(constructorBasedFactory!T(factory.locator, args)); 71 72 return factory; 73 } 74 75 /** 76 Invoke T's method to create aggregate of type X. 77 78 Configures aggregate's factory to call method of factoryMethod with args, 79 in order to create aggregate of type X. 80 In case when method is not a static member, the function requires to 81 pass a instance of factoryMethod or a reference to it. 82 The algorithm will check for args compatiblity with parameters of 83 factory method. No type check is done for arguments that are references 84 at compile time. 85 86 Params: 87 factory = aggregate's factory that is configured to call factoryMethod methods to spawn aggregate 88 factoryMethod = instance of factory method that will be used to instantiate aggregate 89 args = a list of arguments passed to factory method 90 T = type of factoryMethod 91 method = the method that is called from T to instantiate aggregate 92 W = either LocatorReference or T 93 X = the return type of T.method member 94 **/ 95 MetadataDecoratedGenericFactory!(X) factoryMethod(T, string method, X, W, Args...)(MetadataDecoratedGenericFactory!(X) factory, auto ref W factoryMethod, auto ref Args args) 96 if ( 97 isNonStaticMethodCompatible!(T, method, Args) && 98 (is(W : T) || is(W : LocatorReference)) 99 ) { 100 factory.setInstanceFactory(factoryMethodBasedFactory!(T, method)(factory.locator, factoryMethod, args)); 101 102 return factory; 103 } 104 105 /** 106 ditto 107 **/ 108 MetadataDecoratedGenericFactory!(X) factoryMethod(T, string method, X, Args...)(MetadataDecoratedGenericFactory!(X) factory, auto ref Args args) 109 if ( 110 isStaticMethodCompatible!(T, method, Args) 111 ) { 112 113 factory.setInstanceFactory(factoryMethodBasedFactory!(T, method)(factory.locator, args)); 114 return factory; 115 } 116 117 /** 118 Invoke aggregate's method with supplied args. 119 120 Configures aggregate's factory to call specified method with passed args. 121 The function will check if the arguments passed to it are compatible with at 122 least one method from possible overload set. 123 The args list can contain references to other objects in locator as well, though 124 no type compatibility checks will be performed at compile time. 125 126 Params: 127 factory = the factory which will be configured to invoke method. 128 args = the arguments that will be used to invoke method on the new object. 129 130 Returns: 131 MetadataDecoratedGenericFactory!T. 132 **/ 133 auto set(string property, T, Args...)(MetadataDecoratedGenericFactory!T factory, auto ref Args args) 134 if (!isField!(T, property)) { 135 mixin assertObjectMethodCompatible!(T, property, Args); 136 137 factory.addPropertyConfigurer(methodConfigurer!(property, T)(factory.locator, args)); 138 139 return factory; 140 } 141 142 /** 143 Set aggregate's public field to passed arg. 144 145 Configures aggregate's factory to set specified field to passed arg. 146 The function will check if passed argument is type compatible with specified field. 147 The argument can be a reference as well. In case of argument being reference to another data 148 in container, no type compatiblity checking will be done. 149 150 Params 151 factory = the factory which will be configured to set property. 152 arg = the value of property to be set, or reference to data in container. 153 154 Returns: 155 MetadataDecoratedGenericFactory!T. 156 **/ 157 auto set(string property, T, Arg)(MetadataDecoratedGenericFactory!T factory, auto ref Arg arg) 158 if (isField!(T, property)) { 159 mixin assertFieldCompatible!(T, property, Arg); 160 161 factory.addPropertyConfigurer(fieldConfigurer!(property, T)(factory.locator, arg)); 162 163 return factory; 164 } 165 166 /** 167 Construct aggregate using a delegate. 168 169 Constructs aggregate using a delegate, and a list of arguments passed to delegate. 170 171 Params: 172 factory = the factory which will use delegate to construct aggregate. 173 dg = the delegate that is responsible for creating aggregate, given a list of arguments. 174 args = the arguments that will be used by delegate to construct aggregate. 175 176 Returns: 177 MetadataDecoratedGenericFactory!T. 178 **/ 179 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, T delegate(Locator!(), Args) dg, auto ref Args args) { 180 factory.setInstanceFactory(callbackFactory!T(factory.locator, dg, args)); 181 182 return factory; 183 } 184 185 /** 186 ditto 187 **/ 188 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, T function(Locator!(), Args) dg, auto ref Args args) { 189 factory.setInstanceFactory(callbackFactory!T(factory.locator, dg, args)); 190 191 return factory; 192 } 193 194 /** 195 Call dg on an aggregate that is in configuration phase. 196 197 Call dg on aggregate to perform some modifications, using args as input. 198 199 Params: 200 factory = factory which will call dg with args. 201 dg = delegate that will perform some modifications on aggregate using passed args. 202 args = a list of arguments passed to dg. 203 204 Returns: 205 MetadataDecoratedGenericFactory!T 206 **/ 207 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void delegate(Locator!(), T, Args) dg, auto ref Args args) { 208 factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args)); 209 210 return factory; 211 } 212 213 /** 214 ditto 215 **/ 216 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void function(Locator!(), T, Args) dg, auto ref Args args) { 217 factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args)); 218 219 return factory; 220 } 221 222 /** 223 ditto 224 **/ 225 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void delegate(Locator!(), ref T, Args) dg, auto ref Args args) { 226 factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args)); 227 228 return factory; 229 } 230 231 /** 232 ditto 233 **/ 234 auto callback(T, Args...)(MetadataDecoratedGenericFactory!T factory, void function(Locator!(), ref T, Args) dg, auto ref Args args) { 235 factory.addPropertyConfigurer(callbackConfigurer!T(factory.locator, dg, args)); 236 237 return factory; 238 } 239 240 /** 241 Autowire a constructor, field or a method. 242 243 Autowire a constructor, field or a method. 244 A constructor is autowired only when no member is passed as argument. 245 When a member is passed as argument, it will be called with 246 a list of references (where args are identified by their type FQN) in 247 case when member is a function, or it will set the member to the 248 value that is located in container by it's type FQN. 249 Note: In case of constructors as well as methods that are overloaded, 250 the first constructor or method from overload set is selected to be autowired. 251 252 Params: 253 T = the aggregate type 254 member = field or method of aggregate T 255 factory = MetadataDecoratedGenericFactory where to inject the constructor or method configurer 256 257 Returns: 258 MetadataDecoratedGenericFactory!T 259 **/ 260 auto autowire(T)(MetadataDecoratedGenericFactory!T factory) 261 if (getMembersWithProtection!(T, "__ctor", "public").length > 0) { 262 return factory.construct!(T)(staticMap!(toLref, Parameters!(getMembersWithProtection!(T, "__ctor", "public")[0]))); 263 } 264 265 /** 266 ditto 267 **/ 268 auto autowire(string member, T)(MetadataDecoratedGenericFactory!T factory) 269 if (getMembersWithProtection!(T, member, "public").length > 0) { 270 return factory.set!(member)(staticMap!(toLref, Parameters!(getMembersWithProtection!(T, member, "public")[0]))); 271 } 272 273 /** 274 ditto 275 **/ 276 auto autowire(string member, T)(MetadataDecoratedGenericFactory!T factory) 277 if (isField!(T, member)) { 278 return factory.set!(member)(lref!(typeof(getMember!(T, member)))); 279 } 280 281 /** 282 Move constructed data from one container to another one. 283 284 Move constructed data from one container to another one. 285 Description 286 287 Params: 288 factory = factory for constructed data 289 storage = new location for factory 290 291 Returns: 292 factory 293 **/ 294 auto container(T)(MetadataDecoratedGenericFactory!T factory, Storage!(ObjectFactory, string) storage) { 295 if (factory.storage !is null) { 296 factory.storage.remove(factory.identity); 297 } 298 299 factory.storage = storage; 300 factory.storage.set(factory.wrapper, factory.identity); 301 302 return factory; 303 } 304 305 /** 306 ditto 307 **/ 308 auto container(T)(MetadataDecoratedGenericFactory!T factory, string storageId) { 309 import std.algorithm; 310 311 auto storage = factory.locator.locate!(Storage!(ObjectFactory, string))(storageId); 312 313 factory.storageIdentity = storageId; 314 return factory.container(storage); 315 } 316 317 /** 318 Tag constructed data with some information. 319 320 Tag constructed data with some information. 321 Description 322 323 Params: 324 factory = factory for constructed data 325 tag = tag with which to tag factory. 326 327 Returns: 328 factory 329 **/ 330 auto tag(T, Z)(MetadataDecoratedGenericFactory!T factory, auto ref Z tag) { 331 332 auto taggable = findDecorator!(Taggable!Z, ObjectFactoryDecorator)(factory.wrapper); 333 334 if (taggable is null) { 335 auto taggableDecorator = new TaggableFactoryDecorator!(Object, Z); 336 taggableDecorator.decorated = factory.wrapper; 337 factory.wrapper = taggableDecorator; 338 339 taggable = taggableDecorator; 340 factory.storage.set(factory.wrapper, factory.identity); 341 } 342 343 taggable.tag(tag); 344 345 return factory; 346 } 347 348 //Due to BUG 17177, minimal usage is not possible. 349 //Mark constructed object to be provided through a proxy instead of directly doing so. 350 // 351 //Mark constructed object to be provided through a proxy instead of directly doing so. 352 //Object will be proxied only in case when the storage where it is stored support 353 //storing of proxy object factories. 354 // 355 //Params: 356 // factory = factory for constructed object 357 // 358 //Returns: 359 // factory 360 // 361 //auto proxy(T)(MetadataDecoratedGenericFactory!T factory) { 362 // import aermicioi.aedi.factory.proxy_factory; 363 // import aermicioi.aedi.container.proxy_container; 364 // 365 // auto proxyAware = cast(ProxyContainer) factory.storage; 366 // if (proxyAware !is null) { 367 // proxyAware.set( 368 // new ProxyObjectWrappingFactory!T( 369 // new ProxyFactory!T(factory.identity, proxyAware.decorated) 370 // ), 371 // factory.identity, 372 // ); 373 // } 374 // 375 // return factory; 376 //} 377 378 /** 379 Find a decorator in decorator chain that implements Needle type. 380 381 Find a decorator in decorator chain that implements Needle type. 382 383 Params: 384 Needle = the type searched decorator should implement 385 Haystack = type of the chain of decorators through which to traverse 386 decorated = top of decorator chain. 387 388 Returns: 389 Decorator or null if not found. 390 **/ 391 Needle findDecorator(Needle, Haystack : Decorator!Z, Z, T)(T decorated) { 392 393 Haystack decorator = cast(Haystack) decorated; 394 Needle needle = cast(Needle) decorated; 395 396 while ((needle is null) && (decorator !is null)) { 397 decorator = cast(Haystack) decorator.decorated; 398 needle = cast(Needle) decorator; 399 } 400 401 return needle; 402 }