1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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
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 // ***************************************************************************
57 // ***************************************************************************
59 int xmlOutputWriteCallbackForNeL ( void *context
, const char *buffer
, int len
)
61 // no need to save empty buffer
62 if(len
== 0) return 0;
65 COXml
*object
= (COXml
*) context
;
67 // Serialise the buffer
68 object
->_InternalStream
->serialBuffer ((uint8
*)buffer
, len
);
74 // ***************************************************************************
76 int xmlOutputCloseCallbackForNeL ( void * /* context */ )
79 // COXml *object = (COXml*) context;
85 // ***************************************************************************
87 xmlDocPtr
COXml::getDocument ()
92 // Initialise the document
93 _Document
= xmlNewDoc ((const xmlChar
*)_Version
.c_str());
99 // ***************************************************************************
101 inline void COXml::flushContentString ()
103 // Current node must exist here
104 nlassert (_CurrentNode
);
107 uint size
=(uint
)_ContentString
.length();
109 // Some content to write ?
112 // Write it in the current node
113 xmlNodePtr textNode
= xmlNewText ((const xmlChar
*)_ContentString
.c_str());
114 xmlAddChild (_CurrentNode
, textNode
);
117 _ContentString
.erase ();
121 // ***************************************************************************
123 COXml::COXml () : IStream (false /* Output mode */)
129 _InternalStream
= NULL
;
134 // Initialise the document
141 _ContentString
.clear();
147 // ***************************************************************************
149 bool COXml::init (IStream
*stream
, const std::string
&version
)
156 if (!stream
->isReading())
162 _InternalStream
= stream
;
167 // Initialise the document
174 _ContentString
.clear();
186 // ***************************************************************************
190 // Flush document to the internal stream
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 ?
206 // Write a push attribute ?
209 // Current attrib is set ?
213 xmlSetProp (_CurrentNode
, (const xmlChar
*)_AttribName
.c_str(), (const xmlChar
*)value
.c_str());
215 // The attribute has been used
216 _AttribPresent
= false;
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" );
229 // Get the content buffer size
230 uint size
=(uint
)_ContentString
.length();
233 if ((size
) && (_ContentString
[size
-1]!='\n'))
234 _ContentString
+= SEPARATOR
;
236 // Concat the strings
237 _ContentString
+= value
;
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" );
249 nlerror ( "Output stream has not been setuped" );
253 // ***************************************************************************
255 void COXml::serial(uint8
&b
)
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
)
331 // ***************************************************************************
333 void COXml::serialBit(bool &bit
)
335 uint8 u
= (uint8
)bit
;
339 // ***************************************************************************
342 void COXml::serial(char &b
)
346 serialSeparatedBufferOut( tmp
);
348 #endif // NL_OS_CYGWIN
350 // ***************************************************************************
352 void COXml::serial(std::string
&b
)
354 nlassert( ! isReading() );
359 // Only serial the string
360 serialSeparatedBufferOut( b
);
364 // Open a string node
368 serialSeparatedBufferOut( b
);
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
388 // ***************************************************************************
390 void COXml::serialBuffer(uint8
*buf
, uint len
)
395 // Serialize the buffer
396 for (uint i
=0; i
<len
; i
++)
409 // ***************************************************************************
411 bool COXml::xmlPushBeginInternal (const std::string
&nodeName
)
413 nlassert( ! isReading() );
415 // Check _InternalStream
416 if ( _InternalStream
)
418 // Can make a xmlPushBegin ?
421 // Current node exist ?
422 if (_CurrentNode
==NULL
)
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
);
443 // Flush current content string ?
444 flushContentString ();
447 _CurrentNode
=xmlNewChild (_CurrentNode
, NULL
, (const xmlChar
*)nodeName
.c_str(), NULL
);
449 // Return NULL if error
450 nlassert (_CurrentNode
);
458 nlwarning ( "XML: You must close your xmlPushBegin - xmlPushEnd before calling a new xmlPushBegin.");
464 nlwarning ( "XML: Output stream has not been setuped.");
472 // ***************************************************************************
474 bool COXml::xmlPushEndInternal ()
476 nlassert( ! isReading() );
478 // Check _InternalStream
479 if ( _InternalStream
)
481 // Can make a xmlPushEnd ?
489 nlwarning ( "XML: You must call xmlPushBegin before calling xmlPushEnd.");
495 nlwarning ( "XML: Output stream has not been setuped.");
503 // ***************************************************************************
505 bool COXml::xmlPopInternal ()
507 nlassert( ! isReading() );
509 // Check _InternalStream
510 if ( _InternalStream
)
512 // Not in the push mode ?
515 // Some content to write ?
516 flushContentString ();
519 _CurrentNode
=_CurrentNode
->parent
;
523 nlwarning ( "XML: You must call xmlPop after xmlPushEnd.");
529 nlwarning ( "XML: Output stream has not been setuped.");
537 // ***************************************************************************
539 bool COXml::xmlSetAttribInternal (const std::string
&attribName
)
541 nlassert( ! isReading() );
543 // Check _InternalStream
544 if ( _InternalStream
)
546 // Can make a xmlPushEnd ?
549 // Set attribute name
550 _AttribName
= attribName
;
552 // Attribute name is present
553 _AttribPresent
= true;
557 nlwarning ( "XML: You must call xmlSetAttrib between xmlPushBegin and xmlPushEnd calls.");
563 nlwarning ( "XML: Output stream has not been setuped.");
571 // ***************************************************************************
573 bool COXml::xmlBreakLineInternal ()
575 nlassert( ! isReading() );
577 // Check _InternalStream
578 if ( _InternalStream
)
580 // Not in the push mode ?
584 _ContentString
+= '\n';
588 nlwarning ( "XML: You must call xmlNBreakLine after xmlPushEnd.");
594 nlwarning ( "XML: Output stream has not been setuped.");
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());
618 xmlAddChild (_CurrentNode
, commentPtr
);
622 nlwarning ( "XML: You must call xmlCommentInternal between xmlPushBegin and xmlPushEnd.");
628 nlwarning ( "XML: Output stream has not been setuped.");
635 // ***************************************************************************
641 // Generate indentation
642 xmlKeepBlanksDefault (0);
644 // Create a output context
645 xmlOutputBufferPtr outputBuffer
= xmlOutputBufferCreateIO ( xmlOutputWriteCallbackForNeL
, xmlOutputCloseCallbackForNeL
, this, NULL
);
648 int res
= xmlSaveFormatFileTo (outputBuffer
, _Document
, NULL
, 1);
650 // No error should be returned because, exception should be raised by the internal stream
654 xmlFreeDoc (_Document
);
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();
676 #endif // NL_DONT_USE_EXTERNAL_CODE