1 /******************************************************************************
2 * Warmux is a convivial mass murder game.
3 * Copyright (C) 2001-2011 Warmux Team.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 ******************************************************************************
19 * Xml documents access
20 *****************************************************************************/
21
22 #include <iostream>
23 #include "graphic/color.h"
24 #include "tool/xml_document.h"
25 #include "tool/string_tools.h"
26 #include <WARMUX_file_tools.h>
27 #include <WARMUX_debug.h>
28 #include <libxml/tree.h>
29 #include <libxml/parser.h>
30 #include <libxml/xinclude.h>
31 #ifdef __SYMBIAN32__
32 #include <libxml/globals.h>
33 #endif
34
35 #ifdef DEBUG
36 #include <cstring>
display_xml_tree(const xmlNode * root,uint level,bool neigh)37 void display_xml_tree(const xmlNode* root, uint level, bool neigh)
38 {
39 char space[1024] = "";
40 for (uint i=0; i < level; i++)
41 strcat(space, " ");
42
43 printf("%s %d - %s\n", space, root->type, root->name);
44 for (const xmlNode* c = root->children; c; c = c->next) {
45 if (c->type == XML_ELEMENT_NODE)
46 display_xml_tree(c, level+1, false);
47 }
48
49 if (neigh) {
50 for (const xmlNode* n = root->next; n; n = n->next)
51 if (n->type == XML_ELEMENT_NODE)
52 display_xml_tree(n, level, false);
53 }
54 }
55 #endif
56
Reset()57 void XmlReader::Reset()
58 {
59 if (doc)
60 xmlFreeDoc(doc);
61
62 doc = NULL;
63 }
64
~XmlReader()65 XmlReader::~XmlReader()
66 {
67 Reset();
68 }
69
Load(const std::string & filename)70 bool XmlReader::Load(const std::string &filename)
71 {
72 if (!DoesFileExist(filename))
73 return false;
74
75 Reset();
76
77 // Activate Entities
78 xmlSubstituteEntitiesDefault(1);
79
80 doc = xmlParseFile(filename.c_str());
81 if (!doc)
82 return false;
83
84 // Activate XInclude (to include content of other files)
85 int nb_subst = xmlXIncludeProcessFlags(doc, XML_PARSE_NOENT);
86 if (nb_subst == -1) {
87 printf("(%p) %s: failed to do substitutions\n", this, filename.c_str());
88 ASSERT(nb_subst != -1);
89 }
90
91 //#ifdef DEBUG
92 //if (IsLOGGING("xml.entities"))
93 // xmlDocDump(stderr, doc);
94 //#endif
95
96 // Activate DTD validation parser
97 // parser.set_validate(true);
98
99 return true;
100 }
101
LoadFromString(const std::string & contents)102 bool XmlReader::LoadFromString(const std::string &contents)
103 {
104 Reset();
105
106 // Activate DTD validation parser
107 // parser.set_validate (true);
108
109 // Read string in memory
110 doc = xmlParseMemory(contents.c_str(), contents.size());
111 return IsOk();
112 }
113
ExportToString() const114 std::string XmlReader::ExportToString() const
115 {
116 xmlChar *buffer = NULL;
117 int length = 0;
118 xmlDocDumpFormatMemoryEnc(doc, &buffer, &length,
119 NULL /* default UTF-8 encoding */, 0 /* Don't format */);
120 ASSERT(buffer);
121 std::string ret((char *)buffer, length);
122 xmlFree(buffer);
123 return ret;
124 }
125
GetMarker(const xmlNode * x,const std::string & name)126 const xmlNode* XmlReader::GetMarker(const xmlNode* x, const std::string &name)
127 {
128 ASSERT(x->type == XML_ELEMENT_NODE);
129
130 // is it already the right xmlNode ?
131 if (name.empty() || name == (const char*)x->name)
132 return x;
133
134 // look at its children
135 for (const xmlNode* tmp = x->children; tmp; tmp = tmp->next) {
136 if (tmp->type == XML_ELEMENT_NODE && name == (const char*)tmp->name)
137 {
138 return tmp;
139 }
140 }
141
142 #ifdef DEBUG
143 if (IsLOGGING("xml.tree")) {
144 std::string looked_name = std::string((const char*)(x->name)) + ">" + name;
145 for (const xmlNode *parent = x->parent;
146 parent != xmlDocGetRootElement(x->doc) && parent && parent->name;
147 parent = parent->parent) {
148 looked_name = std::string((const char*)(parent->name)) + ">" + looked_name;
149 }
150 fprintf(stderr, "Fail to read %s\n", looked_name.c_str());
151 }
152 #endif
153 return NULL;
154 }
155
GetNamedChildren(const xmlNode * father,const std::string & name)156 xmlNodeArray XmlReader::GetNamedChildren(const xmlNode* father, const std::string& name)
157 {
158 xmlNodeArray tab;
159 MSG_DEBUG("xml", "Search children of name %s", name.c_str());
160
161 // Load members
162 for (father = father->children; father; father = father->next)
163 {
164 if (name == (const char*)father->name)
165 tab.push_back(father);
166 }
167 return tab;
168 }
169
170 // Forward of GetMarker() with a new name (better)
171 // TODO: Refactor all GetMarker() call to GetFirstNamedChild()
GetFirstNamedChild(const xmlNode * father,const std::string & nodeName)172 const xmlNode * XmlReader::GetFirstNamedChild(const xmlNode * father,
173 const std::string & nodeName)
174 {
175 return GetMarker(father, nodeName);
176 }
177
GetNbChildren(const xmlNode * father)178 uint XmlReader::GetNbChildren(const xmlNode * father)
179 {
180 #if LIBXML_VERSION > 20702
181 return xmlChildElementCount((xmlNode*)father);
182 #else
183 // This code is taken from libxml2, release under the MIT license:
184 // Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
185 uint ret = 0;
186 xmlNodePtr cur = NULL;
187
188 if (father == NULL)
189 return(0);
190 switch (father->type) {
191 case XML_ELEMENT_NODE:
192 case XML_ENTITY_NODE:
193 case XML_DOCUMENT_NODE:
194 case XML_HTML_DOCUMENT_NODE:
195 cur = father->children;
196 break;
197 default:
198 return(0);
199 }
200 while (cur != NULL) {
201 if (cur->type == XML_ELEMENT_NODE)
202 ret++;
203 cur = cur->next;
204 }
205 return(ret);
206 #endif
207 }
208
GetFirstChild(const xmlNode * father)209 const xmlNode * XmlReader::GetFirstChild(const xmlNode * father)
210 {
211 #if LIBXML_VERSION > 20702
212 return xmlFirstElementChild((xmlNode*)father);
213 #else
214 // This code is taken from libxml2, release under the MIT license:
215 // Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
216 xmlNodePtr cur = NULL;
217
218 if (father == NULL)
219 return(NULL);
220 switch (father->type) {
221 case XML_ELEMENT_NODE:
222 case XML_ENTITY_NODE:
223 case XML_DOCUMENT_NODE:
224 case XML_HTML_DOCUMENT_NODE:
225 cur = father->children;
226 break;
227 default:
228 return(NULL);
229 }
230 while (cur != NULL) {
231 if (cur->type == XML_ELEMENT_NODE)
232 return(cur);
233 cur = cur->next;
234 }
235 return(NULL);
236 #endif
237 }
238
GetNextSibling(const xmlNode * node)239 const xmlNode * XmlReader::GetNextSibling(const xmlNode * node)
240 {
241 #if LIBXML_VERSION > 20702
242 return xmlNextElementSibling((xmlNode*)node);
243 #else
244 // This code is taken from libxml2, release under the MIT license:
245 // Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
246 if (node == NULL)
247 return(NULL);
248 switch (node->type) {
249 case XML_ELEMENT_NODE:
250 case XML_TEXT_NODE:
251 case XML_CDATA_SECTION_NODE:
252 case XML_ENTITY_REF_NODE:
253 case XML_ENTITY_NODE:
254 case XML_PI_NODE:
255 case XML_COMMENT_NODE:
256 case XML_DTD_NODE:
257 case XML_XINCLUDE_START:
258 case XML_XINCLUDE_END:
259 node = node->next;
260 break;
261 default:
262 return(NULL);
263 }
264 while (node != NULL) {
265 if (node->type == XML_ELEMENT_NODE)
266 return(node);
267 node = node->next;
268 }
269 return(NULL);
270 #endif
271 }
272
GetNodeName(const xmlNode * node)273 std::string XmlReader::GetNodeName(const xmlNode * node)
274 {
275 return std::string((const char*)(node->name));
276 }
277
Access(const xmlNode * x,const std::string & name,const std::string & attr_name)278 const xmlNode* XmlReader::Access(const xmlNode* x,
279 const std::string &name,
280 const std::string &attr_name)
281 {
282 ASSERT(x->type == XML_ELEMENT_NODE);
283
284 // is it already the right xmlNode ?
285 if (name == (const char*)x->name) {
286
287 xmlAttr* attr = xmlHasProp((xmlNode*)x, // cast to make libxml2 happy...
288 (const xmlChar*)"name");
289 if (attr) {
290 xmlChar *value = xmlGetProp(attr->parent, attr->name);
291 if (attr_name == (const char*)value) {
292 xmlFree(value);
293 return x;
294 }
295 xmlFree(value);
296 }
297 return NULL;
298 }
299
300 // look at its children
301 for (const xmlNode* tmp = x->children; tmp; tmp = tmp->next) {
302 if (tmp->type == XML_ELEMENT_NODE && name == (const char*)tmp->name) {
303 xmlAttr* attr = xmlHasProp((xmlNode*)tmp, // cast to make libxml2 happy...
304 (const xmlChar*)"name");
305 if (attr) {
306 xmlChar *value = xmlGetProp(attr->parent, attr->name);
307 if (attr_name == (const char*)value) {
308 xmlFree(value);
309 return tmp;
310 }
311 xmlFree(value);
312 }
313
314 // do not return as other child may have the same "name" but a different attr
315 // return NULL;
316 }
317 }
318
319 return NULL;
320 }
321
322 /* In spite of what a primary analysis would lead to, it is bothersome to:
323 * - return the type: if function fails, initial value isn't modified
324 * - throwing exeptions: some of the nodes may really be absent, and might be
325 * more costly when comparing generic error handling and special case
326 */
ReadString(const xmlNode * x,const std::string & name,std::string & output)327 bool XmlReader::ReadString(const xmlNode* x,
328 const std::string &name,
329 std::string &output)
330 {
331 const xmlNode* elem = GetMarker(x, name);
332 MSG_DEBUG("xml", "Reading string of name '%s' from %p:", name.c_str(), elem);
333 return ReadMarkerValue(elem, output);
334 }
335
336 /** @see XmlReader::ReadString comment */
ReadDouble(const xmlNode * x,const std::string & name,Double & output)337 bool XmlReader::ReadDouble(const xmlNode *x,
338 const std::string &name,
339 Double &output)
340 {
341 std::string val;
342 if (!ReadString(x, name, val)) return false;
343 return str2Double (val, output);
344 }
345
346 /** @see XmlReader::ReadString comment */
Readfloat(const xmlNode * x,const std::string & name,float & output)347 bool XmlReader::Readfloat(const xmlNode *x,
348 const std::string &name,
349 float &output)
350 {
351 std::string val;
352 if (!ReadString(x, name, val)) return false;
353 return str2float(val, output);
354 }
355
356 /** @see XmlReader::ReadString comment */
ReadInt(const xmlNode * x,const std::string & name,int & output)357 bool XmlReader::ReadInt(const xmlNode* x,
358 const std::string &name,
359 int &output)
360 {
361 std::string val;
362 if (!ReadString(x, name, val)) return false;
363 return str2int (val, output);
364 }
365
366 /** @see XmlReader::ReadString comment */
ReadUint(const xmlNode * x,const std::string & name,uint & output)367 bool XmlReader::ReadUint(const xmlNode* x,
368 const std::string &name,
369 uint &output)
370 {
371 int val;
372 if (!ReadInt(x, name, val)) return false;
373 if (0 <= val) {
374 output = (uint)val;
375 return true;
376 } else {
377 return false;
378 }
379 }
380
381 /** @see XmlReader::ReadString comment */
ReadBool(const xmlNode * x,const std::string & name,bool & output)382 bool XmlReader::ReadBool (const xmlNode* x,
383 const std::string &name,
384 bool &output)
385 {
386 std::string val;
387 if (!ReadString(x, name, val)) return false;
388 return str2bool (val, output);
389 }
390
391 /** @see XmlReader::ReadString comment */
ReadMarkerValue(const xmlNode * marker,std::string & output)392 bool XmlReader::ReadMarkerValue(const xmlNode* marker,
393 std::string &output)
394 {
395 if (!marker || !marker->children) {
396 output = "";
397 return false;
398 }
399
400 // Read node value
401 marker = marker->children;
402 if (std::string("text") != (const char*)marker->name) {
403 printf("Element '%s' had content '%s'\n",
404 marker->name, marker->content);
405 return "";
406 }
407 output = (marker->content) ? (const char*)marker->content : "";
408 MSG_DEBUG("xml", " marker=%s", output.c_str());
409 return true;
410 }
411
412 /** @see XmlReader::ReadString comment */
ReadStringAttr(const xmlNode * x,const std::string & name,std::string & output)413 bool XmlReader::ReadStringAttr(const xmlNode* x,
414 const std::string &name,
415 std::string &output)
416 {
417 ASSERT(x);
418
419 xmlAttr *attr = xmlHasProp((xmlNode*)x, // cast to make libxml2 happy
420 (const xmlChar *)name.c_str()); //xmlpp::Attribute::get_attribute
421 if (!attr) {
422 MSG_DEBUG("xml", " Attribute '%s' not found", name.c_str());
423 return false;
424 }
425
426 char *value = (char*) xmlGetProp(attr->parent, attr->name);
427 if (!value)
428 Error("Unknown attribute " + name);
429
430 MSG_DEBUG("xml", " Attribute '%s'='%s'", name.c_str(), value);
431 output = value;
432 xmlFree(value);
433 return true;
434 }
435
436 /** @see XmlReader::ReadString comment */
ReadIntAttr(const xmlNode * x,const std::string & name,int & output)437 bool XmlReader::ReadIntAttr(const xmlNode* x,
438 const std::string &name,
439 int &output)
440 {
441 std::string val;
442 if (!ReadStringAttr(x, name, val))
443 return false;
444 return str2int (val, output);
445 }
446
ReadPercentageAttr(const xmlNode * node,const std::string & attributName,float & outputValue)447 bool XmlReader::ReadPercentageAttr(const xmlNode* node,
448 const std::string & attributName,
449 float & outputValue)
450 {
451 std::string value;
452 if (!ReadStringAttr(node, attributName, value)) {
453 return false;
454 }
455 size_t foundPos = value.find("%");
456 if (std::string::npos == foundPos) {
457 return false;
458 }
459 value = value.substr(0, foundPos);
460 return str2float(value, outputValue);
461 }
462
ReadPixelAttr(const xmlNode * node,const std::string & attributName,int & outputValue)463 bool XmlReader::ReadPixelAttr(const xmlNode* node,
464 const std::string & attributName,
465 int & outputValue)
466 {
467 std::string value;
468 if (!ReadStringAttr(node, attributName, value)) {
469 return false;
470 }
471 size_t foundPos = value.find("px");
472 if (std::string::npos != foundPos) {
473 value = value.substr(0, foundPos);
474 }
475 return str2int(value, outputValue);
476 }
477
478 /** @see XmlReader::ReadString comment */
ReadUintAttr(const xmlNode * x,const std::string & name,uint & output)479 bool XmlReader::ReadUintAttr(const xmlNode* x,
480 const std::string &name,
481 uint &output)
482 {
483 int val;
484 if (!ReadIntAttr(x, name, val)) return false;
485 if (0 <= val) {
486 output = (uint)val;
487 return true;
488 } else {
489 return false;
490 }
491 }
492
493 /** @see XmlReader::ReadString comment */
ReadBoolAttr(const xmlNode * x,const std::string & name,bool & output)494 bool XmlReader::ReadBoolAttr(const xmlNode* x,
495 const std::string &name,
496 bool &output)
497 {
498 std::string val;
499
500 if (!ReadStringAttr(x, name, val))
501 return false;
502 return str2bool(val, output);
503 }
504
505 /** @see XmlReader::ReadString comment */
ReadDoubleAttr(const xmlNode * x,const std::string & name,Double & output)506 bool XmlReader::ReadDoubleAttr(const xmlNode* x,
507 const std::string &name,
508 Double &output)
509 {
510 std::string val;
511 if (!ReadStringAttr(x, name, val)) return false;
512 return str2Double(val, output);
513 }
514
515 /** @see XmlReader::ReadString comment */
ReadfloatAttr(const xmlNode * x,const std::string & name,float & output)516 bool XmlReader::ReadfloatAttr(const xmlNode* x,
517 const std::string &name,
518 float &output)
519 {
520 std::string val;
521 if (!ReadStringAttr(x, name, val)) return false;
522 return str2float(val, output);
523 }
524
ReadHexColorAttr(const xmlNode * node,const std::string & attributName,Color & outputColor)525 bool XmlReader::ReadHexColorAttr(const xmlNode* node,
526 const std::string & attributName,
527 Color & outputColor)
528 {
529 std::string color;
530 if (!ReadStringAttr(node, attributName, color)) {
531 return false;
532 }
533 if (color.length() < 8) {
534 // Error, malformed Hex Color
535 return false;
536 }
537 uint red, green, blue, alpha;
538
539 if (1 != sscanf(color.substr(0, 2).c_str(), "%2x", &red) ||
540 1 != sscanf(color.substr(2, 2).c_str(), "%2x", &green) ||
541 1 != sscanf(color.substr(4, 2).c_str(), "%2x", &blue) ||
542 1 != sscanf(color.substr(6, 2).c_str(), "%2x", &alpha)) {
543 return false;
544 }
545 outputColor.SetColor(red, green, blue, alpha);
546 return true;
547 }
548
IsAPercentageAttr(const xmlNode * node,const std::string & attributName)549 bool XmlReader::IsAPercentageAttr(const xmlNode * node,
550 const std::string & attributName)
551 {
552 std::string value;
553 if (!ReadStringAttr(node, attributName, value)) {
554 return false;
555 }
556 if (std::string::npos == value.find("%")) {
557 return false;
558 }
559 return true;
560 }
561
GetRoot() const562 const xmlNode* XmlReader::GetRoot() const
563 {
564 ASSERT(IsOk());
565 const xmlNode* root = xmlDocGetRootElement(doc);
566 ASSERT(root);
567
568 return root;
569 }
570
571 //-----------------------------------------------------------------------------
572
Reset()573 void XmlWriter::Reset()
574 {
575 if (m_doc)
576 xmlFreeDoc(m_doc);
577 m_doc = NULL;
578
579 #if 0
580 if (m_root)
581 xmlFreeNode(m_root);
582 m_root = NULL;
583 #endif
584 }
585
~XmlWriter()586 XmlWriter::~XmlWriter()
587 {
588 Save();
589 Reset();
590 }
591
WriteElement(xmlNode * x,const std::string & name,const std::string & value)592 xmlNode *XmlWriter::WriteElement(xmlNode* x,
593 const std::string &name,
594 const std::string &value)
595 {
596 xmlNode *node = xmlAddChild(x, xmlNewNode(NULL /* empty prefix */,
597 (const xmlChar*)name.c_str()));
598 xmlNode *text = xmlNewText((const xmlChar*)value.c_str());
599 xmlAddChild(node, text);
600 m_save = false;
601 return node;
602 }
603
WriteComment(xmlNode * x,const std::string & comment)604 void XmlWriter::WriteComment(xmlNode* x,
605 const std::string& comment)
606 {
607 xmlAddChild(x, xmlNewComment((const xmlChar*)comment.c_str()));
608 m_save = false;
609 }
610
Create(const std::string & filename,const std::string & root,const std::string & version,const std::string & encoding)611 bool XmlWriter::Create(const std::string &filename,const std::string &root,
612 const std::string &version,const std::string &encoding)
613 {
614 Reset();
615 m_save = false;
616 m_filename = filename;
617 m_encoding = encoding;
618 m_doc = xmlNewDoc((const xmlChar*)version.c_str());
619 xmlNode* node = xmlNewDocNode(m_doc, 0, (const xmlChar*)root.c_str(), 0);
620 xmlDocSetRootElement(m_doc, node);
621 m_root = xmlDocGetRootElement(m_doc);
622
623 ASSERT(m_root);
624 return true;
625 }
626
GetRoot() const627 xmlNode* XmlWriter::GetRoot() const
628 {
629 ASSERT(m_root);
630 return m_root;
631 }
632
Save()633 bool XmlWriter::Save()
634 {
635 if (m_save) return true;
636 m_save = true;
637 int result = xmlSaveFormatFileEnc(m_filename.c_str(), m_doc,
638 m_encoding.empty() ? NULL : m_encoding.c_str(), true);
639 return (result != -1);
640 }
641
SaveToString() const642 std::string XmlWriter::SaveToString() const
643 {
644 xmlChar *buffer = NULL;
645 int length = 0;
646
647 xmlDocDumpFormatMemoryEnc(m_doc, &buffer, &length, NULL, 0);
648 ASSERT(buffer);
649
650 std::string ret;
651 ret.copy((char *)buffer, length);
652 xmlFree(buffer);
653 return ret;
654 }
655