1 module text.xml.Parser; 2 3 import dxml.parser; 4 import dxml.util; 5 import std.algorithm; 6 import std.array; 7 import std.conv; 8 import std.format; 9 import std.typecons; 10 import text.xml.Tree; 11 import text.xml.XmlException; 12 13 /** 14 * Parses the specified content as an XML document. 15 * 16 * This wrapper function improves the behavior of the library implementation that XML well-formedness violations 17 * either result in a range error or are not properly indicated. 18 * 19 * Throws: XmlException on well-formedness violation. 20 */ 21 public XmlNode parse(string content) 22 { 23 try 24 { 25 auto range = parseXML!simpleXML(content); 26 return parseDocumentImpl(range, new MemoryManager); 27 } 28 catch (XMLParsingException exception) 29 { 30 throw new XmlException(format!"not well-formed XML: %s"(exception.msg), 31 exception.file, exception.line, exception); 32 } 33 } 34 35 private XmlNode parseDocumentImpl(ref EntityRange!(simpleXML, string) range, 36 MemoryManager memoryManager) 37 in (!range.empty) 38 in (memoryManager !is null) 39 { 40 XmlNode xmlNode; 41 alias toAttribute = attr => Attribute(attr.name, attr.value); 42 43 final switch (range.front.type) with (EntityType) 44 { 45 case cdata: 46 case comment: 47 case text: 48 xmlNode = XmlNode(range.front.type.asOriginalType.to!(XmlNode.Type), 49 range.front.text.decodeXML); 50 break; 51 case elementStart: 52 xmlNode = XmlNode(XmlNode.Type.element, range.front.name); 53 xmlNode.attributes = map!toAttribute(range.front.attributes); 54 range.popFront; 55 56 auto children = memoryManager.getAppender; 57 58 scope (exit) 59 { 60 memoryManager.releaseAppender(children); 61 } 62 for (; range.front.type != EntityType.elementEnd; range.popFront) 63 { 64 children.put(parseDocumentImpl(range, memoryManager)); 65 } 66 xmlNode.children = children.data.dup; 67 68 break; 69 case elementEmpty: 70 xmlNode = XmlNode(XmlNode.Type.element, range.front.name); 71 xmlNode.attributes = map!toAttribute(range.front.attributes); 72 break; 73 case pi: 74 xmlNode = XmlNode(XmlNode.Type.pi, range.front.text); 75 break; 76 case elementEnd: 77 assert(false); 78 } 79 80 return xmlNode; 81 } 82 83 private class MemoryManager 84 { 85 Appender!(Appender!(XmlNode[])[]) appenders; 86 87 Appender!(XmlNode[]) getAppender() 88 { 89 if (this.appenders.data.empty) 90 { 91 return appender!(XmlNode[]); 92 } 93 auto appender = this.appenders.data.back; 94 95 this.appenders.shrinkTo(this.appenders.data.length - 1); 96 return appender; 97 } 98 99 void releaseAppender(Appender!(XmlNode[]) appender) 100 { 101 appender.clear; 102 this.appenders.put(appender); 103 } 104 }