1 /**
2 License:
3     Boost Software License - Version 1.0 - August 17th, 2003
4 
5     Permission is hereby granted, free of charge, to any person or organization
6     obtaining a copy of the software and accompanying documentation covered by
7     this license (the "Software") to use, reproduce, display, distribute,
8     execute, and transmit the Software, and to prepare derivative works of the
9     Software, and to permit third-parties to whom the Software is furnished to
10     do so, all subject to the following:
11 
12     The copyright notices in the Software and this entire statement, including
13     the above license grant, this restriction and the following disclaimer,
14     must be included in all copies of the Software, in whole or in part, and
15     all derivative works of the Software, unless such copies or derivative
16     works are solely in the form of machine-executable object code generated by
17     a source language processor.
18 
19     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25     DEALINGS IN THE SOFTWARE.
26 
27 Authors:
28     Alexandru Ermicioi
29 **/
30 module aermicioi.aedi_property_reader.core.mapper;
31 
32 import aermicioi.aedi_property_reader.core.exception;
33 import aermicioi.aedi_property_reader.core.convertor;
34 import aermicioi.aedi_property_reader.core.inspector;
35 import aermicioi.aedi_property_reader.core.setter;
36 import aermicioi.aedi_property_reader.core.accessor;
37 import aermicioi.aedi_property_reader.core.placeholder;
38 import std.experimental.allocator;
39 import std.experimental.logger;
40 import std.exception;
41 import std.algorithm;
42 import std.conv;
43 
44 /**
45 Interface for components that are able to map from one type to another one.
46 **/
47 interface Mapper(To, From = To) {
48 
49     /**
50     Map from component to component.
51 
52     Map from component to component, or transfer data from component to component
53     with optional conversion of data along the way.
54 
55     Params:
56         from = original component that has it's data transferred
57         to = destination component that receives transferred data
58         allocator = optional allocator that could be used by convertors when doing field conversion
59     **/
60     void map(From from, ref To to, RCIAllocator allocator = theAllocator);
61 
62     @property {
63         /**
64         Set convertors
65 
66         Params:
67             convertors = set a list of convertors to be used by mapper to automatically convert a mapped field to designated type
68         Returns:
69             typeof(this)
70         **/
71         typeof(this) convertors(Convertor[] convertors) @safe nothrow pure;
72 
73         /**
74         Get convertors
75 
76         Returns:
77             Convertor[]
78         **/
79         inout(Convertor[]) convertors() @safe nothrow pure inout;
80 
81         /**
82         Set force
83 
84         Params:
85             force = forces mapper to try and set a field even if it is not existent
86 
87         Returns:
88             typeof(this)
89         **/
90         typeof(this) force(bool force) @safe nothrow pure;
91 
92         /**
93         Get force
94 
95         Returns:
96             bool
97         **/
98         inout(bool) force() @safe nothrow pure inout;
99 
100         /**
101         Set conversion
102 
103         Params:
104             conversion = whether to enable or not automatic conversion of fields using convertors.
105 
106         Returns:
107             typeof(this)
108         **/
109         typeof(this) conversion(bool conversion) @safe nothrow pure;
110 
111         /**
112         Get conversion
113 
114         Returns:
115             bool
116         **/
117         inout(bool) conversion() @safe nothrow pure inout;
118 
119         /**
120         Set skip
121 
122         Params:
123             skip = whether to skip mapping of fields that have their type not identifiable in destination component
124 
125         Returns:
126             typeof(this)
127         **/
128         typeof(this) skip(bool skip) @safe nothrow pure;
129 
130         /**
131         Get skip
132 
133         Returns:
134             bool
135         **/
136         inout(bool) skip() @safe nothrow pure inout;
137     }
138 }
139 
140 /**
141 An implementation of a mapper that specifically converts From component To component.
142 
143 An implementation of a mapper that specifically converts From component To component.
144 It will use inspectors for From and To component to get information about component fields
145 at runtime, and then use accessor and setter implementations to transfer data from one
146 component to another, with optional conversion of data using passed convertors.
147 **/
148 class CompositeMapper(To, From) : Mapper!(To, From) {
149 
150     private {
151         import std.typecons : Rebindable;
152         bool conversion_;
153         bool force_;
154         bool skip_;
155 
156         Convertor[] convertors_;
157 
158         Rebindable!(const PropertySetter!(To, Object)) setter_;
159         Rebindable!(const PropertyAccessor!(From, Object)) accessor_;
160         Rebindable!(const Inspector!From) fromInspector_;
161         Rebindable!(const Inspector!To) toInspector_;
162     }
163 
164     public {
165 
166         @property {
167             /**
168             Set convertors
169 
170             Params:
171                 convertors = a list of optional convertors used to convert from one format to another one
172 
173             Returns:
174                 typeof(this)
175             **/
176             typeof(this) convertors(Convertor[] convertors) @safe nothrow pure {
177                 this.convertors_ = convertors;
178 
179                 return this;
180             }
181 
182             /**
183             Get convertors
184 
185             Returns:
186                 Convertor[]
187             **/
188             inout(Convertor[]) convertors() @safe nothrow pure inout {
189                 return this.convertors_;
190             }
191 
192             /**
193             Set conversion
194 
195             Params:
196                 conversion = wether to convert or not values using convertors.
197 
198             Returns:
199                 typeof(this)
200             **/
201             typeof(this) conversion(bool conversion) @safe nothrow pure {
202                 this.conversion_ = conversion;
203 
204                 return this;
205             }
206 
207             /**
208             Get conversion
209 
210             Returns:
211                 bool
212             **/
213             inout(bool) conversion() @safe nothrow pure inout {
214                 return this.conversion_;
215             }
216 
217             /**
218             Set setter
219 
220             Params:
221                 setter = setter used to pass values to component.
222 
223             Returns:
224                 typeof(this)
225             **/
226             typeof(this) setter(in PropertySetter!(To, Object) setter) @safe nothrow pure {
227                 this.setter_ = setter;
228 
229                 return this;
230             }
231 
232             /**
233             Get setter
234 
235             Returns:
236                 PropertySetter!(To, Object)
237             **/
238             inout(const PropertySetter!(To, Object)) setter() @safe nothrow pure inout {
239                 return this.setter_.get;
240             }
241 
242             /**
243             Set accessor
244 
245             Params:
246                 accessor = property accessor used to extract values from mapped component
247 
248             Returns:
249                 typeof(this)
250             **/
251             typeof(this) accessor(in PropertyAccessor!(From, Object) accessor) @safe nothrow pure {
252                 this.accessor_ = accessor;
253 
254                 return this;
255             }
256 
257             /**
258             Get accessor
259 
260             Returns:
261                 PropertyAccessor!(From, Object)
262             **/
263             inout(const PropertyAccessor!(From, Object)) accessor() @safe nothrow pure inout {
264                 return this.accessor_.get;
265             }
266 
267             /**
268             Set fromInspector
269 
270             Params:
271                 fromInspector = inspector providing information about mapped component
272 
273             Returns:
274                 typeof(this)
275             **/
276             typeof(this) fromInspector(in Inspector!From fromInspector) @safe nothrow pure {
277                 this.fromInspector_ = fromInspector;
278 
279                 return this;
280             }
281 
282             /**
283             Get fromInspector
284 
285             Returns:
286                 Inspector!From
287             **/
288             inout(const Inspector!From) fromInspector() @safe nothrow pure inout {
289                 return this.fromInspector_.get;
290             }
291 
292             /**
293             Set toInspector
294 
295             Params:
296                 toInspector = inspector used to provide information about component that will store mapped data
297 
298             Returns:
299                 typeof(this)
300             **/
301             typeof(this) toInspector(in Inspector!To toInspector) @safe nothrow pure {
302                 this.toInspector_ = toInspector;
303 
304                 return this;
305             }
306 
307             /**
308             Get toInspector
309 
310             Returns:
311                 Inspector!To
312             **/
313             inout(const Inspector!To) toInspector() @safe nothrow pure inout {
314                 return this.toInspector_.get;
315             }
316 
317             /**
318             Set force
319 
320             Params:
321                 force = whether to force attempt in setting a property in a mapped component
322 
323             Returns:
324                 typeof(this)
325             **/
326             typeof(this) force(bool force) @safe nothrow pure {
327                 this.force_ = force;
328 
329                 return this;
330             }
331 
332             /**
333             Get force
334 
335             Returns:
336                 bool
337             **/
338             inout(bool) force() @safe nothrow pure inout {
339                 return this.force_;
340             }
341 
342             /**
343             Set skip
344 
345             Params:
346                 skip = whether to skip mapping of fields that have their type not identifiable in destination component
347 
348             Returns:
349                 typeof(this)
350             **/
351             typeof(this) skip(bool skip) @safe nothrow pure {
352                 this.skip_ = skip;
353 
354                 return this;
355             }
356 
357             /**
358             Get skip
359 
360             Returns:
361                 bool
362             **/
363             inout(bool) skip() @safe nothrow pure inout {
364                 return this.skip_;
365             }
366         }
367 
368         /**
369         Map from component to component.
370 
371         Map from component to component, or transfer data from component to component
372         with optional conversion of data along the way.
373 
374         Params:
375             from = original component that has it's data transferred
376             to = destination component that receives transferred data
377             allocator = optional allocator that could be used by convertors when doing field conversion
378         **/
379         void map(From from, ref To to, RCIAllocator allocator = theAllocator) {
380 
381             debug(trace) trace("Mapping ", this.fromInspector.properties(from), " of ", from.identify, " to ", to.identify);
382             foreach (property; this.fromInspector.properties(from)) {
383 
384                 debug(trace) trace("Migrating \"", property, "\" property ");
385                 if (this.toInspector.has(to, property) || this.force) {
386 
387                     Object value = this.accessor.access(from, property);
388                     import std.stdio;
389 
390                     if (
391                         (this.fromInspector.typeOf(from, property) != this.toInspector.typeOf(to, property))
392                     ) {
393                         if (this.conversion) {
394                             if (this.toInspector.typeOf(to, property) is typeid(void)) {
395                                 if (this.skip) {
396                                     debug(trace) trace("Skipping migration of \"", property, "\" due to missing type information in destination component");
397                                     continue;
398                                 }
399 
400                                 throw new ConvertorException(text(
401                                         "Cannot identify type of \"", property, "\" in destination component, probably missing"
402                                 ));
403                             }
404 
405                             debug(trace) trace("\"",
406                                 property,
407                                 "\"'s type differs in original component and destination component, ",
408                                 this.fromInspector.typeOf(from, property), " and ",
409                                 this.toInspector.typeOf(to, property)
410                             );
411 
412                             auto compatible = convertors.filter!(c =>
413                                 c.convertsFrom(this.fromInspector.typeOf(from, property)) &&
414                                 c.convertsTo(this.toInspector.typeOf(to, property))
415                             );
416 
417                             enforce!ConvertorException(!compatible.empty, text(
418                                 "Could not find convertor to convert \"", property, "\" from ", this.fromInspector.typeOf(from, property),
419                                 " to ", this.toInspector.typeOf(to, property)
420                             ));
421 
422                             debug(trace) trace("Found convertor for \"", property, "\" from ", compatible.front.from, " to ", compatible.front.to);
423 
424                             value = compatible.front.convert(value, this.toInspector.typeOf(to, property), allocator);
425                         } else {
426 
427                             throw new InvalidArgumentException(text(
428                                 "Invalid assignment \"", property, "\" has type of ", this.fromInspector.typeOf(from, property),
429                                 " in from component while in to component it is ", this.toInspector.typeOf(to, property)
430                             ));
431                         }
432                     }
433 
434                     try {
435 
436                         this.setter.set(
437                             to,
438                             value,
439                             property
440                         );
441 
442                         debug(trace) trace("Migrated \"", property, "\" from ", from.identify, " to ", to.identify);
443                     } catch (Exception e) {
444 
445                         debug(trace) trace("Couldn't ", this.force ? "forcefully " : "", "set property \"", property, "\" to ", to.identify, " from ", from.identify, " due to ", e);
446                     }
447                 } else {
448 
449                     debug(trace) error(to.identify, " element does not have: ", property);
450                 }
451             }
452         }
453     }
454 }
455 
456 /**
457 An implementation of convertor that is using a mapper to map from component to component.
458 **/
459 class CompositeConvertor(To, From) : CombinedConvertor {
460     import std.algorithm;
461     import std.array;
462 
463     private {
464         Mapper!(To, From) mapper_;
465     }
466 
467     public {
468         /**
469         Set used convertors
470 
471         Params:
472             convertors = list of convertors to be used.
473 
474         Returns:
475             typeof(this)
476         **/
477         typeof(this) convertors(Convertor[] convertors) @safe nothrow {
478             this.mapper.convertors = convertors;
479 
480             return this;
481         }
482 
483         /**
484         Add a convertor to existing list
485 
486         Params:
487             convertor = convertor to be added to
488 
489         Returns:
490             typeof(this)
491         **/
492         typeof(this) add(Convertor convertor) @safe nothrow {
493             this.mapper.convertors = this.mapper.convertors ~ convertor;
494 
495             return this;
496         }
497 
498         /**
499         Remove a convertor from existing list
500 
501         Params:
502             convertor = convertor to be removed
503 
504         Returns:
505             typeof(this)
506         **/
507         typeof(this) remove(Convertor convertor) @trusted nothrow {
508             try {
509                 this.mapper.convertors = this.mapper.convertors.filter!(c => c != convertor).array;
510 
511             } catch (Exception e) {
512                 assert(false, text("Failed to remove convertor due to ", e));
513             }
514 
515             return this;
516         }
517 
518         @property {
519             /**
520             Set mapper
521 
522             Params:
523                 mapper = mapper used to map from component to component
524 
525             Returns:
526                 typeof(this)
527             **/
528             typeof(this) mapper(Mapper!(To, From) mapper) @safe nothrow pure {
529                 this.mapper_ = mapper;
530 
531                 return this;
532             }
533 
534             /**
535             Get mapper
536 
537             Returns:
538                 Mapper!(To, From)
539             **/
540             inout(Mapper!(To, From)) mapper() @safe nothrow pure inout {
541                 return this.mapper_;
542             }
543 
544             /**
545             Get the type info of component that convertor can convert from.
546 
547             Get the type info of component that convertor can convert from.
548             The method is returning the default type that it is able to convert,
549             though it is not necessarily limited to this type only. More generalistic
550             checks should be done by convertsFrom method.
551 
552             Returns:
553                 type info of component that convertor is able to convert.
554             **/
555             TypeInfo from() const {
556                 return typeid(From);
557             }
558 
559             /**
560             Get the type info of component that convertor is able to convert to.
561 
562             Get the type info of component that convertor is able to convert to.
563             The method is returning the default type that is able to convert,
564             though it is not necessarily limited to this type only. More generalistic
565             checks should be done by convertsTo method.
566 
567             Returns:
568                 type info of component that can be converted to.
569             **/
570             TypeInfo to() const {
571                 return typeid(To);
572             }
573         }
574 
575         /**
576         Check whether convertor is able to convert from.
577 
578         Check whether convertor is able to convert from.
579         The intent of method is to implement customized type checking
580         is not limited immediatly to supported default from component.
581 
582         Params:
583             from = the type info of component that could potentially be converted by convertor.
584         Returns:
585             true if it is able to convert from, or false otherwise.
586         **/
587         bool convertsFrom(TypeInfo from) const {
588             return this.from is from;
589         }
590 
591         /**
592         Check whether convertor is able to convert from.
593 
594         Check whether convertor is able to convert from.
595         The method will try to extract type info out of from
596         object and use for subsequent type checking.
597         The intent of method is to implement customized type checking
598         is not limited immediatly to supported default from component.
599 
600         Params:
601             from = the type info of component that could potentially be converted by convertor.
602         Returns:
603             true if it is able to convert from, or false otherwise.
604         **/
605         bool convertsFrom(in Object from) const {
606             return this.convertsFrom(from.identify);
607         }
608 
609         /**
610         Check whether convertor is able to convert to.
611 
612         Check whether convertor is able to convert to.
613         The intent of the method is to implement customized type checking
614         that is not limited immediatly to supported default to component.
615 
616         Params:
617             to = type info of component that convertor could potentially convert to.
618 
619         Returns:
620             true if it is able to convert to, false otherwise.
621         **/
622         bool convertsTo(TypeInfo to) const nothrow {
623             return this.to is to;
624         }
625 
626         /**
627         Check whether convertor is able to convert to.
628 
629         Check whether convertor is able to convert to.
630         The method will try to extract type info out of to object and use
631         for subsequent type checking.
632         The intent of the method is to implement customized type checking
633         that is not limited immediatly to supported default to component.
634 
635         Params:
636             to = type info of component that convertor could potentially convert to.
637 
638         Returns:
639             true if it is able to convert to, false otherwise.
640         **/
641         bool convertsTo(in Object to) const nothrow {
642             return this.convertsTo(to.identify);
643         }
644 
645         /**
646         Convert from component to component.
647 
648         Params:
649             from = original component that is to be converted.
650             to = destination object that will be constructed out for original one.
651             allocator = optional allocator that could be used to construct to component.
652         Throws:
653             ConvertorException when there is a converting error
654             InvalidArgumentException when arguments passed are not of right type or state
655         Returns:
656             Resulting converted component.
657         **/
658         Object convert(in Object from, TypeInfo to, RCIAllocator allocator = theAllocator) {
659             enforce!InvalidArgumentException(this.convertsFrom(from), text(
660                 "Cannot convert ", from.identify, " to ", typeid(To), ", ", from.identify, " is not supported by ", typeid(this)
661             ));
662 
663             enforce!InvalidArgumentException(this.convertsTo(to), text(
664                 "Cannot convert ", from.identify, " to ", typeid(To), ", ", to, " is not supported by ", typeid(this)
665             ));
666 
667             static if (is(To : Object)) {
668                 To placeholder = allocator.make!To;
669                 this.mapper.map(from.unwrap!From, placeholder, allocator);
670             } else {
671                 auto placeholder = allocator.make!(PlaceholderImpl!To)(To.init);
672                 this.mapper.map(from.unwrap!From, placeholder.value, allocator);
673             }
674 
675             return placeholder;
676         }
677 
678         /**
679         Destroy component created using this convertor.
680 
681         Destroy component created using this convertor.
682         Since convertor could potentially allocate memory for
683         converted component, only itself is containing history of allocation,
684         and therefore it is responsible as well to destroy and free allocated
685         memory with allocator.
686 
687         Params:
688             converted = component that should be destroyed.
689             allocator = allocator used to allocate converted component.
690         **/
691         void destruct(ref Object converted, RCIAllocator allocator = theAllocator) {
692             enforce!InvalidArgumentException(this.convertsFrom(from), text(
693                 "Cannot destruct ", from.identify, " to ", typeid(To), " not supported by ", typeid(this)
694             ));
695 
696             allocator.dispose(converted);
697             converted = Object.init;
698         }
699     }
700 }
701 
702 /**
703 An implementation of mapper, that works solely with components that have their type erased.
704 
705 An implementation of mapper, that works solely with components that have their type erased.
706 At runtime it will attempt to match inspectors, accessor and setter for original component and
707 destination, then use them to create a specific mapper for that configuration and use it to
708 transfer data from origin component to destination one. If no matches are found, no transfer is performed
709 and an exception should be thrown.
710 **/
711 class RuntimeMapper : Mapper!(Object, Object) {
712 
713     private {
714         bool conversion_;
715         bool force_;
716         bool skip_;
717 
718         Convertor[] convertors_;
719 
720         PropertyAccessor!Object[] accessors_;
721         PropertySetter!Object[] setters_;
722         Inspector!Object[] inspectors_;
723         Mapper!Object delegate (
724             in PropertyAccessor!Object,
725             in PropertySetter!Object,
726             in Inspector!Object,
727             in Inspector!Object
728         ) factory_;
729 
730         void delegate(Mapper!Object) destructor_;
731     }
732 
733     public {
734         @property {
735             /**
736             Set convertors
737 
738             Params:
739                 convertors = a list of convertors that could optionally be used to convert mapped fields
740 
741             Returns:
742                 typeof(this)
743             **/
744             typeof(this) convertors(Convertor[] convertors) @safe nothrow pure {
745                 this.convertors_ = convertors;
746 
747                 return this;
748             }
749 
750             /**
751             Get convertors
752 
753             Returns:
754                 Convertor[]
755             **/
756             inout(Convertor[]) convertors() @safe nothrow pure inout {
757                 return this.convertors_;
758             }
759 
760             /**
761             Set conversion
762 
763             Params:
764                 conversion = whether to automatically convert or not mapped fields to desired type
765 
766             Returns:
767                 typeof(this)
768             **/
769             typeof(this) conversion(bool conversion) @safe nothrow pure {
770                 this.conversion_ = conversion;
771 
772                 return this;
773             }
774 
775             /**
776             Get conversion
777 
778             Returns:
779                 bool
780             **/
781             inout(bool) conversion() @safe nothrow pure inout {
782                 return this.conversion_;
783             }
784 
785             /**
786             Set force
787 
788             Params:
789                 force = whether force or not an attempt to set an inexistent field.
790 
791             Returns:
792                 typeof(this)
793             **/
794             typeof(this) force(bool force) @safe nothrow pure {
795                 this.force_ = force;
796 
797                 return this;
798             }
799 
800             /**
801             Get force
802 
803             Returns:
804                 bool
805             **/
806             inout(bool) force() @safe nothrow pure inout {
807                 return this.force_;
808             }
809 
810             /**
811             Set skip
812 
813             Params:
814                 skip = whether to skip mapping of fields that have their type not identifiable in destination component
815 
816             Returns:
817                 typeof(this)
818             **/
819             typeof(this) skip(bool skip) @safe nothrow pure {
820                 this.skip_ = skip;
821 
822                 return this;
823             }
824 
825             /**
826             Get skip
827 
828             Returns:
829                 bool
830             **/
831             inout(bool) skip() @safe nothrow pure inout {
832                 return this.skip_;
833             }
834 
835             /**
836             Set factory
837 
838             Params:
839                 factory = factory used to create a mapper
840 
841             Returns:
842                 typeof(this)
843             **/
844             typeof(this) factory(Mapper!Object delegate (
845                     in PropertyAccessor!Object,
846                     in PropertySetter!Object,
847                     in Inspector!Object,
848                     in Inspector!Object
849                 ) factory) @safe nothrow pure {
850                 this.factory_ = factory;
851 
852                 return this;
853             }
854 
855             /**
856             Get factory
857 
858             Returns:
859                 Mapper!Object delegate ()
860             **/
861             inout(Mapper!Object delegate (
862                 in PropertyAccessor!Object,
863                 in PropertySetter!Object,
864                 in Inspector!Object,
865                 in Inspector!Object
866             )) factory() @safe nothrow pure inout {
867                 return this.factory_;
868             }
869 
870             /**
871             Set destructor
872 
873             Params:
874                 destructor = destructor used to destroy created mapper
875 
876             Returns:
877                 typeof(this)
878             **/
879             typeof(this) destructor(void delegate(Mapper!Object) destructor) @safe nothrow pure {
880                 this.destructor_ = destructor;
881 
882                 return this;
883             }
884 
885             /**
886             Get destructor
887 
888             Returns:
889                 void delegate(Mapper!Object)
890             **/
891             inout(void delegate(Mapper!Object)) destructor() @safe nothrow pure inout {
892                 return this.destructor_;
893             }
894 
895             /**
896             Set accessors
897 
898             Params:
899                 accessors = list of runtime accessors used to access data
900 
901             Returns:
902                 typeof(this)
903             **/
904             typeof(this) accessors(PropertyAccessor!Object[] accessors) @safe nothrow pure {
905                 this.accessors_ = accessors;
906 
907                 return this;
908             }
909 
910             /**
911             Get accessors
912 
913             Returns:
914                 PropertyAccessor!Object[]
915             **/
916             inout(PropertyAccessor!Object[]) accessors() @safe nothrow pure inout {
917                 return this.accessors_;
918             }
919 
920             /**
921             Set setters
922 
923             Params:
924                 setters = list of runtime setters used to map from one to another
925 
926             Returns:
927                 typeof(this)
928             **/
929             typeof(this) setters(PropertySetter!Object[] setters) @safe nothrow pure {
930                 this.setters_ = setters;
931 
932                 return this;
933             }
934 
935             /**
936             Get setters
937 
938             Returns:
939                 PropertySetter!Object[]
940             **/
941             inout(PropertySetter!Object[]) setters() @safe nothrow pure inout {
942                 return this.setters_;
943             }
944 
945             /**
946             Set inspectors
947 
948             Params:
949                 inspectors = list of runtime inspectors used to inspect various components
950 
951             Returns:
952                 typeof(this)
953             **/
954             typeof(this) inspectors(Inspector!Object[] inspectors) @safe nothrow pure {
955                 this.inspectors_ = inspectors;
956 
957                 return this;
958             }
959 
960             /**
961             Get inspectors
962 
963             Returns:
964                 Inspector!Object[]
965             **/
966             inout(Inspector!Object[]) inspectors() @safe nothrow pure inout {
967                 return this.inspectors_;
968             }
969 
970         }
971 
972         /**
973         Map from component to component.
974 
975         Map from component to component, or transfer data from component to component
976         with optional conversion of data along the way.
977 
978         Params:
979             from = original component that has it's data transferred
980             to = destination component that receives transferred data
981             allocator = optional allocator that could be used by convertors when doing field conversion
982         Throws:
983             InvalidArgumentException when no either accessor, setter, or inspector is found.
984         **/
985         void map(Object from, ref Object to, RCIAllocator allocator = theAllocator) {
986             auto accessors = this.accessors.filter!(accessor => accessor.componentType(from) is from.identify);
987             auto setters = this.setters.filter!(setter => setter.componentType(to) is to.identify);
988             auto fromInspectors = this.inspectors.filter!(inspector => inspector.typeOf(from) is from.identify);
989             auto toInspectors = this.inspectors.filter!(inspector => inspector.typeOf(to) is to.identify);
990 
991             enforce!InvalidArgumentException(!accessors.empty, text("No field accessor for ", from.identify, " has been provided, cannot map to ", to.identify));
992             enforce!InvalidArgumentException(!setters.empty, text("No field setter for ", to.identify, " has been provided, cannot map from ", from.identify));
993             enforce!InvalidArgumentException(!fromInspectors.empty, text("No inspector for ", from.identify, " has been provided, cannot map to ", to.identify));
994             enforce!InvalidArgumentException(!toInspectors.empty, text("No inspector for ", to.identify, " has been provided, cannot map from ", to.identify));
995 
996             import std.stdio;
997             writeln(to.identify);
998             auto mapper = this.factory()(
999                 accessors.front,
1000                 setters.front,
1001                 fromInspectors.front,
1002                 toInspectors.front
1003             );
1004             mapper.force = this.force;
1005             mapper.conversion = this.conversion;
1006             mapper.skip = this.skip;
1007             mapper.convertors = this.convertors;
1008 
1009             mapper.map(from, to, allocator);
1010 
1011             this.destructor()(mapper);
1012         }
1013 
1014     }
1015 }