1 /** 2 * Very simple write-only API for building XML documents. 3 * Abuses operator overloading to allow a very terse syntax. 4 * 5 * License: 6 * This Source Code Form is subject to the terms of 7 * the Mozilla Public License, v. 2.0. If a copy of 8 * the MPL was not distributed with this file, You 9 * can obtain one at http://mozilla.org/MPL/2.0/. 10 * 11 * Authors: 12 * Vladimir Panteleev <vladimir@thecybershadow.net> 13 */ 14 15 module ae.utils.xmlbuild; 16 17 import ae.utils.xmlwriter; 18 19 /// Create an XML node. Entry point. 20 XmlBuildNode newXml() 21 { 22 return new XmlBuildNode(); 23 } 24 25 /// The node type. Avoid using directly. 26 // Can't use struct pointers, because node["attr"] is 27 // interpreted as indexing the pointer. 28 final class XmlBuildNode 29 { 30 /// Create a child node by calling a "method" on the node 31 XmlBuildNode opDispatch(string name)(string[string] attributes = null) 32 { 33 auto result = new XmlBuildNode(); 34 result._xmlbuild_info.tag = name; 35 foreach (key, value; attributes) 36 result._xmlbuild_info.attributes ~= StringPair(key, value); 37 _xmlbuild_info.children ~= result; 38 return result; 39 } 40 41 /// Add attribute by assigning a "field" on the node 42 @property string opDispatch(string name)(string value) 43 { 44 _xmlbuild_info.attributes ~= StringPair(name, value); 45 return value; 46 } 47 48 /// Add attribute via index 49 string opIndexAssign(string value, string name) 50 { 51 _xmlbuild_info.attributes ~= StringPair(name, value); 52 return value; 53 } 54 55 /// Set inner text via assigning a string 56 void opAssign(string text) 57 { 58 _xmlbuild_info.text = text; 59 } 60 61 override string toString() const 62 { 63 XmlWriter writer; 64 writeTo(writer); 65 return writer.output.get(); 66 } 67 68 string toPrettyString() const 69 { 70 PrettyXmlWriter writer; 71 writeTo(writer); 72 return writer.output.get(); 73 } 74 75 final void writeTo(XmlWriter)(ref XmlWriter output) const 76 { 77 with (_xmlbuild_info) 78 { 79 output.startTagWithAttributes(tag); 80 foreach (ref attribute; attributes) 81 output.addAttribute(attribute.key, attribute.value); 82 if (!children.length && !text) 83 { 84 output.endAttributesAndTag(); 85 return; 86 } 87 output.endAttributes(); 88 89 foreach (child; children) 90 child.writeTo(output); 91 output.text(text); 92 93 output.endTag(tag); 94 } 95 } 96 97 // Use a unique name, unlikely to occur in an XML file as a field or attribute. 98 private XmlBuildInfo _xmlbuild_info; 99 } 100 101 private: 102 103 struct StringPair { string key, value; } 104 105 struct XmlBuildInfo 106 { 107 string tag, text; 108 StringPair[] attributes; 109 XmlBuildNode[] children; 110 } 111 112 unittest 113 { 114 auto svg = newXml().svg(); 115 svg.xmlns = "http://www.w3.org/2000/svg"; 116 svg["version"] = "1.1"; 117 auto text = svg.text(["x" : "0", "y" : "15", "fill" : "red"]); 118 text = "I love SVG"; 119 120 auto s = svg.toString(); 121 string s1 = `<svg xmlns="http://www.w3.org/2000/svg" version="1.1"><text fill="red" x="0" y="15">I love SVG</text></svg>`; 122 string s2 = `<svg xmlns="http://www.w3.org/2000/svg" version="1.1"><text x="0" y="15" fill="red">I love SVG</text></svg>`; 123 assert(s == s1 || s == s2, s); 124 }