00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "stdmisc.h"
00027
00028 #include "nel/misc/i_xml.h"
00029
00030 #ifndef NL_DONT_USE_EXTERNAL_CODE
00031
00032
00033 #include <libxml/xmlerror.h>
00034
00035 using namespace std;
00036
00037 #define NLMISC_READ_BUFFER_SIZE 1024
00038
00039 namespace NLMISC
00040 {
00041
00042
00043
00044 const char SEPARATOR = ' ';
00045
00046
00047
00048 #define readnumber(dest,thetype,digits,convfunc) \
00049 string number_as_string; \
00050 serialSeparatedBufferIn( number_as_string ); \
00051 dest = (thetype)convfunc( number_as_string.c_str() );
00052
00053
00054
00055 inline void CIXml::flushContentString ()
00056 {
00057
00058 _ContentString.erase ();
00059
00060
00061 _ContentStringIndex = 0;
00062 }
00063
00064
00065
00066 CIXml::CIXml () : IStream (true )
00067 {
00068
00069 _Parser = NULL;
00070 _CurrentElement = NULL;
00071 _CurrentNode = NULL;
00072 _PushBegin = false;
00073 _AttribPresent = false;
00074 _ErrorString = "";
00075 }
00076
00077
00078
00079 CIXml::~CIXml ()
00080 {
00081
00082 release ();
00083 }
00084
00085
00086
00087 void CIXml::release ()
00088 {
00089
00090 if (_Parser)
00091 {
00092
00093 xmlFreeDoc (_Parser->myDoc);
00094 xmlFreeParserCtxt (_Parser);
00095
00096 _Parser = NULL;
00097 }
00098
00099
00100 _Parser = NULL;
00101 _CurrentElement = NULL;
00102 _CurrentNode = NULL;
00103 _PushBegin = false;
00104 _AttribPresent = false;
00105 _ErrorString = "";
00106 }
00107
00108
00109
00110 void xmlGenericErrorFuncForNeL (void *ctx, const char *msg, ...)
00111 {
00112
00113 string str;
00114 NLMISC_CONVERT_VARGS (str, msg, NLMISC::MaxCStringSize);
00115 ((CIXml*)ctx)->_ErrorString += str;
00116 }
00117
00118
00119
00120 bool CIXml::init (IStream &stream)
00121 {
00122
00123 release ();
00124
00125
00126 if (stream.isReading())
00127 {
00128
00129 setXMLMode (true);
00130
00131
00132 sint32 pos = stream.getPos ();
00133
00134
00135 bool seekGood = stream.seek (0, end);
00136 nlassert (seekGood);
00137
00138
00139 sint32 length = stream.getPos () - pos;
00140
00141
00142 stream.seek (pos, begin);
00143
00144
00145 char buffer[NLMISC_READ_BUFFER_SIZE];
00146
00147
00148 stream.serialBuffer ((uint8*)buffer, 4);
00149 length -= 4;
00150
00151
00152 xmlSetGenericErrorFunc (this, xmlGenericErrorFuncForNeL);
00153
00154
00155 xmlLineNumbersDefault(1);
00156
00157
00158 _Parser = xmlCreatePushParserCtxt(NULL, NULL, buffer, 4, NULL);
00159 nlassert (_Parser);
00160
00161
00162 while (length>=NLMISC_READ_BUFFER_SIZE)
00163 {
00164
00165 stream.serialBuffer ((uint8*)buffer, NLMISC_READ_BUFFER_SIZE);
00166
00167
00168 int res = xmlParseChunk(_Parser, buffer, NLMISC_READ_BUFFER_SIZE, 0);
00169
00170
00171 if (res)
00172 {
00173 throw EXmlParsingError (_ErrorString);
00174 }
00175
00176
00177 length -= NLMISC_READ_BUFFER_SIZE;
00178 }
00179
00180
00181 stream.serialBuffer ((uint8*)buffer, length);
00182
00183
00184 int res = xmlParseChunk(_Parser, buffer, length, 1);
00185
00186
00187 if (res)
00188 {
00189 throw EXmlParsingError (_ErrorString);
00190 }
00191
00192
00193 return true;
00194 }
00195 else
00196 {
00197 nlwarning ("The stream is not an input stream.");
00198 }
00199
00200
00201 return false;
00202 }
00203
00204
00205
00206 void CIXml::serialSeparatedBufferIn ( string &value, bool checkSeparator )
00207 {
00208 nlassert( isReading() );
00209
00210
00211 if ( _Parser )
00212 {
00213
00214 if (_CurrentElement)
00215 {
00216
00217 if (_PushBegin)
00218 {
00219
00220 if (_AttribPresent)
00221 {
00222
00223 nlassert (_CurrentElement);
00224
00225
00226 xmlChar *attributeValue = xmlGetProp (_CurrentElement, (const xmlChar*)_AttribName.c_str());
00227
00228
00229 if (attributeValue)
00230 {
00231
00232 value = (const char*)attributeValue;
00233
00234
00235 xmlFree ((void*)attributeValue);
00236 }
00237 else
00238 {
00239
00240 nlassert (_CurrentElement->name);
00241
00242
00243 char tmp[512];
00244 smprintf (tmp, 512, "NeL XML Syntax error in block line %d\nAttribute \"%s\" is missing in node \"%s\"",
00245 (int)_CurrentElement->content, _AttribName.c_str(), _CurrentElement->name);
00246 throw EXmlParsingError (tmp);
00247 }
00248
00249
00250 _AttribPresent = false;
00251 }
00252 else
00253 {
00254
00255
00256
00257
00258 nlerror ( "Error, the stream don't use XML streaming properly" );
00259 }
00260 }
00261 else
00262 {
00263
00264 uint length = _ContentString.length();
00265
00266
00267 if (length==0)
00268 {
00269
00270 do
00271 {
00272
00273 if (_CurrentNode == NULL)
00274 {
00275 value = "";
00276 _ContentStringIndex = 0;
00277 _ContentString.erase ();
00278 return;
00279 }
00280
00281
00282 if (_CurrentNode->type == XML_TEXT_NODE)
00283 {
00284
00285 break;
00286 }
00287 else
00288
00289 _CurrentNode = _CurrentNode->next;
00290 }
00291 while (_CurrentNode);
00292
00293
00294 if (_CurrentNode != NULL)
00295 {
00296
00297 const char *content = (const char*)xmlNodeGetContent (_CurrentNode);
00298 if (content)
00299 {
00300 _ContentString = content;
00301
00302
00303 xmlFree ((void*)content);
00304 }
00305 else
00306 _ContentString.erase ();
00307
00308
00309 _ContentStringIndex = 0;
00310
00311
00312 length = _ContentString.length();
00313 }
00314 }
00315
00316
00317 if (_ContentStringIndex < length)
00318 {
00319
00320 uint first = _ContentStringIndex;
00321
00322
00323 if (checkSeparator)
00324 {
00325
00326 while (_ContentStringIndex < length)
00327 {
00328
00329 if ( (_ContentString[_ContentStringIndex]==SEPARATOR) || (_ContentString[_ContentStringIndex]=='\n') )
00330 {
00331 _ContentStringIndex++;
00332 break;
00333 }
00334
00335
00336 _ContentStringIndex++;
00337 }
00338 }
00339 else
00340 {
00341
00342 _ContentStringIndex = length;
00343 }
00344
00345
00346 value.assign (_ContentString, first, _ContentStringIndex-first);
00347 }
00348 else
00349 {
00350
00351 nlassert (_CurrentElement->name);
00352
00353
00354 char tmp[512];
00355 smprintf (tmp, 512, "NeL XML Syntax error in block line %d \nMissing keywords in text child node in the node %s",
00356 (int)_CurrentElement->content, _CurrentElement->name);
00357 throw EXmlParsingError (tmp);
00358 }
00359 }
00360 }
00361 else
00362 {
00363
00364
00365 nlerror ( "Error, the stream don't use XML streaming properly" );
00366 }
00367 }
00368 else
00369 {
00370 nlerror ( "Output stream has not been initialized" );
00371 }
00372 }
00373
00374
00375
00376 void CIXml::serial(uint8 &b)
00377 {
00378
00379 readnumber( b, uint8, 3, atoi );
00380 }
00381
00382
00383
00384 void CIXml::serial(sint8 &b)
00385 {
00386 readnumber( b, sint8, 4, atoi );
00387 }
00388
00389
00390
00391 void CIXml::serial(uint16 &b)
00392 {
00393 readnumber( b, uint16, 5, atoi );
00394 }
00395
00396
00397
00398 void CIXml::serial(sint16 &b)
00399 {
00400 readnumber( b, sint16, 6, atoi );
00401 }
00402
00403
00404
00405 inline uint32 atoui( const char *ident)
00406 {
00407 return (uint32) strtoul (ident, NULL, 10);
00408 }
00409
00410 void CIXml::serial(uint32 &b)
00411 {
00412 readnumber( b, uint32, 10, atoui );
00413 }
00414
00415
00416
00417 void CIXml::serial(sint32 &b)
00418 {
00419 readnumber( b, sint32, 11, atoi );
00420 }
00421
00422
00423
00424 void CIXml::serial(uint64 &b)
00425 {
00426 readnumber( b, uint64, 20, atoiInt64 );
00427 }
00428
00429
00430
00431 void CIXml::serial(sint64 &b)
00432 {
00433 readnumber( b, sint64, 20, atoiInt64 );
00434 }
00435
00436
00437
00438 void CIXml::serial(float &b)
00439 {
00440 readnumber( b, float, 128, atof );
00441 }
00442
00443
00444
00445 void CIXml::serial(double &b)
00446 {
00447 readnumber( b, double, 128, atof );
00448 }
00449
00450
00451
00452 void CIXml::serial(bool &b)
00453 {
00454 serialBit(b);
00455 }
00456
00457
00458
00459 void CIXml::serialBit(bool &bit)
00460 {
00461 uint8 u = (uint8)bit;
00462 serial( u );
00463 }
00464
00465
00466
00467 #ifndef NL_OS_CYGWIN
00468 void CIXml::serial(char &b)
00469 {
00470 string toto;
00471 serialSeparatedBufferIn ( toto );
00472
00473
00474 if (toto.length()!=1)
00475 {
00476
00477 if (_Parser)
00478 {
00479
00480 nlassert (_CurrentElement->name);
00481
00482
00483 char tmp[512];
00484 smprintf (tmp, 512, "NeL XML Syntax error in block line %d \nValue is not a char in the node named %s",
00485 (int)_CurrentElement->content, _CurrentElement->name);
00486 throw EXmlParsingError (tmp);
00487 }
00488 else
00489 {
00490 nlerror ( "Output stream has not been initialized" );
00491 }
00492 }
00493 else
00494 b=toto[0];
00495 }
00496 #endif // NL_OS_CYGWIN
00497
00498
00499
00500 void CIXml::serial(std::string &b)
00501 {
00502 nlassert( isReading() );
00503
00504
00505 if (_PushBegin)
00506 {
00507
00508 serialSeparatedBufferIn ( b, false );
00509 }
00510 else
00511 {
00512
00513 xmlPush ("S");
00514
00515
00516 serialSeparatedBufferIn ( b, false );
00517
00518
00519 xmlPop ();
00520 }
00521 }
00522
00523
00524
00525 void CIXml::serial(ucstring &b)
00526 {
00527 nlassert( isReading() );
00528
00530
00531
00532 string tmp;
00533
00534
00535 serial (tmp);
00536
00537
00538 b = tmp;
00539 }
00540
00541
00542
00543 void CIXml::serialBuffer(uint8 *buf, uint len)
00544 {
00545
00546 xmlPush ("BUFFER");
00547
00548
00549 for (uint i=0; i<len; i++)
00550 {
00551 xmlPush ("ELM");
00552
00553 serial (buf[i]);
00554
00555 xmlPop ();
00556 }
00557
00558
00559 xmlPop ();
00560 }
00561
00562
00563
00564 bool CIXml::xmlPushBeginInternal (const char *nodeName)
00565 {
00566 nlassert( isReading() );
00567
00568
00569 if ( _Parser )
00570 {
00571
00572 if ( ! _PushBegin )
00573 {
00574
00575 if (_CurrentNode==NULL)
00576 {
00577
00578 _CurrentNode = xmlDocGetRootElement (_Parser->myDoc);
00579
00580
00581 if (_CurrentNode)
00582 {
00583
00584 nlassert (_CurrentNode->name);
00585
00586
00587 if ( (_CurrentNode->type != XML_ELEMENT_NODE) || ( (const char*)_CurrentNode->name != string(nodeName)) )
00588 {
00589
00590 char tmp[512];
00591 smprintf (tmp, 512, "NeL XML Syntax error : root node has the wrong name : \"%s\" should have \"%s\"",
00592 _CurrentNode->name, nodeName);
00593 throw EXmlParsingError (tmp);
00594 }
00595 }
00596 else
00597 {
00598
00599 char tmp[512];
00600 smprintf (tmp, 512, "NeL XML Syntax error : no root node found.");
00601 throw EXmlParsingError (tmp);
00602 }
00603 }
00604
00605
00606 do
00607 {
00608
00609 nlassert (_CurrentNode->name);
00610
00611
00612 if ( (_CurrentNode->type == XML_ELEMENT_NODE) && ( (const char*)_CurrentNode->name == string(nodeName)) )
00613 {
00614
00615 _CurrentElement = _CurrentNode;
00616
00617
00618 break;
00619 }
00620 else
00621
00622 _CurrentNode = _CurrentNode->next;
00623 }
00624 while (_CurrentNode);
00625
00626
00627 if (_CurrentNode == NULL)
00628 {
00629
00630 char tmp[512];
00631 smprintf (tmp, 512, "NeL XML Syntax error in block line %d \nCan't open the node named %s in node named %s",
00632 (int)_CurrentElement->content, nodeName, _CurrentElement->name);
00633 throw EXmlParsingError (tmp);
00634 }
00635
00636
00637 _CurrentNode = _CurrentNode->children;
00638
00639
00640 _PushBegin = true;
00641
00642
00643 flushContentString ();
00644 }
00645 else
00646 {
00647 nlerror ( "You must close your xmlPushBegin - xmlPushEnd before calling a new xmlPushBegin.");
00648 return false;
00649 }
00650 }
00651 else
00652 {
00653 nlerror ( "Output stream has not been initialized.");
00654 return false;
00655 }
00656
00657
00658 return true;
00659 }
00660
00661
00662
00663 bool CIXml::xmlPushEndInternal ()
00664 {
00665 nlassert( isReading() );
00666
00667
00668 if ( _Parser )
00669 {
00670
00671 if ( _PushBegin )
00672 {
00673
00674 _PushBegin = false;
00675 }
00676 else
00677 {
00678 nlerror ( "You must call xmlPushBegin before calling xmlPushEnd.");
00679 return false;
00680 }
00681 }
00682 else
00683 {
00684 nlerror ( "Output stream has not been initialized.");
00685 return false;
00686 }
00687
00688
00689 return true;
00690 }
00691
00692
00693
00694 bool CIXml::xmlPopInternal ()
00695 {
00696 nlassert( isReading() );
00697
00698
00699 if ( _Parser )
00700 {
00701
00702 if ( ! _PushBegin )
00703 {
00704
00705 flushContentString ();
00706
00707
00708 _CurrentNode = _CurrentElement;
00709 _CurrentElement = _CurrentElement->parent;
00710 _CurrentNode = _CurrentNode->next;
00711 }
00712 else
00713 {
00714 nlerror ( "You must call xmlPop after xmlPushEnd.");
00715 return false;
00716 }
00717 }
00718 else
00719 {
00720 nlerror ( "Output stream has not been initialized.");
00721 return false;
00722 }
00723
00724
00725 return true;
00726 }
00727
00728
00729
00730 bool CIXml::xmlSetAttribInternal (const char *attribName)
00731 {
00732 nlassert( isReading() );
00733
00734
00735 if ( _Parser )
00736 {
00737
00738 if ( _PushBegin )
00739 {
00740
00741 _AttribName = attribName;
00742
00743
00744 _AttribPresent = true;
00745 }
00746 else
00747 {
00748 nlerror ( "You must call xmlSetAttrib between xmlPushBegin and xmlPushEnd calls.");
00749 return false;
00750 }
00751 }
00752 else
00753 {
00754 nlerror ( "Output stream has not been initialized.");
00755 return false;
00756 }
00757
00758
00759 return true;
00760 }
00761
00762
00763
00764 bool CIXml::xmlBreakLineInternal ()
00765 {
00766
00767 return true;
00768 }
00769
00770
00771
00772 bool CIXml::xmlCommentInternal (const char *comment)
00773 {
00774
00775 return true;
00776 }
00777
00778
00779
00780 xmlNodePtr CIXml::getFirstChildNode (xmlNodePtr parent, const char *childName)
00781 {
00782 xmlNodePtr child = parent->children;
00783 while (child)
00784 {
00785 if (strcmp ((const char*)child->name, childName) == 0)
00786 return child;
00787 child = child->next;
00788 }
00789 return NULL;
00790 }
00791
00792
00793
00794 xmlNodePtr CIXml::getNextChildNode (xmlNodePtr last, const char *childName)
00795 {
00796 last = last->next;
00797 while (last)
00798 {
00799 if (strcmp ((const char*)last->name, childName) == 0)
00800 return last;
00801 last = last->next;
00802 }
00803 return NULL;
00804 }
00805
00806
00807
00808 xmlNodePtr CIXml::getFirstChildNode (xmlNodePtr parent, xmlElementType type)
00809 {
00810 xmlNodePtr child = parent->children;
00811 while (child)
00812 {
00813 if (child->type == type)
00814 return child;
00815 child = child->next;
00816 }
00817 return NULL;
00818 }
00819
00820
00821
00822 xmlNodePtr CIXml::getNextChildNode (xmlNodePtr last, xmlElementType type)
00823 {
00824 last = last->next;
00825 while (last)
00826 {
00827 if (last->type == type)
00828 return last;
00829 last = last->next;
00830 }
00831 return NULL;
00832 }
00833
00834
00835
00836 uint CIXml::countChildren (xmlNodePtr node, const char *childName)
00837 {
00838 uint count=0;
00839 xmlNodePtr child = getFirstChildNode (node, childName);
00840 while (child)
00841 {
00842 count++;
00843 child = getNextChildNode (child, childName);
00844 }
00845 return count;
00846 }
00847
00848
00849
00850 uint CIXml::countChildren (xmlNodePtr node, xmlElementType type)
00851 {
00852 uint count=0;
00853 xmlNodePtr child = getFirstChildNode (node, type);
00854 while (child)
00855 {
00856 count++;
00857 child = getNextChildNode (child, type);
00858 }
00859 return count;
00860 }
00861
00862
00863
00864 xmlNodePtr CIXml::getRootNode () const
00865 {
00866 if (_Parser)
00867 if (_Parser->myDoc)
00868 return xmlDocGetRootElement (_Parser->myDoc);
00869 return NULL;
00870 }
00871
00872
00873
00874 bool CIXml::getPropertyString (std::string &result, xmlNodePtr node, const char *property)
00875 {
00876
00877 const char *value = (const char*)xmlGetProp (node, (xmlChar*)property);
00878 if (value)
00879 {
00880
00881 result = value;
00882
00883
00884 xmlFree ((void*)value);
00885
00886
00887 return true;
00888 }
00889 return false;
00890 }
00891
00892
00893
00894 bool CIXml::getContentString (std::string &result, xmlNodePtr node)
00895 {
00896 const char *valueText = (const char*)xmlNodeGetContent (node);
00897 if (valueText)
00898 {
00899 result = valueText;
00900
00901
00902 xmlFree ((void*)valueText);
00903
00904
00905 return true;
00906 }
00907 return false;
00908 }
00909
00910
00911
00912 }
00913
00914 #endif // NL_DONT_USE_EXTERNAL_CODE