2 Copyright (C) 2006, Stefan Greven.
5 This file is part of GtkRadiant.
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.
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.
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
22 #include "xmltextags.h"
26 #include "qerplugin.h"
27 #include "stream/stringstream.h"
29 XmlTagBuilder::XmlTagBuilder()
33 XmlTagBuilder::~XmlTagBuilder()
37 xmlXPathFreeContext(context);
40 bool XmlTagBuilder::CreateXmlDocument()
42 /* Creates an XML file
44 returns TRUE if the file was created successfully or FALSE when failed
47 xmlTextWriterPtr writer;
49 writer = xmlNewTextWriterDoc(&doc, 0);
51 // begin a new UTF-8 formatted xml document
52 xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
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);
67 // end of the xml document
68 xmlTextWriterEndDocument(writer);
69 xmlFreeTextWriter(writer);
75 context = xmlXPathNewContext(doc);
80 bool XmlTagBuilder::OpenXmlDoc(const char* file, const char* savefile)
82 /* Reads a XML document from a file
84 returns TRUE if the document was read successfully or FALSE when failed
88 m_savefilename = savefile;
90 m_savefilename = file;
92 doc = xmlParseFile(file); // TODO error checking!
98 context = xmlXPathNewContext(doc);
103 bool XmlTagBuilder::SaveXmlDoc(void)
105 return SaveXmlDoc(m_savefilename.c_str());
108 bool XmlTagBuilder::SaveXmlDoc(const char* file)
110 /* Writes the XML document
112 returns TRUE if the document was saved successfully or FALSE when saving failed
115 xmlSaveNoEmptyTags = 1;
117 if(xmlSaveFile(file, doc) != -1)
124 bool XmlTagBuilder::AddShaderNode(const char* shader, TextureType textureType, NodeShaderType nodeShaderType)
126 /* Adds a shader node
128 char* shader - the name of the shader or texture (without trailing .tga or something)
130 returns TRUE if the node was added successfully or FALSE when failed
133 xmlNodeSetPtr nodePtr = NULL;
134 xmlXPathObjectPtr xpathPtr = NULL;
139 xpathPtr = XpathEval("/root/stock");
142 xpathPtr = XpathEval("/root/custom");
146 nodePtr = xpathPtr->nodesetval;
150 if(!xmlXPathNodeSetIsEmpty(nodePtr))
152 xmlNodePtr newnode, newtext;
153 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
155 // create a new node and set the node attribute (shader path)
156 switch(nodeShaderType)
159 newnode = xmlNewNode(NULL, (xmlChar*)"shader");
162 newnode = xmlNewNode(NULL, (xmlChar*)"texture");
165 newnode = xmlDocCopyNode(newnode, doc, 1);
166 xmlSetProp(newnode, (xmlChar*)"path", (xmlChar*)shader);
167 xmlNodeSetContent(newnode, (xmlChar*)"\n ");
169 if(nodePtr->nodeTab[0]->children->next == NULL) // there are no shaders yet
172 newtext = xmlNewText((xmlChar*)" ");
173 xmlAddChild(nodeParent->children, newtext);
176 xmlAddNextSibling(nodeParent->children, newnode);
179 newtext = xmlNewText((xmlChar*)"\n ");
180 xmlAddNextSibling(nodeParent->children->next, newtext);
183 xmlAddNextSibling(nodeParent->children, newnode);
185 // append a new line and spaces
186 newtext = xmlNewText((xmlChar*)"\n ");
187 xmlAddNextSibling(nodeParent->children->next, newtext);
190 xmlXPathFreeObject(xpathPtr);
193 xmlXPathFreeObject(xpathPtr);
198 bool XmlTagBuilder::DeleteShaderNode(const char* shader)
200 /* Deletes a shader node
202 char* shader - the name of the shader or texture (without trailing .tga or something)
204 returns TRUE if the node was deleted successfully or FALSE when failed
208 char* expression = GetTagsXpathExpression(buffer, shader, EMPTY);
209 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
211 xmlNodeSetPtr nodePtr;
213 nodePtr = xpathPtr->nodesetval;
217 if(!xmlXPathNodeSetIsEmpty(nodePtr))
219 xmlNodePtr ptrContent = nodePtr->nodeTab[0];
220 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
223 xmlUnlinkNode(ptrContent);
224 xmlFreeNode(ptrContent);
226 // delete leading whitespace node
227 xmlUnlinkNode(ptrWhitespace);
228 xmlFreeNode(ptrWhitespace);
229 xmlXPathFreeObject(xpathPtr);
232 xmlXPathFreeObject(xpathPtr);
236 bool XmlTagBuilder::CheckShaderTag(const char* shader)
238 /* Checks whether there exists an entry for a shader/texture with at least one tag
240 char* shader - the name of the shader or texture (without trailing .tga or something)
242 returns TRUE if the shader is already stored in the XML tag file and has at least one tag
245 // build the XPath expression to search for
247 strcpy(buffer, "/root/*/*[@path='");
248 strcat(buffer, shader);
249 strcat(buffer, "']");
251 char* expression = buffer;
253 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
254 xmlNodeSetPtr nodePtr;
256 nodePtr = xpathPtr->nodesetval;
260 if(!xmlXPathNodeSetIsEmpty(nodePtr))
262 xmlXPathFreeObject(xpathPtr);
265 xmlXPathFreeObject(xpathPtr);
270 bool XmlTagBuilder::CheckShaderTag(const char* shader, const char* content)
272 /* Checks whether a tag with content already exists
274 char* shader - the name of the shader or texture (without trailing .tga or something)
275 char* content - the node content (a tag name)
277 returns TRUE if the tag with content already exists or FALSE if not
280 // build the XPath expression to search for
281 // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
284 strcpy(buffer, "/root/*/*[@path='");
285 strcat(buffer, shader);
286 strcat(buffer, "'][child::tag='");
287 strcat(buffer, content);
288 strcat(buffer, "']");
290 char* expression = buffer;
292 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
293 xmlNodeSetPtr nodePtr;
295 nodePtr = xpathPtr->nodesetval;
299 if(!xmlXPathNodeSetIsEmpty(nodePtr))
301 xmlXPathFreeObject(xpathPtr);
304 xmlXPathFreeObject(xpathPtr);
309 bool XmlTagBuilder::AddShaderTag(const char* shader, const char* content, NodeTagType nodeTagType)
311 /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
313 char* shader - the name of the shader or texture (without trailing .tga or something)
314 char* content - the node content (a tag name)
316 returns TRUE if the node was added successfully or FALSE when failed
319 // build the XPath expression
321 char* expression = GetTagsXpathExpression(buffer, shader, EMPTY);
323 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
324 xmlNodeSetPtr nodePtr;
326 nodePtr = xpathPtr->nodesetval;
330 if(!xmlXPathNodeSetIsEmpty(nodePtr)) // node was found
332 xmlNodePtr newnode = xmlNewNode(NULL, (xmlChar*)"tag");
333 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
334 newnode = xmlDocCopyNode(newnode, doc, 1);
335 xmlNodeSetContent(newnode, (xmlChar*)content);
337 if(nodePtr->nodeTab[0]->children->next == NULL) // shader node has NO children
340 xmlNodePtr newtext = xmlNewText((xmlChar*)" ");
341 xmlAddChild(nodeParent->children, newtext);
344 xmlAddNextSibling(nodeParent->children, newnode);
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);
354 xmlXPathFreeObject(xpathPtr);
357 xmlXPathFreeObject(xpathPtr);
362 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
363 int XmlTagBuilder::RenameShaderTag(const char* oldtag, CopiedString newtag)
365 /* Replaces tag node contents
367 char* oldtag - the <tag></tag> node content that sould be changed
368 char* newtag - the new <tag></tag> node content
370 returns the number of renamed shaders
375 // build the XPath expression
376 char expression[256];
377 strcpy(expression, "/root/*/*[child::tag='");
378 strcat(expression, oldtag);
379 strcat(expression, "']/*");
381 xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)expression, context);
384 xmlNodeSetPtr nodePtr = result->nodesetval;
386 for(int i = 0; i < nodePtr->nodeNr; i++)
388 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
389 char* content = (char*)xmlNodeGetContent(ptrContent);
391 if(strcmp(content, oldtag) == 0) // found a node with old content?
393 xmlNodeSetContent(ptrContent, (xmlChar*)newtag.c_str());
399 xmlXPathFreeObject(result);// CHANGED
403 bool XmlTagBuilder::DeleteShaderTag(const char* shader, const char* tag)
405 /* Deletes a child node of a shader
407 char* shader - the name of the shader or texture (without trailing .tga or something)
408 char* tag - the tag being deleted
410 returns TRUE if the node was deleted successfully or FALSE when failed
414 char* expression = GetTagsXpathExpression(buffer, shader, TAG);
415 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
416 xmlNodeSetPtr nodePtr;
418 nodePtr = xpathPtr->nodesetval;
422 if(!xmlXPathNodeSetIsEmpty(nodePtr))
424 for(int i = 0; i < nodePtr->nodeNr; i++)
426 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
427 char* content = (char*)(xmlChar*)xmlNodeGetContent(ptrContent);
429 if(strcmp(content, tag) == 0) // find the node
431 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
433 xmlUnlinkNode(ptrContent);
434 xmlFreeNode(ptrContent);
436 // delete leading whitespace node
437 xmlUnlinkNode(ptrWhitespace);
438 xmlFreeNode(ptrWhitespace);
439 xmlXPathFreeObject(xpathPtr);
444 xmlXPathFreeObject(xpathPtr);
448 bool XmlTagBuilder::DeleteTag(const char* tag)
450 /* Deletes a tag from all shaders
452 char* tag - the tag being deleted from all shaders
454 returns TRUE if the tag was deleted successfully or FALSE when failed
457 char expression[256];
458 strcpy(expression, "/root/*/*[child::tag='");
459 strcat(expression, tag);
460 strcat(expression, "']");
462 std::set<CopiedString> dellist;
463 TagSearch(expression, dellist);
464 std::set<CopiedString>::iterator iter;
466 for(iter = dellist.begin(); iter != dellist.end(); iter++)
468 DeleteShaderTag(iter->c_str(), tag);
475 void XmlTagBuilder::GetShaderTags(const char* shader, std::vector<CopiedString>& tags)
477 /* Gets the tags from a shader
479 char* shader - the name of the shader
481 returns a vector containing the tags
486 if(shader == NULL) // get all tags from all shaders
488 expression = "/root/*/*/tag";
491 expression = GetTagsXpathExpression(buffer, shader, TAG);
494 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
495 xmlNodeSetPtr nodePtr;
497 nodePtr = xpathPtr->nodesetval;
501 if(!xmlXPathNodeSetIsEmpty(nodePtr))
503 for(int i = 0; i < nodePtr->nodeNr; i++)
505 tags.push_back((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i]));
508 xmlXPathFreeObject(xpathPtr);
511 void XmlTagBuilder::GetUntagged(std::set<CopiedString>& shaders)
513 /* Gets all textures and shaders listed in the xml file that don't have any tag
515 returns a set containing the shaders (with path)
518 char* expression = "/root/*/*[not(child::tag)]";
520 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
521 xmlNodeSetPtr nodePtr;
523 nodePtr = xpathPtr->nodesetval;
527 if(!xmlXPathNodeSetIsEmpty(nodePtr))
531 for(int i = 0; i < nodePtr->nodeNr; i++)
533 ptr = nodePtr->nodeTab[i];
534 shaders.insert((char*)xmlGetProp(ptr, (xmlChar*)"path"));
538 xmlXPathFreeObject(xpathPtr);
541 void XmlTagBuilder::GetAllTags(std::set<CopiedString>& tags)
543 /* Gets a list of all tags that are used (assigned to any shader)
545 returns a set containing all used tags
548 char* expression = "/root/*/*/tag";
550 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
551 xmlNodeSetPtr nodePtr;
553 nodePtr = xpathPtr->nodesetval;
557 if(!xmlXPathNodeSetIsEmpty(nodePtr))
559 for(int i = 0; i < nodePtr->nodeNr; i++)
561 tags.insert((CopiedString)(char*)xmlNodeGetContent(nodePtr->nodeTab[i]));
565 xmlXPathFreeObject(xpathPtr);
568 void XmlTagBuilder::TagSearch(const char* expression, std::set<CopiedString>& paths)
570 /* Searches shaders by tags
572 char* expression - the XPath expression to search
574 returns a set containing the found shaders
577 xmlXPathObjectPtr xpathPtr = XpathEval(expression);
578 xmlNodeSetPtr nodePtr;
580 nodePtr = xpathPtr->nodesetval;
584 if(!xmlXPathNodeSetIsEmpty(nodePtr))
588 for(int i = 0; i < nodePtr->nodeNr; i++)
590 ptr = nodePtr->nodeTab[i];
591 xmlattrib = xmlGetProp(ptr, (xmlChar*)"path");
592 paths.insert((CopiedString)(char*)xmlattrib);
595 xmlXPathFreeObject(xpathPtr);