Merge branch '138-toggle-free-look-with-hotkey' into 'main/atys-live'
[ryzomcore.git] / nel / src / misc / o_xml.cpp
blob091d54fbfff92441d0fa51d4fbb6e89af29de207
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program 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 Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
22 #include "nel/misc/o_xml.h"
23 #include "nel/misc/i_xml.h"
25 #ifndef NL_DONT_USE_EXTERNAL_CODE
27 // Include from libxml2
28 #include <libxml/xmlerror.h>
30 #if defined(NL_OS_WINDOWS) && defined(NL_COMP_VC_VERSION) && NL_COMP_VC_VERSION >= 80
31 #define USE_LOCALE_SPRINTF
32 #include <locale.h>
33 #endif
35 using namespace std;
37 #ifdef DEBUG_NEW
38 #define new DEBUG_NEW
39 #endif
41 namespace NLMISC
44 // ***************************************************************************
46 const char SEPARATOR = ' ';
48 // ***************************************************************************
50 #define writenumber(src,format,digits) \
51 char number_as_cstring [digits+1]; \
52 sprintf( number_as_cstring, format, src ); \
53 serialSeparatedBufferOut( number_as_cstring );
55 // ***************************************************************************
56 // XML callbacks
57 // ***************************************************************************
59 int xmlOutputWriteCallbackForNeL ( void *context, const char *buffer, int len)
61 // no need to save empty buffer
62 if(len == 0) return 0;
64 // Get the object
65 COXml *object = (COXml*) context;
67 // Serialise the buffer
68 object->_InternalStream->serialBuffer ((uint8*)buffer, len);
70 // Return the value
71 return len;
74 // ***************************************************************************
76 int xmlOutputCloseCallbackForNeL ( void * /* context */ )
78 // Get the object
79 // COXml *object = (COXml*) context;
81 // Does nothing
82 return 1;
85 // ***************************************************************************
87 xmlDocPtr COXml::getDocument ()
89 if (_Document)
90 return _Document;
92 // Initialise the document
93 _Document = xmlNewDoc ((const xmlChar *)_Version.c_str());
95 return _Document;
99 // ***************************************************************************
101 inline void COXml::flushContentString ()
103 // Current node must exist here
104 nlassert (_CurrentNode);
106 // String size
107 uint size=(uint)_ContentString.length();
109 // Some content to write ?
110 if (size)
112 // Write it in the current node
113 xmlNodePtr textNode = xmlNewText ((const xmlChar *)_ContentString.c_str());
114 xmlAddChild (_CurrentNode, textNode);
116 // Empty the string
117 _ContentString.erase ();
121 // ***************************************************************************
123 COXml::COXml () : IStream (false /* Output mode */)
125 // Set XML mode
126 setXMLMode (true);
128 // Set the stream
129 _InternalStream = NULL;
131 // Set the version
132 _Version = "1.0";
134 // Initialise the document
135 _Document = NULL;
137 // Current node
138 _CurrentNode = NULL;
140 // Content string
141 _ContentString.clear();
143 // Push begin
144 _PushBegin = false;
147 // ***************************************************************************
149 bool COXml::init (IStream *stream, const std::string &version)
151 resetPtrTable();
153 CIXml::initLibXml();
155 // Output stream ?
156 if (!stream->isReading())
158 // Set XML mode
159 setXMLMode (true);
161 // Set the stream
162 _InternalStream = stream;
164 // Set the version
165 _Version = version;
167 // Initialise the document
168 _Document = NULL;
170 // Current node
171 _CurrentNode = NULL;
173 // Content string
174 _ContentString.clear();
176 // Push begin
177 _PushBegin = false;
179 // Ok
180 return true;
182 else
183 return false;
186 // ***************************************************************************
188 COXml::~COXml ()
190 // Flush document to the internal stream
191 flush ();
194 // ***************************************************************************
196 void COXml::serialSeparatedBufferOut( const std::string &value )
198 nlassert( ! isReading() );
200 // Output stream has been setuped ?
201 if ( _InternalStream )
203 // Current node presents ?
204 if (_CurrentNode)
206 // Write a push attribute ?
207 if (_PushBegin)
209 // Current attrib is set ?
210 if (_AttribPresent)
212 // Set the attribute
213 xmlSetProp (_CurrentNode, (const xmlChar*)_AttribName.c_str(), (const xmlChar*)value.c_str());
215 // The attribute has been used
216 _AttribPresent = false;
218 else
220 // * Error, the stream don't use XML streaming properly
221 // * You must take care of this in your last serial call:
222 // * - Between xmlPushBegin() and xmlPushEnd(), before each serial, you must set the attribute name with xmlSetAttrib.
223 // * - Between xmlPushBegin() and xmlPushEnd(), you must serial only basic objects (numbers and strings).
224 nlerror ( "Error, the stream don't use XML streaming properly" );
227 else
229 // Get the content buffer size
230 uint size=(uint)_ContentString.length();
232 // Add a separator
233 if ((size) && (_ContentString[size-1]!='\n'))
234 _ContentString += SEPARATOR;
236 // Concat the strings
237 _ContentString += value;
240 else
242 // * Error, no current node present.
243 // * Check that your serial is initialy made between a xmlPushBegin and xmlPushEnd calls.
244 nlerror ( "Error, the stream don't use XML streaming properly" );
247 else
249 nlerror ( "Output stream has not been setuped" );
253 // ***************************************************************************
255 void COXml::serial(uint8 &b)
257 // Write the number
258 writenumber( (uint16)b,"%hu", 3 );
261 // ***************************************************************************
263 void COXml::serial(sint8 &b)
265 writenumber( (sint16)b, "%hd", 4 );
268 // ***************************************************************************
270 void COXml::serial(uint16 &b)
272 writenumber( b, "%hu", 5 );
275 // ***************************************************************************
277 void COXml::serial(sint16 &b)
279 writenumber( b, "%hd", 6 );
282 // ***************************************************************************
284 void COXml::serial(uint32 &b)
286 writenumber( b, "%u", 10 );
289 // ***************************************************************************
291 void COXml::serial(sint32 &b)
293 writenumber( b, "%d", 11 );
296 // ***************************************************************************
298 void COXml::serial(uint64 &b)
300 writenumber( b, "%" NL_I64 "u", 20 );
303 // ***************************************************************************
305 void COXml::serial(sint64 &b)
307 writenumber( b, "%" NL_I64 "d", 20 );
310 // ***************************************************************************
312 void COXml::serial(float &b)
314 writenumber( (double)b, "%f", 128 );
317 // ***************************************************************************
319 void COXml::serial(double &b)
321 writenumber( b, "%f", 128 );
324 // ***************************************************************************
326 void COXml::serial(bool &b)
328 serialBit(b);
331 // ***************************************************************************
333 void COXml::serialBit(bool &bit)
335 uint8 u = (uint8)bit;
336 serial( u );
339 // ***************************************************************************
341 #ifndef NL_OS_CYGWIN
342 void COXml::serial(char &b)
344 std::string tmp;
345 tmp += b;
346 serialSeparatedBufferOut( tmp );
348 #endif // NL_OS_CYGWIN
350 // ***************************************************************************
352 void COXml::serial(std::string &b)
354 nlassert( ! isReading() );
356 // Attibute ?
357 if (_PushBegin)
359 // Only serial the string
360 serialSeparatedBufferOut( b );
362 else
364 // Open a string node
365 xmlPush ("S");
367 // Serial the string
368 serialSeparatedBufferOut( b );
370 // Close the node
371 xmlPop ();
375 // ***************************************************************************
377 void COXml::serial(ucstring &b)
379 nlassert( ! isReading() );
381 // convert ucstring to utf-8 std::string
382 std::string output = b.toUtf8();
384 // Serial this string
385 serial (output);
388 // ***************************************************************************
390 void COXml::serialBuffer(uint8 *buf, uint len)
392 // Open a node
393 xmlPush ("BUFFER");
395 // Serialize the buffer
396 for (uint i=0; i<len; i++)
398 xmlPush ("ELM");
400 serial (buf[i]);
402 xmlPop ();
405 // Close the node
406 xmlPop ();
409 // ***************************************************************************
411 bool COXml::xmlPushBeginInternal (const std::string &nodeName)
413 nlassert( ! isReading() );
415 // Check _InternalStream
416 if ( _InternalStream )
418 // Can make a xmlPushBegin ?
419 if ( ! _PushBegin )
421 // Current node exist ?
422 if (_CurrentNode==NULL)
424 // No document ?
425 if (_Document == NULL)
427 // Initialise the document
428 _Document = xmlNewDoc ((const xmlChar *)_Version.c_str());
430 // Return NULL if error
431 nlassert (_Document);
434 // Create the first node
435 _CurrentNode=xmlNewDocNode (_Document, NULL, (const xmlChar*)nodeName.c_str(), NULL);
436 xmlDocSetRootElement (_Document, _CurrentNode);
438 // Return NULL if error
439 nlassert (_CurrentNode);
441 else
443 // Flush current content string ?
444 flushContentString ();
446 // Create a new node
447 _CurrentNode=xmlNewChild (_CurrentNode, NULL, (const xmlChar*)nodeName.c_str(), NULL);
449 // Return NULL if error
450 nlassert (_CurrentNode);
453 // Push begun
454 _PushBegin = true;
456 else
458 nlwarning ( "XML: You must close your xmlPushBegin - xmlPushEnd before calling a new xmlPushBegin.");
459 return false;
462 else
464 nlwarning ( "XML: Output stream has not been setuped.");
465 return false;
468 // Ok
469 return true;
472 // ***************************************************************************
474 bool COXml::xmlPushEndInternal ()
476 nlassert( ! isReading() );
478 // Check _InternalStream
479 if ( _InternalStream )
481 // Can make a xmlPushEnd ?
482 if ( _PushBegin )
484 // Push begun
485 _PushBegin = false;
487 else
489 nlwarning ( "XML: You must call xmlPushBegin before calling xmlPushEnd.");
490 return false;
493 else
495 nlwarning ( "XML: Output stream has not been setuped.");
496 return false;
499 // Ok
500 return true;
503 // ***************************************************************************
505 bool COXml::xmlPopInternal ()
507 nlassert( ! isReading() );
509 // Check _InternalStream
510 if ( _InternalStream )
512 // Not in the push mode ?
513 if ( ! _PushBegin )
515 // Some content to write ?
516 flushContentString ();
518 // Get parent
519 _CurrentNode=_CurrentNode->parent;
521 else
523 nlwarning ( "XML: You must call xmlPop after xmlPushEnd.");
524 return false;
527 else
529 nlwarning ( "XML: Output stream has not been setuped.");
530 return false;
533 // Ok
534 return true;
537 // ***************************************************************************
539 bool COXml::xmlSetAttribInternal (const std::string &attribName)
541 nlassert( ! isReading() );
543 // Check _InternalStream
544 if ( _InternalStream )
546 // Can make a xmlPushEnd ?
547 if ( _PushBegin )
549 // Set attribute name
550 _AttribName = attribName;
552 // Attribute name is present
553 _AttribPresent = true;
555 else
557 nlwarning ( "XML: You must call xmlSetAttrib between xmlPushBegin and xmlPushEnd calls.");
558 return false;
561 else
563 nlwarning ( "XML: Output stream has not been setuped.");
564 return false;
567 // Ok
568 return true;
571 // ***************************************************************************
573 bool COXml::xmlBreakLineInternal ()
575 nlassert( ! isReading() );
577 // Check _InternalStream
578 if ( _InternalStream )
580 // Not in the push mode ?
581 if ( ! _PushBegin )
583 // Add a break line
584 _ContentString += '\n';
586 else
588 nlwarning ( "XML: You must call xmlNBreakLine after xmlPushEnd.");
589 return false;
592 else
594 nlwarning ( "XML: Output stream has not been setuped.");
595 return false;
598 // Ok
599 return true;
602 // ***************************************************************************
604 bool COXml::xmlCommentInternal (const std::string &comment)
606 nlassert( ! isReading() );
608 // Check _InternalStream
609 if ( _InternalStream )
611 // Not in the push mode ?
612 if ( _CurrentNode != NULL)
614 // Add a comment node
615 xmlNodePtr commentPtr = xmlNewComment ((const xmlChar *)comment.c_str());
617 // Add the node
618 xmlAddChild (_CurrentNode, commentPtr);
620 else
622 nlwarning ( "XML: You must call xmlCommentInternal between xmlPushBegin and xmlPushEnd.");
623 return false;
626 else
628 nlwarning ( "XML: Output stream has not been setuped.");
629 return false;
631 // Ok
632 return true;
635 // ***************************************************************************
637 void COXml::flush ()
639 if (_Document)
641 // Generate indentation
642 xmlKeepBlanksDefault (0);
644 // Create a output context
645 xmlOutputBufferPtr outputBuffer = xmlOutputBufferCreateIO ( xmlOutputWriteCallbackForNeL, xmlOutputCloseCallbackForNeL, this, NULL );
647 // Save the file
648 int res = xmlSaveFormatFileTo (outputBuffer, _Document, NULL, 1);
650 // No error should be returned because, exception should be raised by the internal stream
651 nlassert (res!=-1);
653 // Free the document
654 xmlFreeDoc (_Document);
655 _Document = NULL;
659 // ***************************************************************************
661 bool COXml::isStringValidForProperties (const std::string &str)
663 return str.find('\n') == std::string::npos;
666 // ***************************************************************************
668 std::string COXml::getErrorString()
670 // error string is managed by CIXml
671 return CIXml::getErrorString();
674 } // NLMISC
676 #endif // NL_DONT_USE_EXTERNAL_CODE