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 }