ok
[xonotic/netradiant.git] / libs / xml / xmlwriter.h
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #if !defined(INCLUDED_XML_XMLWRITER_H)
23 #define INCLUDED_XML_XMLWRITER_H
24
25 #include "convert.h"
26 #include <vector>
27 #include "xml/ixml.h"
28
29 class BufferedTextOutputStream : public TextOutputStream
30 {
31   static const int m_bufsize = 1024;
32   TextOutputStream& m_ostream;
33   char m_buffer[m_bufsize];
34   char* m_pos;
35   const char* m_end;
36
37   const char* end() const
38   {
39     return m_end;
40   }
41   void reset()
42   {
43     m_pos = m_buffer;
44   }
45   void flush()
46   {
47     m_ostream.write(m_buffer, m_pos - m_buffer);
48     reset();
49   }
50 public:
51   BufferedTextOutputStream(TextOutputStream& ostream) : m_ostream(ostream), m_pos(m_buffer), m_end(m_buffer+m_bufsize)
52   {
53   }
54   ~BufferedTextOutputStream()
55   {
56     flush();
57   }
58   void write(const char c)
59   {
60     if(m_pos == end())
61     {
62       flush();
63     }
64     *m_pos++ = c;
65   }
66   std::size_t write(const char* buffer, std::size_t length)
67   {
68     const char*const end = buffer + length;
69     for(const char* p = buffer; p != end; ++p)
70     {
71       write(*p);
72     }
73     return length;
74   }
75 };
76
77 class XMLEntityOutputStream
78 {
79   BufferedTextOutputStream m_ostream;
80 public:
81   XMLEntityOutputStream(TextOutputStream& ostream)
82     : m_ostream(ostream)
83   {
84   }
85   void write(const char c)
86   {
87     m_ostream.write(c);
88   }
89   void writeEscaped(const char c)
90   {
91     switch(c)
92     {
93     case '<':
94       write('&');
95       write('l');
96       write('t');
97       write(';');
98       break;
99     case '>':
100       write('&');
101       write('g');
102       write('t');
103       write(';');
104       break;
105     case '"':
106       write('&');
107       write('q');
108       write('u');
109       write('o');
110       write('t');
111       write(';');
112       break;
113     case '&':
114       write('&');
115       write('a');
116       write('m');
117       write('p');
118       write(';');
119       break;
120     default:
121       write(c);
122       break;
123     }
124   }
125   std::size_t write(const char* buffer, std::size_t length)
126   {
127     const char*const end = buffer + length;
128     for(const char* p = buffer; p != end; ++p)
129     {
130       writeEscaped(*p);
131     }
132     return length;
133   }
134 };
135
136 template<typename T>
137 inline XMLEntityOutputStream& operator<<(XMLEntityOutputStream& ostream, const T& t)
138 {
139   return ostream_write(ostream, t);
140 }
141
142
143 class XMLStreamWriter : public XMLImporter, public XMLAttrVisitor
144 {
145   class state_t
146   {
147   public:
148     enum EState
149     {
150       eStartElement,
151       eContent,
152     };
153     state_t()
154       : m_state(eStartElement)
155     {}
156     EState m_state;
157   };
158
159   XMLEntityOutputStream m_ostream;
160   std::vector<state_t> m_elements;
161
162   void write_cdata(const char* buffer, std::size_t length)
163   {
164     m_ostream << ConvertLocaleToUTF8(StringRange(buffer, buffer + length));
165   }
166   void write_string(const char* string)
167   {
168     m_ostream << string;
169   }
170   void write_quoted_string(const char* string)
171   {
172     m_ostream.write('"');
173     m_ostream << string;
174     m_ostream.write('"');
175   }
176 public:
177   XMLStreamWriter(TextOutputStream& ostream)
178     : m_ostream(ostream)
179   {
180     m_elements.push_back(state_t());
181     m_elements.back().m_state = state_t::eContent;
182     m_ostream.write('<');
183     m_ostream.write('?');
184     write_string("xml");
185     visit("version", "1.0");
186     m_ostream.write('?');
187     m_ostream.write('>');
188   }
189
190   void pushElement(const XMLElement& element)
191   {
192     if(m_elements.back().m_state == state_t::eStartElement)
193     {
194       m_elements.back().m_state = state_t::eContent;
195       m_ostream.write('>');
196     }
197
198     m_elements.push_back(state_t());
199
200     m_ostream.write('<');
201     write_string(element.name());
202     element.forEachAttribute(*this);
203   }
204   void popElement(const char* name)
205   {
206     if(m_elements.back().m_state == state_t::eStartElement)
207     {
208       m_ostream.write('/');
209       m_ostream.write('>');
210     }
211     else
212     {
213       m_ostream.write('<');
214       m_ostream.write('/');
215       write_string(name);
216       m_ostream.write('>');
217     }
218   }
219   std::size_t write(const char* data, std::size_t length)
220   {
221     if(m_elements.back().m_state == state_t::eStartElement)
222     {
223       m_elements.back().m_state = state_t::eContent;
224       m_ostream.write('>');
225     }
226     write_cdata(data, length);
227     return length;
228   }
229
230   void visit(const char* name, const char* value)
231   {
232     m_ostream.write(' ');
233     write_string(name);
234     m_ostream.write('=');
235     write_quoted_string(value);
236   }
237 };
238
239
240 #endif