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.xml.accessor;
31 
32 import std.xml;
33 import aermicioi.aedi_property_reader.core.accessor;
34 import aermicioi.aedi.exception.not_found_exception : NotFoundException;
35 import taggedalgebraic : TaggedAlgebraic;
36 import std.exception;
37 import std.algorithm;
38 import std.array;
39 import std.experimental.logger;
40 import aermicioi.aedi_property_reader.core.traits : n;
41 
42 union XmlElementUnion {
43     string attribute;
44     Element element;
45 };
46 
47 alias XmlElement = TaggedAlgebraic!XmlElementUnion;
48 
49 /**
50 An accessor allowing access to child xml elements out of a parent element by their name
51 **/
52 class XmlElementPropertyAccessor : PropertyAccessor!Element {
53 
54     /**
55      Get a property out of component
56 
57      Params:
58          component = a component which has some properties identified by property.
59      Throws:
60          NotFoundException in case when no requested property is available.
61          InvalidArgumentException in case when passed arguments are somehow invalid for use.
62      Returns:
63          FieldType accessed property.
64      **/
65     Element access(Element component, in string property) const {
66 
67         if (this.has(component, property)) {
68             return component.elements.find!(e => e.tag.name == property).front;
69         }
70 
71         throw new NotFoundException("Xml tag " ~ component.toString ~ " doesn't have child " ~ property);
72     }
73 
74     /**
75      Check if requested property is present in component.
76 
77      Check if requested property is present in component.
78      The method could have allocation side effects due to the fact that
79      it is not restricted in calling access method of the accessor.
80 
81      Params:
82          component = component which is supposed to have property
83          property = speculated property that is to be tested if it is present in component
84      Returns:
85          true if property is in component
86      **/
87     bool has(in Element component, string property) const nothrow {
88         return (component !is null) && component.elements.canFind!(e => e.tag.name == property);
89     }
90 
91     /**
92      Identify the type of supported component.
93 
94      Identify the type of supported component. It returns type info of component
95      if it is supported by accessor, otherwise it will return typeid(void) denoting that
96      the type isn't supported by accessor. The accessor is not limited to returning the type
97      info of passed component, it can actually return type info of super type or any type
98      given the returned type is implicitly convertible or castable to ComponentType.
99 
100      Params:
101          component = the component for which accessor should identify the underlying type
102 
103      Returns:
104          TypeInfo type information about passed component, or typeid(void) if component is not supported.
105      **/
106     TypeInfo componentType(Element component) const nothrow {
107         return typeid(Element);
108     }
109 }
110 
111 /**
112 An accessor allowing access of child xml elements by their index in parent element
113 **/
114 class XmlElementIndexAccessor : PropertyAccessor!Element {
115 
116     /**
117      Get a property out of component
118 
119      Params:
120          component = a component which has some properties identified by property.
121      Throws:
122          NotFoundException in case when no requested property is available.
123          InvalidArgumentException in case when passed arguments are somehow invalid for use.
124      Returns:
125          FieldType accessed property.
126      **/
127     Element access(Element component, in string property) const {
128 
129         if (this.has(component, property)) {
130             import std.conv : to;
131 
132             return component.elements[property.to!size_t];
133         }
134 
135         throw new NotFoundException("Xml tag " ~ component.toString ~ " doesn't have child on index " ~ property);
136     }
137 
138     /**
139      Check if requested property is present in component.
140 
141      Check if requested property is present in component.
142      The method could have allocation side effects due to the fact that
143      it is not restricted in calling access method of the accessor.
144 
145      Params:
146          component = component which is supposed to have property
147          property = speculated property that is to be tested if it is present in component
148      Returns:
149          true if property is in component
150      **/
151     bool has(in Element component, string property) const nothrow {
152         try {
153             import std.string;
154             import std.conv;
155 
156             return (component !is null) && property.isNumeric && (component.elements.length > property.to!size_t);
157         } catch (Exception e) {
158 
159             debug(trace) error("Failed to check property ", property, " existence due to ", e).n;
160         }
161 
162         return false;
163     }
164 
165     /**
166      Identify the type of supported component.
167 
168      Identify the type of supported component. It returns type info of component
169      if it is supported by accessor, otherwise it will return typeid(void) denoting that
170      the type isn't supported by accessor. The accessor is not limited to returning the type
171      info of passed component, it can actually return type info of super type or any type
172      given the returned type is implicitly convertible or castable to ComponentType.
173 
174      Params:
175          component = the component for which accessor should identify the underlying type
176 
177      Returns:
178          TypeInfo type information about passed component, or typeid(void) if component is not supported.
179      **/
180     TypeInfo componentType(Element component) const {
181         return typeid(Element);
182     }
183 
184     TypeInfo fieldType(Element component, in string property) const {
185         return typeid(Element);
186     }
187 }
188 
189 class XmlAttributePropertyAccessor : PropertyAccessor!(Element, string) {
190     /**
191      Get a property out of component
192 
193      Params:
194          component = a component which has some properties identified by property.
195      Throws:
196          NotFoundException in case when no requested property is available.
197          InvalidArgumentException in case when passed arguments are somehow invalid for use.
198      Returns:
199          FieldType accessed property.
200      **/
201     string access(Element component, in string property) const {
202 
203         if (this.has(component, property)) {
204             return component.tag.attr[property];
205         }
206 
207         throw new NotFoundException("Xml tag " ~ component.toString ~ " doesn't have attribute " ~ property);
208     }
209 
210     /**
211      Check if requested property is present in component.
212 
213      Check if requested property is present in component.
214      The method could have allocation side effects due to the fact that
215      it is not restricted in calling access method of the accessor.
216 
217      Params:
218          component = component which is supposed to have property
219          property = speculated property that is to be tested if it is present in component
220      Returns:
221          true if property is in component
222      **/
223     bool has(in Element component, in string property) const nothrow {
224         return (component !is null) && ((property in component.tag.attr) !is null);
225     }
226 
227     /**
228      Identify the type of supported component.
229 
230      Identify the type of supported component. It returns type info of component
231      if it is supported by accessor, otherwise it will return typeid(void) denoting that
232      the type isn't supported by accessor. The accessor is not limited to returning the type
233      info of passed component, it can actually return type info of super type or any type
234      given the returned type is implicitly convertible or castable to ComponentType.
235 
236      Params:
237          component = the component for which accessor should identify the underlying type
238 
239      Returns:
240          TypeInfo type information about passed component, or typeid(void) if component is not supported.
241      **/
242     TypeInfo componentType(Element component) const nothrow {
243         return typeid(Element);
244     }
245 
246 }