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.configuration_context_factory; 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 component using args. 52 53 Constructs component 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 Z. 67 **/ 68 69 auto construct(Z : InstanceFactoryAware!T, T, Args...)(Z 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 component of type X. 77 78 Configures component's factory to call method of factoryMethod with args, 79 in order to create component 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 = component's factory that is configured to call factoryMethod methods to spawn component 88 factoryMethod = instance of factory method that will be used to instantiate component 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 component 92 W = either LocatorReference or T 93 X = the return type of T.method member 94 **/ 95 Z factoryMethod(T, string method, Z : InstanceFactoryAware!X, X, W, Args...)(Z factory, auto ref W factoryMethod, auto ref Args args) 96 if ( 97 isNonStaticMethodCompatible!(T, method, Args) && 98 (is(W : T) || is(W : RuntimeReference)) 99 ) { 100 factory.setInstanceFactory(factoryMethodBasedFactory!(T, method)(factory.locator, factoryMethod, args)); 101 102 return factory; 103 } 104 105 /** 106 ditto 107 **/ 108 Z factoryMethod(T, string method, Z : InstanceFactoryAware!X, X, Args...)(Z 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 component's method with supplied args. 119 120 Configures component'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 Z. 132 **/ 133 auto set(string property, Z : PropertyConfigurersAware!T, T, Args...)(Z 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 component's public field to passed arg. 144 145 Configures component'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 Z. 156 **/ 157 auto set(string property, Z : PropertyConfigurersAware!T, T, Arg)(Z 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 component using a delegate. 168 169 Constructs component using a delegate, and a list of arguments passed to delegate. 170 171 Params: 172 factory = the factory which will use delegate to construct component. 173 dg = the delegate that is responsible for creating component, given a list of arguments. 174 args = the arguments that will be used by delegate to construct component. 175 176 Returns: 177 Z. 178 **/ 179 auto callback(Z : InstanceFactoryAware!T, T, Args...)(Z 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(Z : InstanceFactoryAware!T, T, Args...)(Z 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 component that is in configuration phase. 196 197 Call dg on component 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 component using passed args. 202 args = a list of arguments passed to dg. 203 204 Returns: 205 Z 206 **/ 207 auto callback(Z : PropertyConfigurersAware!T, T, Args...)(Z 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(Z : PropertyConfigurersAware!T, T, Args...)(Z 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(Z : PropertyConfigurersAware!T, T, Args...)(Z 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(Z : PropertyConfigurersAware!T, T, Args...)(Z 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 component type 254 member = field or method of component T 255 factory = ConfigurationContextFactory where to inject the constructor or method configurer 256 257 Returns: 258 Z 259 **/ 260 auto autowire(Z : InstanceFactoryAware!T, T)(Z factory) 261 if (getMembersWithProtection!(T, "__ctor", "public").length > 0) { 262 return factory.construct(staticMap!(toLref, Parameters!(getMembersWithProtection!(T, "__ctor", "public")[0]))); 263 } 264 265 /** 266 ditto 267 **/ 268 auto autowire(string member, Z : PropertyConfigurersAware!T, T)(Z 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, Z : PropertyConfigurersAware!T, T)(Z factory) 277 if (isField!(T, member)) { 278 return factory.set!(member)(lref!(typeof(getMember!(T, member)))); 279 } 280 281 /** 282 Instantiates a component using a value as basis. 283 284 Instantiates a component using a value as basis. 285 As a consequence, any reference based type will 286 point to same content when it is instantiated 287 multiple times. 288 289 Params: 290 T = the component type 291 factory = ConfigurationContextFactory where to inject the constructor or method configurer 292 value = default value used to instantiate component 293 **/ 294 auto value(Z : InstanceFactoryAware!T, T)(Z factory, auto ref T value) { 295 return factory.setInstanceFactory(new ValueInstanceFactory!T(value)); 296 } 297 298 /** 299 Instantiates a component using as basis some third party factory. 300 301 Params: 302 T = the type of component that is to be configured 303 factory = factory that uses the parent factory for component instantiation 304 delegated = the factory used by factory to instantiate an object. 305 **/ 306 auto parent(Z : InstanceFactoryAware!T, T, X : Factory!W, W : T)(Z factory, X delegated) { 307 return factory.setInstanceFactory(new DelegatingInstanceFactory!(T, W)(delegated)); 308 } 309 310 /** 311 Instantiates a component using as basis some third party factory. 312 313 Instantiates a component using as basis some third party factory. 314 When a parent factory from a source of factories is used, 315 type checking of component returned by parent factory 316 is done at runtime, and will throw an exception if type 317 of parent factory mismatches the type of component that 318 uses respective factory. 319 320 Params: 321 T = the type of component that is to be configured 322 factory = factory that uses the parent factory for component instantiation 323 source = source of factories from which parent factory is extracted 324 identity = identity of factory used by factory to instantiate component 325 **/ 326 auto parent(Z : InstanceFactoryAware!T, T, X : FactoryLocator!ObjectFactory)(Z factory, X source, string identity) { 327 return factory.setInstanceFactory( 328 new DelegatingInstanceFactory!(T, T)( 329 new ClassUnwrappingFactory!T( 330 source.getFactory(identity) 331 ) 332 ) 333 ); 334 } 335 336 /** 337 Move constructed data from one container to another one. 338 339 Move constructed data from one container to another one. 340 Description 341 342 Params: 343 factory = factory for constructed data 344 storage = new location for factory 345 346 Returns: 347 factory 348 **/ 349 auto container(Z : ConfigurationContextFactory!T, T)(Z factory, Storage!(ObjectFactory, string) storage) { 350 if (factory.storage !is null) { 351 factory.storage.remove(factory.identity); 352 } 353 354 factory.storage = storage; 355 factory.storage.set(factory.wrapper, factory.identity); 356 357 return factory; 358 } 359 360 /** 361 ditto 362 **/ 363 auto container(Z : ConfigurationContextFactory!T, T)(Z factory, string storageId) { 364 import std.algorithm; 365 366 auto storage = factory.locator.locate!(Storage!(ObjectFactory, string))(storageId); 367 368 factory.storageIdentity = storageId; 369 return factory.container(storage); 370 } 371 372 /** 373 Tag constructed data with some information. 374 375 Tag constructed data with some information. 376 Description 377 378 Params: 379 factory = factory for constructed data 380 tag = tag with which to tag factory. 381 382 Returns: 383 factory 384 **/ 385 auto tag(W : ConfigurationContextFactory!T, T, Z)(W factory, auto ref Z tag) { 386 387 auto taggable = findDecorator!(Taggable!Z, ObjectFactoryDecorator)(factory.wrapper); 388 389 if (taggable is null) { 390 auto taggableDecorator = new TaggableFactoryDecorator!(Object, Z); 391 taggableDecorator.decorated = factory.wrapper; 392 factory.wrapper = taggableDecorator; 393 394 taggable = taggableDecorator; 395 factory.storage.set(factory.wrapper, factory.identity); 396 } 397 398 taggable.tag(tag); 399 400 return factory; 401 } 402 403 /** 404 Mark constructed object to be provided through a proxy instead of directly doing so. 405 Object will be proxied only in case when the storage where it is stored support 406 storing of proxy object factories. 407 408 Params: 409 factory = factory for constructed object 410 411 Returns: 412 factory 413 **/ 414 auto proxy(Z : ConfigurationContextFactory!T, T)(Z factory) { 415 import aermicioi.aedi.factory.proxy_factory; 416 import aermicioi.aedi.container.proxy_container; 417 418 auto proxyAware = cast(ProxyContainer) factory.storage; 419 if (proxyAware !is null) { 420 proxyAware.set( 421 new ProxyObjectWrappingFactory!T( 422 new ProxyFactory!T(factory.identity, proxyAware.decorated) 423 ), 424 factory.identity, 425 ); 426 } 427 428 return factory; 429 }