1 /** 2 Contains primitives related to reference resolving during construction of component 3 (objects, structs, basic types, etc.). 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 aermicioi 32 **/ 33 module aermicioi.aedi.factory.reference; 34 35 import aermicioi.aedi.exception.invalid_cast_exception; 36 import aermicioi.aedi.factory.factory; 37 import aermicioi.aedi.storage.locator; 38 import std.traits; 39 import std.conv : text; 40 41 /** 42 Represents a reference that some component is dependent on it. 43 44 Represents a reference that some component is dependent on it. 45 It will resolve itself to the referenced component, that is 46 subclass of Object, or component that is encapsulated in Wrapper object. 47 **/ 48 @safe interface RuntimeReference { 49 50 /** 51 Resolve the reference, to referenced component. 52 53 Resolve the reference, to referenced component. 54 55 Params: 56 locator = an optional source of component used to resolve reference 57 58 Returns: 59 Object the actual object, or component that is wrapped in Wrapper object. 60 **/ 61 Object get(Locator!() locator); 62 } 63 64 /** 65 Represents a reference that is located in locator. 66 67 Represents a reference that is located in locator. 68 It uses referenced component's identity in locator to 69 find it and serve. 70 **/ 71 @safe class LocatorReference : RuntimeReference { 72 private { 73 string identity_; 74 } 75 76 public { 77 /** 78 Constructor for LocatorReference 79 80 Params: 81 id = identity of component that is referenced 82 **/ 83 this(string id) { 84 this.identity = id; 85 } 86 87 @property { 88 89 /** 90 Set the identity of referenced component. 91 92 Set the identity of referenced component. 93 Description 94 95 Params: 96 identity = the identity of referenced component. 97 98 Returns: 99 this 100 **/ 101 LocatorReference identity(string identity) @safe nothrow { 102 this.identity_ = identity; 103 104 return this; 105 } 106 107 /** 108 Get the identity of referenced component. 109 110 Get the identity of referenced component. 111 112 Returns: 113 string the identity of referenced component 114 **/ 115 string identity() @safe nothrow { 116 return this.identity_; 117 } 118 } 119 120 /** 121 Resolve the reference, to referenced component. 122 123 Resolve the reference, to referenced component. 124 125 Params: 126 locator = an optional source of components used to resolve reference 127 128 Returns: 129 Object the actual object, or component that is wrapped in Wrapper object. 130 **/ 131 Object get(Locator!() locator) { 132 return locator.get(this.identity); 133 } 134 135 override string toString() const { 136 return text("IdRef(", identity_, ")"); 137 } 138 } 139 } 140 141 /** 142 ditto 143 **/ 144 @safe auto lref(string id) { 145 return new LocatorReference(id); 146 } 147 148 /** 149 ditto 150 **/ 151 @safe auto lref(string name)() { 152 return name.lref; 153 } 154 155 /** 156 Reference to a component stored in a locator by it's type. 157 **/ 158 @safe class TypeLocatorReference(T) : RuntimeReference { 159 160 public { 161 /** 162 Resolve the reference, to referenced component. 163 164 Resolve the reference, to referenced component. 165 166 Params: 167 locator = an optional source of components used to resolve reference 168 169 Returns: 170 Object the actual object, or component that is wrapped in Wrapper object. 171 **/ 172 Object get(Locator!() locator) { 173 auto type = typeid(T); 174 175 if (locator.has(type.toString())) { 176 177 return locator.get(type.toString()); 178 } else { 179 180 return locator.get(fullyQualifiedName!T); 181 } 182 } 183 184 override string toString() { 185 return text("TypeRef(", typeid(T), ")"); 186 } 187 } 188 } 189 190 /** 191 ditto 192 **/ 193 @safe auto lref(T)() { 194 return new TypeLocatorReference!T; 195 } 196 197 /** 198 Represents a reference to component yet to be constructed. 199 200 Represents a reference to component yet to be constructed. 201 It will instantiate the referenced component using an object 202 factory, and will serve it to requestor. 203 **/ 204 @safe class AnonymousFactoryReference : RuntimeReference { 205 206 private { 207 ObjectFactory factory_; 208 } 209 210 public { 211 @property { 212 /** 213 Set factory 214 215 Params: 216 factory = factory used by anonymous reference to create component 217 Returns: 218 typeof(this) 219 **/ 220 AnonymousFactoryReference factory(ObjectFactory factory) @safe nothrow { 221 this.factory_ = factory; 222 223 return this; 224 } 225 226 /** 227 Get factory 228 229 Returns: 230 ObjectFactory 231 **/ 232 ObjectFactory factory() @safe nothrow { 233 return this.factory_; 234 } 235 } 236 237 /** 238 Resolve the reference, to referenced component. 239 240 Resolve the reference, to referenced component. 241 242 Params: 243 locator = an optional source of components used to resolve reference 244 245 Returns: 246 Object the actual object, or component that is wrapped in Wrapper object. 247 **/ 248 Object get(Locator!() locator) { 249 this.factory.locator = locator; 250 scope(exit) this.factory.locator = null; 251 252 return this.factory.factory; 253 } 254 255 override string toString() { 256 return text("AnonRef(", factory.type, ")"); 257 } 258 } 259 } 260 261 /** 262 ditto 263 **/ 264 @safe auto anonymous(T : Factory!X, X)(T factory) { 265 import aermicioi.aedi.factory.wrapping_factory : WrappingFactory; 266 return anonymous(new WrappingFactory!T(factory)); 267 } 268 269 /** 270 ditto 271 **/ 272 @safe auto anonymous(ObjectFactory factory) { 273 auto anonymous = new AnonymousFactoryReference(); 274 anonymous.factory = factory; 275 276 return anonymous; 277 } 278 279 /** 280 Reference that defaults to alternate component in case that original one is not fetchable from container 281 282 Params: 283 original = original reference to a component that is attempted to be fetched. 284 alternate = reference to alternate component that is meant to substitute original component in case of some failure. 285 Throws: 286 287 Returns: 288 AlternateReference 289 **/ 290 @safe AlternateReference alternate(RuntimeReference original, RuntimeReference alternate) { 291 AlternateReference reference = new AlternateReference(); 292 293 reference.original = original; 294 reference.alternative = alternate; 295 296 return reference; 297 } 298 299 /** 300 ditto 301 **/ 302 @safe class AlternateReference : RuntimeReference { 303 private { 304 RuntimeReference original_; 305 RuntimeReference alternative_; 306 } 307 308 public { 309 /** 310 Set original 311 312 Params: 313 original = primary reference used to fetch dependency 314 Returns: 315 typeof(this) 316 **/ 317 typeof(this) original(RuntimeReference original) @safe nothrow pure { 318 this.original_ = original; 319 320 return this; 321 } 322 323 /** 324 Get original 325 326 Returns: 327 RuntimeReference 328 **/ 329 RuntimeReference original() @safe nothrow pure { 330 return this.original_; 331 } 332 333 /** 334 Set alternative 335 336 Params: 337 alternative = the second reference used when first throws exception 338 339 Returns: 340 typeof(this) 341 **/ 342 typeof(this) alternative(RuntimeReference alternative) @safe nothrow pure { 343 this.alternative_ = alternative; 344 345 return this; 346 } 347 348 /** 349 Get alternative 350 351 Returns: 352 RuntimeReference 353 **/ 354 RuntimeReference alternative() @safe nothrow pure { 355 return this.alternative_; 356 } 357 358 /** 359 Resolve the reference, to referenced component. 360 361 Resolve the reference, to referenced component. 362 363 Params: 364 locator = an optional source of components used to resolve reference 365 366 Returns: 367 Object the actual object, or component that is wrapped in Wrapper object. 368 **/ 369 Object get(Locator!() locator) { 370 import aermicioi.aedi.exception.not_found_exception : NotFoundException; 371 372 try { 373 374 return this.original.get(locator); 375 } catch (NotFoundException e) { 376 377 return this.alternative.get(locator); 378 } 379 } 380 381 override string toString() @trusted { 382 return text("OptRef(", this.original, ", ", this.alternative, ")"); 383 } 384 } 385 } 386 387 /** 388 Create a reference with type enforcement. 389 390 The resulting reference will check for returned object to be compliant 391 with specified T type, otherwise a not found exception is thrown. 392 393 Params: 394 reference = reference to be enforced with expected type 395 T = expected type returned from container 396 397 Returns: 398 TypeEnforcedRuntimeReference!T enforced reference with type. 399 **/ 400 auto typeEnforcedRef(T)(RuntimeReference reference) { 401 return new TypeEnforcedRuntimeReference!T(reference); 402 } 403 404 /** 405 ditto 406 **/ 407 @safe class TypeEnforcedRuntimeReference(T) : RuntimeReference { 408 private { 409 RuntimeReference reference; 410 } 411 412 public { 413 /** 414 Constructor for enforced type reference accepting reference to be enforced. 415 416 Params: 417 reference = reference to enforce with type 418 **/ 419 this(RuntimeReference reference) 420 in (reference !is null, "Expected a reference, not null value") { 421 this.reference = reference; 422 } 423 424 /** 425 Resolve the reference, to referenced component. 426 427 Resolve the reference, to referenced component. 428 429 Params: 430 locator = an optional source of components used to resolve reference 431 432 Returns: 433 Object the actual object, or component that is wrapped in Wrapper object. 434 **/ 435 Object get(Locator!() locator) @trusted { 436 import aermicioi.aedi.exception.not_found_exception : NotFoundException; 437 import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException; 438 import aermicioi.aedi.storage.wrapper : unwrap; 439 440 Object value = this.reference.get(locator); 441 442 try { 443 cast(void) value.unwrap!T; 444 } catch (InvalidCastException exception) { 445 446 throw new NotFoundException(text( 447 "The component was found using ", this.reference, " however it wasn't of expected type ", typeid(T), " but of ", value, "." 448 ), null, exception); 449 } 450 451 return value; 452 } 453 454 override string toString() @trusted { 455 return text("TypeEnfRef!(", typeid(T), ")(", this.reference, ")"); 456 } 457 } 458 } 459 460 /** 461 Resolve a reference, and attempt to convert to component of type T. 462 463 See: 464 aermicioi.aedi.storage.wrapper : unwrap for downcasting semantics. 465 466 Params: 467 T = the expected type of resolved component. 468 locator = optional source of components for resolving reference 469 470 Throws: 471 InvalidCastException when resolved component is not of expected type. 472 473 Returns: 474 T referenced object 475 Wrapper!T referenced component that is not of Object subclass. 476 **/ 477 @trusted auto resolve(T)(RuntimeReference reference, Locator!() locator) { 478 import aermicioi.aedi.storage.wrapper : unwrap; 479 return reference.get(locator).unwrap!T; 480 } 481 482 /** 483 ditto 484 **/ 485 @trusted auto ref Z resolve(T, Z)(auto ref Z reference, Locator!() locator) 486 if (!is(Z : RuntimeReference)) { 487 return reference; 488 } 489 490 /** 491 Alias to fullyQualifiedName from std.traits, for shorter notation. 492 **/ 493 template name(alias T) 494 if (is(typeof(T))) { 495 alias name = fullyQualifiedName!(typeof(T)); 496 } 497 498 /** 499 ditto 500 **/ 501 template name(T) { 502 alias name = fullyQualifiedName!T; 503 } 504 505 RuntimeReference withDefault(T)(RuntimeReference reference, T defaults) { 506 import aermicioi.aedi.factory.generic_factory : genericFactory, ValueInstanceFactory; 507 auto factory = genericFactory!T(null); 508 509 factory.setInstanceFactory(new ValueInstanceFactory!T(defaults)); 510 return reference.alternate(factory.anonymous); 511 } 512 513 auto transformToReference(string reference, string symbol) { 514 string delegate (string) toTypeGen = (s) => "toType!(" ~ s ~ ")"; 515 string delegate (string, string) typeEnforcedRefGen = (t, s) => "typeEnforcedRef!(" ~ toTypeGen(t) ~ ")(" ~ s ~ ")"; 516 string delegate (string) identifierGen = (s) => "__traits(identifier, " ~ s ~ ")"; 517 string delegate (string) lrefGen = (s) => s ~ ".lref"; 518 string delegate (string) typeLrefGen = (s) => "lref!(" ~ s ~ ")"; 519 string delegate (string, string) alternateGen = (f, s) => f ~ ".alternate(" ~ s ~ ")"; 520 return " 521 import aermicioi.aedi.util.traits : toType; 522 static if (is(typeof(" ~ identifierGen(symbol) ~ "))) { 523 " ~ reference ~ " = " ~ alternateGen(typeEnforcedRefGen(symbol, lrefGen(identifierGen(symbol))), typeEnforcedRefGen(symbol, typeLrefGen(toTypeGen(symbol)))) ~ "; 524 } 525 526 if (" ~ reference ~ " is null) { 527 " ~ reference ~ " = " ~ typeLrefGen(toTypeGen(symbol)) ~ "; 528 } 529 530 static if (is(typeof(((" ~ symbol ~ " arg) => arg[0])()))) { 531 import aermicioi.aedi.factory.reference : withDefault; 532 " ~ reference ~ " = " ~ reference ~ ".withDefault(((" ~ symbol ~ " arg) => arg[0])()); 533 } 534 "; 535 } 536 537 auto makeFunctionParameterReferences(alias FunctionType, alias transformer = transformToReference)() { 538 static if (is(FunctionTypeOf!FunctionType params == __parameters)) { 539 import std.meta : Repeat; 540 import std.conv : to; 541 import aermicioi.aedi.util.typecons : tuple; 542 import aermicioi.aedi.util.traits : toType; 543 544 Repeat!(params.length, RuntimeReference) references; 545 546 static foreach (index, reference; references) {{ 547 mixin(transformer("references[" ~ index.to!string ~ "]", "params[" ~ index.to!string ~ ".." ~ (index + 1).to!string ~ "]")); 548 }} 549 550 return tuple(references); 551 } 552 }