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(){
32 XmlTagBuilder::~XmlTagBuilder(){
35 xmlXPathFreeContext( context );
38 bool XmlTagBuilder::CreateXmlDocument(){
39 /* Creates an XML file
41 returns TRUE if the file was created successfully or FALSE when failed
44 xmlTextWriterPtr writer;
46 writer = xmlNewTextWriterDoc( &doc, 0 );
48 // begin a new UTF-8 formatted xml document
49 xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL );
51 // create the root node with stock and custom elements
52 xmlTextWriterStartElement( writer, (xmlChar*)"root" );
53 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
54 xmlTextWriterStartElement( writer, (xmlChar*)"stock" );
55 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
56 xmlTextWriterEndElement( writer );
57 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
58 xmlTextWriterStartElement( writer, (xmlChar*)"custom" );
59 xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
60 xmlTextWriterEndElement( writer );
61 xmlTextWriterWriteString( writer, (xmlChar*)"\n" );
62 xmlTextWriterEndElement( writer );
64 // end of the xml document
65 xmlTextWriterEndDocument( writer );
66 xmlFreeTextWriter( writer );
72 context = xmlXPathNewContext( doc );
77 bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){
78 /* Reads a XML document from a file
80 returns TRUE if the document was read successfully or FALSE when failed
84 m_savefilename = savefile;
87 m_savefilename = file;
90 doc = xmlParseFile( file ); // TODO error checking!
96 context = xmlXPathNewContext( doc );
101 bool XmlTagBuilder::SaveXmlDoc( void ){
102 return SaveXmlDoc( m_savefilename.c_str() );
105 bool XmlTagBuilder::SaveXmlDoc( const char* file ){
106 /* Writes the XML document
108 returns TRUE if the document was saved successfully or FALSE when saving failed
111 xmlSaveNoEmptyTags = 1;
113 if ( xmlSaveFile( file, doc ) != -1 ) {
119 bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){
120 /* Adds a shader node
122 char* shader - the name of the shader or texture (without trailing .tga or something)
124 returns TRUE if the node was added successfully or FALSE when failed
127 xmlNodeSetPtr nodePtr = NULL;
128 xmlXPathObjectPtr xpathPtr = NULL;
130 switch ( textureType )
133 xpathPtr = XpathEval( "/root/stock" );
136 xpathPtr = XpathEval( "/root/custom" );
140 nodePtr = xpathPtr->nodesetval;
146 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
147 xmlNodePtr newnode, newtext;
148 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
150 // create a new node and set the node attribute (shader path)
151 switch ( nodeShaderType )
154 newnode = xmlNewNode( NULL, (xmlChar*)"shader" );
157 newnode = xmlNewNode( NULL, (xmlChar*)"texture" );
160 newnode = xmlDocCopyNode( newnode, doc, 1 );
161 xmlSetProp( newnode, (xmlChar*)"path", (xmlChar*)shader );
162 xmlNodeSetContent( newnode, (xmlChar*)"\n " );
164 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // there are no shaders yet
166 newtext = xmlNewText( (xmlChar*)" " );
167 xmlAddChild( nodeParent->children, newtext );
170 xmlAddNextSibling( nodeParent->children, newnode );
173 newtext = xmlNewText( (xmlChar*)"\n " );
174 xmlAddNextSibling( nodeParent->children->next, newtext );
178 xmlAddNextSibling( nodeParent->children, newnode );
180 // append a new line and spaces
181 newtext = xmlNewText( (xmlChar*)"\n " );
182 xmlAddNextSibling( nodeParent->children->next, newtext );
185 xmlXPathFreeObject( xpathPtr );
189 xmlXPathFreeObject( xpathPtr );
194 bool XmlTagBuilder::DeleteShaderNode( const char* shader ){
195 /* Deletes a shader node
197 char* shader - the name of the shader or texture (without trailing .tga or something)
199 returns TRUE if the node was deleted successfully or FALSE when failed
203 char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
204 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
206 xmlNodeSetPtr nodePtr;
208 nodePtr = xpathPtr->nodesetval;
214 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
215 xmlNodePtr ptrContent = nodePtr->nodeTab[0];
216 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
219 xmlUnlinkNode( ptrContent );
220 xmlFreeNode( ptrContent );
222 // delete leading whitespace node
223 xmlUnlinkNode( ptrWhitespace );
224 xmlFreeNode( ptrWhitespace );
225 xmlXPathFreeObject( xpathPtr );
228 xmlXPathFreeObject( xpathPtr );
232 bool XmlTagBuilder::CheckShaderTag( const char* shader ){
233 /* Checks whether there exists an entry for a shader/texture with at least one tag
235 char* shader - the name of the shader or texture (without trailing .tga or something)
237 returns TRUE if the shader is already stored in the XML tag file and has at least one tag
240 // build the XPath expression to search for
242 strcpy( buffer, "/root/*/*[@path='" );
243 strcat( buffer, shader );
244 strcat( buffer, "']" );
246 char* expression = buffer;
248 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
249 xmlNodeSetPtr nodePtr;
251 nodePtr = xpathPtr->nodesetval;
257 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
258 xmlXPathFreeObject( xpathPtr );
262 xmlXPathFreeObject( xpathPtr );
267 bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){
268 /* Checks whether a tag with content already exists
270 char* shader - the name of the shader or texture (without trailing .tga or something)
271 char* content - the node content (a tag name)
273 returns TRUE if the tag with content already exists or FALSE if not
276 // build the XPath expression to search for
277 // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
280 strcpy( buffer, "/root/*/*[@path='" );
281 strcat( buffer, shader );
282 strcat( buffer, "'][child::tag='" );
283 strcat( buffer, content );
284 strcat( buffer, "']" );
286 char* expression = buffer;
288 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
289 xmlNodeSetPtr nodePtr;
291 nodePtr = xpathPtr->nodesetval;
297 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
298 xmlXPathFreeObject( xpathPtr );
302 xmlXPathFreeObject( xpathPtr );
307 bool XmlTagBuilder::AddShaderTag( const char* shader, const char* content, NodeTagType nodeTagType ){
308 /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
310 char* shader - the name of the shader or texture (without trailing .tga or something)
311 char* content - the node content (a tag name)
313 returns TRUE if the node was added successfully or FALSE when failed
316 // build the XPath expression
318 char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
320 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
321 xmlNodeSetPtr nodePtr;
323 nodePtr = xpathPtr->nodesetval;
329 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { // node was found
330 xmlNodePtr newnode = xmlNewNode( NULL, (xmlChar*)"tag" );
331 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
332 newnode = xmlDocCopyNode( newnode, doc, 1 );
333 xmlNodeSetContent( newnode, (xmlChar*)content );
335 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children
337 xmlNodePtr newtext = xmlNewText( (xmlChar*)" " );
338 xmlAddChild( nodeParent->children, newtext );
341 xmlAddNextSibling( nodeParent->children, newnode );
343 // append a new line + spaces
344 newtext = xmlNewText( (xmlChar*)"\n " );
345 xmlAddNextSibling( nodeParent->children->next, newtext );
347 else { // shader node has children already - the new node will be the first sibling
348 xmlAddNextSibling( nodeParent->children, newnode );
349 xmlNodePtr newtext = xmlNewText( (xmlChar*)"\n " );
350 xmlAddNextSibling( nodeParent->children->next, newtext );
352 xmlXPathFreeObject( xpathPtr );
356 xmlXPathFreeObject( xpathPtr );
361 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
362 int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){
363 /* Replaces tag node contents
365 char* oldtag - the <tag></tag> node content that sould be changed
366 char* newtag - the new <tag></tag> node content
368 returns the number of renamed shaders
373 // build the XPath expression
374 char expression[256];
375 strcpy( expression, "/root/*/*[child::tag='" );
376 strcat( expression, oldtag );
377 strcat( expression, "']/*" );
379 xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context );
383 xmlNodeSetPtr nodePtr = result->nodesetval;
385 for ( int i = 0; i < nodePtr->nodeNr; i++ )
387 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
388 char* content = (char*)xmlNodeGetContent( ptrContent );
390 if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content?
391 xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() );
397 xmlXPathFreeObject( result ); // CHANGED
401 bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){
402 /* Deletes a child node of a shader
404 char* shader - the name of the shader or texture (without trailing .tga or something)
405 char* tag - the tag being deleted
407 returns TRUE if the node was deleted successfully or FALSE when failed
411 char* expression = GetTagsXpathExpression( buffer, shader, TAG );
412 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
413 xmlNodeSetPtr nodePtr;
415 nodePtr = xpathPtr->nodesetval;
421 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
422 for ( int i = 0; i < nodePtr->nodeNr; i++ )
424 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
425 char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent );
427 if ( strcmp( content, tag ) == 0 ) { // find the node
428 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
430 xmlUnlinkNode( ptrContent );
431 xmlFreeNode( ptrContent );
433 // delete leading whitespace node
434 xmlUnlinkNode( ptrWhitespace );
435 xmlFreeNode( ptrWhitespace );
436 xmlXPathFreeObject( xpathPtr );
441 xmlXPathFreeObject( xpathPtr );
445 bool XmlTagBuilder::DeleteTag( const char* tag ){
446 /* Deletes a tag from all shaders
448 char* tag - the tag being deleted from all shaders
450 returns TRUE if the tag was deleted successfully or FALSE when failed
453 char expression[256];
454 strcpy( expression, "/root/*/*[child::tag='" );
455 strcat( expression, tag );
456 strcat( expression, "']" );
458 std::set<CopiedString> dellist;
459 TagSearch( expression, dellist );
460 std::set<CopiedString>::iterator iter;
462 for ( iter = dellist.begin(); iter != dellist.end(); iter++ )
464 DeleteShaderTag( iter->c_str(), tag );
471 void XmlTagBuilder::GetShaderTags( const char* shader, std::vector<CopiedString>& tags ){
472 /* Gets the tags from a shader
474 char* shader - the name of the shader
476 returns a vector containing the tags
481 if ( shader == NULL ) { // get all tags from all shaders
482 expression = "/root/*/*/tag";
486 expression = GetTagsXpathExpression( buffer, shader, TAG );
489 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
490 xmlNodeSetPtr nodePtr;
492 nodePtr = xpathPtr->nodesetval;
498 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
499 for ( int i = 0; i < nodePtr->nodeNr; i++ )
501 tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
504 xmlXPathFreeObject( xpathPtr );
507 void XmlTagBuilder::GetUntagged( std::set<CopiedString>& shaders ){
508 /* Gets all textures and shaders listed in the xml file that don't have any tag
510 returns a set containing the shaders (with path)
513 char* expression = "/root/*/*[not(child::tag)]";
515 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
516 xmlNodeSetPtr nodePtr;
518 nodePtr = xpathPtr->nodesetval;
524 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
527 for ( int i = 0; i < nodePtr->nodeNr; i++ )
529 ptr = nodePtr->nodeTab[i];
530 shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) );
534 xmlXPathFreeObject( xpathPtr );
537 void XmlTagBuilder::GetAllTags( std::set<CopiedString>& tags ){
538 /* Gets a list of all tags that are used (assigned to any shader)
540 returns a set containing all used tags
543 char* expression = "/root/*/*/tag";
545 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
546 xmlNodeSetPtr nodePtr;
548 nodePtr = xpathPtr->nodesetval;
554 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
555 for ( int i = 0; i < nodePtr->nodeNr; i++ )
557 tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
561 xmlXPathFreeObject( xpathPtr );
564 void XmlTagBuilder::TagSearch( const char* expression, std::set<CopiedString>& paths ){
565 /* Searches shaders by tags
567 char* expression - the XPath expression to search
569 returns a set containing the found shaders
572 xmlXPathObjectPtr xpathPtr = XpathEval( expression );
573 xmlNodeSetPtr nodePtr;
575 nodePtr = xpathPtr->nodesetval;
581 if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
584 for ( int i = 0; i < nodePtr->nodeNr; i++ )
586 ptr = nodePtr->nodeTab[i];
587 xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" );
588 paths.insert( (CopiedString)(char*)xmlattrib );
591 xmlXPathFreeObject( xpathPtr );