worldspawn/libs/xml/xmltextags.cpp

593 lines
15 KiB
C++

/*
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 <string>
#include "qerplugin.h"
#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 <tag></tag> node content that sould be changed
char* newtag - the new <tag></tag> 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<CopiedString> dellist;
TagSearch( expression, dellist );
std::set<CopiedString>::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<CopiedString>& 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<CopiedString>& 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<CopiedString>& 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<CopiedString>& 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 );
}