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"
27 #include "qerplugin.h"
28 #include "stream/stringstream.h"
30 XmlTagBuilder::XmlTagBuilder(){
33 XmlTagBuilder::~XmlTagBuilder(){
36 xmlXPathFreeContext( context );
39 bool XmlTagBuilder::CreateXmlDocument(){
40 /* Creates an XML file
42 returns TRUE if the file was created successfully or FALSE when failed
45 xmlTextWriterPtr writer;
47 writer = xmlNewTextWriterDoc( &doc, 0 );
49 // begin a new UTF-8 formatted xml document
50 xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL );
52 // create the root node with stock and custom elements
53 xmlTextWriterStartElement( writer, (xmlChar*)"root" );
54 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
55 xmlTextWriterStartElement( writer, (xmlChar*)"stock" );
56 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
57 xmlTextWriterEndElement( writer );
58 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
59 xmlTextWriterStartElement( writer, (xmlChar*)"custom" );
60 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
61 xmlTextWriterEndElement( writer );
62 xmlTextWriterWriteString( writer, (xmlChar*)"\n" );
63 xmlTextWriterEndElement( writer );
65 // end of the xml document
66 xmlTextWriterEndDocument( writer );
67 xmlFreeTextWriter( writer );
73 context = xmlXPathNewContext( doc );
78 bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){
79 /* Reads a XML document from a file
81 returns TRUE if the document was read successfully or FALSE when failed
85 m_savefilename = savefile;
88 m_savefilename = file;
91 doc = xmlParseFile( file ); // TODO error checking!
97 context = xmlXPathNewContext( doc );
102 bool XmlTagBuilder::SaveXmlDoc( void ){
103 return SaveXmlDoc( m_savefilename.c_str() );
106 bool XmlTagBuilder::SaveXmlDoc( const char* file ){
107 /* Writes the XML document
109 returns TRUE if the document was saved successfully or FALSE when saving failed
112 xmlSaveNoEmptyTags = 1;
114 if ( xmlSaveFile( file, doc ) != -1 ) {
120 bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){
121 /* Adds a shader node
123 char* shader - the name of the shader or texture (without trailing .tga or something)
125 returns TRUE if the node was added successfully or FALSE when failed
128 xmlNodeSetPtr nodePtr = NULL;
129 xmlXPathObjectPtr xpathPtr = NULL;
131 switch ( textureType )
134 xpathPtr = XpathEval( "/root/stock" );
137 xpathPtr = XpathEval( "/root/custom" );
141 nodePtr = xpathPtr->nodesetval;
147 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
148 xmlNodePtr newnode, newtext;
149 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
151 // create a new node and set the node attribute (shader path)
152 switch ( nodeShaderType )
155 newnode = xmlNewNode( NULL, (xmlChar*)"shader" );
158 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
171 newtext = xmlNewText( (xmlChar*)" " );
172 xmlAddChild( nodeParent->children, newtext );
175 xmlAddNextSibling( nodeParent->children, newnode );
178 newtext = xmlNewText( (xmlChar*)"\n " );
179 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 );
194 xmlXPathFreeObject( xpathPtr );
199 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;
219 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
220 xmlNodePtr ptrContent = nodePtr->nodeTab[0];
221 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
224 xmlUnlinkNode( ptrContent );
225 xmlFreeNode( ptrContent );
227 // delete leading whitespace node
228 xmlUnlinkNode( ptrWhitespace );
229 xmlFreeNode( ptrWhitespace );
230 xmlXPathFreeObject( xpathPtr );
233 xmlXPathFreeObject( xpathPtr );
237 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;
262 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
263 xmlXPathFreeObject( xpathPtr );
267 xmlXPathFreeObject( xpathPtr );
272 bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){
273 /* Checks whether a tag with content already exists
275 char* shader - the name of the shader or texture (without trailing .tga or something)
276 char* content - the node content (a tag name)
278 returns TRUE if the tag with content already exists or FALSE if not
281 // build the XPath expression to search for
282 // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
285 strcpy( buffer, "/root/*/*[@path='" );
286 strcat( buffer, shader );
287 strcat( buffer, "'][child::tag='" );
288 strcat( buffer, content );
289 strcat( buffer, "']" );
291 char* expression = buffer;
293 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
294 xmlNodeSetPtr nodePtr;
296 nodePtr = xpathPtr->nodesetval;
302 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
303 xmlXPathFreeObject( xpathPtr );
307 xmlXPathFreeObject( xpathPtr );
312 bool XmlTagBuilder::AddShaderTag( const char* shader, const char* content, NodeTagType nodeTagType ){
313 /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
315 char* shader - the name of the shader or texture (without trailing .tga or something)
316 char* content - the node content (a tag name)
318 returns TRUE if the node was added successfully or FALSE when failed
321 // build the XPath expression
323 char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
325 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
326 xmlNodeSetPtr nodePtr;
328 nodePtr = xpathPtr->nodesetval;
334 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { // node was found
335 xmlNodePtr newnode = xmlNewNode( NULL, (xmlChar*)"tag" );
336 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
337 newnode = xmlDocCopyNode( newnode, doc, 1 );
338 xmlNodeSetContent( newnode, (xmlChar*)content );
340 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children
342 xmlNodePtr newtext = xmlNewText( (xmlChar*)" " );
343 xmlAddChild( nodeParent->children, newtext );
346 xmlAddNextSibling( nodeParent->children, newnode );
348 // append a new line + spaces
349 newtext = xmlNewText( (xmlChar*)"\n " );
350 xmlAddNextSibling( nodeParent->children->next, newtext );
352 else { // shader node has children already - the new node will be the first sibling
353 xmlAddNextSibling( nodeParent->children, newnode );
354 xmlNodePtr newtext = xmlNewText( (xmlChar*)"\n " );
355 xmlAddNextSibling( nodeParent->children->next, newtext );
357 xmlXPathFreeObject( xpathPtr );
361 xmlXPathFreeObject( xpathPtr );
366 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
367 int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){
368 /* Replaces tag node contents
370 char* oldtag - the <tag></tag> node content that sould be changed
371 char* newtag - the new <tag></tag> node content
373 returns the number of renamed shaders
378 // build the XPath expression
379 char expression[256];
380 strcpy( expression, "/root/*/*[child::tag='" );
381 strcat( expression, oldtag );
382 strcat( expression, "']/*" );
384 xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context );
388 xmlNodeSetPtr nodePtr = result->nodesetval;
390 for ( int i = 0; i < nodePtr->nodeNr; i++ )
392 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
393 char* content = (char*)xmlNodeGetContent( ptrContent );
395 if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content?
396 xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() );
402 xmlXPathFreeObject( result ); // CHANGED
406 bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){
407 /* Deletes a child node of a shader
409 char* shader - the name of the shader or texture (without trailing .tga or something)
410 char* tag - the tag being deleted
412 returns TRUE if the node was deleted successfully or FALSE when failed
416 char* expression = GetTagsXpathExpression( buffer, shader, TAG );
417 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
418 xmlNodeSetPtr nodePtr;
420 nodePtr = xpathPtr->nodesetval;
426 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
427 for ( int i = 0; i < nodePtr->nodeNr; i++ )
429 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
430 char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent );
432 if ( strcmp( content, tag ) == 0 ) { // find the node
433 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
435 xmlUnlinkNode( ptrContent );
436 xmlFreeNode( ptrContent );
438 // delete leading whitespace node
439 xmlUnlinkNode( ptrWhitespace );
440 xmlFreeNode( ptrWhitespace );
441 xmlXPathFreeObject( xpathPtr );
446 xmlXPathFreeObject( xpathPtr );
450 bool XmlTagBuilder::DeleteTag( const char* tag ){
451 /* Deletes a tag from all shaders
453 char* tag - the tag being deleted from all shaders
455 returns TRUE if the tag was deleted successfully or FALSE when failed
458 char expression[256];
459 strcpy( expression, "/root/*/*[child::tag='" );
460 strcat( expression, tag );
461 strcat( expression, "']" );
463 std::set<CopiedString> dellist;
464 TagSearch( expression, dellist );
465 std::set<CopiedString>::iterator iter;
467 for ( iter = dellist.begin(); iter != dellist.end(); iter++ )
469 DeleteShaderTag( iter->c_str(), tag );
476 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
484 char const *expression;
486 if ( shader == NULL ) { // get all tags from all shaders
487 expression = "/root/*/*/tag";
491 expression = GetTagsXpathExpression( buffer, shader, TAG );
494 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
495 xmlNodeSetPtr nodePtr;
497 nodePtr = xpathPtr->nodesetval;
503 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
504 for ( int i = 0; i < nodePtr->nodeNr; i++ )
506 tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
509 xmlXPathFreeObject( xpathPtr );
512 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 const *expression = "/root/*/*[not(child::tag)]";
520 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
521 xmlNodeSetPtr nodePtr;
523 nodePtr = xpathPtr->nodesetval;
529 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
532 for ( int i = 0; i < nodePtr->nodeNr; i++ )
534 ptr = nodePtr->nodeTab[i];
535 shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) );
539 xmlXPathFreeObject( xpathPtr );
542 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 const *expression = "/root/*/*/tag";
550 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
551 xmlNodeSetPtr nodePtr;
553 nodePtr = xpathPtr->nodesetval;
559 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
560 for ( int i = 0; i < nodePtr->nodeNr; i++ )
562 tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
566 xmlXPathFreeObject( xpathPtr );
569 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;
586 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
589 for ( int i = 0; i < nodePtr->nodeNr; i++ )
591 ptr = nodePtr->nodeTab[i];
592 xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" );
593 paths.insert( (CopiedString)(char*)xmlattrib );
596 xmlXPathFreeObject( xpathPtr );