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 }