1 /** 2 * Copyright © DiamondMVC 2019 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.web.soap.service.parser.typeparser; 7 8 import std..string : toLower, strip, format; 9 import std.array : split; 10 import std.algorithm : endsWith; 11 12 import diamond.dom; 13 import diamond.xml; 14 import diamond.web.soap.service.soaptype; 15 import diamond.web.soap.service.complextype; 16 import diamond.web.soap.service.simpletype; 17 import diamond.web.soap.service.element; 18 import diamond.web.soap.service.aliastype; 19 import diamond.errors.exceptions.soapexception; 20 21 package(diamond.web.soap.service.parser): 22 /** 23 * Parses a schema. 24 * Params: 25 * schema = The schema to parse. 26 * Returns: 27 * The parsed soap types from the schema. 28 */ 29 SoapType[] parseSchema(XmlNode schema) 30 { 31 auto elementFormDefaultAttribute = schema.getAttribute("elementFormDefault"); 32 auto elementFormDefault = elementFormDefaultAttribute ? elementFormDefaultAttribute.value : ""; 33 34 auto targetNamespaceAttribute = schema.getAttribute("targetNamespace"); 35 auto targetNamespace = targetNamespaceAttribute ? targetNamespaceAttribute.value : ""; 36 37 if (!schema.children || !schema.children.length) 38 { 39 return null; 40 } 41 42 SoapType[] result; 43 44 foreach (child; schema.children) 45 { 46 switch (child.name.toLower()) 47 { 48 case "xs:complextype": 49 case "xsd:complextype": 50 { 51 result ~= parseComplexType(child); 52 break; 53 } 54 55 case "xs:simpletype": 56 case "xsd:simpletype": 57 { 58 result ~= parseSimpleType(child); 59 break; 60 } 61 62 case "xs:attribute": break; // Although unsupported, attributes such as unions shouldn't break parsing. 63 case "xsd:attribute": break; // Although unsupported, attributes such as unions shouldn't break parsing. 64 65 case "xs:element": 66 case "xsd:element": 67 { 68 auto elementType = parseElementType(child); 69 70 if (elementType) 71 { 72 result ~= elementType; 73 } 74 break; 75 } 76 77 default: 78 { 79 throw new SoapException("Unsupported type definition."); 80 } 81 } 82 } 83 84 return result; 85 } 86 87 /** 88 * Parses an element type. 89 * Params: 90 * elementTypeNode = The element node. 91 * Returns: 92 * A soap type equivalent to the parsed element type. 93 */ 94 SoapType parseElementType(XmlNode elementTypeNode) 95 { 96 auto nameAttribute = elementTypeNode.getAttribute("name"); 97 auto name = nameAttribute ? nameAttribute.value.strip() : null; 98 99 if (!name || !name.length) 100 { 101 throw new SoapException("Expected an element type name."); 102 } 103 104 if (!elementTypeNode.children || !elementTypeNode.children.length) 105 { 106 // The type might use an alias ... 107 108 auto aliasType = elementTypeNode.getAttribute("type"); 109 110 if (aliasType && aliasType.value.split(":").length == 2) 111 { 112 return new SoapAliasType(name, aliasType.value.split(":")[1]); 113 } 114 115 return null; 116 } 117 118 auto type = elementTypeNode.children[0]; 119 120 if (type.name.toLower() != "xs:complextype" && type.name.toLower() != "xs:simpletype" && type.name.toLower() != "xsd:complextype" && type.name.toLower() != "xsd:simpletype") 121 { 122 throw new SoapException("Expected a simple or complex type for '%s'.".format(name)); 123 } 124 125 final switch (type.name.toLower()) 126 { 127 case "xs:complextype": 128 case "xsd:complextype": 129 { 130 if (!type.hasAttribute("name")) 131 { 132 type.addAttribute("name", name); 133 } 134 135 auto complexType = parseComplexType(type); 136 137 return complexType; 138 } 139 140 case "xs:simpletype": 141 case "xsd:simpletype": 142 { 143 if (!type.hasAttribute("name")) 144 { 145 type.addAttribute("name", name); 146 } 147 148 auto simpleType = parseSimpleType(type); 149 150 return simpleType; 151 } 152 } 153 } 154 155 /** 156 * Parses a simple type. 157 * Params: 158 * simpleTypeNode = The simple type node. 159 * Returns: 160 * The simple typed parsed. 161 */ 162 SoapSimpleType parseSimpleType(XmlNode simpleTypeNode) 163 { 164 auto nameAttribute = simpleTypeNode.getAttribute("name"); 165 auto name = nameAttribute ? nameAttribute.value.strip() : null; 166 167 if (!name || !name.length) 168 { 169 throw new SoapException("Expected a simple type name."); 170 } 171 172 if (!simpleTypeNode.children || simpleTypeNode.children.length != 1) 173 { 174 throw new SoapException("'%s' is either an empty simple type or has too many defintions.".format(name)); 175 } 176 177 auto type = simpleTypeNode.children[0]; 178 179 switch (type.name.toLower()) 180 { 181 case "xs:restriction": 182 case "xsd:restriction": 183 { 184 auto baseAttribute = type.getAttribute("base"); 185 auto base = baseAttribute ? baseAttribute.value.strip() : null; 186 187 if (!base || base.split(":").length != 2) 188 { 189 throw new SoapException("'%s' has no valid base type."); 190 } 191 192 base = base.split(":")[1]; 193 194 return new SoapSimpleType(name, base, SoapSimpleTypeDefinition.restriction); 195 } 196 197 case "xs:list": 198 case "xsd:list": 199 { 200 auto itemTypeAttribute = type.getAttribute("itemType"); 201 auto itemType = itemTypeAttribute ? itemTypeAttribute.value.strip() : null; 202 203 if (!itemType || itemType.split(":").length != 2) 204 { 205 throw new SoapException("'%s' has no valid item type."); 206 } 207 208 itemType = itemType.split(":")[1]; 209 210 return new SoapSimpleType(name, itemType, SoapSimpleTypeDefinition.list); 211 } 212 213 default: 214 { 215 throw new SoapException("Unsupported simple type definition."); 216 } 217 } 218 } 219 220 /** 221 * Parses a complex type. 222 * Params: 223 * complexTypeNode = The complex node. 224 * Returns: 225 * The complex node parsed. 226 */ 227 SoapComplexType parseComplexType(XmlNode complexTypeNode) 228 { 229 auto nameAttribute = complexTypeNode.getAttribute("name"); 230 auto name = nameAttribute ? nameAttribute.value.strip() : null; 231 232 if (!name || !name.length) 233 { 234 throw new SoapException("Expected a complex type name."); 235 } 236 237 if (!complexTypeNode.children || !complexTypeNode.children.length) 238 { 239 throw new SoapException("'%s' is an empty complex type.".format(name)); 240 } 241 242 auto sequence = complexTypeNode.children[0]; 243 244 if (sequence.name.toLower() != "xs:sequence" && sequence.name.toLower() != "xsd:sequence") 245 { 246 throw new SoapException("Expected a sequence element for '%s'.".format(name)); 247 } 248 249 auto complexType = new SoapComplexType(name); 250 251 auto elements = sequence.getByTagName("xs:element"); 252 253 if (!elements) 254 { 255 elements = sequence.getByTagName("xsd:element"); 256 } 257 258 if (elements && elements.length) 259 { 260 foreach (elementNode; elements) 261 { 262 auto element = parseElement(elementNode); 263 264 complexType.addElement(element); 265 } 266 } 267 268 return complexType; 269 } 270 271 /** 272 * Parses an element. 273 * Params: 274 * elementNode = The element. 275 * Returns: 276 * A soap element equivalent to the element node. 277 */ 278 SoapElement parseElement(XmlNode elementNode) 279 { 280 auto nameAttribute = elementNode.getAttribute("name"); 281 auto name = nameAttribute ? nameAttribute.value.strip() : null; 282 283 if (!name || !name.length) 284 { 285 throw new SoapException("Expected an element name."); 286 } 287 288 auto typeAttribute = elementNode.getAttribute("type"); 289 auto type = typeAttribute ? typeAttribute.value.strip() : null; 290 291 if (!type || type.split(":").length != 2) 292 { 293 throw new SoapException("Element '%s' has no type.".format(name)); 294 } 295 296 type = type.split(":")[1]; 297 298 if (elementNode.hasAttribute("maxOccurs") && elementNode.getAttribute("maxOccurs").value.strip().toLower() == "unbounded" && type != "string") 299 { 300 type ~= "[]"; 301 } 302 303 return new SoapElement(name, type); 304 } 305 306 /** 307 * Parses an array of soap types into d types. 308 * Params: 309 * soapTypes = The array of soap types. 310 * Returns: 311 * A string equivalent to all the d types. 312 */ 313 string parseDTypes(SoapType[] soapTypes) 314 { 315 if (!soapTypes || !soapTypes.length) 316 { 317 return ""; 318 } 319 320 string result; 321 322 enum classTypeFormat = q{ 323 final class %s : SoapEnvelopeType 324 { 325 public: 326 final: 327 this() {} 328 329 %s} 330 }; 331 332 enum classTypeElementFormat = " %s %s;\r\n"; 333 334 enum aliasTypeFormat = "public alias %s = %s;\r\n"; 335 336 foreach (type; soapTypes) 337 { 338 auto complex = cast(SoapComplexType)type; 339 340 if (complex) 341 { 342 string elementResult = ""; 343 344 if (complex.elements) 345 { 346 foreach (element; complex.elements) 347 { 348 if (complex.elements.length == 1 && element.type.endsWith("[]")) 349 { 350 elementResult ~= classTypeElementFormat.format(element.type, "_array_"); 351 elementResult ~= classTypeElementFormat.format("alias", "_array_ this"); 352 } 353 else if (element.type == element.name) 354 { 355 elementResult ~= classTypeElementFormat.format(element.type, "_%s_".format(element.name)); 356 } 357 else 358 { 359 elementResult ~= classTypeElementFormat.format(element.type, element.name); 360 } 361 } 362 } 363 364 result ~= classTypeFormat.format(complex.name, elementResult); 365 continue; 366 } 367 368 auto simpleType = cast(SoapSimpleType)type; 369 370 if (simpleType) 371 { 372 final switch (simpleType.definition) 373 { 374 case SoapSimpleTypeDefinition.restriction: 375 { 376 result ~= aliasTypeFormat.format(simpleType.name, simpleType.typeName); 377 break; 378 } 379 380 case SoapSimpleTypeDefinition.list: 381 { 382 auto listTypeResult = classTypeElementFormat.format(simpleType.typeName, "_value_"); 383 listTypeResult ~= classTypeElementFormat.format("alias", "_value_ this"); 384 385 result ~= classTypeFormat.format(simpleType.name, listTypeResult); 386 break; 387 } 388 } 389 continue; 390 } 391 392 auto aliasType = cast(SoapAliasType)type; 393 394 if (aliasType) 395 { 396 result ~= aliasTypeFormat.format(aliasType.name, aliasType.aliasName); 397 continue; 398 } 399 } 400 401 return result ? result : ""; 402 }