xmlstorage.cpp

Go to the documentation of this file.
00001 
00002  //
00003  // XML storage C++ classes version 1.5
00004  //
00005  // Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Martin Fuchs <martin-fuchs@gmx.net>
00006  //
00007 
00008  /// \file xmlstorage.cpp
00009  /// XMLStorage implementation file
00010 
00011 
00012 /*
00013 
00014   All rights reserved.
00015 
00016   Redistribution and use in source and binary forms, with or without
00017   modification, are permitted provided that the following conditions are met:
00018 
00019   * Redistributions of source code must retain the above copyright
00020     notice, this list of conditions and the following disclaimer.
00021   * Redistributions in binary form must reproduce the above copyright
00022     notice, this list of conditions and the following disclaimer in
00023     the documentation and/or other materials provided with the
00024     distribution.
00025 
00026   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00027   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00028   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00029   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00030   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00031   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00032   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00033   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00034   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00035   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00036   POSSIBILITY OF SUCH DAMAGE.
00037 
00038 */
00039 
00040 #include <precomp.h>
00041 
00042 #ifndef XS_NO_COMMENT
00043 #define XS_NO_COMMENT   // no #pragma comment(lib, ...) statements in .lib files to enable static linking
00044 #endif
00045 
00046 //#include "xmlstorage.h"
00047 
00048 
00049 namespace XMLStorage {
00050 
00051 
00052  // work around GCC's wide string constant bug
00053 #ifdef __GNUC__
00054 const LPCXSSTR XS_EMPTY = XS_EMPTY_STR;
00055 const LPCXSSTR XS_TRUE = XS_TRUE_STR;
00056 const LPCXSSTR XS_FALSE = XS_FALSE_STR;
00057 const LPCXSSTR XS_INTFMT = XS_INTFMT_STR;
00058 const LPCXSSTR XS_INT64FMT = XS_INT64FMT_STR;
00059 const LPCXSSTR XS_FLOATFMT = XS_FLOATFMT_STR;
00060 #endif
00061 
00062 const XS_String XS_KEY = XS_KEY_STR;
00063 const XS_String XS_VALUE = XS_VALUE_STR;
00064 const XS_String XS_PROPERTY = XS_PROPERTY_STR;
00065 
00066 
00067  /// remove escape characters from zero terminated string
00068 static XS_String unescape(LPCXSSTR s, TCHAR b, TCHAR e)
00069 {
00070     if (*s == b) {
00071         LPCXSSTR end = s + _tcslen(s);
00072 
00073         if (end>s && end[-1]==e)
00074             ++s, --end;
00075 
00076         return XS_String(s, end-s);
00077     } else
00078         return s;
00079 }
00080 
00081 inline XS_String unescape(LPCXSSTR s)
00082 {
00083     if (*s == _T('\''))
00084         return unescape(s, _T('\''), _T('\''));
00085     else
00086         return unescape(s, _T('"'), _T('"'));
00087 }
00088 
00089  /// remove escape characters from string with specified length
00090 static XS_String unescape(LPCXSSTR s, size_t l, char b, char e)
00091 {
00092     LPCXSSTR end = s + l;
00093 
00094     if (*s == b)
00095         if (end>s && end[-1]==e)
00096             ++s, --end;
00097 
00098     return XS_String(s, end-s);
00099 }
00100 
00101 inline XS_String unescape(LPCXSSTR s, size_t l)
00102 {
00103     if (*s == '\'')
00104         return unescape(s, l, _T('\''), _T('\''));
00105     else if (*s == '"')
00106         return unescape(s, l, _T('"'), _T('"'));
00107     else
00108         return XS_String(s, l);
00109 }
00110 
00111 
00112  /// move to the position defined by xpath in XML tree
00113 bool XMLPos::go(const XPath& xpath)
00114 {
00115     XMLNode* node = xpath._absolute? _root: _cur;
00116 
00117     node = node->find_relative(xpath);
00118 
00119     if (node) {
00120         go_to(node);
00121         return true;
00122     } else
00123         return false;
00124 }
00125 
00126  /// move to the position defined by xpath in XML tree
00127 bool const_XMLPos::go(const XPath& xpath)
00128 {
00129     const XMLNode* node = xpath._absolute? _root: _cur;
00130 
00131     node = node->find_relative(xpath);
00132 
00133     if (node) {
00134         go_to(node);
00135         return true;
00136     } else
00137         return false;
00138 }
00139 
00140 
00141 LPCXSSTR XPathElement::parse(LPCXSSTR path)
00142 {
00143     LPCXSSTR slash = _tcschr(path, '/');
00144     if (slash == path)
00145         return NULL;
00146 
00147      // parse relative path
00148     size_t l = slash? slash-path: _tcslen(path);
00149     XS_String comp(path, l);
00150     path += l;
00151 
00152      // look for [n] and [@attr_name="attr_value"] expressions in path components
00153     LPCXSSTR bracket = _tcschr(comp.c_str(), _T('['));
00154     l = bracket? bracket-comp.c_str(): comp.length();
00155     _child_name.assign(comp.c_str(), l);
00156 
00157     if (bracket) {
00158         XS_String expr = unescape(bracket, _T('['), _T(']'));
00159         LPCXSSTR p = expr.c_str();
00160 
00161         while(_istspace((_TUCHAR)*p)) ++p;
00162 
00163         if (isdigit(*p)) {
00164             int n = _ttoi(p);   // read index number
00165 
00166             if (n)
00167                 _child_idx = n - 1; // convert into zero based index
00168         } else {
00169             while(*p == '@') {
00170                 LPCXSSTR equal = _tcschr(++p, '=');
00171 
00172                 if (equal) {
00173                      // look for delimiters
00174                     LPCXSSTR del1 = equal+1;
00175                     LPCXSSTR del2 = *del1==_T('\'')||*del1==_T('"')? _tcschr(del1+1, *del1): NULL;
00176 
00177                      // read attribute name and value
00178                     const XS_String& attrName = unescape(p, equal-p);
00179                     const XS_String& attrValue = del2? unescape(del1, ++del2-del1): unescape(del1);
00180 
00181                     _mAttrAndAttr[attrName] = attrValue;
00182 
00183                     if (!del2)
00184                         break;
00185 
00186                     p = del2 + 1;
00187                     while(isspace((unsigned char)*p)) ++p;
00188 
00189                      // handle [@attr1="value1" and @attr2="value2"] expressions
00190                      // handle expressions like [@name="1.2" and @profil="IPE" and @date="2006_07"]
00191                     if (!_tcsnicmp(p, _T("and "), 4))
00192                         p += 4;
00193                 }
00194 
00195                 while(_istspace((_TUCHAR)*p)) ++p;
00196             }
00197         }
00198     }
00199 
00200     return path;
00201 }
00202 
00203 XMLNode* XPathElement::find(XMLNode* node) const
00204 {
00205     int n = 0;
00206 
00207     for(XMLNode::Children::const_iterator it=node->_children.begin(); it!=node->_children.end(); ++it)
00208         if (matches(**it, n))
00209             return *it;
00210 
00211     return NULL;
00212 }
00213 
00214 const XMLNode* XPathElement::const_find(const XMLNode* node) const
00215 {
00216     int n = 0;
00217 
00218     for(XMLNode::Children::const_iterator it=node->_children.begin(); it!=node->_children.end(); ++it)
00219         if (matches(**it, n))
00220             return *it;
00221 
00222     return NULL;
00223 }
00224 
00225 bool XPathElement::matches(const XMLNode& node, int& n) const
00226 {
00227     if (node != _child_name)
00228         if (_child_name != XS_TEXT("*"))    // use asterisk as wildcard
00229             return false;
00230 
00231     for(MAPATTR::const_iterator b=_mAttrAndAttr.begin(), e=_mAttrAndAttr.end(); b!=e; ++b)
00232         if (node.get(b->first) != b->second)
00233             return false;
00234 
00235     if (_child_idx == -1)
00236         return true;
00237     else if (n++ == _child_idx)
00238         return true;
00239     else
00240         return false;
00241 }
00242 
00243 
00244 void XPath::init(LPCXSSTR path)
00245 {
00246      // Is this an absolute path?
00247     if (*path == '/') {
00248         _absolute = true;
00249         ++path;
00250     } else
00251         _absolute = false;
00252 
00253      // parse path
00254     while(*path) {
00255         XPathElement elem;
00256 
00257         path = elem.parse(path);
00258 
00259         if (!path)
00260             break;
00261 
00262         push_back(elem);
00263 
00264         if (*path == '/')
00265             ++path;
00266     }
00267 }
00268 
00269 
00270 const XMLNode* XMLNode::find_relative(const XPath& xpath) const
00271 {
00272     const XMLNode* node = this;
00273 
00274     for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
00275         node = it->const_find(node);
00276 
00277         if (!node)
00278             break;
00279     }
00280 
00281     return node;
00282 }
00283 
00284 XMLNode* XMLNode::find_relative(const XPath& xpath)
00285 {
00286     XMLNode* node = this;
00287 
00288     for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
00289         node = it->find(node);
00290 
00291         if (!node)
00292             break;
00293     }
00294 
00295     return node;
00296 }
00297 
00298 XMLNode* XMLNode::create_relative(const XPath& xpath)
00299 {
00300     XMLNode* node = this;
00301 
00302     for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
00303         XMLNode* child = it->find(node);
00304 
00305         if (!child) {
00306             child = new XMLNode(it->_child_name);
00307             node->add_child(child);
00308 
00309             for(XPathElement::MAPATTR::const_iterator b=it->_mAttrAndAttr.begin(), e=it->_mAttrAndAttr.end(); b!=e; ++b)
00310                 (*this)[b->first] = b->second;
00311         }
00312 
00313         node = child;
00314     }
00315 
00316     return node;
00317 }
00318 
00319  /// count the nodes matching the given relative XPath expression
00320 int XMLNode::count(XPath::const_iterator from, const XPath::const_iterator& to) const
00321 {
00322     const XPathElement& elem = *from++;
00323     int cnt = 0;
00324     int n = 0;
00325 
00326     for(XMLNode::Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
00327         if (elem.matches(**it, n)) {
00328             if (from != to)
00329                  // iterate deeper
00330                 cnt += (*it)->count(from, to);
00331             else
00332                  // increment match counter
00333                 ++cnt;
00334         }
00335 
00336     return cnt;
00337 }
00338 
00339  /// copy matching tree nodes using the given XPath filter expression
00340 bool XMLNode::filter(const XPath& xpath, XMLNode& target) const
00341 {
00342     XMLNode* ret = filter(xpath.begin(), xpath.end());
00343     if (!ret)
00344         return false;
00345 
00346      // move returned nodes to target node
00347     target._children.move(ret->_children);
00348     target._attributes = ret->_attributes;
00349 
00350     delete ret;
00351 
00352     return true;
00353 }
00354 
00355  /// create a new node tree using the given XPath filter expression
00356 XMLNode* XMLNode::filter(XPath::const_iterator from, const XPath::const_iterator& to) const
00357 {
00358     XMLNode* copy = NULL;
00359 
00360     const XPathElement& elem = *from++;
00361     int cnt = 0;
00362     int n = 0;
00363 
00364     for(XMLNode::Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
00365         if (elem.matches(**it, n)) {
00366             if (!copy)
00367                 copy = new XMLNode(*this, XMLNode::COPY_NOCHILDREN);
00368 
00369             if (from != to) {
00370                 XMLNode* ret = (*it)->filter(from, to);
00371 
00372                 if (ret) {
00373                     copy->add_child(ret);
00374                     ++cnt;
00375                 }
00376             } else {
00377                 copy->add_child(new XMLNode(**it, XMLNode::COPY_NOCHILDREN));
00378                 ++cnt;
00379             }
00380         }
00381 
00382     if (!cnt) {
00383         delete copy;
00384         copy = NULL;
00385     }
00386 
00387     return copy;
00388 }
00389 
00390 
00391  /// encode XML string literals
00392 std::string EncodeXMLString(const XS_String& str, bool cdata)
00393 {
00394     LPCXSSTR s = str.c_str();
00395     size_t l = XS_len(s);
00396 
00397     if (cdata) {
00398          // encode the whole string in a CDATA section
00399         std::string ret = CDATA_START;
00400 
00401 #ifdef XS_STRING_UTF8
00402         ret += str;
00403 #else
00404         ret += get_utf8(str);
00405 #endif
00406 
00407 #ifdef XS_STRICT_XML_1_0
00408         for(char*p=&ret.at(9); *p; ++p) // strlen(CDATA_START) == 9
00409             if ((unsigned)*p<0x20 && *p!='\t' && *p!='\r' && *p!='\n')
00410                 *p = '?';
00411 #endif
00412 
00413         ret += CDATA_END;
00414 
00415         return ret;
00416     } else if (l <= BUFFER_LEN) {
00417         LPXSSTR buffer = (LPXSSTR)alloca(6*sizeof(XS_CHAR)*XS_len(s));  // worst case "&quot;" / "&apos;"
00418         LPXSSTR o = buffer;
00419 
00420         for(LPCXSSTR p=s; *p; ++p)
00421             switch(*p) {
00422               case '&':
00423                 *o++ = '&'; *o++ = 'a'; *o++ = 'm'; *o++ = 'p'; *o++ = ';';             // "&amp;"
00424                 break;
00425 
00426               case '<':
00427                 *o++ = '&'; *o++ = 'l'; *o++ = 't'; *o++ = ';';                         // "&lt;"
00428                 break;
00429 
00430               case '>':
00431                 *o++ = '&'; *o++ = 'g'; *o++ = 't'; *o++ = ';';                         // "&gt;"
00432                 break;
00433 
00434               case '"':
00435                 *o++ = '&'; *o++ = 'q'; *o++ = 'u'; *o++ = 'o'; *o++ = 't'; *o++ = ';'; // "&quot;"
00436                 break;
00437 
00438               case '\'':
00439                 *o++ = '&'; *o++ = 'a'; *o++ = 'p'; *o++ = 'o'; *o++ = 's'; *o++ = ';'; // "&apos;"
00440                 break;
00441 
00442               default:
00443                 if ((unsigned)*p>=0x20 || *p=='\t' || *p=='\r' || *p=='\n')
00444                     *o++ = *p;
00445                 else {
00446 #ifdef XS_STRICT_XML_1_0
00447                     *o++ = '?';
00448 #else
00449                     char b[16];
00450                     sprintf(b, "&#%d;", (unsigned)*p);
00451                     for(const char*q=b; *q; )
00452                         *o++ = *q++;
00453 #endif
00454                 }
00455             }
00456 
00457 #ifdef XS_STRING_UTF8
00458         return XS_String(buffer, o-buffer);
00459 #else
00460         return get_utf8(buffer, o-buffer);
00461 #endif
00462     } else { // l > BUFFER_LEN
00463          // alternative code for larger strings using ostringstream
00464          // and avoiding to use alloca() for preallocated memory
00465         fast_ostringstream out;
00466 
00467 #ifdef XS_STRING_UTF8
00468         LPCXSSTR s = str.c_str();
00469 #else
00470         std::string utf8_str = get_utf8(str);
00471         const char* s = utf8_str.c_str();
00472 #endif
00473 
00474         for(const char* p=s; *p; ++p)
00475             switch(*p) {
00476               case '&':
00477                 out << "&amp;";
00478                 break;
00479 
00480               case '<':
00481                 out << "&lt;";
00482                 break;
00483 
00484               case '>':
00485                 out << "&gt;";
00486                 break;
00487 
00488               case '"':
00489                 out << "&quot;";
00490                 break;
00491 
00492               case '\'':
00493                 out << "&apos;";
00494                 break;
00495 
00496               default:
00497                 if ((unsigned)*p>=0x20 || *p=='\t' || *p=='\r' || *p=='\n')
00498                     out << (unsigned char)*p;
00499                 else {
00500 #ifdef XS_STRICT_XML_1_0
00501                     out << '?';
00502 #else
00503                     out << "&#" << (unsigned)*p << ";";
00504 #endif
00505                 }
00506             }
00507 
00508 #ifdef XS_STRING_UTF8
00509         return XS_String(out.str());
00510 #else
00511         return out.str();
00512 #endif
00513     }
00514 }
00515 
00516  /// decode XML string literals
00517 XS_String DecodeXMLString(const std::string& str_utf8)
00518 {
00519 #ifdef XS_STRING_UTF8
00520     const XS_String& str = str_utf8;
00521 #else
00522     XS_String str;
00523     assign_utf8(str, str_utf8.c_str(), str_utf8.length());
00524 #endif
00525 
00526     LPCXSSTR s = str.c_str();
00527     size_t l = XS_len(s);
00528 
00529     TMP_ALLOC(XS_CHAR, buffer, heap, l);
00530 
00531     LPXSSTR o = buffer;
00532     for(LPCXSSTR p=s; *p; ++p)
00533         if (*p == '&') {
00534             if (!XS_nicmp(p+1, XS_TEXT("lt;"), 3)) {
00535                 *o++ = '<';
00536                 p += 3;
00537             } else if (!XS_nicmp(p+1, XS_TEXT("gt;"), 3)) {
00538                 *o++ = '>';
00539                 p += 3;
00540             } else if (!XS_nicmp(p+1, XS_TEXT("amp;"), 4)) {
00541                 *o++ = '&';
00542                 p += 4;
00543             } else if (!XS_nicmp(p+1, XS_TEXT("quot;"), 5)) {
00544                 *o++ = '"';
00545                 p += 5;
00546             } else if (!XS_nicmp(p+1, XS_TEXT("apos;"), 5)) {
00547                 *o++ = '\'';
00548                 p += 5;
00549             } else  //@@ maybe decode "&#xx;" special characters
00550                 *o++ = *p;
00551         } else if (*p=='<' && !XS_nicmp(p+1,XS_TEXT("![CDATA["),8)) {
00552             LPCXSSTR e = XS_strstr(p+9, XS_TEXT(CDATA_END));
00553             if (e) {
00554                 p += 9;
00555                 size_t l = e - p;
00556                 memcpy(o, p, l);
00557                 o += l;
00558                 p = e + 2;
00559             } else
00560                 *o++ = *p;
00561         } else
00562             *o++ = *p;
00563 
00564     return XS_String(buffer, o-buffer);
00565 }
00566 
00567 
00568  /// write node with children tree to output stream using original white space
00569 void XMLNode::original_write_worker(std::ostream& out) const
00570 {
00571     out << _leading << '<' << EncodeXMLString(*this);
00572 
00573     for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
00574         out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
00575 
00576     if (!_children.empty() || !_content.empty()) {
00577         out << '>';
00578 
00579         if (_cdata_content)
00580             out << CDATA_START << _content << CDATA_END;
00581         else
00582             out << _content;
00583 
00584         for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
00585             (*it)->original_write_worker(out);
00586 
00587         out << _end_leading << "</" << EncodeXMLString(*this) << '>';
00588     } else
00589         out << "/>";
00590 
00591     out << _trailing;
00592 }
00593 
00594 
00595  /// print node without any white space
00596 void XMLNode::plain_write_worker(std::ostream& out) const
00597 {
00598     out << '<' << EncodeXMLString(*this);
00599 
00600     for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
00601         out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
00602 
00603      // strip leading white space from content
00604     const char* content = _content.c_str();
00605     while(isspace((unsigned char)*content)) ++content;
00606 
00607     if (!_children.empty() || *content) {
00608         out << ">" << content;
00609 
00610         for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
00611             (*it)->plain_write_worker(out);
00612 
00613         out << "</" << EncodeXMLString(*this) << ">";
00614     } else
00615         out << "/>";
00616 }
00617 
00618 
00619  /// pretty print node with children tree to output stream
00620 void XMLNode::pretty_write_worker(std::ostream& out, const XMLFormat& format, int indent) const
00621 {
00622     for(int i=indent; i--; )
00623         out << XML_INDENT_SPACE;
00624 
00625     out << '<' << EncodeXMLString(*this);
00626 
00627     for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
00628         out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
00629 
00630      // strip leading white space from content
00631     const char* content = _content.c_str();
00632     while(isspace((unsigned char)*content)) ++content;
00633 
00634     if (!_children.empty() || *content) {
00635         out << '>' << content;
00636 
00637         if (!_children.empty())
00638             out << format._endl;
00639 
00640         for(Children::const_iterator it=_children.begin(); it!=_children.end(); ++it)
00641             (*it)->pretty_write_worker(out, format, indent+1);
00642 
00643         for(int i=indent; i--; )
00644             out << XML_INDENT_SPACE;
00645 
00646         out << "</" << EncodeXMLString(*this) << '>' << format._endl;
00647     } else
00648         out << "/>" << format._endl;
00649 }
00650 
00651 
00652  /// write node with children tree to output stream using smart formating
00653 void XMLNode::smart_write_worker(std::ostream& out, const XMLFormat& format, int indent) const
00654 {
00655      // strip the first line feed from _leading
00656     const char* leading = _leading.c_str();
00657     if (*leading == '\n') ++leading;
00658 
00659     if (!*leading)
00660         for(int i=indent; i--; )
00661             out << XML_INDENT_SPACE;
00662     else
00663         out << leading;
00664 
00665     out << '<' << EncodeXMLString(*this);
00666 
00667     for(AttributeMap::const_iterator it=_attributes.begin(); it!=_attributes.end(); ++it)
00668         out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
00669 
00670      // strip leading white space from content
00671     const char* content = _content.c_str();
00672     while(isspace((unsigned char)*content)) ++content;
00673 
00674     if (_children.empty() && !*content)
00675         out << "/>";
00676     else {
00677         out << '>';
00678 
00679         if (_cdata_content)
00680             out << CDATA_START << _content << CDATA_END;
00681         else if (!*content)
00682             out << format._endl;
00683         else
00684             out << content;
00685 
00686         Children::const_iterator it = _children.begin();
00687 
00688         if (it != _children.end()) {
00689             for(; it!=_children.end(); ++it)
00690                 (*it)->smart_write_worker(out, format, indent+1);
00691 
00692              // strip the first line feed from _end_leading
00693             const char* end_leading = _end_leading.c_str();
00694             if (*end_leading == '\n') ++end_leading;
00695 
00696             if (!*end_leading)
00697                 for(int i=indent; i--; )
00698                     out << XML_INDENT_SPACE;
00699             else
00700                 out << end_leading;
00701         } else
00702             out << _end_leading;
00703 
00704         out << "</" << EncodeXMLString(*this) << '>';
00705     }
00706 
00707     if (_trailing.empty())
00708         out << format._endl;
00709     else
00710         out << _trailing;
00711 }
00712 
00713 
00714 std::ostream& operator<<(std::ostream& out, const XMLError& err)
00715 {
00716     out << err._systemId << "(" << err._line << ")";
00717 
00718     if (err._column > 1)
00719         out << " [column " << err._column << "]";
00720 
00721     out << " : " << err._message;
00722 
00723     return out;
00724 }
00725 
00726 
00727 #if defined(XS_NATIVE) || defined(XMLNODE_LOCATION)
00728 
00729 std::string XMLLocation::str() const
00730 {
00731     std::ostringstream out;
00732 
00733     if (_pdisplay_path)
00734         out << _pdisplay_path;
00735 
00736     out << "(" << _line << ")";
00737 
00738     if (_column > 1)
00739         out << " [column " << _column << "]";
00740 
00741     out << " :";
00742 
00743     return out.str();
00744 }
00745 
00746 #endif
00747 
00748 
00749 const char* get_xmlsym_end_utf8(const char* p)
00750 {
00751     for(; *p; ++p) {
00752         char c = *p;
00753 
00754         // NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
00755         if (c == '\xC3')    // UTF-8 escape character
00756             ++p;    //TODO only continue on umlaut characters
00757         else if (!isalnum(c) && c!='.' && c!='-' && c!='_' && c!=':')
00758             break;
00759     }
00760 
00761     return p;
00762 }
00763 
00764 
00765 void DocType::parse(const char* p)
00766 {
00767     while(isspace((unsigned char)*p)) ++p;
00768 
00769     const char* start = p;
00770     p = get_xmlsym_end_utf8(p);
00771     _name.assign(start, p-start);
00772 
00773     while(isspace((unsigned char)*p)) ++p;
00774 
00775     start = p;
00776     p = get_xmlsym_end_utf8(p);
00777     std::string keyword(p, p-start);    // "PUBLIC" or "SYSTEM"
00778 
00779     while(isspace((unsigned char)*p)) ++p;
00780 
00781     if (*p=='"' || *p=='\'') {
00782         char delim = *p;
00783 
00784         start = ++p;
00785         while(*p && *p!=delim) ++p;
00786 
00787         if (*p == delim)
00788             _public.assign(start, p++-start);
00789     } else
00790         _public.erase();
00791 
00792     while(isspace((unsigned char)*p)) ++p;
00793 
00794     if (*p=='"' || *p=='\'') {
00795         char delim = *p;
00796 
00797         start = ++p;
00798         while(*p && *p!=delim) ++p;
00799 
00800         if (*p == delim)
00801             _system.assign(start, p++-start);
00802     } else
00803         _system.erase();
00804 }
00805 
00806 
00807 void XMLFormat::print_header(std::ostream& out, bool lf) const
00808 {
00809     if (_inhibit_header)
00810         return;
00811 
00812     if (_utf8_bom) {
00813          // write UTF-8 signature at file start
00814         out.put('\xEF');
00815         out.put('\xBB');
00816         out.put('\xBF');
00817         assert(!_stricmp(_encoding.c_str(), "utf-8"));
00818     }
00819 
00820     out << "<?xml version=\"" << _version << "\" encoding=\"" << _encoding << "\"";
00821 
00822     if (_standalone != -1)
00823         out << " standalone=\"yes\"";
00824 
00825     out << "?>";
00826 
00827     if (lf)
00828         out << _endl;
00829 
00830     if (!_doctype.empty()) {
00831         out << "<!DOCTYPE " << _doctype._name;
00832 
00833         if (!_doctype._public.empty()) {
00834             out << " PUBLIC \"" << _doctype._public << '"';
00835 
00836             if (lf)
00837                 out << _endl;
00838 
00839             out << " \"" << _doctype._system << '"';
00840         } else if (!_doctype._system.empty())
00841             out << " SYSTEM \"" << _doctype._system << '"';
00842 
00843         out << "?>";
00844 
00845         if (lf)
00846             out << _endl;
00847     }
00848 
00849     for(StyleSheetList::const_iterator it=_stylesheets.begin(); it!=_stylesheets.end(); ++it) {
00850         it->print(out);
00851 
00852         if (lf)
00853             out << _endl;
00854     }
00855 
00856 /*  if (!_additional.empty()) {
00857         out << _additional;
00858 
00859         if (lf)
00860             out << _endl;
00861     } */
00862 }
00863 
00864 void StyleSheet::print(std::ostream& out) const
00865 {
00866     out << "<?xml-stylesheet"
00867             " href=\"" << _href << "\""
00868             " type=\"" << _type << "\"";
00869 
00870     if (!_title.empty())
00871         out << " title=\"" << _title << "\"";
00872 
00873     if (!_media.empty())
00874         out << " media=\"" << _media << "\"";
00875 
00876     if (!_charset.empty())
00877         out << " charset=\"" << _charset << "\"";
00878 
00879     if (_alternate)
00880         out << " alternate=\"yes\"";
00881 
00882     out << "?>";
00883 }
00884 
00885 
00886  /// return formated error message
00887 std::string XMLError::str() const
00888 {
00889     std::ostringstream out;
00890 
00891     out << *this;
00892 
00893     return out.str();
00894 }
00895 
00896 
00897  /// return merged error strings
00898 XS_String XMLErrorList::str(const char* del) const
00899 {
00900     std::ostringstream out;
00901 
00902     for(const_iterator it=begin(); it!=end(); ++it)
00903         out << *it << del;
00904 
00905     return out.str();
00906 }
00907 
00908 
00909 void XMLReaderBase::finish_read()
00910 {
00911     if (_pos != NULL) {
00912         if (_pos->_children.empty())
00913             _pos->_trailing.append(_content);
00914         else
00915             _pos->_children.back()->_trailing.append(_content);
00916     }
00917 
00918     _content.erase();
00919 }
00920 
00921 
00922  /// store XML version and encoding into XML reader
00923 void XMLReaderBase::XmlDeclHandler(const char* version, const char* encoding, int standalone)
00924 {
00925     if (version)
00926         _format._version = version;
00927 
00928     if (encoding)
00929         _format._encoding = encoding;
00930 
00931     _format._standalone = standalone;
00932 }
00933 
00934 
00935  /// notifications about XML start tag
00936 void XMLReaderBase::StartElementHandler(const XS_String& name, const XMLNode::AttributeMap& attributes)
00937 {
00938     const char* s = _content.c_str();
00939     const char* e = s + _content.length();
00940     const char* p = s;
00941 
00942      // search for content end leaving only white space for leading
00943     for(p=e; p>s; --p)
00944         if (!isspace((unsigned char)p[-1]))
00945             break;
00946 
00947     if (p != s) {
00948         if (_pos->_children.empty()) {  // no children in last node?
00949             if (_last_tag == TAG_START)
00950                 _pos->_content.append(s, p-s);
00951             else if (_last_tag == TAG_END)
00952                 _pos->_trailing.append(s, p-s);
00953             else // TAG_NONE at root node
00954                 p = s;
00955         } else
00956             _pos->_children.back()->_trailing.append(s, p-s);
00957     }
00958 
00959     std::string leading;
00960 
00961     if (p != e)
00962         leading.assign(p, e-p);
00963 
00964     XMLNode* node = new XMLNode(name, leading);
00965 
00966     _pos.add_down(node);
00967 
00968 #ifdef XMLNODE_LOCATION
00969     node->_location = get_location();
00970 #endif
00971 
00972     node->_attributes = attributes;
00973 
00974     _last_tag = TAG_START;
00975     _content.erase();
00976 }
00977 
00978  /// notifications about XML end tag
00979 void XMLReaderBase::EndElementHandler()
00980 {
00981     const char* s = _content.c_str();
00982     const char* e = s + _content.length();
00983     const char* p;
00984 
00985     if (!strncmp(s,CDATA_START,9) && !strncmp(e-3,CDATA_END,3)) {
00986         s += 9;
00987         p = (e-=3);
00988 
00989         _pos->_cdata_content = true;
00990     } else {
00991          // search for content end leaving only white space for _end_leading
00992         for(p=e; p>s; --p)
00993             if (!isspace((unsigned char)p[-1]))
00994                 break;
00995 
00996         _pos->_cdata_content = false;
00997     }
00998 
00999     if (p != s) {
01000         if (_pos->_children.empty())    // no children in current node?
01001             _pos->_content.append(s, p-s);
01002         else if (_last_tag == TAG_START)
01003             _pos->_content.append(s, p-s);
01004         else
01005             _pos->_children.back()->_trailing.append(s, p-s);
01006     }
01007 
01008     if (p != e)
01009         _pos->_end_leading.assign(p, e-p);
01010 
01011     _pos.back();
01012 
01013     _last_tag = TAG_END;
01014     _content.erase();
01015 }
01016 
01017 #if defined(XS_USE_XERCES) || defined(XS_USE_EXPAT)
01018  /// store content, white space and comments
01019 void XMLReaderBase::DefaultHandler(const XML_Char* s, int len)
01020 {
01021 #if defined(XML_UNICODE) || defined(XS_USE_XERCES)
01022     _content.append(String_from_XML_Char(s, len));
01023 #else
01024     _content.append(s, len);
01025 #endif
01026 }
01027 #endif
01028 
01029 
01030 XS_String XMLWriter::s_empty_attr;
01031 
01032 void XMLWriter::create(const XS_String& name)
01033 {
01034     if (!_stack.empty()) {
01035         StackEntry& last = _stack.top();
01036 
01037         if (last._state < PRE_CLOSED) {
01038             write_attributes(last);
01039             close_pre(last);
01040         }
01041 
01042         ++last._children;
01043     }
01044 
01045     StackEntry entry;
01046     entry._node_name = name;
01047     _stack.push(entry);
01048 
01049     write_pre(entry);
01050 }
01051 
01052 bool XMLWriter::back()
01053 {
01054     if (!_stack.empty()) {
01055         write_post(_stack.top());
01056 
01057         _stack.pop();
01058         return true;
01059     } else {
01060         assert(!_stack.empty());
01061         return false;
01062     }
01063 }
01064 
01065 void XMLWriter::close_pre(StackEntry& entry)
01066 {
01067     _out << '>';
01068 
01069     entry._state = PRE_CLOSED;
01070 }
01071 
01072 void XMLWriter::write_pre(StackEntry& entry)
01073 {
01074     if (_format._pretty >= PRETTY_LINEFEED)
01075         _out << _format._endl;
01076 
01077     if (_format._pretty == PRETTY_INDENT) {
01078         for(size_t i=_stack.size(); --i>0; )
01079             _out << XML_INDENT_SPACE;
01080     }
01081 
01082     _out << '<' << EncodeXMLString(entry._node_name);
01083     //entry._state = PRE;
01084 }
01085 
01086 void XMLWriter::write_attributes(StackEntry& entry)
01087 {
01088     for(AttrMap::const_iterator it=entry._attributes.begin(); it!=entry._attributes.end(); ++it)
01089         _out << ' ' << EncodeXMLString(it->first) << "=\"" << EncodeXMLString(it->second) << "\"";
01090 
01091     entry._state = ATTRIBUTES;
01092 }
01093 
01094 void XMLWriter::write_post(StackEntry& entry)
01095 {
01096     if (entry._state < ATTRIBUTES)
01097         write_attributes(entry);
01098 
01099     if (entry._children || !entry._content.empty()) {
01100         if (entry._state < PRE_CLOSED)
01101             close_pre(entry);
01102 
01103         _out << entry._content;
01104         //entry._state = CONTENT;
01105 
01106         if (_format._pretty>=PRETTY_LINEFEED && entry._content.empty())
01107             _out << _format._endl;
01108 
01109         if (_format._pretty==PRETTY_INDENT && entry._content.empty()) {
01110             for(size_t i=_stack.size(); --i>0; )
01111                 _out << XML_INDENT_SPACE;
01112         }
01113 
01114         _out << "</" << EncodeXMLString(entry._node_name) << ">";
01115     } else {
01116         _out << "/>";
01117     }
01118 
01119     entry._state = POST;
01120 }
01121 
01122 void XMLWriter::write(const std::string& s)
01123 {
01124     if (!_stack.empty()) {
01125         StackEntry& last = _stack.top();
01126 
01127         if (last._state < PRE_CLOSED) {
01128             write_attributes(last);
01129             close_pre(last);
01130         }
01131     }
01132 
01133     _out.write(s.c_str(), s.length());
01134 }
01135 
01136 
01137  /// XPath find function
01138 bool XMLStreamReader::find_relative(const XPath& xpath)
01139 {
01140     for(XPath::const_iterator it=xpath.begin(); it!=xpath.end(); ++it) {
01141         const XPathElement& xelem = *it;
01142 
01143         if (!find(xelem))
01144             return false;
01145     }
01146 
01147     return true;
01148 }
01149 
01150 bool XMLStreamReader::find_node(LPCXSSTR target_tag, int target_level, int min_level, bool stop_on_others)
01151 {
01152     bool found = false;
01153 
01154     for(; _state_rdr.has_next(); _state_rdr.next()) {
01155         switch(_state_rdr._state) {
01156             case XMLStateReader::XSS_START_ELEMENT: {
01157                  // stop parsing if we found the target node and its content is already processed.
01158                 if (found)
01159                     goto break_parsingLoop;
01160 
01161                  // check for match in target level and eventually node name
01162                 if (_level+1 == target_level) {
01163                     if (target_tag==NULL || _state_rdr._node_name==target_tag)
01164                         found = true;
01165                     else if (stop_on_others)
01166                         return false;
01167                 }
01168 
01169                 StartElementHandler(_state_rdr._node_name, _state_rdr._attrs);
01170                 break;}
01171 
01172             case XMLStateReader::XSS_ELEMENT:
01173                  // stop parsing if we found the target node and its content is already processed.
01174                 if (found)
01175                     goto break_parsingLoop;
01176 
01177                  // check for match in target level and eventually node name
01178                 if (_level+1 == target_level) {
01179                     if (target_tag==NULL || _state_rdr._node_name==target_tag)
01180                         found = true;
01181                     else if (stop_on_others)
01182                         return false;
01183                 }
01184 
01185                 StartElementHandler(_state_rdr._node_name, _state_rdr._attrs);
01186                 // fall through...
01187 
01188             case XMLStateReader::XSS_END_ELEMENT: {
01189                  // stop parsing if we found the target node and its content is already processed.
01190                 if (found)
01191                     goto break_parsingLoop;
01192 
01193                  // stop before leaving the targeted level range; used by next_stop_on_others()
01194                 if (_level == min_level)
01195                     return false;
01196 
01197                 EndElementHandler();
01198 
01199                  // stop searching if the current level leaves the target range; used by back()
01200                 if (_level < min_level) {
01201                     _state_rdr.next();
01202                     return false;
01203                 }
01204                 break;}
01205 
01206             case XMLStateReader::XSS_CHARACTERS:
01207 //          case XMLStateReader::XSS_COMMENT:
01208 //          case XMLStateReader::XSS_SPACE:
01209 //          case XMLStateReader::XSS_CDATA:
01210                 DefaultHandler(_state_rdr.get_encoded_content());
01211                 break;
01212 
01213 //          case XMLStateReader::XSS_PROCESSING_INSTRUCTION:
01214 //              XmlDeclHandler();
01215 //              break;
01216         }
01217     } break_parsingLoop:
01218 
01219     if (found) {
01220         _node.set_encoded_content(_content);
01221         _content.erase();
01222         return true;
01223     } else
01224         return false;
01225 }
01226 
01227 void XMLStreamReader::StartElementHandler(const XS_String& name, const XMLNode::AttributeMap& attr)
01228 {
01229     _node.assign(name);
01230     _node.get_attributes() = attr;
01231     _content.erase();
01232 
01233     ++_level;
01234 }
01235 
01236 void XMLStreamReader::EndElementHandler()
01237 {
01238     _node.clear();
01239     _node.get_attributes().clear();
01240     _content.erase();
01241 
01242     --_level;
01243 }
01244 
01245 void XMLStreamReader::DefaultHandler(const std::string& s)
01246 {
01247     _content.append(EncodeXMLString(s));
01248 }
01249 
01250 
01251 bool XMLStateReader::has_next()
01252 {
01253     while(_state == XSS_NONE)
01254         if (!_parse_ctx.proceed())
01255             break;
01256 
01257     return _state != XSS_NONE;
01258 }
01259 
01260 void XMLStateReader::next()
01261 {
01262     _state = XSS_NONE;
01263 
01264     _parse_ctx.proceed();
01265 }
01266 
01267 void XMLStateReader::StartElementHandler(const XS_String& name, const XMLNode::AttributeMap& attr)
01268 {
01269     _node_name = name;
01270     _attrs = attr;
01271     _content.erase();
01272 
01273     assert(_state==XSS_NONE);
01274     _state = XSS_START_ELEMENT;
01275 }
01276 
01277 void XMLStateReader::EndElementHandler()
01278 {
01279 //  _node_name.erase();
01280 //  _attrs.clear();
01281     _content.erase();
01282 
01283     if (_state == XSS_START_ELEMENT)
01284         _state = XSS_ELEMENT;
01285     else {
01286         assert(_state==XSS_NONE);
01287         _state = XSS_END_ELEMENT;
01288     }
01289 }
01290 
01291 void XMLStateReader::DefaultHandler(const std::string& s)
01292 {
01293     _content.append(EncodeXMLString(s));
01294 
01295     assert(_state==XSS_NONE);
01296     _state = XSS_CHARACTERS;
01297 }
01298 
01299 /*@@
01300 void XMLStateReader::XmlDeclHandler(const char* version, const char* encoding, int standalone)
01301 {
01302     _instructions.append("<?").append(_state_rdr.getPITarget())
01303         .append(" ").append(_state_rdr.getPIData()).append("?>");
01304 }
01305 */
01306 
01307 
01308 }   // namespace XMLStorage

ROS Explorer Source Code Documentation
generated on 02.01.2013 by doxygen

ROS Explorer Homepage