Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
XMLWriter.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "XMLWriter.h"
21 
22 #include "ps/CLogger.h"
23 #include "ps/Filesystem.h"
24 #include "ps/XML/Xeromyces.h"
25 #include "lib/utf8.h"
27 #include "lib/sysdep/cpu.h"
28 #include "maths/Fixed.h"
29 
30 
31 // TODO (maybe): Write to the file frequently, instead of buffering
32 // the entire file, so that large files get written faster.
33 
34 namespace
35 {
36  CStr escapeAttributeValue(const char* input)
37  {
38  // Spec says:
39  // AttValue ::= '"' ([^<&"] | Reference)* '"'
40  // so > is allowed in attribute values, so we don't bother escaping it.
41 
42  CStr ret = input;
43  ret.Replace("&", "&amp;");
44  ret.Replace("<", "&lt;");
45  ret.Replace("\"", "&quot;");
46  return ret;
47  }
48 
49  CStr escapeCharacterData(const char* input)
50  {
51  // CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
52 
53  CStr ret = input;
54  ret.Replace("&", "&amp;");
55  ret.Replace("<", "&lt;");
56  ret.Replace("]]>", "]]&gt;");
57  return ret;
58  }
59 
60  CStr escapeCDATA(const char* input)
61  {
62  CStr ret = input;
63  ret.Replace("]]>", "]]>]]&gt;<![CDATA[");
64  return ret;
65  }
66 
67  CStr escapeComment(const char* input)
68  {
69  // Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
70  // This just avoids double-hyphens, and doesn't enforce the no-hyphen-at-end
71  // rule, since it's only used in contexts where there's already a space
72  // between this data and the -->.
73  CStr ret = input;
74  ret.Replace("--", "\xE2\x80\x90\xE2\x80\x90");
75  // replace with U+2010 HYPHEN, because it's close enough and it's
76  // probably nicer than inserting spaces or deleting hyphens or
77  // any alternative
78  return ret;
79  }
80 }
81 
83 
85  : m_Indent(0), m_LastElement(NULL),
86  m_PrettyPrint(true)
87 {
88  // Encoding is always UTF-8 - that's one of the only two guaranteed to be
89  // supported by XML parsers (along with UTF-16), and there's not much need
90  // to let people choose another.
91  m_Data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
92 }
93 
94 bool XMLWriter_File::StoreVFS(const PIVFS& vfs, const VfsPath& pathname)
95 {
96  if (m_LastElement) debug_warn(L"ERROR: Saving XML while an element is still open");
97 
98  const size_t size = m_Data.length();
99  shared_ptr<u8> data;
100  AllocateAligned(data, size, maxSectorSize);
101  memcpy(data.get(), m_Data.data(), size);
102  Status ret = vfs->CreateFile(pathname, data, size);
103  if (ret < 0)
104  {
105  LOGERROR(L"Error saving XML data through VFS: %lld '%ls'", (long long)ret, pathname.string().c_str());
106  return false;
107  }
108  return true;
109 }
110 
112 {
113  return m_Data;
114 }
115 
116 
117 void XMLWriter_File::XMB(const XMBFile& file)
118 {
119  ElementXMB(file, file.GetRoot());
120 }
121 
123 {
124  XMLWriter_Element writer(*this, file.GetElementString(el.GetNodeName()).c_str());
125 
126  XERO_ITER_ATTR(el, attr)
127  writer.Attribute(file.GetAttributeString(attr.Name).c_str(), attr.Value);
128 
129  XERO_ITER_EL(el, child)
130  ElementXMB(file, child);
131 }
132 
133 void XMLWriter_File::Comment(const char* text)
134 {
135  ElementStart(NULL, "!-- ");
136  m_Data += escapeComment(text);
137  m_Data += " -->";
138  --m_Indent;
139 }
140 
142 {
143  return std::string(m_Indent, '\t');
144 }
145 
146 void XMLWriter_File::ElementStart(XMLWriter_Element* element, const char* name)
147 {
149  m_LastElement = element;
150 
151  if (m_PrettyPrint)
152  {
153  m_Data += "\n";
154  m_Data += Indent();
155  }
156  m_Data += "<";
157  m_Data += name;
158 
159  ++m_Indent;
160 }
161 
163 {
164  m_Data += ">";
165 }
166 
167 void XMLWriter_File::ElementEnd(const char* name, int type)
168 {
169  --m_Indent;
170  m_LastElement = NULL;
171 
172  switch (type)
173  {
174  case EL_ATTR:
175  m_Data += "/>";
176  break;
177  case EL_TEXT:
178  m_Data += "</";
179  m_Data += name;
180  m_Data += ">";
181  break;
182  case EL_SUBEL:
183  if (m_PrettyPrint)
184  {
185  m_Data += "\n";
186  m_Data += Indent();
187  }
188  m_Data += "</";
189  m_Data += name;
190  m_Data += ">";
191  break;
192  default:
194  }
195 }
196 
197 void XMLWriter_File::ElementText(const char* text, bool cdata)
198 {
199  if (cdata)
200  {
201  m_Data += "<![CDATA[";
202  m_Data += escapeCDATA(text);
203  m_Data += "]]>";
204  }
205  else
206  {
207  m_Data += escapeCharacterData(text);
208  }
209 }
210 
211 
213  : m_File(&file), m_Name(name), m_Type(EL_ATTR)
214 {
215  m_File->ElementStart(this, name);
216 }
217 
218 
220 {
221  m_File->ElementEnd(m_Name.c_str(), m_Type);
222 }
223 
224 
226 {
227  if (m_Type == type)
228  return;
229 
230  m_File->ElementClose();
231  m_Type = type;
232 }
233 
234 
235 // Template specialisations for various string types:
236 
237 template <> void XMLWriter_Element::Text<const char*>(const char* text, bool cdata)
238 {
239  Close(EL_TEXT);
240  m_File->ElementText(text, cdata);
241 }
242 
243 template <> void XMLWriter_Element::Text<const wchar_t*>(const wchar_t* text, bool cdata)
244 {
245  Text( CStrW(text).ToUTF8().c_str(), cdata );
246 }
247 
248 //
249 
250 template <> void XMLWriter_File::ElementAttribute<const char*>(const char* name, const char* const& value, bool newelement)
251 {
252  if (newelement)
253  {
254  ElementStart(NULL, name);
255  m_Data += ">";
256  ElementText(value, false);
257  ElementEnd(name, EL_TEXT);
258  }
259  else
260  {
261  ENSURE(m_LastElement && m_LastElement->m_Type == EL_ATTR);
262  m_Data += " ";
263  m_Data += name;
264  m_Data += "=\"";
265  m_Data += escapeAttributeValue(value);
266  m_Data += "\"";
267  }
268 }
269 
270 // Attribute/setting value-to-string template specialisations.
271 //
272 // These only deal with basic types. Anything more complicated should
273 // be converted into a basic type by whatever is making use of XMLWriter,
274 // to keep game-related logic out of the not-directly-game-related code here.
275 
276 template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, const CStr& value, bool newelement)
277 {
278  ElementAttribute(name, value.c_str(), newelement);
279 }
280 template <> void XMLWriter_File::ElementAttribute<std::string>(const char* name, const std::string& value, bool newelement)
281 {
282  ElementAttribute(name, value.c_str(), newelement);
283 }
284 // Encode Unicode strings as UTF-8
285 template <> void XMLWriter_File::ElementAttribute<CStrW>(const char* name, const CStrW& value, bool newelement)
286 {
287  ElementAttribute(name, value.ToUTF8(), newelement);
288 }
289 template <> void XMLWriter_File::ElementAttribute<std::wstring>(const char* name, const std::wstring& value, bool newelement)
290 {
291  ElementAttribute(name, utf8_from_wstring(value).c_str(), newelement);
292 }
293 
294 template <> void XMLWriter_File::ElementAttribute<fixed>(const char* name, const fixed& value, bool newelement)
295 {
296  ElementAttribute(name, value.ToString().c_str(), newelement);
297 }
298 
299 template <> void XMLWriter_File::ElementAttribute<int>(const char* name, const int& value, bool newelement)
300 {
301  std::stringstream ss;
302  ss << value;
303  ElementAttribute(name, ss.str().c_str(), newelement);
304 }
305 
306 template <> void XMLWriter_File::ElementAttribute<unsigned int>(const char* name, const unsigned int& value, bool newelement)
307 {
308  std::stringstream ss;
309  ss << value;
310  ElementAttribute(name, ss.str().c_str(), newelement);
311 }
312 
313 template <> void XMLWriter_File::ElementAttribute<float>(const char* name, const float& value, bool newelement)
314 {
315  std::stringstream ss;
316  ss << value;
317  ElementAttribute(name, ss.str().c_str(), newelement);
318 }
319 
320 template <> void XMLWriter_File::ElementAttribute<double>(const char* name, const double& value, bool newelement)
321 {
322  std::stringstream ss;
323  ss << value;
324  ElementAttribute(name, ss.str().c_str(), newelement);
325 }
A simple fixed-point number class.
Definition: Fixed.h:115
const Status LOGIC
Definition: status.h:409
CStr escapeCharacterData(const char *input)
Definition: XMLWriter.cpp:49
CStr escapeCDATA(const char *input)
Definition: XMLWriter.cpp:60
const CStr & GetOutput()
Definition: XMLWriter.cpp:111
#define LOGERROR
Definition: CLogger.h:35
#define XERO_ITER_ATTR(parent_element, attribute)
Definition: Xeromyces.h:99
std::string utf8_from_wstring(const std::wstring &src, Status *err)
opposite of wstring_from_utf8
Definition: utf8.cpp:208
XMLWriter_File * m_File
Definition: XMLWriter.h:162
static const uintptr_t maxSectorSize
Definition: alignment.h:82
shared_ptr< IVFS > PIVFS
Definition: vfs.h:226
void XMB(const XMBFile &file)
Definition: XMLWriter.cpp:117
void ElementStart(XMLWriter_Element *element, const char *name)
Definition: XMLWriter.cpp:146
#define XERO_ITER_EL(parent_element, child_element)
Definition: Xeromyces.h:91
CStr escapeComment(const char *input)
Definition: XMLWriter.cpp:67
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
Definition: path.h:75
XMLWriter_Element * m_LastElement
Definition: XMLWriter.h:144
const String & string() const
Definition: path.h:123
void Close(int type)
Definition: XMLWriter.cpp:225
i64 Status
Error handling system.
Definition: status.h:171
void Comment(const char *text)
Definition: XMLWriter.cpp:133
CStr escapeAttributeValue(const char *input)
Definition: XMLWriter.cpp:36
std::string GetElementString(const int ID) const
Definition: XeroXMB.cpp:148
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
void ElementEnd(const char *name, int type)
Definition: XMLWriter.cpp:167
void ElementClose()
Definition: XMLWriter.cpp:162
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
XMBElement GetRoot() const
Definition: XeroXMB.cpp:84
bool m_PrettyPrint
Definition: XMLWriter.h:140
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
XMLWriter_Element(XMLWriter_File &file, const char *name)
Definition: XMLWriter.cpp:212
tuple input
Definition: tests.py:115
std::string GetAttributeString(const int ID) const
Definition: XeroXMB.cpp:156
void ElementText(const char *text, bool cdata)
Definition: XMLWriter.cpp:197
void ElementXMB(const XMBFile &file, XMBElement el)
Definition: XMLWriter.cpp:122
bool StoreVFS(const PIVFS &vfs, const VfsPath &pathname)
Definition: XMLWriter.cpp:94
int GetNodeName() const
Definition: XeroXMB.cpp:166