fix lots of CRLFs
[xonotic/netradiant.git] / libs / xml / xmltextags.cpp
1 /*
2 Copyright (C) 2006, Stefan Greven.
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 #include "xmltextags.h"
23
24 #include <string>
25
26 #include "qerplugin.h"
27 #include "stream/stringstream.h"
28
29 XmlTagBuilder::XmlTagBuilder()
30 {
31 }
32
33 XmlTagBuilder::~XmlTagBuilder()
34 {
35   // clean up
36   xmlFreeDoc(doc);
37   xmlXPathFreeContext(context);
38 }
39
40 bool XmlTagBuilder::CreateXmlDocument()
41 {
42   /* Creates an XML file
43
44      returns TRUE if the file was created successfully or FALSE when failed
45   */
46
47   xmlTextWriterPtr writer;
48
49   writer = xmlNewTextWriterDoc(&doc, 0);
50
51   // begin a new UTF-8 formatted xml document
52   xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
53
54   // create the root node with stock and custom elements
55   xmlTextWriterStartElement(writer, (xmlChar*)"root");
56   xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");
57   xmlTextWriterStartElement(writer, (xmlChar*)"stock");
58   xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");
59   xmlTextWriterEndElement(writer);
60   xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");
61   xmlTextWriterStartElement(writer, (xmlChar*)"custom");
62   xmlTextWriterWriteString(writer, (xmlChar*)"\n  ");
63   xmlTextWriterEndElement(writer);
64   xmlTextWriterWriteString(writer, (xmlChar*)"\n");
65   xmlTextWriterEndElement(writer);
66
67   // end of the xml document
68   xmlTextWriterEndDocument(writer);
69   xmlFreeTextWriter(writer);
70
71   if(!doc)
72   {
73     return false;
74   } else {
75     context = xmlXPathNewContext(doc);
76         return true;
77   }
78 }
79
80 bool XmlTagBuilder::OpenXmlDoc(const char* file, const char* savefile)
81 {
82   /* Reads a XML document from a file
83
84      returns TRUE if the document was read successfully or FALSE when failed
85   */
86
87   if(savefile)
88     m_savefilename = savefile;
89   else
90     m_savefilename = file;
91     
92   doc = xmlParseFile(file);     // TODO error checking!
93
94   if(!doc)
95   {
96     return false;
97   } else {
98     context = xmlXPathNewContext(doc);
99         return true;
100   }
101 }
102
103 bool XmlTagBuilder::SaveXmlDoc(void)
104 {
105         return SaveXmlDoc(m_savefilename.c_str());
106 }
107
108 bool XmlTagBuilder::SaveXmlDoc(const char* file)
109 {
110   /* Writes the XML document
111
112      returns TRUE if the document was saved successfully or FALSE when saving failed
113   */
114   
115   xmlSaveNoEmptyTags = 1;
116
117   if(xmlSaveFile(file, doc) != -1)
118   {
119     return true;
120   }
121   return false;
122 }
123
124 bool XmlTagBuilder::AddShaderNode(const char* shader, TextureType textureType, NodeShaderType nodeShaderType)
125 {
126   /* Adds a shader node
127
128      char* shader - the name of the shader or texture (without trailing .tga or something)
129
130      returns TRUE if the node was added successfully or FALSE when failed
131   */
132
133   xmlNodeSetPtr nodePtr = NULL;
134   xmlXPathObjectPtr xpathPtr = NULL;
135   
136   switch(textureType)
137   {
138     case STOCK:
139       xpathPtr = XpathEval("/root/stock");
140       break;
141     case CUSTOM:
142       xpathPtr = XpathEval("/root/custom");
143   };
144   
145   if(xpathPtr)
146     nodePtr = xpathPtr->nodesetval;
147   else
148     return false;
149
150   if(!xmlXPathNodeSetIsEmpty(nodePtr))
151   {
152     xmlNodePtr newnode, newtext;
153     xmlNodePtr nodeParent = nodePtr->nodeTab[0];
154
155     // create a new node and set the node attribute (shader path)
156     switch(nodeShaderType)
157     {
158       case SHADER:
159         newnode = xmlNewNode(NULL, (xmlChar*)"shader");
160         break;
161       case TEXTURE:
162         newnode = xmlNewNode(NULL, (xmlChar*)"texture");
163       };
164
165       newnode = xmlDocCopyNode(newnode, doc, 1);
166       xmlSetProp(newnode, (xmlChar*)"path", (xmlChar*)shader);
167       xmlNodeSetContent(newnode, (xmlChar*)"\n    ");
168
169       if(nodePtr->nodeTab[0]->children->next == NULL)   // there are no shaders yet
170       {
171         // add spaces
172         newtext = xmlNewText((xmlChar*)"  ");
173         xmlAddChild(nodeParent->children, newtext);
174
175         // add the new node
176         xmlAddNextSibling(nodeParent->children, newnode);
177
178         // append a new line
179         newtext = xmlNewText((xmlChar*)"\n  ");
180         xmlAddNextSibling(nodeParent->children->next, newtext);
181       } else {
182         // add the node
183         xmlAddNextSibling(nodeParent->children, newnode);
184
185         // append a new line and spaces
186         newtext = xmlNewText((xmlChar*)"\n    ");
187         xmlAddNextSibling(nodeParent->children->next, newtext);
188       }
189       
190       xmlXPathFreeObject(xpathPtr);
191       return true;
192   } else {
193     xmlXPathFreeObject(xpathPtr);
194     return false;
195   }
196 }
197
198 bool XmlTagBuilder::DeleteShaderNode(const char* shader)
199 {
200   /* Deletes a shader node
201
202      char* shader - the name of the shader or texture (without trailing .tga or something)
203
204      returns TRUE if the node was deleted successfully or FALSE when failed
205   */
206
207   char buffer[256];
208   char* expression = GetTagsXpathExpression(buffer, shader, EMPTY);
209   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
210   
211   xmlNodeSetPtr nodePtr;
212   if(xpathPtr)
213         nodePtr = xpathPtr->nodesetval;
214   else
215         return false;
216
217   if(!xmlXPathNodeSetIsEmpty(nodePtr))
218   {
219     xmlNodePtr ptrContent = nodePtr->nodeTab[0];
220     xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
221
222     // delete the node
223     xmlUnlinkNode(ptrContent);
224     xmlFreeNode(ptrContent);
225
226     // delete leading whitespace node
227     xmlUnlinkNode(ptrWhitespace);
228     xmlFreeNode(ptrWhitespace);
229     xmlXPathFreeObject(xpathPtr);
230     return true;
231   }
232   xmlXPathFreeObject(xpathPtr);
233   return false;
234 }
235
236 bool XmlTagBuilder::CheckShaderTag(const char* shader)
237 {
238   /* Checks whether there exists an entry for a shader/texture with at least one tag
239
240      char* shader  - the name of the shader or texture (without trailing .tga or something)
241
242      returns TRUE if the shader is already stored in the XML tag file and has at least one tag
243   */
244
245   // build the XPath expression to search for
246   char buffer[256];
247   strcpy(buffer, "/root/*/*[@path='");
248   strcat(buffer, shader);
249   strcat(buffer, "']");
250
251   char* expression = buffer;
252
253   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
254   xmlNodeSetPtr nodePtr;
255   if(xpathPtr)
256     nodePtr = xpathPtr->nodesetval;
257   else
258         return false;
259
260   if(!xmlXPathNodeSetIsEmpty(nodePtr))
261   {
262     xmlXPathFreeObject(xpathPtr);
263     return true;
264   } else {
265     xmlXPathFreeObject(xpathPtr);
266     return false;
267   }
268 }
269
270 bool XmlTagBuilder::CheckShaderTag(const char* shader, const char* content)
271 {
272   /* Checks whether a tag with content already exists
273
274      char* shader  - the name of the shader or texture (without trailing .tga or something)
275      char* content - the node content (a tag name)
276
277      returns TRUE if the tag with content already exists or FALSE if not
278   */
279
280   // build the XPath expression to search for
281   // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
282
283   char buffer[256];
284   strcpy(buffer, "/root/*/*[@path='");
285   strcat(buffer, shader);
286   strcat(buffer, "'][child::tag='");
287   strcat(buffer, content);
288   strcat(buffer, "']");
289
290   char* expression = buffer;
291
292   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
293   xmlNodeSetPtr nodePtr;
294   if(xpathPtr)
295     nodePtr = xpathPtr->nodesetval;
296   else
297         return false;
298
299   if(!xmlXPathNodeSetIsEmpty(nodePtr))
300   {
301     xmlXPathFreeObject(xpathPtr);
302     return true;
303   } else {
304     xmlXPathFreeObject(xpathPtr);
305     return false;
306   }
307 }
308
309 bool XmlTagBuilder::AddShaderTag(const char* shader, const char* content, NodeTagType nodeTagType)
310 {
311   /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
312
313      char* shader  - the name of the shader or texture (without trailing .tga or something)
314      char* content - the node content (a tag name)
315
316      returns TRUE if the node was added successfully or FALSE when failed
317   */
318
319   // build the XPath expression
320   char buffer[256];
321   char* expression = GetTagsXpathExpression(buffer, shader, EMPTY);
322   
323   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
324   xmlNodeSetPtr nodePtr;
325   if(xpathPtr)
326     nodePtr = xpathPtr->nodesetval;
327   else
328         return false;
329
330   if(!xmlXPathNodeSetIsEmpty(nodePtr))  // node was found
331   {
332     xmlNodePtr newnode = xmlNewNode(NULL, (xmlChar*)"tag");
333     xmlNodePtr nodeParent = nodePtr->nodeTab[0];
334     newnode = xmlDocCopyNode(newnode, doc, 1);
335     xmlNodeSetContent(newnode, (xmlChar*)content);
336
337     if(nodePtr->nodeTab[0]->children->next == NULL)     // shader node has NO children
338     {
339       // add spaces
340       xmlNodePtr newtext = xmlNewText((xmlChar*)"  ");
341       xmlAddChild(nodeParent->children, newtext);
342
343       // add new node
344       xmlAddNextSibling(nodeParent->children, newnode);
345
346       // append a new line + spaces
347       newtext = xmlNewText((xmlChar*)"\n    ");
348       xmlAddNextSibling(nodeParent->children->next, newtext);
349     } else { // shader node has children already - the new node will be the first sibling
350       xmlAddNextSibling(nodeParent->children, newnode);
351           xmlNodePtr newtext = xmlNewText((xmlChar*)"\n      ");
352       xmlAddNextSibling(nodeParent->children->next, newtext);
353     }
354         xmlXPathFreeObject(xpathPtr);
355     return true;
356   } else {
357     xmlXPathFreeObject(xpathPtr);
358     return false;
359   }
360 }
361
362 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
363 int XmlTagBuilder::RenameShaderTag(const char* oldtag, CopiedString newtag)
364 {
365   /* Replaces tag node contents
366
367      char* oldtag - the <tag></tag> node content that sould be changed
368      char* newtag - the new <tag></tag> node content
369
370      returns the number of renamed shaders
371   */
372
373   int num = 0;
374
375   // build the XPath expression
376   char expression[256];
377   strcpy(expression, "/root/*/*[child::tag='");
378   strcat(expression, oldtag);
379   strcat(expression, "']/*");
380
381   xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)expression, context);
382   if(!result)
383     return 0;
384   xmlNodeSetPtr nodePtr = result->nodesetval;
385
386   for(int i = 0; i < nodePtr->nodeNr; i++)
387   {
388     xmlNodePtr ptrContent = nodePtr->nodeTab[i];
389     char* content = (char*)xmlNodeGetContent(ptrContent);
390
391     if(strcmp(content, oldtag) == 0)    // found a node with old content?
392     {
393       xmlNodeSetContent(ptrContent, (xmlChar*)newtag.c_str());
394       num++;
395     }
396   }
397
398   SaveXmlDoc();
399   xmlXPathFreeObject(result);// CHANGED
400   return num;
401 }
402
403 bool XmlTagBuilder::DeleteShaderTag(const char* shader, const char* tag)
404 {
405   /* Deletes a child node of a shader
406
407      char* shader  - the name of the shader or texture (without trailing .tga or something)
408      char* tag     - the tag being deleted
409
410      returns TRUE if the node was deleted successfully or FALSE when failed
411   */
412
413   char buffer[256];
414   char* expression = GetTagsXpathExpression(buffer, shader, TAG);
415   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
416   xmlNodeSetPtr nodePtr;
417   if(xpathPtr)
418     nodePtr = xpathPtr->nodesetval;
419   else
420         return false;
421
422   if(!xmlXPathNodeSetIsEmpty(nodePtr))
423   {
424     for(int i = 0; i < nodePtr->nodeNr; i++)
425     {
426       xmlNodePtr ptrContent = nodePtr->nodeTab[i];
427       char* content = (char*)(xmlChar*)xmlNodeGetContent(ptrContent);
428
429       if(strcmp(content, tag) == 0)     // find the node
430       {
431         xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
432         // delete the node
433         xmlUnlinkNode(ptrContent);
434         xmlFreeNode(ptrContent);
435
436         // delete leading whitespace node
437         xmlUnlinkNode(ptrWhitespace);
438         xmlFreeNode(ptrWhitespace);
439         xmlXPathFreeObject(xpathPtr);
440         return true;
441       }
442     }
443   }
444   xmlXPathFreeObject(xpathPtr);
445   return false;
446 }
447
448 bool XmlTagBuilder::DeleteTag(const char* tag)
449 {
450   /* Deletes a tag from all shaders
451
452      char* tag - the tag being deleted from all shaders
453
454      returns TRUE if the tag was deleted successfully or FALSE when failed
455   */
456
457   char expression[256];
458   strcpy(expression, "/root/*/*[child::tag='");
459   strcat(expression, tag);
460   strcat(expression, "']");
461
462   std::set<CopiedString> dellist;
463   TagSearch(expression, dellist);
464   std::set<CopiedString>::iterator iter;
465
466   for(iter = dellist.begin(); iter != dellist.end(); iter++)
467   {
468     DeleteShaderTag(iter->c_str(), tag);
469   }
470   SaveXmlDoc();
471
472   return true;
473 }
474
475 void XmlTagBuilder::GetShaderTags(const char* shader, std::vector<CopiedString>& tags)
476 {
477   /* Gets the tags from a shader
478
479      char* shader - the name of the shader
480
481      returns a vector containing the tags
482   */
483
484   char* expression;
485
486   if(shader == NULL)    // get all tags from all shaders
487   {
488     expression = "/root/*/*/tag";
489   } else {
490     char buffer[256];
491     expression = GetTagsXpathExpression(buffer, shader, TAG);
492   }
493
494   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
495   xmlNodeSetPtr nodePtr;
496   if(xpathPtr)
497     nodePtr = xpathPtr->nodesetval;
498   else
499         return;
500
501   if(!xmlXPathNodeSetIsEmpty(nodePtr))
502   {
503     for(int i = 0; i < nodePtr->nodeNr; i++)
504     {
505       tags.push_back((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i]));
506     }
507   }
508   xmlXPathFreeObject(xpathPtr);
509 }
510
511 void XmlTagBuilder::GetUntagged(std::set<CopiedString>& shaders)
512 {
513   /* Gets all textures and shaders listed in the xml file that don't have any tag
514
515      returns a set containing the shaders (with path)
516   */
517
518   char* expression = "/root/*/*[not(child::tag)]";
519
520   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
521   xmlNodeSetPtr nodePtr;
522   if(xpathPtr)
523     nodePtr = xpathPtr->nodesetval;
524   else
525         return;
526  
527   if(!xmlXPathNodeSetIsEmpty(nodePtr))
528   {
529     xmlNodePtr ptr;
530
531     for(int i = 0; i < nodePtr->nodeNr; i++)
532     {
533       ptr = nodePtr->nodeTab[i];
534           shaders.insert((char*)xmlGetProp(ptr, (xmlChar*)"path"));
535     }
536   }
537   
538   xmlXPathFreeObject(xpathPtr);
539 }
540
541 void XmlTagBuilder::GetAllTags(std::set<CopiedString>& tags)
542 {
543   /* Gets a list of all tags that are used (assigned to any shader)
544
545      returns a set containing all used tags
546   */
547
548   char* expression = "/root/*/*/tag";
549
550   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
551   xmlNodeSetPtr nodePtr;
552   if(xpathPtr)
553     nodePtr = xpathPtr->nodesetval;
554   else
555         return;
556  
557   if(!xmlXPathNodeSetIsEmpty(nodePtr))
558   {
559     for(int i = 0; i < nodePtr->nodeNr; i++)
560     {
561       tags.insert((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i]));
562     }
563   }
564   
565   xmlXPathFreeObject(xpathPtr);
566 }
567
568 void XmlTagBuilder::TagSearch(const char* expression, std::set<CopiedString>& paths)
569 {
570   /* Searches shaders by tags
571
572      char* expression - the XPath expression to search
573
574      returns a set containing the found shaders
575   */
576
577   xmlXPathObjectPtr xpathPtr = XpathEval(expression);
578   xmlNodeSetPtr nodePtr;
579   if(xpathPtr)
580     nodePtr = xpathPtr->nodesetval;
581   else
582         return;
583
584   if(!xmlXPathNodeSetIsEmpty(nodePtr))
585   {
586     xmlNodePtr ptr;
587     xmlChar* xmlattrib;
588     for(int i = 0; i < nodePtr->nodeNr; i++)
589     {
590       ptr = nodePtr->nodeTab[i];
591       xmlattrib = xmlGetProp(ptr, (xmlChar*)"path");
592           paths.insert((CopiedString)(char*)xmlattrib);
593     }
594   }
595   xmlXPathFreeObject(xpathPtr);
596 }
597