xmlstorage.cpp

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

Generated on Mon Aug 30 00:05:06 2010 for XMLStorage/C++ by  doxygen 1.5.4