cmake: do not look for graphical libs when not building radiant
[xonotic/netradiant.git] / libs / xml / xmltextags.cpp
1 /*
2    Copyright (C) 2006, Stefan Greven.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #include "xmltextags.h"
23
24 #include <string>
25
26 #include "stream/stringstream.h"
27
28 XmlTagBuilder::XmlTagBuilder(){
29 }
30
31 XmlTagBuilder::~XmlTagBuilder(){
32         // clean up
33         xmlFreeDoc( doc );
34         xmlXPathFreeContext( context );
35 }
36
37 bool XmlTagBuilder::CreateXmlDocument(){
38         /* Creates an XML file
39
40            returns TRUE if the file was created successfully or FALSE when failed
41          */
42
43         xmlTextWriterPtr writer;
44
45         writer = xmlNewTextWriterDoc( &doc, 0 );
46
47         // begin a new UTF-8 formatted xml document
48         xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL );
49
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 );
62
63         // end of the xml document
64         xmlTextWriterEndDocument( writer );
65         xmlFreeTextWriter( writer );
66
67         if ( !doc ) {
68                 return false;
69         }
70         else {
71                 context = xmlXPathNewContext( doc );
72                 return true;
73         }
74 }
75
76 bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){
77         /* Reads a XML document from a file
78
79            returns TRUE if the document was read successfully or FALSE when failed
80          */
81
82         if ( savefile ) {
83                 m_savefilename = savefile;
84         }
85         else{
86                 m_savefilename = file;
87         }
88
89         doc = xmlParseFile( file ); // TODO error checking!
90
91         if ( !doc ) {
92                 return false;
93         }
94         else {
95                 context = xmlXPathNewContext( doc );
96                 return true;
97         }
98 }
99
100 bool XmlTagBuilder::SaveXmlDoc( void ){
101         return SaveXmlDoc( m_savefilename.c_str() );
102 }
103
104 bool XmlTagBuilder::SaveXmlDoc( const char* file ){
105         /* Writes the XML document
106
107            returns TRUE if the document was saved successfully or FALSE when saving failed
108          */
109
110         xmlSaveNoEmptyTags = 1;
111
112         if ( xmlSaveFile( file, doc ) != -1 ) {
113                 return true;
114         }
115         return false;
116 }
117
118 bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){
119         /* Adds a shader node
120
121            char* shader - the name of the shader or texture (without trailing .tga or something)
122
123            returns TRUE if the node was added successfully or FALSE when failed
124          */
125
126         xmlNodeSetPtr nodePtr = NULL;
127         xmlXPathObjectPtr xpathPtr = NULL;
128
129         switch ( textureType )
130         {
131         case STOCK:
132                 xpathPtr = XpathEval( "/root/stock" );
133                 break;
134         case CUSTOM:
135                 xpathPtr = XpathEval( "/root/custom" );
136         };
137
138         if ( xpathPtr ) {
139                 nodePtr = xpathPtr->nodesetval;
140         }
141         else{
142                 return false;
143         }
144
145         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
146                 xmlNodePtr newnode, newtext;
147                 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
148
149                 // create a new node and set the node attribute (shader path)
150                 switch ( nodeShaderType )
151                 {
152                 case SHADER:
153                         newnode = xmlNewNode( NULL, (xmlChar*)"shader" );
154                         break;
155                 case TEXTURE:
156                         newnode = xmlNewNode( NULL, (xmlChar*)"texture" );
157                 };
158
159                 newnode = xmlDocCopyNode( newnode, doc, 1 );
160                 xmlSetProp( newnode, (xmlChar*)"path", (xmlChar*)shader );
161                 xmlNodeSetContent( newnode, (xmlChar*)"\n    " );
162
163                 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // there are no shaders yet
164                         // add spaces
165                         newtext = xmlNewText( (xmlChar*)"  " );
166                         xmlAddChild( nodeParent->children, newtext );
167
168                         // add the new node
169                         xmlAddNextSibling( nodeParent->children, newnode );
170
171                         // append a new line
172                         newtext = xmlNewText( (xmlChar*)"\n  " );
173                         xmlAddNextSibling( nodeParent->children->next, newtext );
174                 }
175                 else {
176                         // add the node
177                         xmlAddNextSibling( nodeParent->children, newnode );
178
179                         // append a new line and spaces
180                         newtext = xmlNewText( (xmlChar*)"\n    " );
181                         xmlAddNextSibling( nodeParent->children->next, newtext );
182                 }
183
184                 xmlXPathFreeObject( xpathPtr );
185                 return true;
186         }
187         else {
188                 xmlXPathFreeObject( xpathPtr );
189                 return false;
190         }
191 }
192
193 bool XmlTagBuilder::DeleteShaderNode( const char* shader ){
194         /* Deletes a shader node
195
196            char* shader - the name of the shader or texture (without trailing .tga or something)
197
198            returns TRUE if the node was deleted successfully or FALSE when failed
199          */
200
201         char buffer[256];
202         char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
203         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
204
205         xmlNodeSetPtr nodePtr;
206         if ( xpathPtr ) {
207                 nodePtr = xpathPtr->nodesetval;
208         }
209         else{
210                 return false;
211         }
212
213         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
214                 xmlNodePtr ptrContent = nodePtr->nodeTab[0];
215                 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
216
217                 // delete the node
218                 xmlUnlinkNode( ptrContent );
219                 xmlFreeNode( ptrContent );
220
221                 // delete leading whitespace node
222                 xmlUnlinkNode( ptrWhitespace );
223                 xmlFreeNode( ptrWhitespace );
224                 xmlXPathFreeObject( xpathPtr );
225                 return true;
226         }
227         xmlXPathFreeObject( xpathPtr );
228         return false;
229 }
230
231 bool XmlTagBuilder::CheckShaderTag( const char* shader ){
232         /* Checks whether there exists an entry for a shader/texture with at least one tag
233
234            char* shader  - the name of the shader or texture (without trailing .tga or something)
235
236            returns TRUE if the shader is already stored in the XML tag file and has at least one tag
237          */
238
239         // build the XPath expression to search for
240         char buffer[256];
241         strcpy( buffer, "/root/*/*[@path='" );
242         strcat( buffer, shader );
243         strcat( buffer, "']" );
244
245         char* expression = buffer;
246
247         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
248         xmlNodeSetPtr nodePtr;
249         if ( xpathPtr ) {
250                 nodePtr = xpathPtr->nodesetval;
251         }
252         else{
253                 return false;
254         }
255
256         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
257                 xmlXPathFreeObject( xpathPtr );
258                 return true;
259         }
260         else {
261                 xmlXPathFreeObject( xpathPtr );
262                 return false;
263         }
264 }
265
266 bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){
267         /* Checks whether a tag with content already exists
268
269            char* shader  - the name of the shader or texture (without trailing .tga or something)
270            char* content - the node content (a tag name)
271
272            returns TRUE if the tag with content already exists or FALSE if not
273          */
274
275         // build the XPath expression to search for
276         // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
277
278         char buffer[256];
279         strcpy( buffer, "/root/*/*[@path='" );
280         strcat( buffer, shader );
281         strcat( buffer, "'][child::tag='" );
282         strcat( buffer, content );
283         strcat( buffer, "']" );
284
285         char* expression = buffer;
286
287         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
288         xmlNodeSetPtr nodePtr;
289         if ( xpathPtr ) {
290                 nodePtr = xpathPtr->nodesetval;
291         }
292         else{
293                 return false;
294         }
295
296         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
297                 xmlXPathFreeObject( xpathPtr );
298                 return true;
299         }
300         else {
301                 xmlXPathFreeObject( xpathPtr );
302                 return false;
303         }
304 }
305
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
308
309            char* shader  - the name of the shader or texture (without trailing .tga or something)
310            char* content - the node content (a tag name)
311
312            returns TRUE if the node was added successfully or FALSE when failed
313          */
314
315         // build the XPath expression
316         char buffer[256];
317         char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
318
319         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
320         xmlNodeSetPtr nodePtr;
321         if ( xpathPtr ) {
322                 nodePtr = xpathPtr->nodesetval;
323         }
324         else{
325                 return false;
326         }
327
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 );
333
334                 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children
335                         // add spaces
336                         xmlNodePtr newtext = xmlNewText( (xmlChar*)"  " );
337                         xmlAddChild( nodeParent->children, newtext );
338
339                         // add new node
340                         xmlAddNextSibling( nodeParent->children, newnode );
341
342                         // append a new line + spaces
343                         newtext = xmlNewText( (xmlChar*)"\n    " );
344                         xmlAddNextSibling( nodeParent->children->next, newtext );
345                 }
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 );
350                 }
351                 xmlXPathFreeObject( xpathPtr );
352                 return true;
353         }
354         else {
355                 xmlXPathFreeObject( xpathPtr );
356                 return false;
357         }
358 }
359
360 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
361 int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){
362         /* Replaces tag node contents
363
364            char* oldtag - the <tag></tag> node content that sould be changed
365            char* newtag - the new <tag></tag> node content
366
367            returns the number of renamed shaders
368          */
369
370         int num = 0;
371
372         // build the XPath expression
373         char expression[256];
374         strcpy( expression, "/root/*/*[child::tag='" );
375         strcat( expression, oldtag );
376         strcat( expression, "']/*" );
377
378         xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context );
379         if ( !result ) {
380                 return 0;
381         }
382         xmlNodeSetPtr nodePtr = result->nodesetval;
383
384         for ( int i = 0; i < nodePtr->nodeNr; i++ )
385         {
386                 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
387                 char* content = (char*)xmlNodeGetContent( ptrContent );
388
389                 if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content?
390                         xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() );
391                         num++;
392                 }
393         }
394
395         SaveXmlDoc();
396         xmlXPathFreeObject( result ); // CHANGED
397         return num;
398 }
399
400 bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){
401         /* Deletes a child node of a shader
402
403            char* shader  - the name of the shader or texture (without trailing .tga or something)
404            char* tag     - the tag being deleted
405
406            returns TRUE if the node was deleted successfully or FALSE when failed
407          */
408
409         char buffer[256];
410         char* expression = GetTagsXpathExpression( buffer, shader, TAG );
411         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
412         xmlNodeSetPtr nodePtr;
413         if ( xpathPtr ) {
414                 nodePtr = xpathPtr->nodesetval;
415         }
416         else{
417                 return false;
418         }
419
420         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
421                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
422                 {
423                         xmlNodePtr ptrContent = nodePtr->nodeTab[i];
424                         char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent );
425
426                         if ( strcmp( content, tag ) == 0 ) { // find the node
427                                 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
428                                 // delete the node
429                                 xmlUnlinkNode( ptrContent );
430                                 xmlFreeNode( ptrContent );
431
432                                 // delete leading whitespace node
433                                 xmlUnlinkNode( ptrWhitespace );
434                                 xmlFreeNode( ptrWhitespace );
435                                 xmlXPathFreeObject( xpathPtr );
436                                 return true;
437                         }
438                 }
439         }
440         xmlXPathFreeObject( xpathPtr );
441         return false;
442 }
443
444 bool XmlTagBuilder::DeleteTag( const char* tag ){
445         /* Deletes a tag from all shaders
446
447            char* tag - the tag being deleted from all shaders
448
449            returns TRUE if the tag was deleted successfully or FALSE when failed
450          */
451
452         char expression[256];
453         strcpy( expression, "/root/*/*[child::tag='" );
454         strcat( expression, tag );
455         strcat( expression, "']" );
456
457         std::set<CopiedString> dellist;
458         TagSearch( expression, dellist );
459         std::set<CopiedString>::iterator iter;
460
461         for ( iter = dellist.begin(); iter != dellist.end(); iter++ )
462         {
463                 DeleteShaderTag( iter->c_str(), tag );
464         }
465         SaveXmlDoc();
466
467         return true;
468 }
469
470 void XmlTagBuilder::GetShaderTags( const char* shader, std::vector<CopiedString>& tags ){
471         /* Gets the tags from a shader
472
473            char* shader - the name of the shader
474
475            returns a vector containing the tags
476          */
477
478         char const *expression;
479
480         if ( shader == NULL ) { // get all tags from all shaders
481                 expression = "/root/*/*/tag";
482         }
483         else {
484                 char buffer[256];
485                 expression = GetTagsXpathExpression( buffer, shader, TAG );
486         }
487
488         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
489         xmlNodeSetPtr nodePtr;
490         if ( xpathPtr ) {
491                 nodePtr = xpathPtr->nodesetval;
492         }
493         else{
494                 return;
495         }
496
497         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
498                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
499                 {
500                         tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
501                 }
502         }
503         xmlXPathFreeObject( xpathPtr );
504 }
505
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
508
509            returns a set containing the shaders (with path)
510          */
511
512         char const *expression = "/root/*/*[not(child::tag)]";
513
514         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
515         xmlNodeSetPtr nodePtr;
516         if ( xpathPtr ) {
517                 nodePtr = xpathPtr->nodesetval;
518         }
519         else{
520                 return;
521         }
522
523         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
524                 xmlNodePtr ptr;
525
526                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
527                 {
528                         ptr = nodePtr->nodeTab[i];
529                         shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) );
530                 }
531         }
532
533         xmlXPathFreeObject( xpathPtr );
534 }
535
536 void XmlTagBuilder::GetAllTags( std::set<CopiedString>& tags ){
537         /* Gets a list of all tags that are used (assigned to any shader)
538
539            returns a set containing all used tags
540          */
541
542         char const *expression = "/root/*/*/tag";
543
544         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
545         xmlNodeSetPtr nodePtr;
546         if ( xpathPtr ) {
547                 nodePtr = xpathPtr->nodesetval;
548         }
549         else{
550                 return;
551         }
552
553         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
554                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
555                 {
556                         tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
557                 }
558         }
559
560         xmlXPathFreeObject( xpathPtr );
561 }
562
563 void XmlTagBuilder::TagSearch( const char* expression, std::set<CopiedString>& paths ){
564         /* Searches shaders by tags
565
566            char* expression - the XPath expression to search
567
568            returns a set containing the found shaders
569          */
570
571         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
572         xmlNodeSetPtr nodePtr;
573         if ( xpathPtr ) {
574                 nodePtr = xpathPtr->nodesetval;
575         }
576         else{
577                 return;
578         }
579
580         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
581                 xmlNodePtr ptr;
582                 xmlChar* xmlattrib;
583                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
584                 {
585                         ptr = nodePtr->nodeTab[i];
586                         xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" );
587                         paths.insert( (CopiedString)(char*)xmlattrib );
588                 }
589         }
590         xmlXPathFreeObject( xpathPtr );
591 }