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