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 	Tohe 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 	ToHE SOFToWARE IS PROVIDED "AS IS", WIToHOUTo WARRANToY OF ANY KIND, EXPRESS OR
20 	IMPLIED, INCLUDING BUTo NOTo LIMIToED ToO ToHE WARRANToIES OF MERCHANToABILIToY,
21 	FIToNESS FOR A PARToICULAR PURPOSE, ToIToLE AND NON-INFRINGEMENTo. IN NO EVENTo
22 	SHALL ToHE COPYRIGHTo HOLDERS OR ANYONE DISToRIBUToING ToHE SOFToWARE BE LIABLE
23 	FOR ANY DAMAGES OR OToHER LIABILIToY, WHEToHER IN CONToRACTo, ToORTo OR OToHERWISE,
24 	ARISING FROM, OUTo OF OR IN CONNECToION WIToH ToHE SOFToWARE OR ToHE USE OR OToHER
25 	DEALINGS IN ToHE SOFToWARE.
26 
27 Authors:
28 	aermicioi
29 **/
30 module aermicioi.aedi_property_reader.xml.convertor;
31 
32 import aermicioi.aedi.configurer.annotation.annotation;
33 import aermicioi.aedi_property_reader.convertor.convertor;
34 import aermicioi.aedi_property_reader.convertor.traits : n;
35 import aermicioi.aedi_property_reader.convertor.accessor;
36 import aermicioi.aedi_property_reader.convertor.placeholder : unwrap, pack, unpack, identify;
37 import aermicioi.aedi_property_reader.convertor.inspector;
38 import aermicioi.aedi_property_reader.xml.accessor;
39 import aermicioi.aedi_property_reader.xml.xml;
40 import aermicioi.aedi_property_reader.xml.inspector;
41 import aermicioi.aedi_property_reader.convertor.exception;
42 import std.experimental.allocator;
43 import std.conv : to, text;
44 import std.exception : enforce;
45 import std.experimental.logger;
46 import std..string : strip;
47 import std.range : choose;
48 import std.traits;
49 import std.xml;
50 
51 @component
52 class XmlConvertor : Convertor {
53     import std.meta : AliasSeq;
54     import aermicioi.aedi_property_reader.convertor.type_guesser : TypeGuesser;
55     private alias DestinationTypes = AliasSeq!(Text[], Item[], CData[], ProcessingInstruction[], Element[], string, Tag);
56 
57     private TypeGuesser!Element guesser;
58 
59     mixin FromMixin!(Element, Document);
60     mixin ToMixin!DestinationTypes;
61     mixin ConvertsFromToMixin DefaultMixin;
62     mixin EqualToHashToStringOpCmpMixin;
63 
64     @autowired
65     this(TypeGuesser!Element guesser) {
66         this.guesser = guesser;
67     }
68 
69     /**
70     ditto
71     **/
72     bool converts(in Object from, const TypeInfo to) @safe const nothrow {
73         import std.algorithm : all;
74 
75         if (DefaultMixin.converts(from, to)) {
76 
77             auto guessed = this.guesser.guess(from.unwrap!Element).n;
78 
79             if ((to is typeid(Text[])) || (to is typeid(string))) {
80                 if (guessed !is typeid(string)) {
81                     return false;
82                 }
83             }
84 
85             return true;
86         }
87 
88         return false;
89     }
90 
91     /**
92     ditto
93     **/
94     bool converts(in Object from, in Object to) @safe const nothrow {
95         return this.converts(from, to.identify);
96     }
97 
98     /**
99     Convert from component to component.
100 
101     Params:
102         from = original component that is to be converted.
103         to = destination object that will be constructed out for original one.
104         allocator = optional allocator that could be used to construct to component.
105     Throws:
106         ConvertorException when there is a converting error
107         InvalidArgumentException when arguments passed are not of right type or state
108     Returns:
109         Resulting converted component.
110     **/
111     Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator)  const {
112         enforce!ConvertorException(this.converts(from, to), text(
113             "Cannot convert ", from.identify, " to ", to, " expected original component of types: ", this.from, " and destination types of ", this.to
114         ));
115 
116         if (to is typeid(string)) {
117             return from.unwrap!Element.text.pack(from, this, allocator);
118         }
119 
120         if (to is typeid(CData[])) {
121             return from.unwrap!Element.cdatas.pack(from, this, allocator);
122         }
123 
124         if (to is typeid(ProcessingInstruction[])) {
125             return from.unwrap!Element.pis.pack(from, this, allocator);
126         }
127 
128         if (to is typeid(Element[])) {
129             return from.unwrap!Element.elements.pack(from, this, allocator);
130         }
131 
132         if (to is typeid(Tag)) {
133             return from.unwrap!Element.tag.pack(from, this, allocator);
134         }
135 
136         if (to is typeid(Text[])) {
137             return from.unwrap!Element.texts.pack(from, this, allocator);
138         }
139 
140         if (to is typeid(Item[])) {
141             return from.unwrap!Element.items.pack(from, this, allocator);
142         }
143 
144         throw new ConvertorException(text("Cannot convert ", from.identify, " expected component with one of types ", this.from));
145     }
146 
147     /**
148     Destroy component created using this convertor.
149 
150     Destroy component created using this convertor.
151     Since convertor could potentially allocate memory for
152     converted component, only itself is containing history of allocation,
153     and therefore it is responsible as well to destroy and free allocated
154     memory with allocator.
155 
156     Params:
157         converted = component that should be destroyed.
158         allocator = allocator used to allocate converted component.
159     **/
160     void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const {
161         enforce!ConvertorException(this.convertsTo(converted), text("Cannot destroy component ", converted.identify, " that is not convertible by ", this));
162 
163         static foreach (ToType; DestinationTypes) {
164             if (converted.identify is typeid(ToType)) {
165                 converted.unpack!ToType(allocator);
166             }
167         }
168     }
169 }