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 "stream/stringstream.h"
28 XmlTagBuilder::XmlTagBuilder(){
31 XmlTagBuilder::~XmlTagBuilder(){
34 xmlXPathFreeContext( context );
37 bool XmlTagBuilder::CreateXmlDocument(){
38 /* Creates an XML file
40 returns TRUE if the file was created successfully or FALSE when failed
43 xmlTextWriterPtr writer;
45 writer = xmlNewTextWriterDoc( &doc, 0 );
47 // begin a new UTF-8 formatted xml document
48 xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL );
50 // create the root node with stock and custom elements
51 xmlTextWriterStartElement( writer, (xmlChar*)"root" );
52 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
53 xmlTextWriterStartElement( writer, (xmlChar*)"stock" );
54 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
55 xmlTextWriterEndElement( writer );
56 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
57 xmlTextWriterStartElement( writer, (xmlChar*)"custom" );
58 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
59 xmlTextWriterEndElement( writer );
60 xmlTextWriterWriteString( writer, (xmlChar*)"\n" );
61 xmlTextWriterEndElement( writer );
63 // end of the xml document
64 xmlTextWriterEndDocument( writer );
65 xmlFreeTextWriter( writer );
71 context = xmlXPathNewContext( doc );
76 bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){
77 /* Reads a XML document from a file
79 returns TRUE if the document was read successfully or FALSE when failed
83 m_savefilename = savefile;
86 m_savefilename = file;
89 doc = xmlParseFile( file ); // TODO error checking!
95 context = xmlXPathNewContext( doc );
100 bool XmlTagBuilder::SaveXmlDoc( void ){
101 return SaveXmlDoc( m_savefilename.c_str() );
104 bool XmlTagBuilder::SaveXmlDoc( const char* file ){
105 /* Writes the XML document
107 returns TRUE if the document was saved successfully or FALSE when saving failed
110 xmlSaveNoEmptyTags = 1;
112 if ( xmlSaveFile( file, doc ) != -1 ) {
118 bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){
119 /* Adds a shader node
121 char* shader - the name of the shader or texture (without trailing .tga or something)
123 returns TRUE if the node was added successfully or FALSE when failed
126 xmlNodeSetPtr nodePtr = NULL;
127 xmlXPathObjectPtr xpathPtr = NULL;
129 switch ( textureType )
132 xpathPtr = XpathEval( "/root/stock" );
135 xpathPtr = XpathEval( "/root/custom" );
139 nodePtr = xpathPtr->nodesetval;
145 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
146 xmlNodePtr newnode, newtext;
147 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
149 // create a new node and set the node attribute (shader path)
150 switch ( nodeShaderType )
153 newnode = xmlNewNode( NULL, (xmlChar*)"shader" );
156 newnode = xmlNewNode( NULL, (xmlChar*)"texture" );
159 newnode = xmlDocCopyNode( newnode, doc, 1 );
160 xmlSetProp( newnode, (xmlChar*)"path", (xmlChar*)shader );
161 xmlNodeSetContent( newnode, (xmlChar*)"\n " );
163 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // there are no shaders yet
165 newtext = xmlNewText( (xmlChar*)" " );
166 xmlAddChild( nodeParent->children, newtext );
169 xmlAddNextSibling( nodeParent->children, newnode );
172 newtext = xmlNewText( (xmlChar*)"\n " );
173 xmlAddNextSibling( nodeParent->children->next, newtext );
177 xmlAddNextSibling( nodeParent->children, newnode );
179 // append a new line and spaces
180 newtext = xmlNewText( (xmlChar*)"\n " );
181 xmlAddNextSibling( nodeParent->children->next, newtext );
184 xmlXPathFreeObject( xpathPtr );
188 xmlXPathFreeObject( xpathPtr );
193 bool XmlTagBuilder::DeleteShaderNode( const char* shader ){
194 /* Deletes a shader node
196 char* shader - the name of the shader or texture (without trailing .tga or something)
198 returns TRUE if the node was deleted successfully or FALSE when failed
202 char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
203 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
205 xmlNodeSetPtr nodePtr;
207 nodePtr = xpathPtr->nodesetval;
213 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
214 xmlNodePtr ptrContent = nodePtr->nodeTab[0];
215 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
218 xmlUnlinkNode( ptrContent );
219 xmlFreeNode( ptrContent );
221 // delete leading whitespace node
222 xmlUnlinkNode( ptrWhitespace );
223 xmlFreeNode( ptrWhitespace );
224 xmlXPathFreeObject( xpathPtr );
227 xmlXPathFreeObject( xpathPtr );
231 bool XmlTagBuilder::CheckShaderTag( const char* shader ){
232 /* Checks whether there exists an entry for a shader/texture with at least one tag
234 char* shader - the name of the shader or texture (without trailing .tga or something)
236 returns TRUE if the shader is already stored in the XML tag file and has at least one tag
239 // build the XPath expression to search for
241 strcpy( buffer, "/root/*/*[@path='" );
242 strcat( buffer, shader );
243 strcat( buffer, "']" );
245 char* expression = buffer;
247 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
248 xmlNodeSetPtr nodePtr;
250 nodePtr = xpathPtr->nodesetval;
256 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
257 xmlXPathFreeObject( xpathPtr );
261 xmlXPathFreeObject( xpathPtr );
266 bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){
267 /* Checks whether a tag with content already exists
269 char* shader - the name of the shader or texture (without trailing .tga or something)
270 char* content - the node content (a tag name)
272 returns TRUE if the tag with content already exists or FALSE if not
275 // build the XPath expression to search for
276 // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
279 strcpy( buffer, "/root/*/*[@path='" );
280 strcat( buffer, shader );
281 strcat( buffer, "'][child::tag='" );
282 strcat( buffer, content );
283 strcat( buffer, "']" );
285 char* expression = buffer;
287 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
288 xmlNodeSetPtr nodePtr;
290 nodePtr = xpathPtr->nodesetval;
296 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
297 xmlXPathFreeObject( xpathPtr );
301 xmlXPathFreeObject( xpathPtr );
306 bool XmlTagBuilder::AddShaderTag( const char* shader, const char* content, NodeTagType nodeTagType ){
307 /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
309 char* shader - the name of the shader or texture (without trailing .tga or something)
310 char* content - the node content (a tag name)
312 returns TRUE if the node was added successfully or FALSE when failed
315 // build the XPath expression
317 char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
319 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
320 xmlNodeSetPtr nodePtr;
322 nodePtr = xpathPtr->nodesetval;
328 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { // node was found
329 xmlNodePtr newnode = xmlNewNode( NULL, (xmlChar*)"tag" );
330 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
331 newnode = xmlDocCopyNode( newnode, doc, 1 );
332 xmlNodeSetContent( newnode, (xmlChar*)content );
334 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children
336 xmlNodePtr newtext = xmlNewText( (xmlChar*)" " );
337 xmlAddChild( nodeParent->children, newtext );
340 xmlAddNextSibling( nodeParent->children, newnode );
342 // append a new line + spaces
343 newtext = xmlNewText( (xmlChar*)"\n " );
344 xmlAddNextSibling( nodeParent->children->next, newtext );
346 else { // shader node has children already - the new node will be the first sibling
347 xmlAddNextSibling( nodeParent->children, newnode );
348 xmlNodePtr newtext = xmlNewText( (xmlChar*)"\n " );
349 xmlAddNextSibling( nodeParent->children->next, newtext );
351 xmlXPathFreeObject( xpathPtr );
355 xmlXPathFreeObject( xpathPtr );
360 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
361 int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){
362 /* Replaces tag node contents
364 char* oldtag - the <tag></tag> node content that sould be changed
365 char* newtag - the new <tag></tag> node content
367 returns the number of renamed shaders
372 // build the XPath expression
373 char expression[256];
374 strcpy( expression, "/root/*/*[child::tag='" );
375 strcat( expression, oldtag );
376 strcat( expression, "']/*" );
378 xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context );
382 xmlNodeSetPtr nodePtr = result->nodesetval;
384 for ( int i = 0; i < nodePtr->nodeNr; i++ )
386 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
387 char* content = (char*)xmlNodeGetContent( ptrContent );
389 if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content?
390 xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() );
396 xmlXPathFreeObject( result ); // CHANGED
400 bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){
401 /* Deletes a child node of a shader
403 char* shader - the name of the shader or texture (without trailing .tga or something)
404 char* tag - the tag being deleted
406 returns TRUE if the node was deleted successfully or FALSE when failed
410 char* expression = GetTagsXpathExpression( buffer, shader, TAG );
411 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
412 xmlNodeSetPtr nodePtr;
414 nodePtr = xpathPtr->nodesetval;
420 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
421 for ( int i = 0; i < nodePtr->nodeNr; i++ )
423 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
424 char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent );
426 if ( strcmp( content, tag ) == 0 ) { // find the node
427 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
429 xmlUnlinkNode( ptrContent );
430 xmlFreeNode( ptrContent );
432 // delete leading whitespace node
433 xmlUnlinkNode( ptrWhitespace );
434 xmlFreeNode( ptrWhitespace );
435 xmlXPathFreeObject( xpathPtr );
440 xmlXPathFreeObject( xpathPtr );
444 bool XmlTagBuilder::DeleteTag( const char* tag ){
445 /* Deletes a tag from all shaders
447 char* tag - the tag being deleted from all shaders
449 returns TRUE if the tag was deleted successfully or FALSE when failed
452 char expression[256];
453 strcpy( expression, "/root/*/*[child::tag='" );
454 strcat( expression, tag );
455 strcat( expression, "']" );
457 std::set<CopiedString> dellist;
458 TagSearch( expression, dellist );
459 std::set<CopiedString>::iterator iter;
461 for ( iter = dellist.begin(); iter != dellist.end(); iter++ )
463 DeleteShaderTag( iter->c_str(), tag );
470 void XmlTagBuilder::GetShaderTags( const char* shader, std::vector<CopiedString>& tags ){
471 /* Gets the tags from a shader
473 char* shader - the name of the shader
475 returns a vector containing the tags
478 char const *expression;
480 if ( shader == NULL ) { // get all tags from all shaders
481 expression = "/root/*/*/tag";
485 expression = GetTagsXpathExpression( buffer, shader, TAG );
488 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
489 xmlNodeSetPtr nodePtr;
491 nodePtr = xpathPtr->nodesetval;
497 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
498 for ( int i = 0; i < nodePtr->nodeNr; i++ )
500 tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
503 xmlXPathFreeObject( xpathPtr );
506 void XmlTagBuilder::GetUntagged( std::set<CopiedString>& shaders ){
507 /* Gets all textures and shaders listed in the xml file that don't have any tag
509 returns a set containing the shaders (with path)
512 char const *expression = "/root/*/*[not(child::tag)]";
514 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
515 xmlNodeSetPtr nodePtr;
517 nodePtr = xpathPtr->nodesetval;
523 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
526 for ( int i = 0; i < nodePtr->nodeNr; i++ )
528 ptr = nodePtr->nodeTab[i];
529 shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) );
533 xmlXPathFreeObject( xpathPtr );
536 void XmlTagBuilder::GetAllTags( std::set<CopiedString>& tags ){
537 /* Gets a list of all tags that are used (assigned to any shader)
539 returns a set containing all used tags
542 char const *expression = "/root/*/*/tag";
544 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
545 xmlNodeSetPtr nodePtr;
547 nodePtr = xpathPtr->nodesetval;
553 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
554 for ( int i = 0; i < nodePtr->nodeNr; i++ )
556 tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
560 xmlXPathFreeObject( xpathPtr );
563 void XmlTagBuilder::TagSearch( const char* expression, std::set<CopiedString>& paths ){
564 /* Searches shaders by tags
566 char* expression - the XPath expression to search
568 returns a set containing the found shaders
571 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
572 xmlNodeSetPtr nodePtr;
574 nodePtr = xpathPtr->nodesetval;
580 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
583 for ( int i = 0; i < nodePtr->nodeNr; i++ )
585 ptr = nodePtr->nodeTab[i];
586 xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" );
587 paths.insert( (CopiedString)(char*)xmlattrib );
590 xmlXPathFreeObject( xpathPtr );