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 }