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.core.accessor; 31 32 import aermicioi.aedi.storage.allocator_aware; 33 import aermicioi.aedi.exception.not_found_exception; 34 import aermicioi.aedi.exception.invalid_cast_exception; 35 import aermicioi.aedi_property_reader.core.exception; 36 import aermicioi.util.traits; 37 import aermicioi.aedi_property_reader.core.traits : isD, n; 38 import aermicioi.aedi_property_reader.core.placeholder; 39 import taggedalgebraic; 40 import std.array; 41 import std.conv; 42 import std.algorithm; 43 import std.range; 44 import std.exception : enforce; 45 import std.variant; 46 import std.traits; 47 import std.meta; 48 import std.experimental.logger; 49 50 /** 51 Interface for objects that are able to get child component out of parent one. 52 53 The intent of property accessor is to provide runtime means of accessing various properties 54 out of a component which could be a D object, or struct. The implementation is free in allocation 55 policies in order to satisfy the property access, though the burden of freeing allocated memory 56 is left on code using the accessor. 57 **/ 58 interface PropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) { 59 60 61 /** 62 Get a property out of component 63 64 Params: 65 component = a component which has some properties identified by property. 66 Throws: 67 NotFoundException in case when no requested property is available. 68 InvalidArgumentException in case when passed arguments are somehow invalid for use. 69 Returns: 70 FieldType accessed property. 71 **/ 72 FieldType access(ComponentType component, in KeyType property) const; 73 74 /** 75 Check if requested property is present in component. 76 77 Check if requested property is present in component. 78 The method could have allocation side effects due to the fact that 79 it is not restricted in calling access method of the accessor. 80 81 Params: 82 component = component which is supposed to have property 83 property = speculated property that is to be tested if it is present in component 84 Returns: 85 true if property is in component 86 **/ 87 bool has(in ComponentType component, in KeyType property) const nothrow; 88 89 /** 90 Identify the type of supported component. 91 92 Identify the type of supported component. It returns type info of component 93 if it is supported by accessor, otherwise it will return typeid(void) denoting that 94 the type isn't supported by accessor. The accessor is not limited to returning the type 95 info of passed component, it can actually return type info of super type or any type 96 given the returned type is implicitly convertible or castable to ComponentType. 97 98 Params: 99 component = the component for which accessor should identify the underlying type 100 101 Returns: 102 TypeInfo type information about passed component, or typeid(void) if component is not supported. 103 **/ 104 TypeInfo componentType(ComponentType component) const nothrow; 105 } 106 107 /** 108 Interface for accessor objects that are aware of an allocator and supposedly will use it 109 to allocate memory for returned component. 110 111 Interface for accessor objects that are aware of an allocator and supposedly will use it 112 to allocate memory for returned component. 113 ImplSpec: 114 The allocator in accessor is considered mutable even if the object is constant, therefore 115 it should be safe to access it from const methods. In case of immutable implementor the 116 behavior is undefined, and therefore it is advised to avoid such cases. 117 **/ 118 interface AllocatingPropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 119 PropertyAccessor!(ComponentType, FieldType, KeyType), 120 AllocatorAware!() { 121 122 } 123 124 /** 125 An accessor that queries stored accessors for component. 126 **/ 127 class AggregatePropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 128 PropertyAccessor!(ComponentType, FieldType, KeyType) { 129 130 private { 131 132 PropertyAccessor!(ComponentType, FieldType)[] accessors_; 133 } 134 135 public { 136 137 /** 138 Constructor for aggregate property accessor. 139 140 Params: 141 accessors = list of accessors used to extract fields from component. 142 **/ 143 this(PropertyAccessor!(ComponentType, FieldType)[] accessors...) { 144 this.accessors = accessors.dup; 145 } 146 147 @property { 148 /** 149 Set accessors 150 151 Params: 152 accessors = accessors that implement various logic of accessing a property out of component 153 154 Returns: 155 typeof(this) 156 **/ 157 typeof(this) accessors(PropertyAccessor!(ComponentType, FieldType, KeyType)[] accessors) @safe nothrow pure { 158 this.accessors_ = accessors; 159 160 return this; 161 } 162 163 /** 164 Get accessors 165 166 Returns: 167 PropertyAccessor!(ComponentType, FieldType, KeyType) 168 **/ 169 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)[]) accessors() @safe nothrow pure inout { 170 return this.accessors_; 171 } 172 } 173 174 /** 175 Get a property out of component 176 177 Params: 178 component = a component which has some properties identified by property. 179 Throws: 180 NotFoundException in case when no requested property is available. 181 InvalidArgumentException in case when passed arguments are somehow invalid for use. 182 Returns: 183 FieldType accessed property. 184 **/ 185 FieldType access(ComponentType component, in KeyType property) const { 186 187 foreach (accessor; this.accessors) { 188 189 if (accessor.has(component, property)) { 190 191 return accessor.access(component, property); 192 } 193 } 194 195 import aermicioi.aedi.exception.not_found_exception : NotFoundException; 196 throw new NotFoundException("Could not find element"); 197 } 198 199 /** 200 Check if requested property is present in component. 201 202 Check if requested property is present in component. 203 The method could have allocation side effects due to the fact that 204 it is not restricted in calling access method of the accessor. 205 206 Params: 207 component = component which is supposed to have property 208 property = speculated property that is to be tested if it is present in component 209 Returns: 210 true if property is in component 211 **/ 212 bool has(in ComponentType component, in KeyType property) const nothrow { 213 214 foreach (accessor; this.accessors) { 215 216 if (accessor.has(component, property)) { 217 return true; 218 } 219 } 220 221 return false; 222 } 223 224 /** 225 Identify the type of supported component. 226 227 Identify the type of supported component. It returns type info of component 228 if it is supported by accessor, otherwise it will return typeid(void) denoting that 229 the type isn't supported by accessor. The accessor is not limited to returning the type 230 info of passed component, it can actually return type info of super type or any type 231 given the returned type is implicitly convertible or castable to ComponentType. 232 233 Params: 234 component = the component for which accessor should identify the underlying type 235 236 Returns: 237 TypeInfo type information about passed component, or typeid(void) if component is not supported. 238 **/ 239 TypeInfo componentType(ComponentType component) const nothrow { 240 return typeid(ComponentType); 241 } 242 } 243 } 244 245 /** 246 An accessor that splits property into chunks using a separator, and recursively queries an accessor for next property from 247 returned child property. 248 249 An accessor that splits property into chunks using a separator,and recursively queries an accessor for next property from 250 returned child property. 251 ImplSpec: 252 Since a single accessor is used to fetch subsequent properties, a constraint on property accessors are placed, such as 253 field of a component should be implicitly convertible to component type in order for it to be used to get next child 254 in property chain. 255 **/ 256 class PropertyPathAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 257 PropertyAccessor!(ComponentType, FieldType, KeyType) 258 if (isImplicitlyConvertible!(FieldType, ComponentType) && isInputRange!KeyType) { 259 260 private { 261 PropertyAccessor!(ComponentType, FieldType) accessor_; 262 263 ElementType!KeyType separator_; 264 } 265 266 public { 267 268 /** 269 Constructor for property path accessor 270 271 Params: 272 separator = separator used to separate field identities in a field chain. 273 accessor = accessor used to access consecutively new child field. 274 **/ 275 this( 276 ElementType!KeyType separator, 277 PropertyAccessor!(ComponentType, FieldType) accessor 278 ) { 279 this.separator = separator; 280 this.accessor = accessor; 281 } 282 283 @property { 284 /** 285 Set accessor 286 287 Params: 288 accessor = accessor instance responsible for getting a property out of component 289 290 Returns: 291 typeof(this) 292 **/ 293 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 294 this.accessor_ = accessor; 295 296 return this; 297 } 298 299 /** 300 Get accessor 301 302 Returns: 303 PropertyAccessor!(ComponentType, FieldType, KeyType) 304 **/ 305 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 306 return this.accessor_; 307 } 308 309 /** 310 Set propertyAccessor 311 312 Params: 313 propertyAccessor = property splitter used to cut up the property path into multiple points 314 315 Returns: 316 typeof(this) 317 **/ 318 typeof(this) separator(ElementType!KeyType separator) @safe nothrow pure { 319 this.separator_ = separator; 320 321 return this; 322 } 323 324 /** 325 Get propertyAccessor 326 327 Returns: 328 ElementType!KeyType 329 **/ 330 inout(ElementType!KeyType) separator() @safe nothrow pure inout { 331 return this.separator_; 332 } 333 } 334 335 /** 336 Get a property out of component 337 338 Params: 339 component = a component which has some properties identified by property. 340 Throws: 341 NotFoundException in case when no requested property is available. 342 InvalidArgumentException in case when passed arguments are somehow invalid for use. 343 Returns: 344 FieldType accessed property. 345 **/ 346 FieldType access(ComponentType component, in KeyType path) const { 347 import std.algorithm; 348 import std.range; 349 350 auto identities = path.splitter(this.separator); 351 352 ComponentType current = component; 353 354 foreach (identity; identities) { 355 356 if (this.accessor.has(current, identity)) { 357 358 current = this.accessor.access(current, identity); 359 } else { 360 361 throw new NotFoundException(text( 362 "Could not find ", 363 identity, 364 " in ", 365 typeid(component), 366 " for property path of ", 367 path 368 )); 369 } 370 } 371 372 return current; 373 } 374 375 /** 376 Check if requested property is present in component. 377 378 Check if requested property is present in component. 379 The method could have allocation side effects due to the fact that 380 it is not restricted in calling access method of the accessor. 381 382 Params: 383 component = component which is supposed to have property 384 property = speculated property that is to be tested if it is present in component 385 Returns: 386 true if property is in component 387 **/ 388 bool has(in ComponentType component, in KeyType path) const nothrow { 389 390 try { 391 392 auto identities = path.splitter(this.separator); 393 394 ComponentType current = cast() component; 395 396 foreach (identity; identities) { 397 if (!this.accessor.has(current, identity)) { 398 return false; 399 } 400 401 if (this.accessor.has(current, identity)) { 402 current = this.accessor.access(current, identity); 403 } 404 } 405 406 return true; 407 } catch (Exception e) { 408 debug(trace) error("Failed to access a property path ", path, " due to ", e).n; 409 } 410 411 return false; 412 } 413 414 /** 415 Identify the type of supported component. 416 417 Identify the type of supported component. It returns type info of component 418 if it is supported by accessor, otherwise it will return typeid(void) denoting that 419 the type isn't supported by accessor. The accessor is not limited to returning the type 420 info of passed component, it can actually return type info of super type or any type 421 given the returned type is implicitly convertible or castable to ComponentType. 422 423 Params: 424 component = the component for which accessor should identify the underlying type 425 426 Returns: 427 TypeInfo type information about passed component, or typeid(void) if component is not supported. 428 **/ 429 TypeInfo componentType(ComponentType component) const nothrow { 430 return this.accessor.componentType(component); 431 } 432 } 433 } 434 435 /** 436 ditto 437 **/ 438 auto propertyPathAccessor(T : PropertyAccessor!(ComponentType, FieldType, KeyType), ComponentType, FieldType, KeyType) 439 (ElementType!KeyType separator, T accessor) { 440 return new PropertyPathAccessor!(ComponentType, FieldType, KeyType)(separator, accessor); 441 } 442 443 /** 444 An accessor that allows accessing of a property and it's childs with array syntax. 445 446 An accessor that allows accessing of a property and it's childs with array syntax. 447 ImplSpec: 448 Two accessors are used for getting an indexed property's child. The first one is used 449 for accessing an indexed property of a component, and the second one is used for subsequent 450 access of child components recursively. 451 **/ 452 class ArrayIndexedPropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : PropertyAccessor!(ComponentType, FieldType, KeyType) 453 if (isBidirectionalRange!KeyType) { 454 455 private { 456 alias EType = ElementType!KeyType; 457 EType beggining_; 458 EType ending_; 459 460 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 461 PropertyAccessor!(ComponentType, FieldType, KeyType) indexer_; 462 } 463 464 public { 465 466 /** 467 Constructor for array indexed accessor 468 469 Params: 470 beggining = beggining token denoting the start of an array indexing 471 ending = ending token denoting the end of an array indexing 472 accessor = accessor used to access property part of indexing sequence 473 indexer = accessor used to access indexed part of indexing sequence 474 **/ 475 this( 476 EType beggining, 477 EType ending, 478 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor, 479 PropertyAccessor!(ComponentType, FieldType, KeyType) indexer 480 ) { 481 this.beggining = beggining; 482 this.ending = ending; 483 this.accessor = accessor; 484 this.indexer = indexer; 485 } 486 487 /** 488 Set beggining 489 490 Params: 491 beggining = element denoting beggining of array syntax in identity, ex. [ 492 493 Returns: 494 typeof(this) 495 **/ 496 typeof(this) beggining(EType beggining) @safe nothrow pure { 497 this.beggining_ = beggining; 498 499 return this; 500 } 501 502 /** 503 Get beggining 504 505 Returns: 506 EType 507 **/ 508 inout(EType) beggining() @safe nothrow pure inout { 509 return this.beggining_; 510 } 511 512 /** 513 Set ending 514 515 Params: 516 ending = element denoting the end of array indexing, ex. ] 517 518 Returns: 519 typeof(this) 520 **/ 521 typeof(this) ending(EType ending) @safe nothrow pure { 522 this.ending_ = ending; 523 524 return this; 525 } 526 527 /** 528 Get ending 529 530 Returns: 531 EType 532 **/ 533 inout(EType) ending() @safe nothrow pure inout { 534 return this.ending_; 535 } 536 537 /** 538 Set accessor 539 540 Params: 541 accessor = accessor used to access property part from indexed property 542 543 Returns: 544 typeof(this) 545 **/ 546 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 547 this.accessor_ = accessor; 548 549 return this; 550 } 551 552 /** 553 Get accessor 554 555 Returns: 556 PropertyAccessor!(ComponentType, FieldType, KeyType) 557 **/ 558 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 559 return this.accessor_; 560 } 561 562 /** 563 Set indexer 564 565 Params: 566 indexer = property accessor used to access element based on contents in index part of property 567 568 Returns: 569 typeof(this) 570 **/ 571 typeof(this) indexer(PropertyAccessor!(ComponentType, FieldType, KeyType) indexer) @safe nothrow pure { 572 this.indexer_ = indexer; 573 574 return this; 575 } 576 577 /** 578 Get indexer 579 580 Returns: 581 PropertyAccessor!(ComponentType, FieldType, KeyType) 582 **/ 583 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) indexer() @safe nothrow pure inout { 584 return this.indexer_; 585 } 586 587 /** 588 Get a property out of component 589 590 Params: 591 component = a component which has some properties identified by property. 592 Throws: 593 NotFoundException in case when no requested property is available. 594 InvalidArgumentException in case when passed arguments are somehow invalid for use. 595 Returns: 596 FieldType accessed property. 597 **/ 598 FieldType access(ComponentType component, in KeyType path) const { 599 enforce!NotFoundException(this.has(component, path), text("Property ", path, " not found in ", component)); 600 601 auto splitted = path.splitter(this.beggining); 602 enforce!InvalidArgumentException( 603 !splitted.empty && !splitted.front.empty, 604 text("Malformed indexed property ", path, ", no property part found") 605 ); 606 607 FieldType property = this.accessor.access(component, splitted.front); 608 splitted.popFront; 609 enforce!InvalidArgumentException( 610 !splitted.empty, 611 text("Malformed indexed property ", path, ", no index part found") 612 ); 613 614 foreach (identity; splitted) { 615 enforce!InvalidArgumentException( 616 identity.endsWith(this.ending), 617 text("Malformed indexed property ", path, ", no closing ] found") 618 ); 619 620 property = this.indexer.access(property, identity.dropBack(1)); 621 } 622 623 return property; 624 } 625 626 /** 627 Check if requested property is present in component. 628 629 Check if requested property is present in component. 630 The method could have allocation side effects due to the fact that 631 it is not restricted in calling access method of the accessor. 632 633 Params: 634 component = component which is supposed to have property 635 property = speculated property that is to be tested if it is present in component 636 Returns: 637 true if property is in component 638 **/ 639 bool has(in ComponentType component, in KeyType path) const nothrow { 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)) { 654 return false; 655 } 656 657 FieldType property = this.accessor.access(cast(ComponentType) component, splitted.front); 658 splitted.popFront; 659 if (splitted.empty) { 660 return false; 661 } 662 663 foreach (identity; splitted) { 664 if (!identity.endsWith(this.ending)) { 665 return false; 666 } 667 668 if (!this.indexer.has(property, identity.dropBack(1))) { 669 return false; 670 } 671 672 property = this.indexer.access(property, identity.dropBack(1)); 673 } 674 675 return true; 676 } catch (Exception e) { 677 debug(trace) error("Failed to check existance of indexed path ", path, " due to ", e).n; 678 } 679 680 return false; 681 } 682 683 /** 684 Identify the type of supported component. 685 686 Identify the type of supported component. It returns type info of component 687 if it is supported by accessor, otherwise it will return typeid(void) denoting that 688 the type isn't supported by accessor. The accessor is not limited to returning the type 689 info of passed component, it can actually return type info of super type or any type 690 given the returned type is implicitly convertible or castable to ComponentType. 691 692 Params: 693 component = the component for which accessor should identify the underlying type 694 695 Returns: 696 TypeInfo type information about passed component, or typeid(void) if component is not supported. 697 **/ 698 TypeInfo componentType(ComponentType component) const nothrow { 699 return typeid(ComponentType); 700 } 701 } 702 } 703 704 /** 705 ditto 706 **/ 707 auto arrayIndexedPropertyAccessor(T : PropertyAccessor!(ComponentType, FieldType, KeyType), ComponentType, FieldType, KeyType) 708 (ElementType!KeyType beggining, ElementType!KeyType ending, T accessor, T indexer) { 709 return new ArrayIndexedPropertyAccessor!(ComponentType, FieldType, KeyType)(beggining, ending, accessor, indexer); 710 } 711 712 /** 713 Class that allows accessing properties that are wrapped in ticks. 714 **/ 715 class TickedPropertyAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 716 PropertyAccessor!(ComponentType, FieldType, KeyType) 717 if (isBidirectionalRange!KeyType) { 718 719 private { 720 alias EType = ElementType!KeyType; 721 EType tick_; 722 723 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 724 } 725 726 public { 727 728 /** 729 Constructor for ticked accessor 730 731 Params: 732 tick = token used to tick the property 733 accessor = accessor used to access property wrapped in ticks 734 **/ 735 this(EType tick, PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 736 this.tick = tick; 737 this.accessor = accessor; 738 } 739 740 /** 741 Set tick 742 743 Params: 744 tick = ticking element used to encapsulate property 745 746 Returns: 747 typeof(this) 748 **/ 749 typeof(this) tick(EType tick) @safe nothrow pure { 750 this.tick_ = tick; 751 752 return this; 753 } 754 755 /** 756 Get tick 757 758 Returns: 759 EType 760 **/ 761 inout(EType) tick() @safe nothrow pure inout { 762 return this.tick_; 763 } 764 765 /** 766 Set accessor 767 768 Params: 769 accessor = accessor used to access property enclosed in ticks 770 771 Returns: 772 typeof(this) 773 **/ 774 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 775 this.accessor_ = accessor; 776 777 return this; 778 } 779 780 /** 781 Get accessor 782 783 Returns: 784 PropertyAccessor!(ComponentType, FieldType, KeyType) 785 **/ 786 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 787 return this.accessor_; 788 } 789 790 /** 791 Get a property out of component 792 793 Params: 794 component = a component which has some properties identified by property. 795 Throws: 796 NotFoundException in case when no requested property is available. 797 InvalidArgumentException in case when passed arguments are somehow invalid for use. 798 Returns: 799 FieldType accessed property. 800 **/ 801 FieldType access(ComponentType component, in KeyType path) const { 802 enforce!InvalidArgumentException(this.valid(path), text("Not found or malformed ticked property ", path)); 803 804 return this.accessor.access(component, path.drop(1).dropBack(1)); 805 } 806 807 /** 808 Check if requested property is present in component. 809 810 Check if requested property is present in component. 811 The method could have allocation side effects due to the fact that 812 it is not restricted in calling access method of the accessor. 813 814 Params: 815 component = component which is supposed to have property 816 property = speculated property that is to be tested if it is present in component 817 Returns: 818 true if property is in component 819 **/ 820 bool has(in ComponentType component, in KeyType path) const { 821 822 try { 823 824 return this.valid(path) && this.accessor.has(component, path.strip(this.tick)); 825 } catch (Exception e) { 826 827 debug(trace) error("Failed to check if ticked property exists due to: ", e).n; 828 } 829 830 return false; 831 } 832 833 /** 834 Identify the type of supported component. 835 836 Identify the type of supported component. It returns type info of component 837 if it is supported by accessor, otherwise it will return typeid(void) denoting that 838 the type isn't supported by accessor. The accessor is not limited to returning the type 839 info of passed component, it can actually return type info of super type or any type 840 given the returned type is implicitly convertible or castable to ComponentType. 841 842 Params: 843 component = the component for which accessor should identify the underlying type 844 845 Returns: 846 TypeInfo type information about passed component, or typeid(void) if component is not supported. 847 **/ 848 TypeInfo componentType(ComponentType component) const nothrow { 849 return this.accessor.componentType(component); 850 } 851 852 private bool valid(in KeyType path) const nothrow { 853 try { 854 855 return (path.front == this.tick) && (path.back == this.tick); 856 } catch (Exception e) { 857 858 debug(trace) error("Failed to check if path is ticked due to: ", e).n; 859 } 860 861 return false; 862 } 863 } 864 } 865 866 /** 867 ditto 868 **/ 869 auto tickedAccessor(T : PropertyAccessor!(ComponentType, FieldType, KeyType), ComponentType, FieldType, KeyType)(ElementType!KeyType tick, T accessor) { 870 return new TickedPropertyAccessor!(ComponentType, FieldType, KeyType)(tick, accessor); 871 } 872 873 /** 874 Property accessor that is decaying tagged algebraic to a type accepted by decorated accessor. 875 876 ImplSpec: 877 Any tagged algebraic will be decayed to type X which is accepted by decorated property or fail to do so. 878 Any field returned from accessor are wrapped in same tagged algebraic, therefore there is a constraint that 879 tagged algebraic should be able to hold both the component and it's field. 880 **/ 881 class TaggedElementPropertyAccessorWrapper( 882 Tagged : TaggedAlgebraic!Y, 883 PropertyAccessorType : PropertyAccessor!(X, Z, KeyType), 884 X, 885 Z, 886 KeyType = string, 887 Y 888 ) : PropertyAccessor!(Tagged, Tagged, KeyType) { 889 890 private { 891 PropertyAccessorType accessor_; 892 } 893 894 public { 895 896 /** 897 Constructor for tagged element property accessor. 898 899 Params: 900 accessor = underlying accessor used to access fields out of a particular component type in a tagged algebraic type. 901 **/ 902 this(PropertyAccessorType accessor) { 903 this.accessor = accessor; 904 } 905 906 @property { 907 /** 908 Set accessor 909 910 Params: 911 accessor = the property accessor that is wrapped 912 913 Returns: 914 typeof(this) 915 **/ 916 typeof(this) accessor(PropertyAccessorType accessor) @safe nothrow pure { 917 this.accessor_ = accessor; 918 919 return this; 920 } 921 922 /** 923 Get accessor 924 925 Returns: 926 PropertyAccessorType 927 **/ 928 inout(PropertyAccessorType) accessor() @safe nothrow pure inout { 929 return this.accessor_; 930 } 931 } 932 933 /** 934 Get a property out of component 935 936 Params: 937 component = a component which has some properties identified by property. 938 Throws: 939 NotFoundException in case when no requested property is available. 940 InvalidArgumentException in case when passed arguments are somehow invalid for use. 941 Returns: 942 FieldType accessed property. 943 **/ 944 Tagged access(Tagged component, in KeyType property) const 945 { 946 if (this.has(component, property)) { 947 return Tagged(this.accessor.access(cast(X) component, property)); 948 } 949 950 import aermicioi.aedi.exception.not_found_exception : NotFoundException; 951 import std.conv : text; 952 throw new NotFoundException(text(component.kind, " does not have ", property)); 953 } 954 955 /** 956 Check if requested property is present in component. 957 958 Check if requested property is present in component. 959 The method could have allocation side effects due to the fact that 960 it is not restricted in calling access method of the accessor. 961 962 Params: 963 component = component which is supposed to have property 964 property = speculated property that is to be tested if it is present in component 965 Returns: 966 true if property is in component 967 **/ 968 bool has(in Tagged component, in KeyType property) const nothrow { 969 try { 970 971 import std.meta; 972 import aermicioi.util.traits; 973 974 static foreach (e; staticMap!(identifier, EnumMembers!(Tagged.Kind))) { 975 static if (mixin("is(typeof(Y." ~ e ~ ") : X)")) { 976 if (mixin("component.Kind." ~ e ~ " == component.kind")) { 977 978 return this.accessor.has(cast(const(X)) component, property); 979 } 980 981 return false; 982 } 983 } 984 985 assert(false, "TaggedAlgebraic does not have " ~ fullyQualifiedName!X ~ " as member"); 986 987 } catch (Exception e) { 988 debug(trace) error("Failed to unwrap tagged component ", component.kind, " due to ", e).n; 989 } 990 991 return false; 992 } 993 994 /** 995 Identify the type of supported component. 996 997 Identify the type of supported component. It returns type info of component 998 if it is supported by accessor, otherwise it will return typeid(void) denoting that 999 the type isn't supported by accessor. The accessor is not limited to returning the type 1000 info of passed component, it can actually return type info of super type or any type 1001 given the returned type is implicitly convertible or castable to ComponentType. 1002 1003 Params: 1004 component = the component for which accessor should identify the underlying type 1005 1006 Returns: 1007 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1008 **/ 1009 TypeInfo componentType(Tagged component) const nothrow { 1010 try { 1011 1012 import std.meta; 1013 import aermicioi.util.traits; 1014 1015 static foreach (e; staticMap!(identifier, EnumMembers!(Tagged.Kind))) { 1016 static if (mixin("is(typeof(Y." ~ e ~ ") : X)")) { 1017 if (mixin("component.Kind." ~ e ~ " == component.kind")) { 1018 1019 return this.accessor.componentType(cast(X) component); 1020 } 1021 } 1022 } 1023 1024 } catch (Exception e) { 1025 debug(trace) error("Failed to unwrap tagged component ", component.kind, " due to ", e).n; 1026 } 1027 1028 assert(false, "Got stored a value that is not in tagged algebraic"); 1029 } 1030 } 1031 } 1032 1033 /** 1034 ditto 1035 **/ 1036 auto taggedAccessor(Tagged, T : PropertyAccessor!(Composite, Field, Key), Composite, Field, Key)(T accessor) { 1037 return new TaggedElementPropertyAccessorWrapper!(Tagged, T)(accessor); 1038 } 1039 1040 /** 1041 Implements logic for accessing associative arrays. 1042 **/ 1043 class AssociativeArrayAccessor(Type, Key = Type) : PropertyAccessor!(Type[Key], Type, Key) { 1044 1045 public { 1046 1047 /** 1048 Get a property out of component 1049 1050 Params: 1051 component = a component which has some properties identified by property. 1052 Throws: 1053 NotFoundException in case when no requested property is available. 1054 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1055 Returns: 1056 FieldType accessed property. 1057 **/ 1058 Type access(Type[Key] component, in Key property) const { 1059 auto peek = property in component; 1060 enforce!NotFoundException(peek, text("Could not find ", property, " in associative array ", component)); 1061 1062 return *peek; 1063 } 1064 1065 /** 1066 Check if requested property is present in component. 1067 1068 Check if requested property is present in component. 1069 The method could have allocation side effects due to the fact that 1070 it is not restricted in calling access method of the accessor. 1071 1072 Params: 1073 component = component which is supposed to have property 1074 property = speculated property that is to be tested if it is present in component 1075 Returns: 1076 true if property is in component 1077 **/ 1078 bool has(in Type[Key] component, in Key property) const nothrow { 1079 return (property in component) !is null; 1080 } 1081 1082 /** 1083 Identify the type of supported component. 1084 1085 Identify the type of supported component. It returns type info of component 1086 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1087 the type isn't supported by accessor. The accessor is not limited to returning the type 1088 info of passed component, it can actually return type info of super type or any type 1089 given the returned type is implicitly convertible or castable to ComponentType. 1090 1091 Params: 1092 component = the component for which accessor should identify the underlying type 1093 1094 Returns: 1095 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1096 **/ 1097 TypeInfo componentType(Type[Key] component) const nothrow { 1098 return typeid(Type[Key]); 1099 } 1100 } 1101 } 1102 1103 /** 1104 Accessor implementing array access. 1105 **/ 1106 class ArrayAccessor(Type) : PropertyAccessor!(Type[], Type, size_t) { 1107 1108 public { 1109 1110 /** 1111 Get a property out of component 1112 1113 Params: 1114 component = a component which has some properties identified by property. 1115 Throws: 1116 NotFoundException in case when no requested property is available. 1117 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1118 Returns: 1119 FieldType accessed property. 1120 **/ 1121 Type access(Type[] component, in size_t property) const { 1122 enforce!NotFoundException(this.has(component, property), text("Could not find property ", property, " in array ", component)); 1123 1124 return component[property]; 1125 } 1126 1127 /** 1128 Check if requested property is present in component. 1129 1130 Check if requested property is present in component. 1131 The method could have allocation side effects due to the fact that 1132 it is not restricted in calling access method of the accessor. 1133 1134 Params: 1135 component = component which is supposed to have property 1136 property = speculated property that is to be tested if it is present in component 1137 Returns: 1138 true if property is in component 1139 **/ 1140 bool has(in Type[] component, in size_t property) const nothrow { 1141 return property < component.length; 1142 } 1143 1144 /** 1145 Identify the type of supported component. 1146 1147 Identify the type of supported component. It returns type info of component 1148 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1149 the type isn't supported by accessor. The accessor is not limited to returning the type 1150 info of passed component, it can actually return type info of super type or any type 1151 given the returned type is implicitly convertible or castable to ComponentType. 1152 1153 Params: 1154 component = the component for which accessor should identify the underlying type 1155 1156 Returns: 1157 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1158 **/ 1159 TypeInfo componentType(Type[] component) const nothrow { 1160 return typeid(Type[]); 1161 } 1162 } 1163 } 1164 1165 /** 1166 Accessor implementing logic for accessingstd.variant. 1167 1168 ImplSpec: 1169 The accessor can accept any variant of ComponentType. The restriction is that the underlying stored data should be 1170 of either associative array or dynamic array in order to access the contents of them. The return type is as well a 1171 variant that must be able to store associative's array field, or arrays elements. 1172 **/ 1173 class VariantAccessor( 1174 ComponentType, 1175 FieldType = ComponentType, 1176 KeyType = ComponentType 1177 ) : PropertyAccessor!(ComponentType, FieldType, KeyType) 1178 if ( 1179 is(ComponentType : VariantN!(ComponentSize, ComponentTypes), size_t ComponentSize, ComponentTypes...) && 1180 is(FieldType : VariantN!(FieldSize, FieldTypes), size_t FieldSize, FieldTypes...) && 1181 is(KeyType : VariantN!(KeySize, KeyTypes), size_t KeySize, KeyTypes...) 1182 ) { 1183 1184 private { 1185 static if (is(ComponentType : VariantN!(ComponentSize, CTypes), size_t ComponentSize, CTypes...)) { 1186 alias ComponentTypes = CTypes; 1187 } 1188 1189 static if (is(FieldType : VariantN!(FieldSize, FTypes), size_t FieldSize, FTypes...)) { 1190 alias FieldTypes = FTypes; 1191 } 1192 1193 static if (is(KeyType : VariantN!(KeySize, KTypes), size_t KeySize, KTypes...)) { 1194 alias KeyTypes = KTypes; 1195 } 1196 } 1197 1198 public { 1199 static foreach (KType; KeyTypes) { 1200 /** 1201 Get a property out of component 1202 1203 Params: 1204 component = a component which has some properties identified by property. 1205 Throws: 1206 NotFoundException in case when no requested property is available. 1207 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1208 Returns: 1209 FieldType accessed property. 1210 **/ 1211 FieldType access(ComponentType component, KType key) const { 1212 return this.access(component, KeyType(key)); 1213 } 1214 1215 /** 1216 Check if requested property is present in component. 1217 1218 Check if requested property is present in component. 1219 The method could have allocation side effects due to the fact that 1220 it is not restricted in calling access method of the accessor. 1221 1222 Params: 1223 component = component which is supposed to have property 1224 property = speculated property that is to be tested if it is present in component 1225 Returns: 1226 true if property is in component 1227 **/ 1228 bool has(ComponentType component, KType key) const nothrow { 1229 return this.has(component, KeyType(key)); 1230 } 1231 } 1232 1233 /** 1234 Get a property out of component 1235 1236 Params: 1237 component = a component which has some properties identified by property. 1238 Throws: 1239 NotFoundException in case when no requested property is available. 1240 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1241 Returns: 1242 FieldType accessed property. 1243 **/ 1244 FieldType access(ComponentType component, in KeyType key) const { 1245 static foreach (Component; ComponentTypes) {{ 1246 static if ( 1247 is(Component : X[W], X, W) && 1248 anySatisfy!(ApplyLeft!(isD, X), FieldTypes) && 1249 anySatisfy!(ApplyLeft!(isD, W), KeyTypes) 1250 ) { 1251 if ( 1252 (component.type is typeid(Component)) && 1253 (key.type is typeid(W)) && 1254 this.has(component, key) 1255 ) { 1256 return FieldType(component.get!Component[key.get!W]); 1257 } 1258 } 1259 1260 static if ( 1261 is(Component : Y[], Y) && 1262 anySatisfy!(ApplyLeft!(isD, Y), FieldTypes) && 1263 anySatisfy!(ApplyLeft!(isD, size_t), KeyTypes) 1264 ) { 1265 if ( 1266 (component.type is typeid(Component)) && 1267 (key.type is typeid(size_t)) && 1268 this.has(component, key) 1269 ) { 1270 return FieldType(component.get!Component[key.get!size_t]); 1271 } 1272 } 1273 }} 1274 1275 throw new NotFoundException(text("Could not find ", key, " in ", component)); 1276 } 1277 1278 /** 1279 Check if requested property is present in component. 1280 1281 Check if requested property is present in component. 1282 The method could have allocation side effects due to the fact that 1283 it is not restricted in calling access method of the accessor. 1284 1285 Params: 1286 component = component which is supposed to have property 1287 property = speculated property that is to be tested if it is present in component 1288 Returns: 1289 true if property is in component 1290 **/ 1291 bool has(in ComponentType component, in KeyType key) const nothrow { 1292 try { 1293 static foreach (Component; ComponentTypes) {{ 1294 static if ( 1295 is(Component : X[W], X, W) && 1296 anySatisfy!(ApplyLeft!(isD, X), FieldTypes) && 1297 anySatisfy!(ApplyLeft!(isD, W), KeyTypes) 1298 ) { 1299 if ( 1300 (component.type is typeid(Component)) && 1301 (key.type is typeid(W)) && 1302 ((key.get!W in component.get!Component) !is null) 1303 ) { 1304 return true; 1305 } 1306 } 1307 1308 static if ( 1309 is(Component : Y[], Y) && 1310 anySatisfy!(ApplyLeft!(isD, Y), FieldTypes) && 1311 anySatisfy!(ApplyLeft!(isD, size_t), KeyTypes) 1312 ) { 1313 if ( 1314 (component.type is typeid(Component)) && 1315 (key.type is typeid(size_t)) && 1316 (key.get!size_t < component.get!Component.length) 1317 ) { 1318 return true; 1319 } 1320 } 1321 }} 1322 } catch (Exception e) { 1323 debug(trace) error("Accessing contents of a variant failed with following exception: ", e).n; 1324 } 1325 1326 return false; 1327 } 1328 1329 /** 1330 Identify the type of supported component. 1331 1332 Identify the type of supported component. It returns type info of component 1333 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1334 the type isn't supported by accessor. The accessor is not limited to returning the type 1335 info of passed component, it can actually return type info of super type or any type 1336 given the returned type is implicitly convertible or castable to ComponentType. 1337 1338 Params: 1339 component = the component for which accessor should identify the underlying type 1340 1341 Returns: 1342 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1343 **/ 1344 TypeInfo componentType(ComponentType component) const nothrow { 1345 return typeid(ComponentType); 1346 } 1347 } 1348 } 1349 1350 /** 1351 Accessor that accepts ComponentType with it's type erased up to Object or wrapped in a Placeholder!ComponentType if it is 1352 not rooted into Object class (i.e. any value type, and extern c++ objects) 1353 1354 ImplSpec: 1355 The accessor will accept any object, after which it will attempt to downcast it's original type ComponentType if it is 1356 rooted in Object, otherwise the object will be downcasted to Placeholder!ComponentType. Failing to do so will result in 1357 an exception 1358 **/ 1359 class RuntimeCompositeAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : PropertyAccessor!(Object, FieldType, KeyType) { 1360 import aermicioi.aedi_property_reader.core.placeholder : identify, unwrap; 1361 private { 1362 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 1363 } 1364 1365 public { 1366 /** 1367 Constructor for runtime composite accessor. 1368 1369 Params: 1370 accessor = underlying accessor used to access downcasted component. 1371 **/ 1372 this(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 1373 this.accessor = accessor; 1374 } 1375 1376 /** 1377 Set accessor 1378 1379 Params: 1380 accessor = underlying accessor working on unwrapped element 1381 1382 Returns: 1383 typeof(this) 1384 **/ 1385 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 1386 this.accessor_ = accessor; 1387 1388 return this; 1389 } 1390 1391 /** 1392 Get accessor 1393 1394 Returns: 1395 PropertyAccessor!(ComponentType, FieldType, KeyType) 1396 **/ 1397 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 1398 return this.accessor_; 1399 } 1400 1401 /** 1402 Get a property out of component 1403 1404 Params: 1405 component = a component which has some properties identified by property. 1406 Throws: 1407 NotFoundException in case when no requested property is available. 1408 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1409 Returns: 1410 FieldType accessed property. 1411 **/ 1412 FieldType access(Object component, in KeyType path) const { 1413 enforce!InvalidArgumentException( 1414 component.identify is typeid(ComponentType), 1415 text("Invalid component passed ", component.identify, " when expected ", typeid(ComponentType)) 1416 ); 1417 1418 enforce!NotFoundException(this.has(component, path), text("Could not find property ", path)); 1419 1420 return this.accessor.access(component.unwrap!ComponentType, path); 1421 } 1422 1423 /** 1424 Check if requested property is present in component. 1425 1426 Check if requested property is present in component. 1427 The method could have allocation side effects due to the fact that 1428 it is not restricted in calling access method of the accessor. 1429 1430 Params: 1431 component = component which is supposed to have property 1432 property = speculated property that is to be tested if it is present in component 1433 Returns: 1434 true if property is in component 1435 **/ 1436 bool has(in Object component, in KeyType path) const nothrow { 1437 return (component !is null) && 1438 (component.identify is typeid(ComponentType)) && 1439 component.unwrap!ComponentType && 1440 this.accessor.has(component.unwrap!ComponentType, path); 1441 } 1442 1443 /** 1444 Identify the type of supported component. 1445 1446 Identify the type of supported component. It returns type info of component 1447 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1448 the type isn't supported by accessor. The accessor is not limited to returning the type 1449 info of passed component, it can actually return type info of super type or any type 1450 given the returned type is implicitly convertible or castable to ComponentType. 1451 1452 Params: 1453 component = the component for which accessor should identify the underlying type 1454 1455 Returns: 1456 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1457 **/ 1458 TypeInfo componentType(Object component) const nothrow { 1459 if (component.identify is typeid(ComponentType)) { 1460 1461 return this.accessor.componentType(component.unwrap!ComponentType); 1462 } 1463 1464 return typeid(void); 1465 } 1466 } 1467 } 1468 1469 /** 1470 An accessor that erases returned property's type. 1471 1472 ImplSpec: 1473 The accessor will simply upcast any property field to Object if they are rooted in Object class, otherwise 1474 it will allocate a Placeholder!FieldType for returned value and return it erased up to Object class. As such 1475 it is advised to use an allocator that will dispose automatically it's contents once they are not required. 1476 The accessor itself is not responsible for deallocation of placeholders that it returned. As a consequence 1477 no components should point to placeholders allocated by this accessor. 1478 **/ 1479 class RuntimeFieldAccessor(ComponentType, FieldType = ComponentType, KeyType = string) : 1480 AllocatingPropertyAccessor!(ComponentType, Object, KeyType) { 1481 import aermicioi.aedi_property_reader.core.placeholder : identify, unwrap; 1482 import std.experimental.allocator; 1483 1484 mixin AllocatorAwareMixin!(typeof(this)); 1485 1486 private { 1487 PropertyAccessor!(ComponentType, FieldType, KeyType) accessor_; 1488 } 1489 1490 public { 1491 1492 /** 1493 Constructor for runtime field accessor. 1494 1495 Params: 1496 accessor = accessor used to access fields out of component that are to be type erased on return. 1497 **/ 1498 this(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) { 1499 this.allocator = theAllocator; 1500 this.accessor = accessor; 1501 } 1502 1503 /** 1504 Set accessor 1505 1506 Params: 1507 accessor = underlying accessor working on unwrapped element 1508 1509 Returns: 1510 typeof(this) 1511 **/ 1512 typeof(this) accessor(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor) @safe nothrow pure { 1513 this.accessor_ = accessor; 1514 1515 return this; 1516 } 1517 1518 /** 1519 Get accessor 1520 1521 Returns: 1522 PropertyAccessor!(ComponentType, FieldType, KeyType) 1523 **/ 1524 inout(PropertyAccessor!(ComponentType, FieldType, KeyType)) accessor() @safe nothrow pure inout { 1525 return this.accessor_; 1526 } 1527 1528 /** 1529 Get a property out of component 1530 1531 Params: 1532 component = a component which has some properties identified by property. 1533 Throws: 1534 NotFoundException in case when no requested property is available. 1535 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1536 Returns: 1537 FieldType accessed property. 1538 **/ 1539 Object access(ComponentType component, in KeyType path) const { 1540 import aermicioi.aedi_property_reader.core.placeholder : placeholder; 1541 enforce!NotFoundException(this.has(component, path), text("Could not find property ", path)); 1542 1543 return this.accessor.access(component, path).placeholder(cast() this.allocator); 1544 } 1545 1546 /** 1547 Check if requested property is present in component. 1548 1549 Check if requested property is present in component. 1550 The method could have allocation side effects due to the fact that 1551 it is not restricted in calling access method of the accessor. 1552 1553 Params: 1554 component = component which is supposed to have property 1555 property = speculated property that is to be tested if it is present in component 1556 Returns: 1557 true if property is in component 1558 **/ 1559 bool has(in ComponentType component, in KeyType path) const nothrow { 1560 1561 return this.accessor.has(component, path); 1562 } 1563 1564 /** 1565 Identify the type of supported component. 1566 1567 Identify the type of supported component. It returns type info of component 1568 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1569 the type isn't supported by accessor. The accessor is not limited to returning the type 1570 info of passed component, it can actually return type info of super type or any type 1571 given the returned type is implicitly convertible or castable to ComponentType. 1572 1573 Params: 1574 component = the component for which accessor should identify the underlying type 1575 1576 Returns: 1577 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1578 **/ 1579 TypeInfo componentType(ComponentType component) const nothrow { 1580 return this.accessor.componentType(component); 1581 } 1582 1583 } 1584 } 1585 1586 /** 1587 Accessor that allows access to fields and properties of a component of Type. 1588 1589 ImplSpec: 1590 The returned field will be erased up to Object class if it is rooted in Object class, or will 1591 be placed into a Placeholder!(field type) that is allocated using an allocator. As such it is advised 1592 that allocator used in the accessor to be disposable after accessing of components was done. As a consequence 1593 no components should point to placeholders allocated by this accessor. 1594 **/ 1595 class CompositeAccessor(Type) : AllocatingPropertyAccessor!(Type, Object, string) { 1596 import std.traits; 1597 import std.meta; 1598 import aermicioi.util.traits; 1599 import aermicioi.aedi_property_reader.core.convertor; 1600 1601 mixin AllocatorAwareMixin!(typeof(this)); 1602 1603 public { 1604 /** 1605 Default constructor for composite accessor. 1606 **/ 1607 this() { 1608 import std.experimental.allocator : theAllocator; 1609 1610 this.allocator = theAllocator; 1611 } 1612 1613 /** 1614 Get a property out of component 1615 1616 Params: 1617 component = a component which has some properties identified by property. 1618 Throws: 1619 NotFoundException in case when no requested property is available. 1620 InvalidArgumentException in case when passed arguments are somehow invalid for use. 1621 Returns: 1622 FieldType accessed property. 1623 **/ 1624 Object access(Type component, in string property) const { 1625 foreach (string member; __traits(allMembers, Type)) { 1626 1627 static if (isPublic!(component, member)) { 1628 if (member == property) { 1629 static if ( 1630 isField!(Type, member) || 1631 ( 1632 isSomeFunction!(__traits(getMember, component, member)) && 1633 anySatisfy!(isPropertyGetter, __traits(getOverloads, component, member))) 1634 ) { 1635 1636 return (__traits(getMember, component, member)).placeholder(cast() this.allocator); 1637 } 1638 } 1639 } 1640 } 1641 1642 throw new NotFoundException(text(typeid(Type), " does not have ", property, " property")); 1643 } 1644 1645 /** 1646 Check if requested property is present in component. 1647 1648 Check if requested property is present in component. 1649 The method could have allocation side effects due to the fact that 1650 it is not restricted in calling access method of the accessor. 1651 1652 Params: 1653 component = component which is supposed to have property 1654 property = speculated property that is to be tested if it is present in component 1655 Returns: 1656 true if property is in component 1657 **/ 1658 bool has(in Type component, in string property) const nothrow { 1659 1660 foreach (string member; __traits(allMembers, Type)) { 1661 1662 static if (isPublic!(component, member)) { 1663 if (member == property) { 1664 static if ( 1665 isField!(Type, member) || 1666 ( 1667 isSomeFunction!(__traits(getMember, component, member)) && 1668 anySatisfy!(isPropertyGetter, __traits(getOverloads, component, member))) 1669 ) { 1670 1671 return true; 1672 } 1673 } 1674 } 1675 } 1676 1677 return false; 1678 } 1679 1680 /** 1681 Identify the type of supported component. 1682 1683 Identify the type of supported component. It returns type info of component 1684 if it is supported by accessor, otherwise it will return typeid(void) denoting that 1685 the type isn't supported by accessor. The accessor is not limited to returning the type 1686 info of passed component, it can actually return type info of super type or any type 1687 given the returned type is implicitly convertible or castable to ComponentType. 1688 1689 Params: 1690 component = the component for which accessor should identify the underlying type 1691 1692 Returns: 1693 TypeInfo type information about passed component, or typeid(void) if component is not supported. 1694 **/ 1695 TypeInfo componentType(Type component) const nothrow { 1696 return typeid(Type); 1697 } 1698 1699 } 1700 } 1701 1702 /** 1703 Creates a dlang dsl language for accessing properties out of a ComponentType. 1704 1705 Params: 1706 accessor = accessor used to access property like values 1707 indexer = accessor used to access indexed like values 1708 1709 Returns: 1710 PropertyPathAccessor!(ComponentType, FieldType, KeyType) with dsl like configuration. 1711 **/ 1712 auto dsl(ComponentType, FieldType, KeyType)(PropertyAccessor!(ComponentType, FieldType, KeyType) accessor, PropertyAccessor!(ComponentType, FieldType, KeyType) indexer) { 1713 return new PropertyPathAccessor!(ComponentType, FieldType, KeyType)( 1714 '.', 1715 new AggregatePropertyAccessor!(ComponentType, FieldType, KeyType)( 1716 accessor, 1717 new ArrayIndexedPropertyAccessor!(ComponentType, FieldType, KeyType)( 1718 '[', ']', 1719 accessor, 1720 new AggregatePropertyAccessor!(ComponentType, FieldType, KeyType)( 1721 new TickedPropertyAccessor!(ComponentType, FieldType, KeyType)( 1722 '\'', 1723 accessor, 1724 ), 1725 new TickedPropertyAccessor!(ComponentType, FieldType, KeyType)( 1726 '"', 1727 accessor, 1728 ), 1729 indexer 1730 ) 1731 ) 1732 ) 1733 ); 1734 }