MyGUI
3.0.3
|
00001 00007 /* 00008 This file is part of MyGUI. 00009 00010 MyGUI is free software: you can redistribute it and/or modify 00011 it under the terms of the GNU Lesser General Public License as published by 00012 the Free Software Foundation, either version 3 of the License, or 00013 (at your option) any later version. 00014 00015 MyGUI is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 GNU Lesser General Public License for more details. 00019 00020 You should have received a copy of the GNU Lesser General Public License 00021 along with MyGUI. If not, see <http://www.gnu.org/licenses/>. 00022 */ 00023 #include "MyGUI_Precompiled.h" 00024 #include "MyGUI_XmlDocument.h" 00025 #include "MyGUI_DataManager.h" 00026 00027 namespace MyGUI 00028 { 00029 namespace xml 00030 { 00031 00032 namespace utility 00033 { 00034 std::string convert_from_xml(const std::string& _string, bool& _ok) 00035 { 00036 std::string ret; 00037 _ok = true; 00038 00039 size_t pos = _string.find("&"); 00040 if (pos == std::string::npos) return _string; 00041 00042 ret.reserve(_string.size()); 00043 size_t old = 0; 00044 while (pos != std::string::npos) 00045 { 00046 ret += _string.substr(old, pos - old); 00047 00048 size_t end = _string.find(";", pos + 1); 00049 if (end == std::string::npos) 00050 { 00051 _ok = false; 00052 return ret; 00053 } 00054 else 00055 { 00056 std::string tag = _string.substr(pos, end - pos + 1); 00057 if (tag == "&") ret += '&'; 00058 else if (tag == "<") ret += '<'; 00059 else if (tag == ">") ret += '>'; 00060 else if (tag == "'") ret += '\''; 00061 else if (tag == """) ret += '\"'; 00062 else 00063 { 00064 _ok = false; 00065 return ret; 00066 } 00067 } 00068 00069 old = end + 1; 00070 pos = _string.find("&", old); 00071 } 00072 ret += _string.substr(old, std::string::npos); 00073 00074 return ret; 00075 } 00076 00077 std::string convert_to_xml(const std::string& _string) 00078 { 00079 std::string ret; 00080 00081 size_t pos = _string.find_first_of("&<>'\""); 00082 if (pos == std::string::npos) return _string; 00083 00084 ret.reserve(_string.size() * 2); 00085 size_t old = 0; 00086 while (pos != std::string::npos) 00087 { 00088 ret += _string.substr(old, pos - old); 00089 00090 if (_string[pos] == '&') ret += "&"; 00091 else if (_string[pos] == '<') ret += "<"; 00092 else if (_string[pos] == '>') ret += ">"; 00093 else if (_string[pos] == '\'') ret += "'"; 00094 else if (_string[pos] == '\"') ret += """; 00095 00096 old = pos + 1; 00097 pos = _string.find_first_of("&<>'\"", old); 00098 } 00099 ret += _string.substr(old, std::string::npos); 00100 00101 return ret; 00102 } 00103 00104 } 00105 00106 //----------------------------------------------------------------------// 00107 // class ElementEnumerator 00108 //----------------------------------------------------------------------// 00109 ElementEnumerator::ElementEnumerator(VectorElement::iterator _begin, VectorElement::iterator _end) : 00110 m_first(true), 00111 m_current(_begin), 00112 m_end(_end) 00113 { 00114 } 00115 00116 bool ElementEnumerator::next() 00117 { 00118 if (m_current == m_end) return false; 00119 else if (m_first) 00120 { 00121 m_first = false; 00122 return true; 00123 } 00124 ++ m_current; 00125 if (m_current == m_end) return false; 00126 return true; 00127 } 00128 00129 bool ElementEnumerator::next(const std::string& _name) 00130 { 00131 while (next()) 00132 { 00133 if ((*m_current)->getName() == _name) return true; 00134 } 00135 return false; 00136 } 00137 00138 //----------------------------------------------------------------------// 00139 // class Element 00140 //----------------------------------------------------------------------// 00141 Element::Element(const std::string &_name, ElementPtr _parent, ElementType _type, const std::string& _content) : 00142 mName(_name), 00143 mContent(_content), 00144 mParent(_parent), 00145 mType(_type) 00146 { 00147 } 00148 00149 Element::~Element() 00150 { 00151 for (VectorElement::iterator iter=mChilds.begin(); iter!=mChilds.end(); ++iter) 00152 { 00153 delete *iter; 00154 } 00155 mChilds.clear(); 00156 } 00157 00158 void Element::save(std::ostream& _stream, size_t _level) 00159 { 00160 // сначала табуляции намутим 00161 for (size_t tab=0; tab<_level; ++tab) _stream << " "; 00162 00163 // теперь заголовок тега 00164 if (mType == ElementType::Declaration) _stream << "<?"; 00165 else _stream << "<"; 00166 _stream << mName; 00167 00168 for (VectorAttributes::iterator iter = mAttributes.begin(); iter != mAttributes.end(); ++iter) 00169 { 00170 _stream << " " << iter->first << "=\"" << utility::convert_to_xml(iter->second) << "\""; 00171 } 00172 00173 bool empty = mChilds.empty(); 00174 // если детей нет то закрываем 00175 if (empty && mContent.empty()) 00176 { 00177 if (mType == ElementType::Declaration) _stream << "?>\n"; 00178 else _stream << "/>\n"; 00179 } 00180 else 00181 { 00182 _stream << ">"; 00183 if (!empty) _stream << "\n"; 00184 // если есть тело то сначало оно 00185 if (!mContent.empty()) 00186 { 00187 if (!empty) 00188 { 00189 for (size_t tab=0; tab<=_level; ++tab) _stream << " "; 00190 } 00191 _stream << utility::convert_to_xml(mContent); 00192 00193 if (!empty) _stream << "\n"; 00194 } 00195 // если есть детишки путь сохранятся 00196 for (size_t child=0; child<mChilds.size(); child++) 00197 { 00198 mChilds[child]->save(_stream, _level + 1); 00199 } 00200 00201 if (!empty) 00202 { 00203 for (size_t tab=0; tab<_level; ++tab) 00204 _stream << " "; 00205 } 00206 _stream << "</" << mName << ">\n"; 00207 } 00208 00209 } 00210 00211 ElementPtr Element::createChild(const std::string& _name, const std::string& _content) 00212 { 00213 ElementPtr node = new Element(_name, this, ElementType::Normal, _content); 00214 mChilds.push_back(node); 00215 return node; 00216 } 00217 00218 void Element::clear() 00219 { 00220 for (VectorElement::iterator iter = mChilds.begin(); iter != mChilds.end(); ++iter) delete *iter; 00221 mChilds.clear(); 00222 mContent.clear(); 00223 mAttributes.clear(); 00224 } 00225 00226 bool Element::findAttribute(const std::string& _name, std::string& _value) 00227 { 00228 for (VectorAttributes::iterator iter=mAttributes.begin(); iter!=mAttributes.end(); ++iter) 00229 { 00230 if ( (*iter).first == _name) 00231 { 00232 _value = (*iter).second; 00233 return true; 00234 } 00235 } 00236 return false; 00237 } 00238 00239 std::string Element::findAttribute(const std::string& _name) 00240 { 00241 for (VectorAttributes::iterator iter=mAttributes.begin(); iter!=mAttributes.end(); ++iter) 00242 { 00243 if ( (*iter).first == _name) return (*iter).second; 00244 } 00245 return ""; 00246 } 00247 00248 void Element::addAttribute(const std::string& _key, const std::string& _value) 00249 { 00250 mAttributes.push_back(PairAttribute(_key, _value)); 00251 } 00252 00253 void Element::removeAttribute(const std::string& _key) 00254 { 00255 for (size_t index=0; index<mAttributes.size(); ++index) 00256 { 00257 if (mAttributes[index].first == _key) 00258 { 00259 mAttributes.erase(mAttributes.begin() + index); 00260 return; 00261 } 00262 } 00263 } 00264 00265 ElementPtr Element::createCopy() 00266 { 00267 Element* elem = new Element(mName, nullptr, mType, mContent); 00268 elem->mAttributes = mAttributes; 00269 00270 for (VectorElement::iterator iter=mChilds.begin(); iter!=mChilds.end(); ++iter) 00271 { 00272 Element* child = (*iter)->createCopy(); 00273 child->mParent = elem; 00274 elem->mChilds.push_back(child); 00275 } 00276 00277 return elem; 00278 } 00279 00280 void Element::setAttribute(const std::string& _key, const std::string& _value) 00281 { 00282 for (size_t index=0; index<mAttributes.size(); ++index) 00283 { 00284 if (mAttributes[index].first == _key) 00285 { 00286 mAttributes[index].second = _value; 00287 return; 00288 } 00289 } 00290 mAttributes.push_back(PairAttribute(_key, _value)); 00291 } 00292 00293 void Element::addContent(const std::string& _content) 00294 { 00295 if (mContent.empty()) mContent = _content; 00296 else 00297 { 00298 mContent += " "; 00299 mContent += _content; 00300 } 00301 } 00302 00303 #if MYGUI_COMPILER == MYGUI_COMPILER_MSVC && !defined(STLPORT) 00304 inline void open_stream(std::ofstream& _stream, const std::wstring& _wide) { _stream.open(_wide.c_str()); } 00305 inline void open_stream(std::ifstream& _stream, const std::wstring& _wide) { _stream.open(_wide.c_str()); } 00306 #else 00307 inline void open_stream(std::ofstream& _stream, const std::wstring& _wide) { _stream.open(UString(_wide).asUTF8_c_str()); } 00308 inline void open_stream(std::ifstream& _stream, const std::wstring& _wide) { _stream.open(UString(_wide).asUTF8_c_str()); } 00309 #endif 00310 00311 //----------------------------------------------------------------------// 00312 // class Document 00313 //----------------------------------------------------------------------// 00314 Document::Document(): 00315 mRoot(0), 00316 mDeclaration(0), 00317 mLastErrorFile(""), 00318 mLine(0), 00319 mCol(0) 00320 { 00321 } 00322 00323 Document::~Document() 00324 { 00325 clear(); 00326 } 00327 00328 // открывает обычным файлом, имя файла в utf8 00329 bool Document::open(const std::string& _filename) 00330 { 00331 std::ifstream stream; 00332 stream.open(_filename.c_str()); 00333 00334 if (!stream.is_open()) 00335 { 00336 mLastError = ErrorType::OpenFileFail; 00337 setLastFileError(_filename); 00338 return false; 00339 } 00340 00341 bool result = open(stream); 00342 00343 stream.close(); 00344 return result; 00345 } 00346 00347 // открывает обычным файлом, имя файла в utf16 или utf32 00348 bool Document::open(const std::wstring& _filename) 00349 { 00350 std::ifstream stream; 00351 open_stream(stream, _filename); 00352 00353 if (!stream.is_open()) 00354 { 00355 mLastError = ErrorType::OpenFileFail; 00356 setLastFileError(_filename); 00357 return false; 00358 } 00359 00360 bool result = open(stream); 00361 00362 stream.close(); 00363 return result; 00364 } 00365 00366 bool Document::open(std::istream& _stream) 00367 { 00368 DataStream* data = new DataStream(&_stream); 00369 00370 bool result = open(data); 00371 delete data; 00372 00373 return result; 00374 } 00375 00376 // сохраняет файл, имя файла в кодировке utf8 00377 bool Document::save(const std::string& _filename) 00378 { 00379 std::ofstream stream; 00380 stream.open(_filename.c_str()); 00381 00382 if (!stream.is_open()) 00383 { 00384 mLastError = ErrorType::CreateFileFail; 00385 setLastFileError(_filename); 00386 return false; 00387 } 00388 00389 bool result = save(stream); 00390 00391 if (!result) 00392 { 00393 setLastFileError(_filename); 00394 } 00395 00396 stream.close(); 00397 return result; 00398 } 00399 00400 // сохраняет файл, имя файла в кодировке utf16 или utf32 00401 bool Document::save(const std::wstring& _filename) 00402 { 00403 std::ofstream stream; 00404 open_stream(stream, _filename); 00405 00406 if (!stream.is_open()) 00407 { 00408 mLastError = ErrorType::CreateFileFail; 00409 setLastFileError(_filename); 00410 return false; 00411 } 00412 00413 bool result = save(stream); 00414 00415 if (!result) 00416 { 00417 setLastFileError(_filename); 00418 } 00419 00420 stream.close(); 00421 return result; 00422 } 00423 00424 // открывает обычным потоком 00425 bool Document::open(IDataStream* _stream) 00426 { 00427 clear(); 00428 00429 // это текущая строка для разбора 00430 std::string line; 00431 // это строка из файла 00432 std::string read; 00433 // текущий узел для разбора 00434 ElementPtr currentNode = 0; 00435 00436 while (!_stream->eof()) 00437 { 00438 // берем новую строку 00439 _stream->readline(read, '\n'); 00440 if (read.empty()) continue; 00441 if (read[read.size()-1] == '\r') read.erase(read.size()-1, 1); 00442 if (read.empty()) continue; 00443 00444 mLine ++; 00445 mCol = 0; // потом проверить на многострочных тэгах 00446 if (read.empty()) continue; 00447 // текущая строка для разбора и то что еще прочитали 00448 line += read; 00449 00450 if (!parseLine(line, currentNode)) 00451 { 00452 return false; 00453 } 00454 00455 } // while (!stream.eof()) 00456 00457 if (currentNode) 00458 { 00459 mLastError = ErrorType::NotClosedElements; 00460 return false; 00461 } 00462 00463 return true; 00464 } 00465 00466 bool Document::save(std::ostream& _stream) 00467 { 00468 if (!mDeclaration) 00469 { 00470 mLastError = ErrorType::NoXMLDeclaration; 00471 return false; 00472 } 00473 00474 // заголовок utf8 00475 _stream << (char)0xEFu; 00476 _stream << (char)0xBBu; 00477 _stream << (char)0xBFu; 00478 00479 mDeclaration->save(_stream, 0); 00480 if (mRoot) mRoot->save(_stream, 0); 00481 00482 return true; 00483 } 00484 00485 void Document::clear() 00486 { 00487 clearDeclaration(); 00488 clearRoot(); 00489 mLine = 0; 00490 mCol = 0; 00491 } 00492 00493 bool Document::parseTag(ElementPtr &_currentNode, std::string _content) 00494 { 00495 00496 // убераем лишнее 00497 MyGUI::utility::trim(_content); 00498 00499 if (_content.empty()) 00500 { 00501 // создаем пустой тег 00502 if (_currentNode) _currentNode = _currentNode->createChild(""); 00503 else 00504 { 00505 _currentNode = new Element("", 0); 00506 // если это первый то запоминаем 00507 if (!mRoot) mRoot = _currentNode; 00508 } 00509 return true; 00510 } 00511 00512 char simbol = _content[0]; 00513 bool tag_info = false; 00514 00515 if (simbol == '!') return true; // проверяем на коментарии 00516 00517 // проверяем на информационный тег 00518 if (simbol == '?') 00519 { 00520 tag_info = true; 00521 _content.erase(0, 1); // удаляем первый символ 00522 } 00523 00524 size_t start, end; 00525 // проверяем на закрытие тега 00526 if (simbol == '/') 00527 { 00528 if (_currentNode == 0) 00529 { 00530 // чета мы закрывам а ниче даже и не открыто 00531 if (!mRoot) 00532 { 00533 mLastError = ErrorType::CloseNotOpenedElement; 00534 return false; 00535 } 00536 } 00537 // обрезаем имя тэга 00538 start = _content.find_first_not_of(" \t", 1); 00539 if (start == _content.npos) 00540 { 00541 // тег пустой 00542 _content.clear(); 00543 } 00544 else 00545 { 00546 end = _content.find_last_not_of(" \t"); 00547 _content = _content.substr(start, end - start+1); 00548 } 00549 // проверяем соответствие открывающего и закрывающего тегов 00550 if (_currentNode->getName() != _content) 00551 { 00552 mLastError = ErrorType::InconsistentOpenCloseElements; 00553 return false; 00554 } 00555 // а теперь снижаем текущий узел вниз 00556 _currentNode = _currentNode->getParent(); 00557 } 00558 else 00559 { 00560 // выделяем имя до первого пробела или закрывающего тега 00561 std::string cut = _content; 00562 start = _content.find_first_of(" \t/?", 1); // << превед 00563 if (start != _content.npos) 00564 { 00565 cut = _content.substr(0, start); 00566 _content = _content.substr(start); 00567 } 00568 else 00569 { 00570 _content.clear(); 00571 } 00572 00573 if (_currentNode) _currentNode = _currentNode->createChild(cut); 00574 else 00575 { 00576 if (tag_info) 00577 { 00578 // информационный тег 00579 if (mDeclaration) 00580 { 00581 mLastError = ErrorType::MoreThanOneXMLDeclaration; 00582 return false; 00583 } 00584 _currentNode = new Element(cut, 0, ElementType::Comment); 00585 mDeclaration = _currentNode; 00586 } 00587 else 00588 { 00589 // рутовый тег 00590 if (mRoot) 00591 { 00592 mLastError = ErrorType::MoreThanOneRootElement; 00593 return false; 00594 } 00595 _currentNode = new Element(cut, 0, ElementType::Normal); 00596 mRoot = _currentNode; 00597 } 00598 } 00599 00600 // проверим на пустоту 00601 start = _content.find_last_not_of(" \t"); 00602 if (start == _content.npos) return true; 00603 00604 // сразу отделим закрывающийся тэг 00605 bool close = false; 00606 if ((_content[start] == '/') || (_content[start] == '?')) 00607 { 00608 close = true; 00609 // не будем резать строку, просто поставим пробел 00610 _content[start] = ' '; 00611 // проверим на пустоту 00612 start = _content.find_last_not_of(" \t"); 00613 if (start == _content.npos) 00614 { 00615 // возвращаем все назад и уходим 00616 _currentNode = _currentNode->getParent(); 00617 return true; 00618 } 00619 } 00620 00621 // а вот здесь уже в цикле разбиваем на атрибуты 00622 while (true) 00623 { 00624 // ищем равно 00625 start = _content.find('='); 00626 if (start == _content.npos) 00627 { 00628 mLastError = ErrorType::IncorrectAttribute; 00629 return false; 00630 } 00631 // ищем вторые ковычки 00632 end = _content.find_first_of("\"\'", start+1); 00633 if (end == _content.npos) 00634 { 00635 mLastError = ErrorType::IncorrectAttribute; 00636 return false; 00637 } 00638 end = _content.find_first_of("\"\'", end+1); 00639 if (end == _content.npos) 00640 { 00641 mLastError = ErrorType::IncorrectAttribute; 00642 return false; 00643 } 00644 00645 std::string key = _content.substr(0, start); 00646 std::string value = _content.substr(start+1, end-start); 00647 00648 // проверка на валидность 00649 if (! checkPair(key, value)) 00650 { 00651 mLastError = ErrorType::IncorrectAttribute; 00652 return false; 00653 } 00654 00655 // добавляем пару в узел 00656 _currentNode->addAttribute(key, value); 00657 00658 // следующий кусок 00659 _content = _content.substr(end+1); 00660 00661 // в строке не осталось символов 00662 start = _content.find_first_not_of(" \t"); 00663 if (start == _content.npos) break; 00664 00665 mCol += start; 00666 } 00667 00668 // был закрывающий тег для текущего тега 00669 if (close) 00670 { 00671 // не проверяем имена, потому что это наш тэг 00672 _currentNode = _currentNode->getParent(); 00673 } 00674 00675 } 00676 return true; 00677 } 00678 00679 bool Document::checkPair(std::string &_key, std::string &_value) 00680 { 00681 // в ключе не должно быть ковычек и пробелов 00682 MyGUI::utility::trim(_key); 00683 if (_key.empty()) return false; 00684 size_t start = _key.find_first_of(" \t\"\'&"); 00685 if (start != _key.npos) return false; 00686 00687 // в значении, ковычки по бокам 00688 MyGUI::utility::trim(_value); 00689 if (_value.size() < 2) return false; 00690 if (((_value[0] != '"') || (_value[_value.length()-1] != '"')) && 00691 ((_value[0] != '\'') || (_value[_value.length()-1] != '\''))) return false; 00692 bool ok = true; 00693 _value = utility::convert_from_xml(_value.substr(1, _value.length() - 2), ok); 00694 return ok; 00695 } 00696 00697 // ищет символ без учета ковычек 00698 size_t Document::find(const std::string& _text, char _char, size_t _start) 00699 { 00700 // ковычки 00701 bool kov = false; 00702 00703 // буфер для поиска 00704 char buff[16] = "\"_\0"; 00705 buff[1] = _char; 00706 00707 size_t pos = _start; 00708 00709 while (true) 00710 { 00711 pos = _text.find_first_of(buff, pos); 00712 00713 // если уже конец, то досвидания 00714 if (pos == _text.npos) break; 00715 00716 // нашли ковычку 00717 else if (_text[pos] == '"') 00718 { 00719 kov = !kov; 00720 pos ++; 00721 } 00722 // если мы в ковычках, то идем дальше 00723 else if (kov) pos ++; 00724 00725 // мы не в ковычках 00726 else break; 00727 00728 } 00729 00730 return pos; 00731 } 00732 00733 void Document::clearDeclaration() 00734 { 00735 if (mDeclaration) 00736 { 00737 delete mDeclaration; 00738 mDeclaration = 0; 00739 } 00740 } 00741 00742 void Document::clearRoot() 00743 { 00744 if (mRoot) 00745 { 00746 delete mRoot; 00747 mRoot = 0; 00748 } 00749 } 00750 00751 ElementPtr Document::createDeclaration(const std::string& _version, const std::string& _encoding) 00752 { 00753 clearDeclaration(); 00754 mDeclaration = new Element("xml", 0, ElementType::Declaration); 00755 mDeclaration->addAttribute("version", _version); 00756 mDeclaration->addAttribute("encoding", _encoding); 00757 return mDeclaration; 00758 } 00759 00760 ElementPtr Document::createRoot(const std::string& _name) 00761 { 00762 clearRoot(); 00763 mRoot = new Element(_name, 0, ElementType::Normal); 00764 return mRoot; 00765 } 00766 00767 bool Document::parseLine(std::string& _line, ElementPtr& _element) 00768 { 00769 // крутимся пока в строке есть теги 00770 while (true) 00771 { 00772 // сначала ищем по угловым скобкам 00773 size_t start = find(_line, '<'); 00774 if (start == _line.npos) break; 00775 size_t end = _line.npos; 00776 00777 // пытаемся вырезать многострочный коментарий 00778 if ((start + 3 < _line.size()) && (_line[start + 1] == '!') && (_line[start + 2] == '-') && (_line[start + 3] == '-')) 00779 { 00780 end = _line.find("-->", start + 4); 00781 if (end == _line.npos) break; 00782 end += 2; 00783 } 00784 else 00785 { 00786 end = find(_line, '>', start+1); 00787 if (end == _line.npos) break; 00788 } 00789 // проверяем на наличее тела 00790 size_t body = _line.find_first_not_of(" \t<"); 00791 if (body < start) 00792 { 00793 std::string body_str = _line.substr(0, start); 00794 // текущий символ 00795 mCol = 0; 00796 00797 if (_element != 0) 00798 { 00799 bool ok = true; 00800 _element->setContent(utility::convert_from_xml(body_str, ok)); 00801 if (!ok) 00802 { 00803 mLastError = ErrorType::IncorrectContent; 00804 return false; 00805 } 00806 } 00807 } 00808 // вырезаем наш тэг и парсим 00809 if (!parseTag(_element, _line.substr(start+1, end-start-1))) 00810 { 00811 return false; 00812 } 00813 // и обрезаем текущую строку разбора 00814 _line = _line.substr(end+1); 00815 } 00816 return true; 00817 } 00818 00819 std::string Document::getLastError() 00820 { 00821 const std::string& error = mLastError.print(); 00822 if (error.empty()) return error; 00823 return MyGUI::utility::toString("'", error, "' , file='", mLastErrorFile, "' , line=", mLine, " , col=", mCol); 00824 } 00825 00826 /*Document Document::createCopyFromElement(ElementPtr _node) 00827 { 00828 Document doc; 00829 doc.mRoot = _node->createCopy(); 00830 return doc; 00831 }*/ 00832 00833 } // namespace xml 00834 00835 } // namespace MyGUI