# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

o_xml.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2000, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "stdmisc.h"
00027 
00028 #include "nel/misc/o_xml.h"
00029 
00030 #ifndef NL_DONT_USE_EXTERNAL_CODE
00031 
00032 using namespace std;
00033 
00034 namespace NLMISC 
00035 {
00036 
00037 // ***************************************************************************
00038 
00039 const char SEPARATOR = ' ';
00040 
00041 // ***************************************************************************
00042 
00043 #define writenumber(src,format,digits) \
00044         char number_as_cstring [digits+1]; \
00045         sprintf( number_as_cstring, format, src ); \
00046         serialSeparatedBufferOut( number_as_cstring );
00047 
00048 // ***************************************************************************
00049 
00050 inline void COXml::flushContentString ()
00051 {
00052         // Current node must exist here
00053         nlassert (_CurrentNode);
00054 
00055         // String size
00056         uint size=_ContentString.length();
00057 
00058         // Some content to write ?
00059         if (size)
00060         {
00061                 // Write it in the current node
00062                 xmlNodePtr textNode = xmlNewText ((const xmlChar *)_ContentString.c_str());
00063                 xmlAddChild (_CurrentNode, textNode);
00064 
00065                 // Empty the string
00066                 _ContentString.erase ();
00067         }
00068 }
00069 
00070 // ***************************************************************************
00071 
00072 COXml::COXml () : IStream (false /* Output mode */)
00073 {
00074         // Set XML mode
00075         setXMLMode (true);
00076 
00077         // Set the stream
00078         _InternalStream = NULL;
00079 
00080         // Set the version
00081         _Version = "1.0";
00082 
00083         // Initialise the document
00084         _Document = NULL;
00085 
00086         // Current node
00087         _CurrentNode = NULL;
00088 
00089         // Content string
00090         _ContentString = "";
00091 
00092         // Push begin
00093         _PushBegin = false;
00094 }
00095 
00096 // ***************************************************************************
00097 
00098 bool COXml::init (IStream *stream, const char *version)
00099 {
00100         // Output stream ?
00101         if (!stream->isReading())
00102         {
00103                 // Set XML mode
00104                 setXMLMode (true);
00105 
00106                 // Set the stream
00107                 _InternalStream = stream;
00108 
00109                 // Set the version
00110                 _Version = version;
00111 
00112                 // Initialise the document
00113                 _Document = NULL;
00114 
00115                 // Current node
00116                 _CurrentNode = NULL;
00117 
00118                 // Content string
00119                 _ContentString = "";
00120 
00121                 // Push begin
00122                 _PushBegin = false;
00123 
00124                 // Ok
00125                 return true;
00126         }
00127         else
00128                 return false;
00129 }
00130 
00131 // ***************************************************************************
00132 
00133 COXml::~COXml ()
00134 {
00135         // Flush document to the internal stream
00136         flush ();
00137 }
00138 
00139 // ***************************************************************************
00140 
00141 void COXml::serialSeparatedBufferOut( const char *value )
00142 {
00143         nlassert( ! isReading() );
00144 
00145         // Output stream has been setuped ?
00146         if ( _InternalStream )
00147         {
00148                 // Current node presents ?
00149                 if (_CurrentNode)
00150                 {
00151                         // Write a push attribute ?
00152                         if (_PushBegin)
00153                         {
00154                                 // Current attrib is set ?
00155                                 if (_AttribPresent)
00156                                 {
00157                                         // Set the attribute
00158                                         xmlSetProp (_CurrentNode, (const xmlChar*)_AttribName.c_str(), (const xmlChar*)value);
00159 
00160                                         // The attribute has been used 
00161                                         _AttribPresent = false;
00162                                 }
00163                                 else
00164                                 {
00165                                         // * Error, the stream don't use XML streaming properly
00166                                         // * You must take care of this in your last serial call:
00167                                         // * - Between xmlPushBegin() and xmlPushEnd(), before each serial, you must set the attribute name with xmlSetAttrib.
00168                                         // * - Between xmlPushBegin() and xmlPushEnd(), you must serial only basic objects (numbers and strings).
00169                                         nlerror ( "Error, the stream don't use XML streaming properly" );
00170                                 }
00171                         }
00172                         else
00173                         {
00174                                 // Get the content buffer size
00175                                 uint size=_ContentString.length();
00176 
00177                                 // Add a separator
00178                                 if ((size) && (_ContentString[size-1]!='\n'))
00179                                         _ContentString += SEPARATOR;
00180 
00181                                 // Concat the strings
00182                                 _ContentString += value;
00183                         }
00184                 }
00185                 else
00186                 {
00187                         // * Error, no current node present.
00188                         // * Check that your serial is initialy made between a xmlPushBegin and xmlPushEnd calls.
00189                         nlerror ( "Error, the stream don't use XML streaming properly" );
00190                 }
00191         }
00192         else
00193         {
00194                 nlerror ( "Output stream has not been setuped" );
00195         }
00196 }
00197 
00198 // ***************************************************************************
00199 
00200 void COXml::serial(uint8 &b)
00201 {
00202         // Write the number
00203         writenumber( (uint16)b,"%hu", 3 );
00204 }
00205 
00206 // ***************************************************************************
00207 
00208 void COXml::serial(sint8 &b)
00209 {
00210         writenumber( (sint16)b, "%hd", 4 );
00211 }
00212 
00213 // ***************************************************************************
00214 
00215 void COXml::serial(uint16 &b)
00216 {
00217         writenumber( b, "%hu", 5 );
00218 }
00219 
00220 // ***************************************************************************
00221 
00222 void COXml::serial(sint16 &b)
00223 {
00224         writenumber( b, "%hd", 6 );
00225 }
00226 
00227 // ***************************************************************************
00228 
00229 void COXml::serial(uint32 &b)
00230 {
00231         writenumber( b, "%u", 10 );
00232 }
00233 
00234 // ***************************************************************************
00235 
00236 void COXml::serial(sint32 &b) 
00237 {
00238         writenumber( b, "%d", 11 );
00239 }
00240 
00241 // ***************************************************************************
00242 
00243 void COXml::serial(uint64 &b) 
00244 {
00245         writenumber( b, "%"NL_I64"u", 20 );
00246 }
00247 
00248 // ***************************************************************************
00249 
00250 void COXml::serial(sint64 &b) 
00251 {
00252         writenumber( b, "%"NL_I64"d", 20 );
00253 }
00254 
00255 // ***************************************************************************
00256 
00257 void COXml::serial(float &b) 
00258 {
00259         writenumber( (double)b, "%f", 128 );
00260 }
00261 
00262 // ***************************************************************************
00263 
00264 void COXml::serial(double &b) 
00265 {
00266         writenumber( b, "%f", 128 );
00267 }
00268 
00269 // ***************************************************************************
00270 
00271 void COXml::serial(bool &b)
00272 {
00273         serialBit(b);
00274 }
00275 
00276 // ***************************************************************************
00277 
00278 void COXml::serialBit(bool &bit)
00279 {
00280         uint8 u = (uint8)bit;
00281         serial( u );
00282 }
00283 
00284 // ***************************************************************************
00285 
00286 #ifndef NL_OS_CYGWIN
00287 void COXml::serial(char &b) 
00288 {
00289         char tmp[2] = {b , 0};
00290         serialSeparatedBufferOut( tmp );
00291 }
00292 #endif // NL_OS_CYGWIN
00293 
00294 // ***************************************************************************
00295 
00296 void COXml::serial(std::string &b)
00297 {
00298         nlassert( ! isReading() );
00299 
00300         // Attibute ?
00301         if (_PushBegin)
00302         {
00303                 // Only serial the string
00304                 serialSeparatedBufferOut( b.c_str() );
00305         }
00306         else
00307         {
00308                 // Open a string node
00309                 xmlPush ("S");
00310 
00311                 // Serial the string
00312                 serialSeparatedBufferOut( b.c_str() );
00313 
00314                 // Close the node
00315                 xmlPop ();
00316         }
00317 }
00318 
00319 // ***************************************************************************
00320 
00321 void COXml::serial(ucstring &b)
00322 {
00323         nlassert( ! isReading() );
00324 
00325         // Iniput size
00326         uint size=b.length();
00327 
00328         // Output string
00329         string output;
00330         output.resize (b.length());
00331 
00332         // For each character
00333         for (uint i=0; i<size; i++)
00334         {
00335                 // 7 bits code ?
00336                 if (b[i]<0x7F)
00337                 {
00338                         output[i]=(char)b[i];
00339                 }
00340                 else
00341                 {
00343                         nlwarning ("handle ucstring to utf-8");
00344                         output[i]=(b[i]&0xff);
00345                 }
00346         }
00347 
00348         // Serial this string
00349         serial (output);
00350 }
00351 
00352 // ***************************************************************************
00353 
00354 void COXml::serialBuffer(uint8 *buf, uint len)
00355 {
00356         // Open a node
00357         xmlPush ("BUFFER");
00358 
00359         // Serialize the buffer
00360         for (uint i=0; i<len; i++)
00361         {
00362                 xmlPush ("ELM");
00363 
00364                 serial (buf[i]);
00365 
00366                 xmlPop ();
00367         }
00368 
00369         // Close the node
00370         xmlPop ();
00371 }
00372 
00373 // ***************************************************************************
00374 
00375 bool COXml::xmlPushBeginInternal (const char *nodeName)
00376 {
00377         nlassert( ! isReading() );
00378 
00379         // Check _InternalStream
00380         if ( _InternalStream )
00381         {
00382                 // Can make a xmlPushBegin ?
00383                 if ( ! _PushBegin )
00384                 {
00385                         // Current node exist ?
00386                         if (_CurrentNode==NULL)
00387                         {
00388                                 // No document ?
00389                                 if (_Document == NULL)
00390                                 {
00391                                         // Initialise the document
00392                                         _Document = xmlNewDoc ((const xmlChar *)_Version.c_str());
00393 
00394                                         // Return NULL if error
00395                                         nlassert (_Document);
00396                                 }
00397 
00398                                 // Create the first node
00399                                 _CurrentNode=xmlNewDocNode (_Document, NULL, (const xmlChar*)nodeName, NULL);
00400                                 xmlDocSetRootElement (_Document, _CurrentNode);
00401 
00402                                 // Return NULL if error
00403                                 nlassert (_CurrentNode);
00404                         }
00405                         else
00406                         {
00407                                 // Flush current content string ?
00408                                 flushContentString ();
00409 
00410                                 // Create a new node
00411                                 _CurrentNode=xmlNewChild (_CurrentNode, NULL, (const xmlChar*)nodeName, NULL);
00412 
00413                                 // Return NULL if error
00414                                 nlassert (_CurrentNode);
00415                         }
00416 
00417                         // Push begun
00418                         _PushBegin = true;
00419                 }
00420                 else
00421                 {
00422                         nlwarning ( "You must close your xmlPushBegin - xmlPushEnd before calling a new xmlPushBegin.");
00423                         return false;
00424                 }
00425         }
00426         else
00427         {
00428                 nlwarning ( "Output stream has not been setuped.");
00429                 return false;
00430         }
00431 
00432         // Ok
00433         return true;
00434 }
00435 
00436 // ***************************************************************************
00437 
00438 bool COXml::xmlPushEndInternal ()
00439 {
00440         nlassert( ! isReading() );
00441 
00442         // Check _InternalStream
00443         if ( _InternalStream )
00444         {
00445                 // Can make a xmlPushEnd ?
00446                 if ( _PushBegin )
00447                 {
00448                         // Push begun
00449                         _PushBegin = false;
00450                 }
00451                 else
00452                 {
00453                         nlwarning ( "You must call xmlPushBegin before calling xmlPushEnd.");
00454                         return false;
00455                 }
00456         }
00457         else
00458         {
00459                 nlwarning ( "Output stream has not been setuped.");
00460                 return false;
00461         }
00462 
00463         // Ok
00464         return true;
00465 }
00466 
00467 // ***************************************************************************
00468 
00469 bool COXml::xmlPopInternal ()
00470 {
00471         nlassert( ! isReading() );
00472 
00473         // Check _InternalStream
00474         if ( _InternalStream )
00475         {
00476                 // Not in the push mode ?
00477                 if ( ! _PushBegin )
00478                 {
00479                         // Some content to write ?
00480                         flushContentString ();
00481 
00482                         // Get parent
00483                         _CurrentNode=_CurrentNode->parent;
00484                 }
00485                 else
00486                 {
00487                         nlwarning ( "You must call xmlPop after xmlPushEnd.");
00488                         return false;
00489                 }
00490         }
00491         else
00492         {
00493                 nlwarning ( "Output stream has not been setuped.");
00494                 return false;
00495         }
00496 
00497         // Ok
00498         return true;
00499 }
00500 
00501 // ***************************************************************************
00502 
00503 bool COXml::xmlSetAttribInternal (const char *attribName)
00504 {
00505         nlassert( ! isReading() );
00506 
00507         // Check _InternalStream
00508         if ( _InternalStream )
00509         {
00510                 // Can make a xmlPushEnd ?
00511                 if ( _PushBegin )
00512                 {
00513                         // Set attribute name
00514                         _AttribName = attribName;
00515 
00516                         // Attribute name is present
00517                         _AttribPresent = true;
00518                 }
00519                 else
00520                 {
00521                         nlwarning ( "You must call xmlSetAttrib between xmlPushBegin and xmlPushEnd calls.");
00522                         return false;
00523                 }
00524         }
00525         else
00526         {
00527                 nlwarning ( "Output stream has not been setuped.");
00528                 return false;
00529         }
00530 
00531         // Ok
00532         return true;
00533 }
00534 
00535 // ***************************************************************************
00536 
00537 bool COXml::xmlBreakLineInternal ()
00538 {
00539         nlassert( ! isReading() );
00540 
00541         // Check _InternalStream
00542         if ( _InternalStream )
00543         {
00544                 // Not in the push mode ?
00545                 if ( ! _PushBegin )
00546                 {
00547                         // Add a break line
00548                         _ContentString += '\n';
00549                 }
00550                 else
00551                 {
00552                         nlwarning ( "You must call xmlNBreakLine after xmlPushEnd.");
00553                         return false;
00554                 }
00555         }
00556         else
00557         {
00558                 nlwarning ( "Output stream has not been setuped.");
00559                 return false;
00560         }
00561 
00562         // Ok
00563         return true;
00564 }
00565 
00566 // ***************************************************************************
00567 
00568 bool COXml::xmlCommentInternal (const char *comment)
00569 {
00570         nlassert( ! isReading() );
00571 
00572         // Check _InternalStream
00573         if ( _InternalStream )
00574         {
00575                 // Not in the push mode ?
00576                 if ( _CurrentNode != NULL)
00577                 {
00578                         // Add a comment node
00579                         xmlNodePtr commentPtr = xmlNewComment ((const xmlChar *)comment);
00580 
00581                         // Add the node
00582                         xmlAddChild (_CurrentNode, commentPtr);
00583                 }
00584                 else
00585                 {
00586                         nlwarning ( "You must call xmlCommentInternal between xmlPushBegin and xmlPushEnd.");
00587                         return false;
00588                 }
00589         }
00590         else
00591         {
00592                 nlwarning ( "Output stream has not been setuped.");
00593                 return false;
00594         }
00595         // Ok
00596         return true;
00597 }
00598 
00599 // ***************************************************************************
00600 
00601 void COXml::flush ()
00602 {
00603         if (_Document)
00604         {
00605                 // Generate indentation
00606                 xmlKeepBlanksDefault (0);
00607 
00608                 // Create a output context
00609                 xmlOutputBufferPtr outputBuffer = xmlOutputBufferCreateIO  ( xmlOutputWriteCallbackForNeL, xmlOutputCloseCallbackForNeL, this, NULL );
00610 
00611                 // Save the file
00612                 int res = xmlSaveFormatFileTo (outputBuffer, _Document, NULL, 1);
00613 
00614                 // No error should be returned because, exception should be raised by the internal stream
00615                 nlassert (res!=-1);
00616 
00617                 // Free the document
00618                 xmlFreeDoc (_Document);
00619                 _Document = NULL;
00620         }
00621 }
00622 
00623 // ***************************************************************************
00624 
00625 // XML callbacks
00626 
00627 // ***************************************************************************
00628 
00629 int xmlOutputWriteCallbackForNeL ( void *context, const char *buffer, int len)
00630 {
00631         // Get the object
00632         COXml *object = (COXml*) context;
00633 
00634         // Serialise the buffer
00635         object->_InternalStream->serialBuffer ((uint8*)buffer, len);
00636 
00637         // Return the value
00638         return len;
00639 }
00640 
00641 // ***************************************************************************
00642 
00643 int xmlOutputCloseCallbackForNeL ( void *context )
00644 {
00645         // Get the object
00646         // COXml *object = (COXml*) context;
00647 
00648         // Does nothing
00649         return 1;
00650 }
00651 
00652 // ***************************************************************************
00653 
00654 xmlDocPtr COXml::getDocument ()
00655 {
00656         if (_Document)
00657                 return _Document;
00658 
00659         // Initialise the document
00660         _Document = xmlNewDoc ((const xmlChar *)_Version.c_str());
00661 
00662         return _Document;
00663 }
00664 
00665 // ***************************************************************************
00666 
00667 bool COXml::isStringValidForProperties (const char *str)
00668 {
00669         while (*str)
00670         {
00671                 if (*str == '\n')
00672                         return false;
00673                 str++;
00674         }
00675         return true;
00676 }
00677 
00678 // ***************************************************************************
00679 
00680 } // NLMISC
00681 
00682 #endif // NL_DONT_USE_EXTERNAL_CODE