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 """ / "'" 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++ = ';'; // "&" 00424 break; 00425 00426 case '<': 00427 *o++ = '&'; *o++ = 'l'; *o++ = 't'; *o++ = ';'; // "<" 00428 break; 00429 00430 case '>': 00431 *o++ = '&'; *o++ = 'g'; *o++ = 't'; *o++ = ';'; // ">" 00432 break; 00433 00434 case '"': 00435 *o++ = '&'; *o++ = 'q'; *o++ = 'u'; *o++ = 'o'; *o++ = 't'; *o++ = ';'; // """ 00436 break; 00437 00438 case '\'': 00439 *o++ = '&'; *o++ = 'a'; *o++ = 'p'; *o++ = 'o'; *o++ = 's'; *o++ = ';'; // "'" 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 << "&"; 00478 break; 00479 00480 case '<': 00481 out << "<"; 00482 break; 00483 00484 case '>': 00485 out << ">"; 00486 break; 00487 00488 case '"': 00489 out << """; 00490 break; 00491 00492 case '\'': 00493 out << "'"; 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 ![]() |