1 module text.xml.Writer; 2 3 import dxml.util; 4 import dxml.writer; 5 import std.array; 6 import std.range; 7 import std.typecons; 8 import text.xml.Tree; 9 10 /** 11 * This struct is used to output XML string representation in a specific 12 * format. 13 */ 14 struct CustomXmlWriter(Flag!"pretty" pretty, Sink) 15 { 16 private XMLWriter!Sink writer; 17 private bool skipIndent = false; 18 19 static if (pretty) 20 { 21 private enum newline = Newline.yes; 22 private enum insertIndent = InsertIndent.yes; 23 } 24 else 25 { 26 private enum newline = Newline.no; 27 private enum insertIndent = InsertIndent.no; 28 } 29 30 public this(Sink sink) 31 { 32 this.writer = xmlWriter(sink); 33 } 34 35 public Sink sink() 36 { 37 return this.writer.output; 38 } 39 40 public void openStartTag(string name) 41 { 42 this.writer.openStartTag(name, newline); 43 } 44 45 public void writeAttr(string name, string value) 46 { 47 this.writer.writeAttr(name, value); 48 } 49 50 public void closeStartTag(Flag!"emptyTag" emptyTag = No.emptyTag) 51 { 52 this.writer.closeStartTag(emptyTag ? EmptyTag.yes : EmptyTag.no); 53 } 54 55 public void writeText(string text) 56 { 57 this.writer.writeText(text.encodeText, Newline.no, InsertIndent.no); 58 this.skipIndent = true; 59 } 60 61 public void writeCDATA(string text) 62 { 63 this.writer.writeCDATA(text.encodeText, newline, insertIndent); 64 } 65 66 public void writeEndTag(string name) 67 { 68 this.writer.writeEndTag(name, this.skipIndent ? Newline.no : newline); 69 this.skipIndent = false; 70 } 71 72 private void finishTag(const XmlNode document) 73 { 74 foreach (attribute; document.attributes) 75 { 76 writeAttr(attribute.name, attribute.value); 77 } 78 if (document.children.empty) 79 { 80 closeStartTag(Yes.emptyTag); 81 } 82 else 83 { 84 closeStartTag(); 85 foreach (child; document.children) 86 { 87 writeImpl(child); 88 } 89 writeEndTag(document.tag); 90 } 91 } 92 93 private void writeImpl(const XmlNode document) 94 { 95 switch (document.type) with (XmlNode.Type) 96 { 97 case element: 98 openStartTag(document.tag); 99 finishTag(document); 100 break; 101 case comment: 102 break; 103 case cdata: 104 writeCDATA(document.text); 105 break; 106 case text: 107 writeText(document.text); 108 break; 109 default: 110 assert(false); 111 } 112 } 113 114 public void write(const XmlNode document) 115 { 116 if (document.type == XmlNode.Type.element) 117 { 118 if (!document.tag.empty) 119 { 120 this.writer.openStartTag(document.tag, Newline.no); 121 finishTag(document); 122 static if (pretty) 123 { 124 this.writer.output.put('\n'); 125 } 126 } 127 else 128 { 129 write(document.children.front); 130 } 131 } 132 else 133 { 134 writeImpl(document); 135 } 136 } 137 } 138 139 template customXmlWriter(Flag!"pretty" pretty) 140 { 141 CustomXmlWriter!(pretty, Sink) customXmlWriter(Sink)(Sink sink) 142 { 143 return typeof(return)(sink); 144 } 145 }