• Main Page
  • Namespaces
  • Classes
  • Files
  • Directories
  • File List
  • File Members

xmlstorage.cpp

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

Generated on Fri Nov 14 2014 00:05:04 for XMLStorage/C++ by  doxygen 1.7.2