/* Copyright (C) 2006, Stefan Greven. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xmltextags.h" #include #include "stream/stringstream.h" XmlTagBuilder::XmlTagBuilder(){ } XmlTagBuilder::~XmlTagBuilder(){ // clean up xmlFreeDoc( doc ); xmlXPathFreeContext( context ); } bool XmlTagBuilder::CreateXmlDocument(){ /* Creates an XML file returns TRUE if the file was created successfully or FALSE when failed */ xmlTextWriterPtr writer; writer = xmlNewTextWriterDoc( &doc, 0 ); // begin a new UTF-8 formatted xml document xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL ); // create the root node with stock and custom elements xmlTextWriterStartElement( writer, (xmlChar*)"root" ); xmlTextWriterWriteString( writer, (xmlChar*)"\n " ); xmlTextWriterStartElement( writer, (xmlChar*)"stock" ); xmlTextWriterWriteString( writer, (xmlChar*)"\n " ); xmlTextWriterEndElement( writer ); xmlTextWriterWriteString( writer, (xmlChar*)"\n " ); xmlTextWriterStartElement( writer, (xmlChar*)"custom" ); xmlTextWriterWriteString( writer, (xmlChar*)"\n " ); xmlTextWriterEndElement( writer ); xmlTextWriterWriteString( writer, (xmlChar*)"\n" ); xmlTextWriterEndElement( writer ); // end of the xml document xmlTextWriterEndDocument( writer ); xmlFreeTextWriter( writer ); if ( !doc ) { return false; } else { context = xmlXPathNewContext( doc ); return true; } } bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){ /* Reads a XML document from a file returns TRUE if the document was read successfully or FALSE when failed */ if ( savefile ) { m_savefilename = savefile; } else{ m_savefilename = file; } doc = xmlParseFile( file ); // TODO error checking! if ( !doc ) { return false; } else { context = xmlXPathNewContext( doc ); return true; } } bool XmlTagBuilder::SaveXmlDoc( void ){ return SaveXmlDoc( m_savefilename.c_str() ); } bool XmlTagBuilder::SaveXmlDoc( const char* file ){ /* Writes the XML document returns TRUE if the document was saved successfully or FALSE when saving failed */ xmlSaveNoEmptyTags = 1; if ( xmlSaveFile( file, doc ) != -1 ) { return true; } return false; } bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){ /* Adds a shader node char* shader - the name of the shader or texture (without trailing .tga or something) returns TRUE if the node was added successfully or FALSE when failed */ xmlNodeSetPtr nodePtr = NULL; xmlXPathObjectPtr xpathPtr = NULL; switch ( textureType ) { case STOCK: xpathPtr = XpathEval( "/root/stock" ); break; case CUSTOM: xpathPtr = XpathEval( "/root/custom" ); }; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return false; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { xmlNodePtr newnode, newtext; xmlNodePtr nodeParent = nodePtr->nodeTab[0]; // create a new node and set the node attribute (shader path) switch ( nodeShaderType ) { case SHADER: newnode = xmlNewNode( NULL, (xmlChar*)"shader" ); break; case TEXTURE: newnode = xmlNewNode( NULL, (xmlChar*)"texture" ); }; newnode = xmlDocCopyNode( newnode, doc, 1 ); xmlSetProp( newnode, (xmlChar*)"path", (xmlChar*)shader ); xmlNodeSetContent( newnode, (xmlChar*)"\n " ); if ( nodePtr->nodeTab[0]->children->next == NULL ) { // there are no shaders yet // add spaces newtext = xmlNewText( (xmlChar*)" " ); xmlAddChild( nodeParent->children, newtext ); // add the new node xmlAddNextSibling( nodeParent->children, newnode ); // append a new line newtext = xmlNewText( (xmlChar*)"\n " ); xmlAddNextSibling( nodeParent->children->next, newtext ); } else { // add the node xmlAddNextSibling( nodeParent->children, newnode ); // append a new line and spaces newtext = xmlNewText( (xmlChar*)"\n " ); xmlAddNextSibling( nodeParent->children->next, newtext ); } xmlXPathFreeObject( xpathPtr ); return true; } else { xmlXPathFreeObject( xpathPtr ); return false; } } bool XmlTagBuilder::DeleteShaderNode( const char* shader ){ /* Deletes a shader node char* shader - the name of the shader or texture (without trailing .tga or something) returns TRUE if the node was deleted successfully or FALSE when failed */ char buffer[256]; char* expression = GetTagsXpathExpression( buffer, shader, EMPTY ); xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return false; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { xmlNodePtr ptrContent = nodePtr->nodeTab[0]; xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev; // delete the node xmlUnlinkNode( ptrContent ); xmlFreeNode( ptrContent ); // delete leading whitespace node xmlUnlinkNode( ptrWhitespace ); xmlFreeNode( ptrWhitespace ); xmlXPathFreeObject( xpathPtr ); return true; } xmlXPathFreeObject( xpathPtr ); return false; } bool XmlTagBuilder::CheckShaderTag( const char* shader ){ /* Checks whether there exists an entry for a shader/texture with at least one tag char* shader - the name of the shader or texture (without trailing .tga or something) returns TRUE if the shader is already stored in the XML tag file and has at least one tag */ // build the XPath expression to search for char buffer[256]; strcpy( buffer, "/root/*/*[@path='" ); strcat( buffer, shader ); strcat( buffer, "']" ); char* expression = buffer; xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return false; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { xmlXPathFreeObject( xpathPtr ); return true; } else { xmlXPathFreeObject( xpathPtr ); return false; } } bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){ /* Checks whether a tag with content already exists char* shader - the name of the shader or texture (without trailing .tga or something) char* content - the node content (a tag name) returns TRUE if the tag with content already exists or FALSE if not */ // build the XPath expression to search for // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']"; char buffer[256]; strcpy( buffer, "/root/*/*[@path='" ); strcat( buffer, shader ); strcat( buffer, "'][child::tag='" ); strcat( buffer, content ); strcat( buffer, "']" ); char* expression = buffer; xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return false; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { xmlXPathFreeObject( xpathPtr ); return true; } else { xmlXPathFreeObject( xpathPtr ); return false; } } bool XmlTagBuilder::AddShaderTag( const char* shader, const char* content, NodeTagType nodeTagType ){ /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet char* shader - the name of the shader or texture (without trailing .tga or something) char* content - the node content (a tag name) returns TRUE if the node was added successfully or FALSE when failed */ // build the XPath expression char buffer[256]; char* expression = GetTagsXpathExpression( buffer, shader, EMPTY ); xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return false; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { // node was found xmlNodePtr newnode = xmlNewNode( NULL, (xmlChar*)"tag" ); xmlNodePtr nodeParent = nodePtr->nodeTab[0]; newnode = xmlDocCopyNode( newnode, doc, 1 ); xmlNodeSetContent( newnode, (xmlChar*)content ); if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children // add spaces xmlNodePtr newtext = xmlNewText( (xmlChar*)" " ); xmlAddChild( nodeParent->children, newtext ); // add new node xmlAddNextSibling( nodeParent->children, newnode ); // append a new line + spaces newtext = xmlNewText( (xmlChar*)"\n " ); xmlAddNextSibling( nodeParent->children->next, newtext ); } else { // shader node has children already - the new node will be the first sibling xmlAddNextSibling( nodeParent->children, newnode ); xmlNodePtr newtext = xmlNewText( (xmlChar*)"\n " ); xmlAddNextSibling( nodeParent->children->next, newtext ); } xmlXPathFreeObject( xpathPtr ); return true; } else { xmlXPathFreeObject( xpathPtr ); return false; } } //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag) int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){ /* Replaces tag node contents char* oldtag - the node content that sould be changed char* newtag - the new node content returns the number of renamed shaders */ int num = 0; // build the XPath expression char expression[256]; strcpy( expression, "/root/*/*[child::tag='" ); strcat( expression, oldtag ); strcat( expression, "']/*" ); xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context ); if ( !result ) { return 0; } xmlNodeSetPtr nodePtr = result->nodesetval; for ( int i = 0; i < nodePtr->nodeNr; i++ ) { xmlNodePtr ptrContent = nodePtr->nodeTab[i]; char* content = (char*)xmlNodeGetContent( ptrContent ); if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content? xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() ); num++; } } SaveXmlDoc(); xmlXPathFreeObject( result ); // CHANGED return num; } bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){ /* Deletes a child node of a shader char* shader - the name of the shader or texture (without trailing .tga or something) char* tag - the tag being deleted returns TRUE if the node was deleted successfully or FALSE when failed */ char buffer[256]; char* expression = GetTagsXpathExpression( buffer, shader, TAG ); xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return false; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { for ( int i = 0; i < nodePtr->nodeNr; i++ ) { xmlNodePtr ptrContent = nodePtr->nodeTab[i]; char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent ); if ( strcmp( content, tag ) == 0 ) { // find the node xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev; // delete the node xmlUnlinkNode( ptrContent ); xmlFreeNode( ptrContent ); // delete leading whitespace node xmlUnlinkNode( ptrWhitespace ); xmlFreeNode( ptrWhitespace ); xmlXPathFreeObject( xpathPtr ); return true; } } } xmlXPathFreeObject( xpathPtr ); return false; } bool XmlTagBuilder::DeleteTag( const char* tag ){ /* Deletes a tag from all shaders char* tag - the tag being deleted from all shaders returns TRUE if the tag was deleted successfully or FALSE when failed */ char expression[256]; strcpy( expression, "/root/*/*[child::tag='" ); strcat( expression, tag ); strcat( expression, "']" ); std::set dellist; TagSearch( expression, dellist ); std::set::iterator iter; for ( iter = dellist.begin(); iter != dellist.end(); iter++ ) { DeleteShaderTag( iter->c_str(), tag ); } SaveXmlDoc(); return true; } void XmlTagBuilder::GetShaderTags( const char* shader, std::vector& tags ){ /* Gets the tags from a shader char* shader - the name of the shader returns a vector containing the tags */ char const *expression; if ( shader == NULL ) { // get all tags from all shaders expression = "/root/*/*/tag"; } else { char buffer[256]; expression = GetTagsXpathExpression( buffer, shader, TAG ); } xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { for ( int i = 0; i < nodePtr->nodeNr; i++ ) { tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) ); } } xmlXPathFreeObject( xpathPtr ); } void XmlTagBuilder::GetUntagged( std::set& shaders ){ /* Gets all textures and shaders listed in the xml file that don't have any tag returns a set containing the shaders (with path) */ char const *expression = "/root/*/*[not(child::tag)]"; xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { xmlNodePtr ptr; for ( int i = 0; i < nodePtr->nodeNr; i++ ) { ptr = nodePtr->nodeTab[i]; shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) ); } } xmlXPathFreeObject( xpathPtr ); } void XmlTagBuilder::GetAllTags( std::set& tags ){ /* Gets a list of all tags that are used (assigned to any shader) returns a set containing all used tags */ char const *expression = "/root/*/*/tag"; xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { for ( int i = 0; i < nodePtr->nodeNr; i++ ) { tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) ); } } xmlXPathFreeObject( xpathPtr ); } void XmlTagBuilder::TagSearch( const char* expression, std::set& paths ){ /* Searches shaders by tags char* expression - the XPath expression to search returns a set containing the found shaders */ xmlXPathObjectPtr xpathPtr = XpathEval( expression ); xmlNodeSetPtr nodePtr; if ( xpathPtr ) { nodePtr = xpathPtr->nodesetval; } else{ return; } if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { xmlNodePtr ptr; xmlChar* xmlattrib; for ( int i = 0; i < nodePtr->nodeNr; i++ ) { ptr = nodePtr->nodeTab[i]; xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" ); paths.insert( (CopiedString)(char*)xmlattrib ); } } xmlXPathFreeObject( xpathPtr ); }