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 Alexandru Ermicioi 29 **/ 30 module aermicioi.aedi_property_reader.convertor.accessor; 31 32 import aermicioi.aedi.storage.allocator_aware; 33 import aermicioi.aedi.configurer.annotation.annotation; 34 import aermicioi.aedi_property_reader.convertor.exception : NotFoundException; 35 import aermicioi.aedi_property_reader.convertor.exception : InvalidCastException; 36 import aermicioi.aedi_property_reader.convertor.exception; 37 import aermicioi.aedi.util.traits; 38 import aermicioi.aedi_property_reader.convertor.traits : isD, n; 39 import aermicioi.aedi_property_reader.convertor.placeholder; 40 import taggedalgebraic; 41 import std.array; 42 import std.conv; 43 import std.algorithm; 44 import std.range; 45 import std.exception : enforce; 46 import std.variant; 47 import std.traits; 48 import std.meta; 49 import std.experimental.logger; 50 import std.experimental.allocator; 51 52 /** 53 Interface for objects that are able to get child component out of parent one. 54 55 The intent of property accessor is to provide runtime means of accessing various properties 56 out of a component which could be a D object, or struct. The implementation is free in allocation 57 policies in order to satisfy the property access, though the burden of freeing allocated memory 58 is left on code using the accessor. 59 **/ 60 interface PropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 61 62 63 /** 64 Get a property out of component 65 66 Params: 67 component = a component which has some properties identified by property. 68 property = property of component that needs to be extracted, even by allocating some memory. 69 allocator = optional allocator used only for allocation of accessed components. It should not be used for other purposes whatsoever. 70 Throws: 71 NotFoundException in case when no requested property is available. 72 InvalidArgumentException in case when passed arguments are somehow invalid for use. 73 Returns: 74 FieldType accessed property. 75 **/ 76 FieldType access(ComponentType component, in KeyType property, RCIAllocator allocator = theAllocator) const; 77 78 /** 79 Check if requested property is present in component. 80 81 Check if requested property is present in component. 82 The method could have allocation side effects due to the fact that 83 it is not restricted in calling access method of the accessor. 84 85 Params: 86 component = component which is supposed to have property 87 property = speculated property that is to be tested if it is present in component 88 allocator = optional allocator used only for allocation of accessed/tested components. It should not be used for other purposes whatsoever. 89 Returns: 90 true if property is in component 91 **/ 92 bool has(ComponentType component, in KeyType property, RCIAllocator allocator = theAllocator) const nothrow; 93 94 /** 95 Identify the type of supported component. 96 97 Identify the type of supported component. It returns type info of component 98 if it is supported by accessor, otherwise it will return typeid(void) denoting that 99 the type isn't supported by accessor. The accessor is not limited to returning the type 100 info of passed component, it can actually return type info of super type or any type 101 given the returned type is implicitly convertible or castable to ComponentType. 102 103 Params: 104 component = the component for which accessor should identify the underlying type 105 106 Returns: 107 TypeInfo type information about passed component, or typeid(void) if component is not supported. 108 **/ 109 TypeInfo componentType(ComponentType component) const nothrow; 110 } 111 112 /** 113 An accessor that queries stored accessors for component. 114 **/ 115 class AggregatePropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 116 PropertyAccessor!(ComponentType, FieldType, KeyType) { 117 118 private { 119 120 PropertyAccessor!(ComponentType, FieldType)[] accessors_; 121 } 122 123 public { 124 125 /** 126 Constructor for aggregate property accessor. 127 128 Params: 129 accessors = list of accessors used to extract fields from component. 130 **/ 131 this(PropertyAccessor!(ComponentType, FieldType)[] accessors...) { 132 this.accessors = accessors.dup; 133 } 134 135 @property { 136 /** 137 Set accessors 138 139 Params: 140 accessors = accessors that implement various logic of accessing a property out of component 141 142 Returns: 143 typeof(this) 144 **/ 145 typeof(this) accessors( 146 PropertyAccessor!(ComponentType, FieldType, KeyType)[] accessors 147 ) @safe nothrow pure { 148 this.accessors_ = accessors; 149 150 return this; 151 } 152 153 /** 154 Get accessors 155 156 Returns: 157 PropertyAccessor!(ComponentType, FieldType, KeyType) 158 **/ 159 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)[]) accessors() @safe nothrow pure inout { 160 return this.accessors_; 161 } 162 } 163 164 /** 165 Get a property out of component 166 167 Params: 168 component = a component which has some properties identified by property. 169 Throws: 170 NotFoundException in case when no requested property is available. 171 InvalidArgumentException in case when passed arguments are somehow invalid for use. 172 Returns: 173 FieldType accessed property. 174 **/ 175 FieldType access(ComponentType component, in KeyType property, RCIAllocator allocator = theAllocator) const { 176 177 foreach (accessor; this.accessors) { 178 179 if (accessor.has(component, property)) { 180 181 return accessor.access(component, property, allocator); 182 } 183 } 184 185 import aermicioi.aedi_property_reader.convertor.exception : NotFoundException; 186 throw new NotFoundException("Could not find element ${property} in ${component}", property.to!string, component.to!string); 187 } 188 189 /** 190 Check if requested property is present in component. 191 192 Check if requested property is present in component. 193 The method could have allocation side effects due to the fact that 194 it is not restricted in calling access method of the accessor. 195 196 Params: 197 component = component which is supposed to have property 198 property = speculated property that is to be tested if it is present in component 199 Returns: 200 true if property is in component 201 **/ 202 bool has(ComponentType component, in KeyType property, RCIAllocator allocator = theAllocator) const nothrow { 203 204 foreach (accessor; this.accessors) { 205 206 if (accessor.has(component, property, allocator)) { 207 return true; 208 } 209 } 210 211 return false; 212 } 213 214 /** 215 Identify the type of supported component. 216 217 Identify the type of supported component. It returns type info of component 218 if it is supported by accessor, otherwise it will return typeid(void) denoting that 219 the type isn't supported by accessor. The accessor is not limited to returning the type 220 info of passed component, it can actually return type info of super type or any type 221 given the returned type is implicitly convertible or castable to ComponentType. 222 223 Params: 224 component = the component for which accessor should identify the underlying type 225 226 Returns: 227 TypeInfo type information about passed component, or typeid(void) if component is not supported. 228 **/ 229 TypeInfo componentType(ComponentType component) const nothrow { 230 return typeid(ComponentType); 231 } 232 } 233 } 234 235 /** 236 An accessor that splits property into chunks using a separator, and recursively queries an accessor for next property from 237 returned child property. 238 239 An accessor that splits property into chunks using a separator,and recursively queries an accessor for next property from 240 returned child property. 241 ImplSpec: 242 Since a single accessor is used to fetch subsequent properties, a constraint on property accessors are placed, such as 243 field of a component should be implicitly convertible to component type in order for it to be used to get next child 244 in property chain. 245 **/ 246 class PropertyPathAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 247 PropertyAccessor!(ComponentType, FieldType, KeyType) 248 if (isImplicitlyConvertible!(FieldType, ComponentType) && isInputRange!KeyType) { 249 250 private { 251 alias QualifierOfComponent = QualifierOf!ComponentType; 252 253 PropertyAccessor!(ComponentType, FieldType) accessor_; 254 255 ElementType!KeyType separator_; 256 } 257 258 public { 259 260 /** 261 Constructor for property path accessor 262 263 Params: 264 separator = separator used to separate field identities in a field chain. 265 accessor = accessor used to access consecutively new child field. 266 **/ 267 this( 268 ElementType!KeyType separator, 269 PropertyAccessor!(ComponentType, FieldType) accessor 270 ) { 271 this.separator = separator; 272 this.accessor = accessor; 273 } 274 275 @property { 276 /** 277 Set accessor 278 279 Params: 280 accessor = accessor instance responsible for getting a property out of component 281 282 Returns: 283 typeof(this) 284 **/ 285 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 286 this.accessor_ = accessor; 287 288 return this; 289 } 290 291 /** 292 Get accessor 293 294 Returns: 295 PropertyAccessor!(ComponentType, FieldType, KeyType) 296 **/ 297 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 298 return this.accessor_; 299 } 300 301 /** 302 Set propertyAccessor 303 304 Params: 305 propertyAccessor = property splitter used to cut up the property path into multiple points 306 307 Returns: 308 typeof(this) 309 **/ 310 typeof(this) separator(ElementType!KeyType separator) @safe nothrow pure { 311 this.separator_ = separator; 312 313 return this; 314 } 315 316 /** 317 Get propertyAccessor 318 319 Returns: 320 ElementType!KeyType 321 **/ 322 inout(ElementType!KeyType) separator() @safe nothrow pure inout { 323 return this.separator_; 324 } 325 } 326 327 /** 328 Get a property out of component 329 330 Params: 331 component = a component which has some properties identified by property. 332 Throws: 333 NotFoundException in case when no requested property is available. 334 InvalidArgumentException in case when passed arguments are somehow invalid for use. 335 Returns: 336 FieldType accessed property. 337 **/ 338 QualifierOfComponent!FieldType access(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 339 import std.experimental.allocator : theAllocator, make; 340 341 auto identities = path.splitter(this.separator); 342 343 auto current = component.pack; 344 scope(exit) current.unpack; 345 346 foreach (identity; identities) { 347 348 if (this.accessor.has(current.value, identity)) { 349 350 auto field = this.accessor.access(current.value, identity, allocator).pack; 351 current.unpack; 352 current = field; 353 } else { 354 355 throw new NotFoundException(text( 356 "Could not find \"", identity, "\" in ${component} for property path of ${property}", 357 ), path.to!string, component.to!string); 358 } 359 } 360 361 return current.value; 362 } 363 364 /** 365 Check if requested property is present in component. 366 367 Check if requested property is present in component. 368 The method could have allocation side effects due to the fact that 369 it is not restricted in calling access method of the accessor. 370 371 Params: 372 component = component which is supposed to have property 373 property = speculated property that is to be tested if it is present in component 374 Returns: 375 true if property is in component 376 **/ 377 bool has(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 378 import std.experimental.allocator : theAllocator, make; 379 380 try { 381 382 auto identities = path.splitter(this.separator); 383 384 auto current = component.pack; 385 scope(exit) current.unpack; 386 387 foreach (identity; identities) { 388 if (!this.accessor.has(current.value, identity, allocator)) { 389 return false; 390 } 391 392 if (this.accessor.has(current.value, identity, allocator)) { 393 auto field = this.accessor.access(current.value, identity, allocator).pack; 394 current.unpack; 395 current = field; 396 } 397 } 398 399 return true; 400 } catch (Exception e) { 401 debug(trace) error("Failed to access a property path ", path, " due to ", e).n; 402 } 403 404 return false; 405 } 406 407 /** 408 Identify the type of supported component. 409 410 Identify the type of supported component. It returns type info of component 411 if it is supported by accessor, otherwise it will return typeid(void) denoting that 412 the type isn't supported by accessor. The accessor is not limited to returning the type 413 info of passed component, it can actually return type info of super type or any type 414 given the returned type is implicitly convertible or castable to ComponentType. 415 416 Params: 417 component = the component for which accessor should identify the underlying type 418 419 Returns: 420 TypeInfo type information about passed component, or typeid(void) if component is not supported. 421 **/ 422 TypeInfo componentType(ComponentType component) const nothrow { 423 return this.accessor.componentType(component); 424 } 425 } 426 } 427 428 /** 429 ditto 430 **/ 431 auto propertyPathAccessor(T : PropertyAccessor!(ComponentType, FieldType, KeyType), ComponentType, FieldType, KeyType) 432 (ElementType!KeyType separator, T accessor) { 433 return new PropertyPathAccessor!(ComponentType, FieldType, KeyType)(separator, accessor); 434 } 435 436 /** 437 An accessor that allows accessing of a property and it's childs with array syntax. 438 439 An accessor that allows accessing of a property and it's childs with array syntax. 440 ImplSpec: 441 Two accessors are used for getting an indexed property's child. The first one is used 442 for accessing an indexed property of a component, and the second one is used for subsequent 443 access of child components recursively. 444 **/ 445 class ArrayIndexedPropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 446 PropertyAccessor!(ComponentType, FieldType, KeyType) 447 if (isBidirectionalRange!KeyType && isImplicitlyConvertible!(FieldType, ComponentType)) { 448 449 private { 450 alias EType = ElementType!KeyType; 451 EType beggining_; 452 EType ending_; 453 454 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 455 PropertyAccessor!(ComponentType, FieldType, KeyType) indexer_; 456 } 457 458 public { 459 460 /** 461 Constructor for array indexed accessor 462 463 Params: 464 beggining = beggining token denoting the start of an array indexing 465 ending = ending token denoting the end of an array indexing 466 accessor = accessor used to access property part of indexing sequence 467 indexer = accessor used to access indexed part of indexing sequence 468 **/ 469 this( 470 EType beggining, 471 EType ending, 472 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor, 473 PropertyAccessor!(ComponentType, FieldType, KeyType) indexer 474 ) { 475 this.beggining = beggining; 476 this.ending = ending; 477 this.accessor = accessor; 478 this.indexer = indexer; 479 } 480 481 /** 482 Set beggining 483 484 Params: 485 beggining = element denoting beggining of array syntax in identity, ex. [ 486 487 Returns: 488 typeof(this) 489 **/ 490 typeof(this) beggining(EType beggining) @safe nothrow pure { 491 this.beggining_ = beggining; 492 493 return this; 494 } 495 496 /** 497 Get beggining 498 499 Returns: 500 EType 501 **/ 502 inout(EType) beggining() @safe nothrow pure inout { 503 return this.beggining_; 504 } 505 506 /** 507 Set ending 508 509 Params: 510 ending = element denoting the end of array indexing, ex. ] 511 512 Returns: 513 typeof(this) 514 **/ 515 typeof(this) ending(EType ending) @safe nothrow pure { 516 this.ending_ = ending; 517 518 return this; 519 } 520 521 /** 522 Get ending 523 524 Returns: 525 EType 526 **/ 527 inout(EType) ending() @safe nothrow pure inout { 528 return this.ending_; 529 } 530 531 /** 532 Set accessor 533 534 Params: 535 accessor = accessor used to access property part from indexed property 536 537 Returns: 538 typeof(this) 539 **/ 540 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 541 this.accessor_ = accessor; 542 543 return this; 544 } 545 546 /** 547 Get accessor 548 549 Returns: 550 PropertyAccessor!(ComponentType, FieldType, KeyType) 551 **/ 552 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 553 return this.accessor_; 554 } 555 556 /** 557 Set indexer 558 559 Params: 560 indexer = property accessor used to access element based on contents in index part of property 561 562 Returns: 563 typeof(this) 564 **/ 565 typeof(this) indexer(PropertyAccessor!(ComponentType, FieldType, KeyType) indexer) @safe nothrow pure { 566 this.indexer_ = indexer; 567 568 return this; 569 } 570 571 /** 572 Get indexer 573 574 Returns: 575 PropertyAccessor!(ComponentType, FieldType, KeyType) 576 **/ 577 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) indexer() @safe nothrow pure inout { 578 return this.indexer_; 579 } 580 581 /** 582 Get a property out of component 583 584 Params: 585 component = a component which has some properties identified by property. 586 Throws: 587 NotFoundException in case when no requested property is available. 588 InvalidArgumentException in case when passed arguments are somehow invalid for use. 589 Returns: 590 FieldType accessed property. 591 **/ 592 FieldType access(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 593 import std.experimental.allocator : make, theAllocator, dispose; 594 enforce(this.has(component, path), new NotFoundException(text("Property ${property} not found in ${component}"), path.to!string, component.to!string)); 595 596 auto splitted = path.splitter(this.beggining); 597 enforce!InvalidArgumentException( 598 !splitted.empty && !splitted.front.empty, 599 text("Malformed indexed property ", path, ", no property part found") 600 ); 601 602 auto property = this.accessor.access(component, splitted.front, allocator).pack; 603 scope(exit) property.unpack; 604 605 splitted.popFront; 606 enforce!InvalidArgumentException( 607 !splitted.empty, 608 text("Malformed indexed property ", path, ", no index part found") 609 ); 610 611 foreach (identity; splitted) { 612 enforce!InvalidArgumentException( 613 identity.endsWith(this.ending), 614 text("Malformed indexed property ", path, ", no closing ] found") 615 ); 616 617 auto field = this.indexer.access(property.value, identity.dropBack(1)).pack; 618 property.unpack; 619 property = field; 620 } 621 622 return property.value; 623 } 624 625 /** 626 Check if requested property is present in component. 627 628 Check if requested property is present in component. 629 The method could have allocation side effects due to the fact that 630 it is not restricted in calling access method of the accessor. 631 632 Params: 633 component = component which is supposed to have property 634 property = speculated property that is to be tested if it is present in component 635 Returns: 636 true if property is in component 637 **/ 638 bool has(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 639 import std.experimental.allocator : make, theAllocator, dispose; 640 641 try { 642 643 auto splitted = path.splitter(this.beggining); 644 645 if (splitted.empty) { 646 return false; 647 } 648 649 if (splitted.front.empty) { 650 return false; 651 } 652 653 if (!this.accessor.has(component, splitted.front, allocator)) { 654 return false; 655 } 656 657 auto property = this.accessor.access(component, splitted.front, allocator).pack; 658 scope(exit) property.unpack; 659 660 splitted.popFront; 661 if (splitted.empty) { 662 return false; 663 } 664 665 foreach (identity; splitted) { 666 if (!identity.endsWith(this.ending)) { 667 return false; 668 } 669 670 if (!this.indexer.has(property.value, identity.dropBack(1), allocator)) { 671 return false; 672 } 673 674 auto field = this.indexer.access(property.value, identity.dropBack(1)).pack; 675 property.unpack; 676 property = field; 677 } 678 679 return true; 680 } catch (Exception e) { 681 debug(trace) error("Failed to check existance of indexed path ", path, " due to ", e).n; 682 } 683 684 return false; 685 } 686 687 /** 688 Identify the type of supported component. 689 690 Identify the type of supported component. It returns type info of component 691 if it is supported by accessor, otherwise it will return typeid(void) denoting that 692 the type isn't supported by accessor. The accessor is not limited to returning the type 693 info of passed component, it can actually return type info of super type or any type 694 given the returned type is implicitly convertible or castable to ComponentType. 695 696 Params: 697 component = the component for which accessor should identify the underlying type 698 699 Returns: 700 TypeInfo type information about passed component, or typeid(void) if component is not supported. 701 **/ 702 TypeInfo componentType(ComponentType component) const nothrow { 703 return typeid(ComponentType); 704 } 705 } 706 } 707 708 /** 709 ditto 710 **/ 711 auto arrayIndexedPropertyAccessor 712 (T : PropertyAccessor!(ComponentType, FieldType, KeyType), ComponentType, FieldType, KeyType) 713 (ElementType!KeyType beggining, ElementType!KeyType ending, T accessor, T indexer) 714 { 715 return new ArrayIndexedPropertyAccessor!(ComponentType, FieldType, KeyType)(beggining, ending, accessor, indexer); 716 } 717 718 /** 719 Class that allows accessing properties that are wrapped in ticks. 720 **/ 721 class TickedPropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 722 PropertyAccessor!(ComponentType, FieldType, KeyType) 723 if (isBidirectionalRange!KeyType) { 724 725 private { 726 alias EType = ElementType!KeyType; 727 EType tick_; 728 729 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 730 } 731 732 public { 733 734 /** 735 Constructor for ticked accessor 736 737 Params: 738 tick = token used to tick the property 739 accessor = accessor used to access property wrapped in ticks 740 **/ 741 this(EType tick, PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 742 this.tick = tick; 743 this.accessor = accessor; 744 } 745 746 /** 747 Set tick 748 749 Params: 750 tick = ticking element used to encapsulate property 751 752 Returns: 753 typeof(this) 754 **/ 755 typeof(this) tick(EType tick) @safe nothrow pure { 756 this.tick_ = tick; 757 758 return this; 759 } 760 761 /** 762 Get tick 763 764 Returns: 765 EType 766 **/ 767 inout(EType) tick() @safe nothrow pure inout { 768 return this.tick_; 769 } 770 771 /** 772 Set accessor 773 774 Params: 775 accessor = accessor used to access property enclosed in ticks 776 777 Returns: 778 typeof(this) 779 **/ 780 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 781 this.accessor_ = accessor; 782 783 return this; 784 } 785 786 /** 787 Get accessor 788 789 Returns: 790 PropertyAccessor!(ComponentType, FieldType, KeyType) 791 **/ 792 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 793 return this.accessor_; 794 } 795 796 /** 797 Get a property out of component 798 799 Params: 800 component = a component which has some properties identified by property. 801 Throws: 802 NotFoundException in case when no requested property is available. 803 InvalidArgumentException in case when passed arguments are somehow invalid for use. 804 Returns: 805 FieldType accessed property. 806 **/ 807 FieldType access(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 808 enforce!InvalidArgumentException(this.valid(path), text("Not found or malformed ticked property ", path)); 809 810 return this.accessor.access(component, path.drop(1).dropBack(1), allocator); 811 } 812 813 /** 814 Check if requested property is present in component. 815 816 Check if requested property is present in component. 817 The method could have allocation side effects due to the fact that 818 it is not restricted in calling access method of the accessor. 819 820 Params: 821 component = component which is supposed to have property 822 property = speculated property that is to be tested if it is present in component 823 Returns: 824 true if property is in component 825 **/ 826 bool has(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 827 828 try { 829 830 return this.valid(path) && this.accessor.has(component, path.strip(this.tick), allocator); 831 } catch (Exception e) { 832 833 debug(trace) error("Failed to check if ticked property exists due to: ", e).n; 834 } 835 836 return false; 837 } 838 839 /** 840 Identify the type of supported component. 841 842 Identify the type of supported component. It returns type info of component 843 if it is supported by accessor, otherwise it will return typeid(void) denoting that 844 the type isn't supported by accessor. The accessor is not limited to returning the type 845 info of passed component, it can actually return type info of super type or any type 846 given the returned type is implicitly convertible or castable to ComponentType. 847 848 Params: 849 component = the component for which accessor should identify the underlying type 850 851 Returns: 852 TypeInfo type information about passed component, or typeid(void) if component is not supported. 853 **/ 854 TypeInfo componentType(ComponentType component) const nothrow { 855 return this.accessor.componentType(component); 856 } 857 858 private bool valid(in KeyType path) const nothrow { 859 try { 860 861 return (path.front == this.tick) && (path.back == this.tick); 862 } catch (Exception e) { 863 864 debug(trace) error("Failed to check if path is ticked due to: ", e).n; 865 } 866 867 return false; 868 } 869 } 870 } 871 872 /** 873 ditto 874 **/ 875 auto tickedAccessor 876 (T : PropertyAccessor!(ComponentType, FieldType, KeyType), ComponentType, FieldType, KeyType) 877 (ElementType!KeyType tick, T accessor) 878 { 879 return new TickedPropertyAccessor!(ComponentType, FieldType, KeyType)(tick, accessor); 880 } 881 882 /** 883 Property accessor that is decaying tagged algebraic to a type accepted by decorated accessor. 884 885 ImplSpec: 886 Any tagged algebraic will be decayed to type X which is accepted by decorated property or fail to do so. 887 Any field returned from accessor are wrapped in same tagged algebraic, therefore there is a constraint that 888 tagged algebraic should be able to hold both the component and it's field. 889 **/ 890 class TaggedElementPropertyAccessorWrapper( 891 Tagged : TaggedAlgebraic!Y, 892 PropertyAccessorType : PropertyAccessor!(X, Z, KeyType), 893 X, 894 Z, 895 KeyType = string, 896 Y 897 ) : PropertyAccessor!(Tagged, Tagged, KeyType) { 898 899 private { 900 PropertyAccessorType accessor_; 901 } 902 903 public { 904 905 /** 906 Constructor for tagged element property accessor. 907 908 Params: 909 accessor = underlying accessor used to access fields out of a particular component type in a tagged algebraic type. 910 **/ 911 this(PropertyAccessorType accessor) { 912 this.accessor = accessor; 913 } 914 915 @property { 916 /** 917 Set accessor 918 919 Params: 920 accessor = the property accessor that is wrapped 921 922 Returns: 923 typeof(this) 924 **/ 925 typeof(this) accessor(PropertyAccessorType accessor) @safe nothrow pure { 926 this.accessor_ = accessor; 927 928 return this; 929 } 930 931 /** 932 Get accessor 933 934 Returns: 935 PropertyAccessorType 936 **/ 937 inout(PropertyAccessorType) accessor() @safe nothrow pure inout { 938 return this.accessor_; 939 } 940 } 941 942 /** 943 Get a property out of component 944 945 Params: 946 component = a component which has some properties identified by property. 947 Throws: 948 NotFoundException in case when no requested property is available. 949 InvalidArgumentException in case when passed arguments are somehow invalid for use. 950 Returns: 951 FieldType accessed property. 952 **/ 953 Tagged access(Tagged component, in KeyType property, RCIAllocator allocator = theAllocator) const 954 { 955 if (this.has(component, property)) { 956 return Tagged(this.accessor.access(cast(X) component, property, allocator)); 957 } 958 959 import aermicioi.aedi_property_reader.convertor.exception : NotFoundException; 960 import std.conv : text; 961 throw new NotFoundException("${component} does not have ${property}", property.to!string, component.to!string); 962 } 963 964 /** 965 Check if requested property is present in component. 966 967 Check if requested property is present in component. 968 The method could have allocation side effects due to the fact that 969 it is not restricted in calling access method of the accessor. 970 971 Params: 972 component = component which is supposed to have property 973 property = speculated property that is to be tested if it is present in component 974 Returns: 975 true if property is in component 976 **/ 977 bool has(Tagged component, in KeyType property, RCIAllocator allocator = theAllocator) const nothrow { 978 try { 979 980 import std.meta : staticMap; 981 982 static foreach (e; staticMap!(identifier, EnumMembers!(Tagged.Kind))) { 983 static if (mixin("is(typeof(Y." ~ e ~ ") : X)")) { 984 if (mixin("component.Kind." ~ e ~ " == component.kind")) { 985 986 return this.accessor.has(cast(X) component, property, allocator); 987 } 988 989 return false; 990 } 991 } 992 993 assert(false, "TaggedAlgebraic does not have " ~ fullyQualifiedName!X ~ " as member"); 994 995 } catch (Exception e) { 996 debug(trace) error("Failed to unwrap tagged component ", component.kind, " due to ", e).n; 997 } 998 999 return false; 1000 } 1001 1002 /** 1003 Identify the type of supported component. 1004 1005 Identify the type of supported component. It returns type info of component 1006 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1007 the type isn't supported by accessor. The accessor is not limited to returning the type 1008 info of passed component, it can actually return type info of super type or any type 1009 given the returned type is implicitly convertible or castable to ComponentType. 1010 1011 Params: 1012 component = the component for which accessor should identify the underlying type 1013 1014 Returns: 1015 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1016 **/ 1017 TypeInfo componentType(Tagged component) const nothrow { 1018 try { 1019 1020 import std.meta : staticMap; 1021 1022 static foreach (e; staticMap!(identifier, EnumMembers!(Tagged.Kind))) { 1023 static if (mixin("is(typeof(Y." ~ e ~ ") : X)")) { 1024 if (mixin("component.Kind." ~ e ~ " == component.kind")) { 1025 1026 return this.accessor.componentType(cast(X) component); 1027 } 1028 } 1029 } 1030 1031 } catch (Exception e) { 1032 debug(trace) error("Failed to unwrap tagged component ", component.kind, " due to ", e).n; 1033 } 1034 1035 assert(false, "Got stored a value that is not in tagged algebraic"); 1036 } 1037 } 1038 } 1039 1040 /** 1041 ditto 1042 **/ 1043 auto taggedAccessor(Tagged, T : PropertyAccessor!(Composite, Field, Key), Composite, Field, Key)(T accessor) { 1044 return new TaggedElementPropertyAccessorWrapper!(Tagged, T)(accessor); 1045 } 1046 1047 /** 1048 Implements logic for accessing associative arrays. 1049 **/ 1050 @component 1051 class AssociativeArrayAccessor(ComponentType : Type[Key], Type, Key) : PropertyAccessor!(ComponentType, Type, Key) { 1052 public { 1053 1054 /** 1055 Get a property out of component 1056 1057 Params: 1058 component = a component which has some properties identified by property. 1059 Throws: 1060 NotFoundException in case when no requested property is available. 1061 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1062 Returns: 1063 FieldType accessed property. 1064 **/ 1065 Type access(ComponentType component, in Key property, RCIAllocator allocator = theAllocator) const { 1066 auto peek = property in component; 1067 enforce(peek, new NotFoundException("Could not find ${property} in associative array ${component}", property.to!string, component.to!string)); 1068 1069 return *peek; 1070 } 1071 1072 /** 1073 Check if requested property is present in component. 1074 1075 Check if requested property is present in component. 1076 The method could have allocation side effects due to the fact that 1077 it is not restricted in calling access method of the accessor. 1078 1079 Params: 1080 component = component which is supposed to have property 1081 property = speculated property that is to be tested if it is present in component 1082 Returns: 1083 true if property is in component 1084 **/ 1085 bool has(ComponentType component, in Key property, RCIAllocator allocator = theAllocator) const nothrow { 1086 return (property in component) !is null; 1087 } 1088 1089 /** 1090 Identify the type of supported component. 1091 1092 Identify the type of supported component. It returns type info of component 1093 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1094 the type isn't supported by accessor. The accessor is not limited to returning the type 1095 info of passed component, it can actually return type info of super type or any type 1096 given the returned type is implicitly convertible or castable to ComponentType. 1097 1098 Params: 1099 component = the component for which accessor should identify the underlying type 1100 1101 Returns: 1102 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1103 **/ 1104 TypeInfo componentType(ComponentType component) const nothrow { 1105 return typeid(ComponentType); 1106 } 1107 } 1108 } 1109 1110 /** 1111 Accessor implementing array access. 1112 **/ 1113 @component 1114 class ArrayAccessor(ComponentType : Type[], Type) : PropertyAccessor!(ComponentType, Type, size_t) { 1115 1116 public { 1117 1118 /** 1119 Get a property out of component 1120 1121 Params: 1122 component = a component which has some properties identified by property. 1123 Throws: 1124 NotFoundException in case when no requested property is available. 1125 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1126 Returns: 1127 FieldType accessed property. 1128 **/ 1129 Type access(ComponentType component, in size_t property, RCIAllocator allocator = theAllocator) const { 1130 enforce( 1131 this.has(component, property), 1132 new NotFoundException("Could not find property ${property} in array ${component}", property.to!string, component.to!string) 1133 ); 1134 1135 return component[property]; 1136 } 1137 1138 /** 1139 Check if requested property is present in component. 1140 1141 Check if requested property is present in component. 1142 The method could have allocation side effects due to the fact that 1143 it is not restricted in calling access method of the accessor. 1144 1145 Params: 1146 component = component which is supposed to have property 1147 property = speculated property that is to be tested if it is present in component 1148 Returns: 1149 true if property is in component 1150 **/ 1151 bool has(ComponentType component, in size_t property, RCIAllocator allocator = theAllocator) const nothrow { 1152 return property < component.length; 1153 } 1154 1155 /** 1156 Identify the type of supported component. 1157 1158 Identify the type of supported component. It returns type info of component 1159 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1160 the type isn't supported by accessor. The accessor is not limited to returning the type 1161 info of passed component, it can actually return type info of super type or any type 1162 given the returned type is implicitly convertible or castable to ComponentType. 1163 1164 Params: 1165 component = the component for which accessor should identify the underlying type 1166 1167 Returns: 1168 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1169 **/ 1170 TypeInfo componentType(ComponentType component) const nothrow { 1171 return typeid(ComponentType); 1172 } 1173 } 1174 } 1175 1176 /** 1177 Accessor implementing logic for accessingstd.variant. 1178 1179 ImplSpec: 1180 The accessor can accept any variant of ComponentType. The restriction is that the underlying stored data should be 1181 of either associative array or dynamic array in order to access the contents of them. The return type is as well a 1182 variant that must be able to store associative's array field, or arrays elements. 1183 **/ 1184 @component 1185 class VariantAccessor( 1186 ComponentType, 1187 FieldType = ComponentType, 1188 KeyType = ComponentType 1189 ) : PropertyAccessor!(ComponentType, FieldType, KeyType) 1190 if ( 1191 is(ComponentType : VariantN!(ComponentSize, ComponentTypes), size_t ComponentSize, ComponentTypes...) && 1192 is(FieldType : VariantN!(FieldSize, FieldTypes), size_t FieldSize, FieldTypes...) && 1193 is(KeyType : VariantN!(KeySize, KeyTypes), size_t KeySize, KeyTypes...) 1194 ) { 1195 1196 private { 1197 static if (is(ComponentType : VariantN!(ComponentSize, CTypes), size_t ComponentSize, CTypes...)) { 1198 alias ComponentTypes = CTypes; 1199 } 1200 1201 static if (is(FieldType : VariantN!(FieldSize, FTypes), size_t FieldSize, FTypes...)) { 1202 alias FieldTypes = FTypes; 1203 } 1204 1205 static if (is(KeyType : VariantN!(KeySize, KTypes), size_t KeySize, KTypes...)) { 1206 alias KeyTypes = KTypes; 1207 } 1208 } 1209 1210 public { 1211 static foreach (KType; KeyTypes) { 1212 /** 1213 Get a property out of component 1214 1215 Params: 1216 component = a component which has some properties identified by property. 1217 Throws: 1218 NotFoundException in case when no requested property is available. 1219 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1220 Returns: 1221 FieldType accessed property. 1222 **/ 1223 FieldType access(ComponentType component, KType key, RCIAllocator allocator = theAllocator) const { 1224 return this.access(component, Unqual!KeyType(key)); 1225 } 1226 1227 /** 1228 Check if requested property is present in component. 1229 1230 Check if requested property is present in component. 1231 The method could have allocation side effects due to the fact that 1232 it is not restricted in calling access method of the accessor. 1233 1234 Params: 1235 component = component which is supposed to have property 1236 property = speculated property that is to be tested if it is present in component 1237 Returns: 1238 true if property is in component 1239 **/ 1240 bool has(ComponentType component, KType key, RCIAllocator allocator = theAllocator) const nothrow { 1241 return this.has(component, Unqual!KeyType(key), allocator); 1242 } 1243 } 1244 1245 /** 1246 Get a property out of component 1247 1248 Params: 1249 component = a component which has some properties identified by property. 1250 Throws: 1251 NotFoundException in case when no requested property is available. 1252 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1253 Returns: 1254 FieldType accessed property. 1255 **/ 1256 FieldType access(ComponentType component, in KeyType key, RCIAllocator allocator = theAllocator) const { 1257 static foreach (Component; ComponentTypes) {{ 1258 static if ( 1259 is(Component : X[W], X, W) && 1260 anySatisfy!(ApplyLeft!(isD, X), FieldTypes) && 1261 anySatisfy!(ApplyLeft!(isD, W), KeyTypes) 1262 ) { 1263 if ( 1264 (component.type is typeid(Component)) && 1265 (key.type is typeid(W)) && 1266 this.has(component, key) 1267 ) { 1268 return Unqual!FieldType(component.get!Component[key.get!W]); 1269 } 1270 } 1271 1272 static if ( 1273 is(Component : Y[], Y) && 1274 anySatisfy!(ApplyLeft!(isD, Y), FieldTypes) && 1275 anySatisfy!(ApplyLeft!(isD, size_t), KeyTypes) 1276 ) { 1277 if ( 1278 (component.type is typeid(Component)) && 1279 (key.type is typeid(size_t)) && 1280 this.has(component, key) 1281 ) { 1282 return Unqual!FieldType(component.get!Component[key.get!size_t]); 1283 } 1284 } 1285 }} 1286 1287 throw new NotFoundException("Could not find ${property} in ${component}", key.to!string, component.to!string); 1288 } 1289 1290 /** 1291 Check if requested property is present in component. 1292 1293 Check if requested property is present in component. 1294 The method could have allocation side effects due to the fact that 1295 it is not restricted in calling access method of the accessor. 1296 1297 Params: 1298 component = component which is supposed to have property 1299 property = speculated property that is to be tested if it is present in component 1300 Returns: 1301 true if property is in component 1302 **/ 1303 bool has(ComponentType component, in KeyType key, RCIAllocator allocator = theAllocator) const nothrow { 1304 try { 1305 static foreach (Component; ComponentTypes) {{ 1306 static if ( 1307 is(Component : X[W], X, W) && 1308 anySatisfy!(ApplyLeft!(isD, X), FieldTypes) && 1309 anySatisfy!(ApplyLeft!(isD, W), KeyTypes) 1310 ) { 1311 if ( 1312 (component.type is typeid(Component)) && 1313 (key.type is typeid(W)) && 1314 ((key.get!W in component.get!Component) !is null) 1315 ) { 1316 return true; 1317 } 1318 } 1319 1320 static if ( 1321 is(Component : Y[], Y) && 1322 anySatisfy!(ApplyLeft!(isD, Y), FieldTypes) && 1323 anySatisfy!(ApplyLeft!(isD, size_t), KeyTypes) 1324 ) { 1325 if ( 1326 (component.type is typeid(Component)) && 1327 (key.type is typeid(size_t)) && 1328 (key.get!size_t < component.get!Component.length) 1329 ) { 1330 return true; 1331 } 1332 } 1333 }} 1334 } catch (Exception e) { 1335 debug(trace) error("Accessing contents of a variant failed with following exception: ", e).n; 1336 } 1337 1338 return false; 1339 } 1340 1341 /** 1342 Identify the type of supported component. 1343 1344 Identify the type of supported component. It returns type info of component 1345 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1346 the type isn't supported by accessor. The accessor is not limited to returning the type 1347 info of passed component, it can actually return type info of super type or any type 1348 given the returned type is implicitly convertible or castable to ComponentType. 1349 1350 Params: 1351 component = the component for which accessor should identify the underlying type 1352 1353 Returns: 1354 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1355 **/ 1356 TypeInfo componentType(ComponentType component) const nothrow { 1357 return typeid(ComponentType); 1358 } 1359 } 1360 } 1361 1362 /** 1363 Accessor that accepts ComponentType with it's type erased up to Object or wrapped in a Placeholder!ComponentType if it is 1364 not rooted into Object class (i.e. any value type, and extern c++ objects) 1365 1366 ImplSpec: 1367 The accessor will accept any object, after which it will attempt to downcast it's original type ComponentType if it is 1368 rooted in Object, otherwise the object will be downcasted to Placeholder!ComponentType. Failing to do so will result in 1369 an exception 1370 **/ 1371 template RuntimeCompositeAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 1372 1373 private alias WithComponentStorageClass = QualifierOf!ComponentType; 1374 1375 class RuntimeCompositeAccessor : 1376 PropertyAccessor!(WithComponentStorageClass!Object, FieldType, KeyType) { 1377 1378 import aermicioi.aedi_property_reader.convertor.placeholder : identify, unwrap; 1379 private { 1380 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 1381 } 1382 1383 public { 1384 /** 1385 Constructor for runtime composite accessor. 1386 1387 Params: 1388 accessor = underlying accessor used to access downcasted component. 1389 **/ 1390 this(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 1391 this.accessor = accessor; 1392 } 1393 1394 /** 1395 Set accessor 1396 1397 Params: 1398 accessor = underlying accessor working on unwrapped element 1399 1400 Returns: 1401 typeof(this) 1402 **/ 1403 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 1404 this.accessor_ = accessor; 1405 1406 return this; 1407 } 1408 1409 /** 1410 Get accessor 1411 1412 Returns: 1413 PropertyAccessor!(ComponentType, FieldType, KeyType) 1414 **/ 1415 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 1416 return this.accessor_; 1417 } 1418 1419 /** 1420 Get a property out of component 1421 1422 Params: 1423 component = a component which has some properties identified by property. 1424 Throws: 1425 NotFoundException in case when no requested property is available. 1426 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1427 Returns: 1428 FieldType accessed property. 1429 **/ 1430 FieldType access(WithComponentStorageClass!Object component, in KeyType path, RCIAllocator allocator = theAllocator) const { 1431 enforce!InvalidArgumentException( 1432 this.isValidComponent(component), 1433 text("Invalid component passed ", component.identify, " when expected ", typeid(ComponentType)) 1434 ); 1435 1436 enforce(this.has(component, path), new NotFoundException("Could not find property ${property} in ${component}", path.to!string, component.to!string)); 1437 1438 return this.accessor.access(component.unwrap!ComponentType, path, allocator); 1439 } 1440 1441 /** 1442 Check if requested property is present in component. 1443 1444 Check if requested property is present in component. 1445 The method could have allocation side effects due to the fact that 1446 it is not restricted in calling access method of the accessor. 1447 1448 Params: 1449 component = component which is supposed to have property 1450 property = speculated property that is to be tested if it is present in component 1451 Returns: 1452 true if property is in component 1453 **/ 1454 bool has(WithComponentStorageClass!Object component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 1455 1456 return this.isValidComponent(component) && 1457 this.accessor.has(component.unwrap!ComponentType, path); 1458 } 1459 1460 /** 1461 Identify the type of supported component. 1462 1463 Identify the type of supported component. It returns type info of component 1464 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1465 the type isn't supported by accessor. The accessor is not limited to returning the type 1466 info of passed component, it can actually return type info of super type or any type 1467 given the returned type is implicitly convertible or castable to ComponentType. 1468 1469 Params: 1470 component = the component for which accessor should identify the underlying type 1471 1472 Returns: 1473 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1474 **/ 1475 TypeInfo componentType(WithComponentStorageClass!Object component) const nothrow { 1476 if (component.identify is typeid(ComponentType)) { 1477 1478 return this.accessor.componentType(component.unwrap!ComponentType); 1479 } 1480 1481 return typeid(void); 1482 } 1483 } 1484 1485 private { 1486 bool isValidComponent(WithComponentStorageClass!Object component) const nothrow { 1487 bool result = component.identify is typeid(ComponentType); 1488 1489 static if (is(ComponentType : Object) || is(ComponentType : const Object) || is(ComponentType : immutable Object)) { 1490 result = result || (component.identify is typeid(Unqual!ComponentType)); 1491 } 1492 1493 return result; 1494 } 1495 } 1496 } 1497 } 1498 1499 /** 1500 An accessor that erases returned property's type. 1501 1502 ImplSpec: 1503 The accessor will simply upcast any property field to Object if they are rooted in Object class, otherwise 1504 it will allocate a Placeholder!FieldType for returned value and return it erased up to Object class. As such 1505 it is advised to use an allocator that will dispose automatically it's contents once they are not required. 1506 The accessor itself is not responsible for deallocation of placeholders that it returned. As a consequence 1507 no components should point to placeholders allocated by this accessor. 1508 **/ 1509 template WrappingFieldAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 1510 1511 private alias WithStorageOfFieldType = QualifierOf!FieldType; 1512 1513 class WrappingFieldAccessor : 1514 PropertyAccessor!(ComponentType, WithStorageOfFieldType!Object, KeyType) { 1515 import aermicioi.aedi_property_reader.convertor.placeholder : identify, unwrap; 1516 1517 private { 1518 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 1519 } 1520 1521 public { 1522 1523 /** 1524 Constructor for runtime field accessor. 1525 1526 Params: 1527 accessor = accessor used to access fields out of component that are to be type erased on return. 1528 **/ 1529 this(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 1530 this.accessor = accessor; 1531 } 1532 1533 /** 1534 Set accessor 1535 1536 Params: 1537 accessor = underlying accessor working on unwrapped element 1538 1539 Returns: 1540 typeof(this) 1541 **/ 1542 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 1543 this.accessor_ = accessor; 1544 1545 return this; 1546 } 1547 1548 /** 1549 Get accessor 1550 1551 Returns: 1552 PropertyAccessor!(ComponentType, FieldType, KeyType) 1553 **/ 1554 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 1555 return this.accessor_; 1556 } 1557 1558 /** 1559 Get a property out of component 1560 1561 Params: 1562 component = a component which has some properties identified by property. 1563 Throws: 1564 NotFoundException in case when no requested property is available. 1565 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1566 Returns: 1567 FieldType accessed property. 1568 **/ 1569 WithStorageOfFieldType!Object access(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 1570 import aermicioi.aedi_property_reader.convertor.placeholder : pack; 1571 enforce(this.has(component, path), new NotFoundException("Could not find property ${property}in ${component}", path.to!string, component.to!string)); 1572 1573 return cast(WithStorageOfFieldType!Object) this.accessor.access(component, path, allocator).pack(allocator); 1574 } 1575 1576 /** 1577 Check if requested property is present in component. 1578 1579 Check if requested property is present in component. 1580 The method could have allocation side effects due to the fact that 1581 it is not restricted in calling access method of the accessor. 1582 1583 Params: 1584 component = component which is supposed to have property 1585 property = speculated property that is to be tested if it is present in component 1586 Returns: 1587 true if property is in component 1588 **/ 1589 bool has(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 1590 1591 return this.accessor.has(component, path, allocator); 1592 } 1593 1594 /** 1595 Identify the type of supported component. 1596 1597 Identify the type of supported component. It returns type info of component 1598 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1599 the type isn't supported by accessor. The accessor is not limited to returning the type 1600 info of passed component, it can actually return type info of super type or any type 1601 given the returned type is implicitly convertible or castable to ComponentType. 1602 1603 Params: 1604 component = the component for which accessor should identify the underlying type 1605 1606 Returns: 1607 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1608 **/ 1609 TypeInfo componentType(ComponentType component) const nothrow { 1610 return this.accessor.componentType(component); 1611 } 1612 1613 } 1614 } 1615 } 1616 1617 /** 1618 An accessor that erases component type's. 1619 1620 ImplSpec: 1621 The accessor will simply upcast any property field to Object if they are rooted in Object class, otherwise 1622 it will allocate a Placeholder!FieldType for returned value and return it erased up to Object class. As such 1623 it is advised to use an allocator that will dispose automatically it's contents once they are not required. 1624 The accessor itself is not responsible for deallocation of placeholders that it returned. As a consequence 1625 no components should point to placeholders allocated by this accessor. 1626 **/ 1627 template WrappingComponentAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 1628 1629 private alias WithStorageOfFieldType = QualifierOf!FieldType; 1630 1631 class WrappingComponentAccessor : PropertyAccessor!(ComponentType, FieldType, KeyType) { 1632 import aermicioi.aedi_property_reader.convertor.placeholder : identify, unwrap; 1633 1634 private { 1635 PropertyAccessor!(Object, FieldType, KeyType) accessor_; 1636 } 1637 1638 public { 1639 1640 /** 1641 Constructor for runtime field accessor. 1642 1643 Params: 1644 accessor = accessor used to access fields out of component that are to be type erased on return. 1645 **/ 1646 this(PropertyAccessor!(Object, FieldType, KeyType) accessor) { 1647 this.accessor = accessor; 1648 } 1649 1650 /** 1651 Set accessor 1652 1653 Params: 1654 accessor = underlying accessor working on unwrapped element 1655 1656 Returns: 1657 typeof(this) 1658 **/ 1659 typeof(this) accessor(PropertyAccessor!(Object, FieldType, KeyType) accessor) @safe nothrow pure 1660 in (accessor !is null, "Expected non null reference to an accessor.") { 1661 this.accessor_ = accessor; 1662 1663 return this; 1664 } 1665 1666 /** 1667 Get accessor 1668 1669 Returns: 1670 PropertyAccessor!(ComponentType, FieldType, KeyType) 1671 **/ 1672 inout(PropertyAccessor!(Object, FieldType, KeyType)) accessor() @safe nothrow pure inout { 1673 return this.accessor_; 1674 } 1675 1676 /** 1677 Get a property out of component 1678 1679 Params: 1680 component = a component which has some properties identified by property. 1681 Throws: 1682 NotFoundException in case when no requested property is available. 1683 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1684 Returns: 1685 FieldType accessed property. 1686 **/ 1687 FieldType access(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 1688 import aermicioi.aedi_property_reader.convertor.placeholder : pack; 1689 enforce(this.has(component, path), new NotFoundException("Could not find property ${property}in ${component}", path.to!string, component.to!string)); 1690 1691 auto temporary = component.stored; 1692 return this.accessor.access(temporary, path, allocator); 1693 } 1694 1695 /** 1696 Check if requested property is present in component. 1697 1698 Check if requested property is present in component. 1699 The method could have allocation side effects due to the fact that 1700 it is not restricted in calling access method of the accessor. 1701 1702 Params: 1703 component = component which is supposed to have property 1704 property = speculated property that is to be tested if it is present in component 1705 Returns: 1706 true if property is in component 1707 **/ 1708 bool has(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 1709 1710 auto temporary = component.pack; 1711 scope(exit) temporary.unpack!ComponentType; 1712 1713 return this.accessor.has(temporary, path, allocator); 1714 } 1715 1716 /** 1717 Identify the type of supported component. 1718 1719 Identify the type of supported component. It returns type info of component 1720 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1721 the type isn't supported by accessor. The accessor is not limited to returning the type 1722 info of passed component, it can actually return type info of super type or any type 1723 given the returned type is implicitly convertible or castable to ComponentType. 1724 1725 Params: 1726 component = the component for which accessor should identify the underlying type 1727 1728 Returns: 1729 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1730 **/ 1731 TypeInfo componentType(ComponentType component) const nothrow { 1732 1733 auto temporary = component.pack; 1734 scope(exit) temporary.unpack!ComponentType; 1735 return this.accessor.componentType(temporary); 1736 } 1737 1738 } 1739 } 1740 } 1741 1742 /** 1743 An accessor that downcasts or unwraps passed component. 1744 1745 ImplSpec: 1746 The accessor will simply upcast any property field to Object if they are rooted in Object class, otherwise 1747 it will allocate a Placeholder!FieldType for returned value and return it erased up to Object class. As such 1748 it is advised to use an allocator that will dispose automatically it's contents once they are not required. 1749 The accessor itself is not responsible for deallocation of placeholders that it returned. As a consequence 1750 no components should point to placeholders allocated by this accessor. 1751 **/ 1752 template UnwrappingComponentAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 1753 1754 private alias WithStorageOfFieldType = QualifierOf!FieldType; 1755 1756 class UnwrappingComponentAccessor : 1757 PropertyAccessor!(Object, FieldType, KeyType) { 1758 import aermicioi.aedi_property_reader.convertor.placeholder : identify, unwrap; 1759 1760 private { 1761 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 1762 } 1763 1764 public { 1765 1766 /** 1767 Constructor for runtime component accessor. 1768 1769 Params: 1770 accessor = accessor used to access fields out of component that are to be type erased on return. 1771 **/ 1772 this(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 1773 this.accessor = accessor; 1774 } 1775 1776 /** 1777 Set accessor 1778 1779 Params: 1780 accessor = underlying accessor working on unwrapped element 1781 1782 Returns: 1783 typeof(this) 1784 **/ 1785 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure 1786 in (accessor !is null, "Expected non null reference to an accessor.") { 1787 this.accessor_ = accessor; 1788 1789 return this; 1790 } 1791 1792 /** 1793 Get accessor 1794 1795 Returns: 1796 PropertyAccessor!(ComponentType, FieldType, KeyType) 1797 **/ 1798 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 1799 return this.accessor_; 1800 } 1801 1802 /** 1803 Get a property out of component 1804 1805 Params: 1806 component = a component which has some properties identified by property. 1807 Throws: 1808 NotFoundException in case when no requested property is available. 1809 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1810 Returns: 1811 FieldType accessed property. 1812 **/ 1813 FieldType access(Object component, in KeyType path, RCIAllocator allocator = theAllocator) const { 1814 import aermicioi.aedi_property_reader.convertor.placeholder : pack; 1815 enforce(this.has(component, path), new NotFoundException("Could not find property ${property} in ${component}", path.to!string, component.to!string)); 1816 1817 return this.accessor.access(component.unwrap!ComponentType, path, allocator); 1818 } 1819 1820 /** 1821 Check if requested property is present in component. 1822 1823 Check if requested property is present in component. 1824 The method could have allocation side effects due to the fact that 1825 it is not restricted in calling access method of the accessor. 1826 1827 Params: 1828 component = component which is supposed to have property 1829 property = speculated property that is to be tested if it is present in component 1830 Returns: 1831 true if property is in component 1832 **/ 1833 bool has(Object component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 1834 1835 return 1836 (component.identify is typeid(ComponentType)) && 1837 this.accessor.has(component.unwrap!ComponentType, path, allocator); 1838 } 1839 1840 /** 1841 Identify the type of supported component. 1842 1843 Identify the type of supported component. It returns type info of component 1844 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1845 the type isn't supported by accessor. The accessor is not limited to returning the type 1846 info of passed component, it can actually return type info of super type or any type 1847 given the returned type is implicitly convertible or castable to ComponentType. 1848 1849 Params: 1850 component = the component for which accessor should identify the underlying type 1851 1852 Returns: 1853 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1854 **/ 1855 TypeInfo componentType(Object component) const nothrow { 1856 if (component.identify is typeid(ComponentType)) { 1857 1858 return this.accessor.componentType(component.unwrap!ComponentType); 1859 } 1860 1861 return typeid(void); 1862 } 1863 } 1864 } 1865 } 1866 1867 /** 1868 An accessor that downcasts or unwraps resulting field. 1869 1870 ImplSpec: 1871 The accessor will simply upcast any property field to Object if they are rooted in Object class, otherwise 1872 it will allocate a Placeholder!FieldType for returned value and return it erased up to Object class. As such 1873 it is advised to use an allocator that will dispose automatically it's contents once they are not required. 1874 The accessor itself is not responsible for deallocation of placeholders that it returned. As a consequence 1875 no components should point to placeholders allocated by this accessor. 1876 **/ 1877 template UnwrappingFieldAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 1878 1879 private alias WithStorageOfFieldType = QualifierOf!FieldType; 1880 1881 class UnwrappingFieldAccessor : 1882 PropertyAccessor!(ComponentType, FieldType, KeyType) { 1883 import aermicioi.aedi_property_reader.convertor.placeholder : identify, unwrap; 1884 1885 private { 1886 PropertyAccessor!(ComponentType, Object, KeyType) accessor_; 1887 } 1888 1889 public { 1890 1891 /** 1892 Constructor for runtime component accessor. 1893 1894 Params: 1895 accessor = accessor used to access fields out of component that are to be type erased on return. 1896 **/ 1897 this(PropertyAccessor!(ComponentType, Object, KeyType) accessor) { 1898 this.accessor = accessor; 1899 } 1900 1901 /** 1902 Set accessor 1903 1904 Params: 1905 accessor = underlying accessor working on unwrapped element 1906 1907 Returns: 1908 typeof(this) 1909 **/ 1910 typeof(this) accessor(PropertyAccessor!(ComponentType, Object, KeyType) accessor) @safe nothrow pure 1911 in (accessor !is null, "Expected non null reference to an accessor.") { 1912 this.accessor_ = accessor; 1913 1914 return this; 1915 } 1916 1917 /** 1918 Get accessor 1919 1920 Returns: 1921 PropertyAccessor!(ComponentType, FieldType, KeyType) 1922 **/ 1923 inout(PropertyAccessor!(ComponentType, Object, KeyType)) accessor() @safe nothrow pure inout { 1924 return this.accessor_; 1925 } 1926 1927 /** 1928 Get a property out of component 1929 1930 Params: 1931 component = a component which has some properties identified by property. 1932 Throws: 1933 NotFoundException in case when no requested property is available. 1934 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1935 Returns: 1936 FieldType accessed property. 1937 **/ 1938 FieldType access(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const { 1939 import aermicioi.aedi_property_reader.convertor.placeholder : pack; 1940 enforce(this.has(component, path), new NotFoundException("Could not find property ${property} in ${component}", path.to!string, component.to!string)); 1941 1942 return this.accessor.access(component, path, allocator).unpack!FieldType(allocator); 1943 } 1944 1945 /** 1946 Check if requested property is present in component. 1947 1948 Check if requested property is present in component. 1949 The method could have allocation side effects due to the fact that 1950 it is not restricted in calling access method of the accessor. 1951 1952 Params: 1953 component = component which is supposed to have property 1954 property = speculated property that is to be tested if it is present in component 1955 Returns: 1956 true if property is in component 1957 **/ 1958 bool has(ComponentType component, in KeyType path, RCIAllocator allocator = theAllocator) const nothrow { 1959 1960 return this.accessor.has(component, path, allocator); 1961 } 1962 1963 /** 1964 Identify the type of supported component. 1965 1966 Identify the type of supported component. It returns type info of component 1967 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1968 the type isn't supported by accessor. The accessor is not limited to returning the type 1969 info of passed component, it can actually return type info of super type or any type 1970 given the returned type is implicitly convertible or castable to ComponentType. 1971 1972 Params: 1973 component = the component for which accessor should identify the underlying type 1974 1975 Returns: 1976 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1977 **/ 1978 TypeInfo componentType(ComponentType component) const nothrow { 1979 if (component.identify is typeid(ComponentType)) { 1980 1981 return this.accessor.componentType(component); 1982 } 1983 1984 return typeid(void); 1985 } 1986 } 1987 } 1988 } 1989 1990 /** 1991 Accessor that allows access to fields and properties of a component of Type. 1992 1993 ImplSpec: 1994 The returned field will be erased up to Object class if it is rooted in Object class, or will 1995 be placed into a Placeholder!(field type) that is allocated using an allocator. As such it is advised 1996 that allocator used in the accessor to be disposable after accessing of components was done. As a consequence 1997 no components should point to placeholders allocated by this accessor. 1998 **/ 1999 @component 2000 template CompositeAccessor(Type) { 2001 2002 private { 2003 alias WithStorageClassOfType = QualifierOf!Type; 2004 2005 static if (is(Type == const)) { 2006 2007 auto modifier(FunctionAttribute attr) { 2008 return attr & FunctionAttribute.const_; 2009 } 2010 2011 } else static if (is(Type == immutable)) { 2012 2013 auto modifier(FunctionAttribute attr) { 2014 return attr & FunctionAttribute.immutable_; 2015 } 2016 } else { 2017 2018 auto modifier(FunctionAttribute attr) { 2019 return (attr & (FunctionAttribute.const_ | FunctionAttribute.immutable_)) == FunctionAttribute.none; 2020 } 2021 } 2022 } 2023 2024 class CompositeAccessor : PropertyAccessor!(Type, WithStorageClassOfType!Object, string) { 2025 import std.traits; 2026 import std.meta; 2027 import aermicioi.aedi.util.traits; 2028 import aermicioi.aedi_property_reader.convertor.convertor; 2029 2030 public { 2031 2032 /** 2033 Get a property out of component 2034 2035 Params: 2036 component = a component which has some properties identified by property. 2037 Throws: 2038 NotFoundException in case when no requested property is available. 2039 InvalidArgumentException in case when passed arguments are somehow invalid for use. 2040 Returns: 2041 FieldType accessed property. 2042 **/ 2043 WithStorageClassOfType!Object access(Type component, in string property, RCIAllocator allocator = theAllocator) const { 2044 foreach (string member; __traits(allMembers, Type)) { 2045 2046 static if (isPublic!(component, member)) { 2047 if (member == property) { 2048 static if (isField!(Type, member)) { 2049 2050 return cast(WithStorageClassOfType!Object) (__traits(getMember, component, member)) 2051 .packWithStorageClass!WithStorageClassOfType(allocator); 2052 } else static if(isSomeFunction!(__traits(getMember, component, member))) { 2053 2054 static foreach(overload; __traits(getOverloads, Type, member)) { 2055 static if ( 2056 isPropertyGetter!overload && 2057 ( 2058 (modifier(functionAttributes!overload)) || 2059 (functionAttributes!overload & FunctionAttribute.inout_) 2060 ) 2061 ) { 2062 return cast(WithStorageClassOfType!Object) (__traits(getMember, component, member)) 2063 .packWithStorageClass!WithStorageClassOfType(allocator); 2064 } 2065 } 2066 } 2067 } 2068 } 2069 } 2070 2071 throw new NotFoundException("${component} does not have ${property} property", property.to!string, component.to!string); 2072 } 2073 2074 /** 2075 Check if requested property is present in component. 2076 2077 Check if requested property is present in component. 2078 The method could have allocation side effects due to the fact that 2079 it is not restricted in calling access method of the accessor. 2080 2081 Params: 2082 component = component which is supposed to have property 2083 property = speculated property that is to be tested if it is present in component 2084 Returns: 2085 true if property is in component 2086 **/ 2087 bool has(Type component, in string property, RCIAllocator allocator = theAllocator) const nothrow { 2088 2089 foreach (string member; __traits(allMembers, Type)) { 2090 2091 static if (isPublic!(component, member)) { 2092 if (member == property) { 2093 static if (isField!(Type, member)) { 2094 return true; 2095 } else static if(isSomeFunction!(__traits(getMember, component, member))) { 2096 static foreach(overload; __traits(getOverloads, Type, member)) { 2097 2098 static if ( 2099 isPropertyGetter!overload && 2100 ( 2101 (modifier(functionAttributes!overload)) || 2102 (functionAttributes!overload & FunctionAttribute.inout_) 2103 ) 2104 ) { 2105 return true; 2106 } 2107 } 2108 } 2109 } 2110 } 2111 } 2112 2113 return false; 2114 } 2115 2116 /** 2117 Identify the type of supported component. 2118 2119 Identify the type of supported component. It returns type info of component 2120 if it is supported by accessor, otherwise it will return typeid(void) denoting that 2121 the type isn't supported by accessor. The accessor is not limited to returning the type 2122 info of passed component, it can actually return type info of super type or any type 2123 given the returned type is implicitly convertible or castable to ComponentType. 2124 2125 Params: 2126 component = the component for which accessor should identify the underlying type 2127 2128 Returns: 2129 TypeInfo type information about passed component, or typeid(void) if component is not supported. 2130 **/ 2131 TypeInfo componentType(WithStorageClassOfType!Type component) const nothrow { 2132 return typeid(Type); 2133 } 2134 2135 } 2136 } 2137 } 2138 2139 /** 2140 Accessor that converts input property identity of InputKeyType converts to ConvertedKeyType, and then uses it to access the ComponentType using decorated convertor. 2141 2142 Notice: const InputKeyType must decay to mutable InputKeyType in order for conversion to work properly, as most convertors do not support const values. 2143 **/ 2144 class KeyConvertingAccessor 2145 (ComponentType, FieldType = ComponentType, InputKeyType = string, ConvertedKeyType = InputKeyType) : 2146 PropertyAccessor!(ComponentType, FieldType, InputKeyType) { 2147 2148 import aermicioi.aedi_property_reader.convertor.convertor; 2149 2150 private { 2151 Convertor convertor_; 2152 PropertyAccessor!(ComponentType, FieldType, ConvertedKeyType) accessor_; 2153 } 2154 2155 public { 2156 2157 /** 2158 Constructor for KeyConvertingAccessor 2159 2160 Params: 2161 accessor = accessor that understands ConvertedKeyType only 2162 convertor = convertor used to convert property path of InputKeyType to ConvertedKeyType 2163 **/ 2164 this(PropertyAccessor!(ComponentType, FieldType, ConvertedKeyType) accessor, Convertor convertor) { 2165 this.accessor = accessor; 2166 this.convertor = convertor; 2167 } 2168 2169 /** 2170 Set convertor 2171 2172 Params: 2173 convertor = convertor used to convert input keytype to underlying keytype understood by decorated accessor 2174 2175 Returns: 2176 typeof(this) 2177 **/ 2178 typeof(this) convertor(Convertor convertor) @safe pure { 2179 enforce( 2180 convertor.convertsTo(typeid(ConvertedKeyType)) && 2181 convertor.convertsFrom(typeid(InputKeyType)), 2182 new InvalidArgumentException(text( 2183 "Invalid convertor provided for converting accessor key. Convertor capable converting from ", 2184 typeid(InputKeyType), " to ", 2185 typeid(ConvertedKeyType), " is required. Got convertor ", convertor, " capable to convert from ", 2186 convertor.from, " to ", 2187 convertor.to 2188 )) 2189 ); 2190 this.convertor_ = convertor; 2191 2192 return this; 2193 } 2194 2195 /** 2196 Get convertor 2197 2198 Returns: 2199 Convertor 2200 **/ 2201 inout(Convertor) convertor() @safe nothrow pure inout { 2202 return this.convertor_; 2203 } 2204 2205 /** 2206 Set accessor 2207 2208 Params: 2209 accessor = underlying accessor doing all heavy lifting 2210 2211 Returns: 2212 typeof(this) 2213 **/ 2214 typeof(this) accessor( 2215 PropertyAccessor!(ComponentType, FieldType, ConvertedKeyType) accessor 2216 ) @safe nothrow pure { 2217 this.accessor_ = accessor; 2218 2219 return this; 2220 } 2221 2222 /** 2223 Get accessor 2224 2225 Returns: 2226 PropertyAccessor!(ComponentType, FieldType, ConvertedKeyType) 2227 **/ 2228 inout(PropertyAccessor!(ComponentType, FieldType, ConvertedKeyType)) accessor() @safe nothrow pure inout { 2229 return this.accessor_; 2230 } 2231 2232 /** 2233 Get a property out of component 2234 2235 Params: 2236 component = a component which has some properties identified by property. 2237 Throws: 2238 NotFoundException in case when no requested property is available. 2239 InvalidArgumentException in case when passed arguments are somehow invalid for use. 2240 Returns: 2241 FieldType accessed property. 2242 **/ 2243 FieldType access(ComponentType component, in InputKeyType property, RCIAllocator allocator = theAllocator) const { 2244 InputKeyType modifiable = property; 2245 2246 auto wrapped = modifiable.stored; 2247 Object key = convertor.convert(wrapped, typeid(ConvertedKeyType)); 2248 scope(exit) convertor.destruct(typeid(InputKeyType), key, allocator); 2249 2250 2251 return this.accessor.access(component, key.unwrap!ConvertedKeyType, allocator); 2252 } 2253 2254 /** 2255 Check if requested property is present in component. 2256 2257 Check if requested property is present in component. 2258 The method could have allocation side effects due to the fact that 2259 it is not restricted in calling access method of the accessor. 2260 2261 Params: 2262 component = component which is supposed to have property 2263 property = speculated property that is to be tested if it is present in component 2264 Returns: 2265 true if property is in component 2266 **/ 2267 bool has(ComponentType component, in InputKeyType property, RCIAllocator allocator = theAllocator) const nothrow { 2268 try { 2269 Object key; 2270 scope(exit) if (key !is null) convertor.destruct(typeid(InputKeyType), key, allocator); 2271 2272 InputKeyType modifiable = property; 2273 auto temporary = modifiable.stored; 2274 2275 try { 2276 key = convertor.convert(temporary, typeid(ConvertedKeyType), allocator); // TODO find a proper way to retain constness while extracting type info from methods 2277 2278 } catch (ConvertorException e) { 2279 info("Attempting to convert input key ", typeid(InputKeyType), " to type of ", typeid(ConvertedKeyType), " ended in an exception, returning false."); 2280 return false; 2281 } 2282 2283 return this.accessor.has(component, key.unwrap!ConvertedKeyType, allocator); 2284 } catch (Exception e) { 2285 2286 throw new Error("Checking for existence of property has thrown an unexpected exception", e); 2287 } 2288 } 2289 2290 /** 2291 Identify the type of supported component. 2292 2293 Identify the type of supported component. It returns type info of component 2294 if it is supported by accessor, otherwise it will return typeid(void) denoting that 2295 the type isn't supported by accessor. The accessor is not limited to returning the type 2296 info of passed component, it can actually return type info of super type or any type 2297 given the returned type is implicitly convertible or castable to ComponentType. 2298 2299 Params: 2300 component = the component for which accessor should identify the underlying type 2301 2302 Returns: 2303 TypeInfo type information about passed component, or typeid(void) if component is not supported. 2304 **/ 2305 TypeInfo componentType(ComponentType component) const nothrow { 2306 return this.accessor.componentType(component); 2307 } 2308 } 2309 } 2310 2311 /** 2312 Creates a dlang dsl language for accessing properties out of a ComponentType. 2313 2314 Params: 2315 accessor = accessor used to access property like values 2316 indexer = accessor used to access indexed like values 2317 2318 Returns: 2319 PropertyPathAccessor!(ComponentType, FieldType, KeyType) with dsl like configuration. 2320 **/ 2321 auto dsl 2322 (ComponentType, FieldType, KeyType) 2323 ( 2324 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor, 2325 PropertyAccessor!(ComponentType, FieldType, KeyType) indexer 2326 ) 2327 { 2328 return new PropertyPathAccessor!(ComponentType, FieldType, KeyType)( 2329 '.', 2330 new AggregatePropertyAccessor!(ComponentType, FieldType, KeyType)( 2331 accessor, 2332 new ArrayIndexedPropertyAccessor!(ComponentType, FieldType, KeyType)( 2333 '[', ']', 2334 accessor, 2335 new AggregatePropertyAccessor!(ComponentType, FieldType, KeyType)( 2336 new TickedPropertyAccessor!(ComponentType, FieldType, KeyType)( 2337 '\'', 2338 accessor, 2339 ), 2340 new TickedPropertyAccessor!(ComponentType, FieldType, KeyType)( 2341 '"', 2342 accessor, 2343 ), 2344 indexer 2345 ) 2346 ) 2347 ) 2348 ); 2349 } 2350 2351 /** 2352 Creates a dlang dsl language for accessing properties out of a ComponentType. 2353 2354 The difference compared to a simple dsl is the fact that runtimeDsl will operate 2355 with erased components (components with erased type), which incurs some additional 2356 cost of creating temporaries to hide component type. 2357 2358 Params: 2359 accessor = accessor used to access property like values 2360 indexer = accessor used to access indexed like values 2361 2362 Returns: 2363 PropertyPathAccessor!(ComponentType, FieldType, KeyType) with dsl like configuration. 2364 **/ 2365 auto runtimeDsl(ComponentType, FieldType, KeyType) 2366 ( 2367 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor, 2368 PropertyAccessor!(ComponentType, FieldType, KeyType) indexer 2369 ) 2370 { 2371 return new WrappingComponentAccessor!(ComponentType, FieldType, KeyType)( 2372 new UnwrappingFieldAccessor!(Object, FieldType, KeyType)( 2373 dsl( 2374 new UnwrappingComponentAccessor!(ComponentType, Object, KeyType)( 2375 new WrappingFieldAccessor!(ComponentType, FieldType, KeyType)( 2376 accessor 2377 ) 2378 ), 2379 new UnwrappingComponentAccessor!(ComponentType, Object, KeyType)( 2380 new WrappingFieldAccessor!(ComponentType, FieldType, KeyType)( 2381 indexer 2382 ) 2383 ) 2384 ) 2385 ) 2386 ); 2387 }