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