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