MyGUI  3.0.1
MyGUI_XmlDocument.cpp
Go to the documentation of this file.
1 
7 /*
8  This file is part of MyGUI.
9 
10  MyGUI is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  MyGUI is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with MyGUI. If not, see <http://www.gnu.org/licenses/>.
22 */
23 #include "MyGUI_Precompiled.h"
24 #include "MyGUI_XmlDocument.h"
25 #include "MyGUI_DataManager.h"
26 
27 namespace MyGUI
28 {
29  namespace xml
30  {
31 
32  namespace utility
33  {
34  std::string convert_from_xml(const std::string& _string, bool& _ok)
35  {
36  std::string ret;
37  _ok = true;
38 
39  size_t pos = _string.find("&");
40  if (pos == std::string::npos) return _string;
41 
42  ret.reserve(_string.size());
43  size_t old = 0;
44  while (pos != std::string::npos)
45  {
46  ret += _string.substr(old, pos - old);
47 
48  size_t end = _string.find(";", pos + 1);
49  if (end == std::string::npos)
50  {
51  _ok = false;
52  return ret;
53  }
54  else
55  {
56  std::string tag = _string.substr(pos, end - pos + 1);
57  if (tag == "&amp;") ret += '&';
58  else if (tag == "&lt;") ret += '<';
59  else if (tag == "&gt;") ret += '>';
60  else if (tag == "&apos;") ret += '\'';
61  else if (tag == "&quot;") ret += '\"';
62  else
63  {
64  _ok = false;
65  return ret;
66  }
67  }
68 
69  old = end + 1;
70  pos = _string.find("&", old);
71  }
72  ret += _string.substr(old, std::string::npos);
73 
74  return ret;
75  }
76 
77  std::string convert_to_xml(const std::string& _string)
78  {
79  std::string ret;
80 
81  size_t pos = _string.find_first_of("&<>'\"");
82  if (pos == std::string::npos) return _string;
83 
84  ret.reserve(_string.size() * 2);
85  size_t old = 0;
86  while (pos != std::string::npos)
87  {
88  ret += _string.substr(old, pos - old);
89 
90  if (_string[pos] == '&') ret += "&amp;";
91  else if (_string[pos] == '<') ret += "&lt;";
92  else if (_string[pos] == '>') ret += "&gt;";
93  else if (_string[pos] == '\'') ret += "&apos;";
94  else if (_string[pos] == '\"') ret += "&quot;";
95 
96  old = pos + 1;
97  pos = _string.find_first_of("&<>'\"", old);
98  }
99  ret += _string.substr(old, std::string::npos);
100 
101  return ret;
102  }
103 
104  }
105 
106  //----------------------------------------------------------------------//
107  // class ElementEnumerator
108  //----------------------------------------------------------------------//
109  ElementEnumerator::ElementEnumerator(VectorElement::iterator _begin, VectorElement::iterator _end) :
110  m_first(true),
111  m_current(_begin),
112  m_end(_end)
113  {
114  }
115 
117  {
118  if (m_current == m_end) return false;
119  else if (m_first)
120  {
121  m_first = false;
122  return true;
123  }
124  ++ m_current;
125  if (m_current == m_end) return false;
126  return true;
127  }
128 
129  bool ElementEnumerator::next(const std::string& _name)
130  {
131  while (next())
132  {
133  if ((*m_current)->getName() == _name) return true;
134  }
135  return false;
136  }
137 
138  //----------------------------------------------------------------------//
139  // class Element
140  //----------------------------------------------------------------------//
141  Element::Element(const std::string &_name, ElementPtr _parent, ElementType _type, const std::string& _content) :
142  mName(_name),
143  mContent(_content),
144  mParent(_parent),
145  mType(_type)
146  {
147  }
148 
150  {
151  for (VectorElement::iterator iter=mChilds.begin(); iter!=mChilds.end(); ++iter)
152  {
153  delete *iter;
154  }
155  mChilds.clear();
156  }
157 
158  void Element::save(std::ostream& _stream, size_t _level)
159  {
160  // сначала табуляции намутим
161  for (size_t tab=0; tab<_level; ++tab) _stream << " ";
162 
163  // теперь заголовок тега
164  if (mType == ElementType::Declaration) _stream << "<?";
165  else _stream << "<";
166  _stream << mName;
167 
168  for (VectorAttributes::iterator iter = mAttributes.begin(); iter != mAttributes.end(); ++iter)
169  {
170  _stream << " " << iter->first << "=\"" << utility::convert_to_xml(iter->second) << "\"";
171  }
172 
173  bool empty = mChilds.empty();
174  // если детей нет то закрываем
175  if (empty && mContent.empty())
176  {
177  if (mType == ElementType::Declaration) _stream << "?>\n";
178  else _stream << "/>\n";
179  }
180  else
181  {
182  _stream << ">";
183  if (!empty) _stream << "\n";
184  // если есть тело то сначало оно
185  if (!mContent.empty())
186  {
187  if (!empty)
188  {
189  for (size_t tab=0; tab<=_level; ++tab) _stream << " ";
190  }
191  _stream << utility::convert_to_xml(mContent);
192 
193  if (!empty) _stream << "\n";
194  }
195  // если есть детишки путь сохранятся
196  for (size_t child=0; child<mChilds.size(); child++)
197  {
198  mChilds[child]->save(_stream, _level + 1);
199  }
200 
201  if (!empty)
202  {
203  for (size_t tab=0; tab<_level; ++tab)
204  _stream << " ";
205  }
206  _stream << "</" << mName << ">\n";
207  }
208 
209  }
210 
211  ElementPtr Element::createChild(const std::string& _name, const std::string& _content)
212  {
213  ElementPtr node = new Element(_name, this, ElementType::Normal, _content);
214  mChilds.push_back(node);
215  return node;
216  }
217 
219  {
220  for (VectorElement::iterator iter = mChilds.begin(); iter != mChilds.end(); ++iter) delete *iter;
221  mChilds.clear();
222  mContent.clear();
223  mAttributes.clear();
224  }
225 
226  bool Element::findAttribute(const std::string& _name, std::string& _value)
227  {
228  for (VectorAttributes::iterator iter=mAttributes.begin(); iter!=mAttributes.end(); ++iter)
229  {
230  if ( (*iter).first == _name)
231  {
232  _value = (*iter).second;
233  return true;
234  }
235  }
236  return false;
237  }
238 
239  std::string Element::findAttribute(const std::string& _name)
240  {
241  for (VectorAttributes::iterator iter=mAttributes.begin(); iter!=mAttributes.end(); ++iter)
242  {
243  if ( (*iter).first == _name) return (*iter).second;
244  }
245  return "";
246  }
247 
248  void Element::addAttribute(const std::string& _key, const std::string& _value)
249  {
250  mAttributes.push_back(PairAttribute(_key, _value));
251  }
252 
253  void Element::removeAttribute(const std::string& _key)
254  {
255  for (size_t index=0; index<mAttributes.size(); ++index)
256  {
257  if (mAttributes[index].first == _key)
258  {
259  mAttributes.erase(mAttributes.begin() + index);
260  return;
261  }
262  }
263  }
264 
266  {
267  Element* elem = new Element(mName, nullptr, mType, mContent);
268  elem->mAttributes = mAttributes;
269 
270  for (VectorElement::iterator iter=mChilds.begin(); iter!=mChilds.end(); ++iter)
271  {
272  Element* child = (*iter)->createCopy();
273  child->mParent = elem;
274  elem->mChilds.push_back(child);
275  }
276 
277  return elem;
278  }
279 
280  void Element::setAttribute(const std::string& _key, const std::string& _value)
281  {
282  for (size_t index=0; index<mAttributes.size(); ++index)
283  {
284  if (mAttributes[index].first == _key)
285  {
286  mAttributes[index].second = _value;
287  return;
288  }
289  }
290  mAttributes.push_back(PairAttribute(_key, _value));
291  }
292 
293  void Element::addContent(const std::string& _content)
294  {
295  if (mContent.empty()) mContent = _content;
296  else
297  {
298  mContent += " ";
299  mContent += _content;
300  }
301  }
302 
303 #if MYGUI_COMPILER == MYGUI_COMPILER_MSVC && !defined(STLPORT)
304  inline void open_stream(std::ofstream& _stream, const std::wstring& _wide) { _stream.open(_wide.c_str()); }
305  inline void open_stream(std::ifstream& _stream, const std::wstring& _wide) { _stream.open(_wide.c_str()); }
306 #else
307  inline void open_stream(std::ofstream& _stream, const std::wstring& _wide) { _stream.open(UString(_wide).asUTF8_c_str()); }
308  inline void open_stream(std::ifstream& _stream, const std::wstring& _wide) { _stream.open(UString(_wide).asUTF8_c_str()); }
309 #endif
310 
311  //----------------------------------------------------------------------//
312  // class Document
313  //----------------------------------------------------------------------//
315  mRoot(0),
316  mDeclaration(0),
317  mLastErrorFile(""),
318  mLine(0),
319  mCol(0)
320  {
321  }
322 
324  {
325  clear();
326  }
327 
328  // открывает обычным файлом, имя файла в utf8
329  bool Document::open(const std::string& _filename)
330  {
331  std::ifstream stream;
332  stream.open(_filename.c_str());
333 
334  if (!stream.is_open())
335  {
336  mLastError = ErrorType::OpenFileFail;
337  setLastFileError(_filename);
338  return false;
339  }
340 
341  bool result = open(stream);
342 
343  stream.close();
344  return result;
345  }
346 
347  // открывает обычным файлом, имя файла в utf16 или utf32
348  bool Document::open(const std::wstring& _filename)
349  {
350  std::ifstream stream;
351  open_stream(stream, _filename);
352 
353  if (!stream.is_open())
354  {
355  mLastError = ErrorType::OpenFileFail;
356  setLastFileError(_filename);
357  return false;
358  }
359 
360  bool result = open(stream);
361 
362  stream.close();
363  return result;
364  }
365 
366  bool Document::open(std::istream& _stream)
367  {
368  DataStream* data = new DataStream(&_stream);
369 
370  bool result = open(data);
371  delete data;
372 
373  return result;
374  }
375 
376  // сохраняет файл, имя файла в кодировке utf8
377  bool Document::save(const std::string& _filename)
378  {
379  std::ofstream stream;
380  stream.open(_filename.c_str());
381 
382  if (!stream.is_open())
383  {
384  mLastError = ErrorType::CreateFileFail;
385  setLastFileError(_filename);
386  return false;
387  }
388 
389  bool result = save(stream);
390 
391  if (!result)
392  {
393  setLastFileError(_filename);
394  }
395 
396  stream.close();
397  return result;
398  }
399 
400  // сохраняет файл, имя файла в кодировке utf16 или utf32
401  bool Document::save(const std::wstring& _filename)
402  {
403  std::ofstream stream;
404  open_stream(stream, _filename);
405 
406  if (!stream.is_open())
407  {
408  mLastError = ErrorType::CreateFileFail;
409  setLastFileError(_filename);
410  return false;
411  }
412 
413  bool result = save(stream);
414 
415  if (!result)
416  {
417  setLastFileError(_filename);
418  }
419 
420  stream.close();
421  return result;
422  }
423 
424  // открывает обычным потоком
426  {
427  clear();
428 
429  // это текущая строка для разбора
430  std::string line;
431  // это строка из файла
432  std::string read;
433  // текущий узел для разбора
434  ElementPtr currentNode = 0;
435 
436  while (!_stream->eof())
437  {
438  // берем новую строку
439  _stream->readline(read, '\n');
440  if (read.empty()) continue;
441  if (read[read.size()-1] == '\r') read.erase(read.size()-1, 1);
442  if (read.empty()) continue;
443 
444  mLine ++;
445  mCol = 0; // потом проверить на многострочных тэгах
446  if (read.empty()) continue;
447  // текущая строка для разбора и то что еще прочитали
448  line += read;
449 
450  if (!parseLine(line, currentNode))
451  {
452  return false;
453  }
454 
455  } // while (!stream.eof())
456 
457  if (currentNode)
458  {
459  mLastError = ErrorType::NotClosedElements;
460  return false;
461  }
462 
463  return true;
464  }
465 
466  bool Document::save(std::ostream& _stream)
467  {
468  if (!mDeclaration)
469  {
470  mLastError = ErrorType::NoXMLDeclaration;
471  return false;
472  }
473 
474  // заголовок utf8
475  _stream << (char)0xEFu;
476  _stream << (char)0xBBu;
477  _stream << (char)0xBFu;
478 
479  mDeclaration->save(_stream, 0);
480  if (mRoot) mRoot->save(_stream, 0);
481 
482  return true;
483  }
484 
486  {
487  clearDeclaration();
488  clearRoot();
489  mLine = 0;
490  mCol = 0;
491  }
492 
493  bool Document::parseTag(ElementPtr &_currentNode, std::string _content)
494  {
495 
496  // убераем лишнее
497  MyGUI::utility::trim(_content);
498 
499  if (_content.empty())
500  {
501  // создаем пустой тег
502  if (_currentNode) _currentNode = _currentNode->createChild("");
503  else
504  {
505  _currentNode = new Element("", 0);
506  // если это первый то запоминаем
507  if (!mRoot) mRoot = _currentNode;
508  }
509  return true;
510  }
511 
512  char simbol = _content[0];
513  bool tag_info = false;
514 
515  if (simbol == '!') return true; // проверяем на коментарии
516 
517  // проверяем на информационный тег
518  if (simbol == '?')
519  {
520  tag_info = true;
521  _content.erase(0, 1); // удаляем первый символ
522  }
523 
524  size_t start, end;
525  // проверяем на закрытие тега
526  if (simbol == '/')
527  {
528  if (_currentNode == 0)
529  {
530  // чета мы закрывам а ниче даже и не открыто
531  if (!mRoot)
532  {
534  return false;
535  }
536  }
537  // обрезаем имя тэга
538  start = _content.find_first_not_of(" \t", 1);
539  if (start == _content.npos)
540  {
541  // тег пустой
542  _content.clear();
543  }
544  else
545  {
546  end = _content.find_last_not_of(" \t");
547  _content = _content.substr(start, end - start+1);
548  }
549  // проверяем соответствие открывающего и закрывающего тегов
550  if (_currentNode->getName() != _content)
551  {
553  return false;
554  }
555  // а теперь снижаем текущий узел вниз
556  _currentNode = _currentNode->getParent();
557  }
558  else
559  {
560  // выделяем имя до первого пробела или закрывающего тега
561  std::string cut = _content;
562  start = _content.find_first_of(" \t/?", 1); // << превед
563  if (start != _content.npos)
564  {
565  cut = _content.substr(0, start);
566  _content = _content.substr(start);
567  }
568  else
569  {
570  _content.clear();
571  }
572 
573  if (_currentNode) _currentNode = _currentNode->createChild(cut);
574  else
575  {
576  if (tag_info)
577  {
578  // информационный тег
579  if (mDeclaration)
580  {
582  return false;
583  }
584  _currentNode = new Element(cut, 0, ElementType::Comment);
585  mDeclaration = _currentNode;
586  }
587  else
588  {
589  // рутовый тег
590  if (mRoot)
591  {
593  return false;
594  }
595  _currentNode = new Element(cut, 0, ElementType::Normal);
596  mRoot = _currentNode;
597  }
598  }
599 
600  // проверим на пустоту
601  start = _content.find_last_not_of(" \t");
602  if (start == _content.npos) return true;
603 
604  // сразу отделим закрывающийся тэг
605  bool close = false;
606  if ((_content[start] == '/') || (_content[start] == '?'))
607  {
608  close = true;
609  // не будем резать строку, просто поставим пробел
610  _content[start] = ' ';
611  // проверим на пустоту
612  start = _content.find_last_not_of(" \t");
613  if (start == _content.npos)
614  {
615  // возвращаем все назад и уходим
616  _currentNode = _currentNode->getParent();
617  return true;
618  }
619  }
620 
621  // а вот здесь уже в цикле разбиваем на атрибуты
622  while (true)
623  {
624  // ищем равно
625  start = _content.find('=');
626  if (start == _content.npos)
627  {
628  mLastError = ErrorType::IncorrectAttribute;
629  return false;
630  }
631  // ищем вторые ковычки
632  end = _content.find_first_of("\"\'", start+1);
633  if (end == _content.npos)
634  {
635  mLastError = ErrorType::IncorrectAttribute;
636  return false;
637  }
638  end = _content.find_first_of("\"\'", end+1);
639  if (end == _content.npos)
640  {
641  mLastError = ErrorType::IncorrectAttribute;
642  return false;
643  }
644 
645  std::string key = _content.substr(0, start);
646  std::string value = _content.substr(start+1, end-start);
647 
648  // проверка на валидность
649  if (! checkPair(key, value))
650  {
651  mLastError = ErrorType::IncorrectAttribute;
652  return false;
653  }
654 
655  // добавляем пару в узел
656  _currentNode->addAttribute(key, value);
657 
658  // следующий кусок
659  _content = _content.substr(end+1);
660 
661  // в строке не осталось символов
662  start = _content.find_first_not_of(" \t");
663  if (start == _content.npos) break;
664 
665  mCol += start;
666  }
667 
668  // был закрывающий тег для текущего тега
669  if (close)
670  {
671  // не проверяем имена, потому что это наш тэг
672  _currentNode = _currentNode->getParent();
673  }
674 
675  }
676  return true;
677  }
678 
679  bool Document::checkPair(std::string &_key, std::string &_value)
680  {
681  // в ключе не должно быть ковычек и пробелов
682  MyGUI::utility::trim(_key);
683  if (_key.empty()) return false;
684  size_t start = _key.find_first_of(" \t\"\'&");
685  if (start != _key.npos) return false;
686 
687  // в значении, ковычки по бокам
688  MyGUI::utility::trim(_value);
689  if (_value.size() < 2) return false;
690  if (((_value[0] != '"') || (_value[_value.length()-1] != '"')) &&
691  ((_value[0] != '\'') || (_value[_value.length()-1] != '\''))) return false;
692  bool ok = true;
693  _value = utility::convert_from_xml(_value.substr(1, _value.length() - 2), ok);
694  return ok;
695  }
696 
697  // ищет символ без учета ковычек
698  size_t Document::find(const std::string& _text, char _char, size_t _start)
699  {
700  // ковычки
701  bool kov = false;
702 
703  // буфер для поиска
704  char buff[16] = "\"_\0";
705  buff[1] = _char;
706 
707  size_t pos = _start;
708 
709  while (true)
710  {
711  pos = _text.find_first_of(buff, pos);
712 
713  // если уже конец, то досвидания
714  if (pos == _text.npos) break;
715 
716  // нашли ковычку
717  else if (_text[pos] == '"')
718  {
719  kov = !kov;
720  pos ++;
721  }
722  // если мы в ковычках, то идем дальше
723  else if (kov) pos ++;
724 
725  // мы не в ковычках
726  else break;
727 
728  }
729 
730  return pos;
731  }
732 
733  void Document::clearDeclaration()
734  {
735  if (mDeclaration)
736  {
737  delete mDeclaration;
738  mDeclaration = 0;
739  }
740  }
741 
742  void Document::clearRoot()
743  {
744  if (mRoot)
745  {
746  delete mRoot;
747  mRoot = 0;
748  }
749  }
750 
751  ElementPtr Document::createDeclaration(const std::string& _version, const std::string& _encoding)
752  {
753  clearDeclaration();
754  mDeclaration = new Element("xml", 0, ElementType::Declaration);
755  mDeclaration->addAttribute("version", _version);
756  mDeclaration->addAttribute("encoding", _encoding);
757  return mDeclaration;
758  }
759 
760  ElementPtr Document::createRoot(const std::string& _name)
761  {
762  clearRoot();
763  mRoot = new Element(_name, 0, ElementType::Normal);
764  return mRoot;
765  }
766 
767  bool Document::parseLine(std::string& _line, ElementPtr& _element)
768  {
769  // крутимся пока в строке есть теги
770  while (true)
771  {
772  // сначала ищем по угловым скобкам
773  size_t start = find(_line, '<');
774  if (start == _line.npos) break;
775  size_t end = _line.npos;
776 
777  // пытаемся вырезать многострочный коментарий
778  if ((start + 3 < _line.size()) && (_line[start + 1] == '!') && (_line[start + 2] == '-') && (_line[start + 3] == '-'))
779  {
780  end = _line.find("-->", start + 4);
781  if (end == _line.npos) break;
782  end += 2;
783  }
784  else
785  {
786  end = find(_line, '>', start+1);
787  if (end == _line.npos) break;
788  }
789  // проверяем на наличее тела
790  size_t body = _line.find_first_not_of(" \t<");
791  if (body < start)
792  {
793  std::string body_str = _line.substr(0, start);
794  // текущий символ
795  mCol = 0;
796 
797  if (_element != 0)
798  {
799  bool ok = true;
800  _element->setContent(utility::convert_from_xml(body_str, ok));
801  if (!ok)
802  {
803  mLastError = ErrorType::IncorrectContent;
804  return false;
805  }
806  }
807  }
808  // вырезаем наш тэг и парсим
809  if (!parseTag(_element, _line.substr(start+1, end-start-1)))
810  {
811  return false;
812  }
813  // и обрезаем текущую строку разбора
814  _line = _line.substr(end+1);
815  }
816  return true;
817  }
818 
820  {
821  const std::string& error = mLastError.print();
822  if (error.empty()) return error;
823  return MyGUI::utility::toString("'", error, "' , file='", mLastErrorFile, "' , line=", mLine, " , col=", mCol);
824  }
825 
826  /*Document Document::createCopyFromElement(ElementPtr _node)
827  {
828  Document doc;
829  doc.mRoot = _node->createCopy();
830  return doc;
831  }*/
832 
833  } // namespace xml
834 
835 } // namespace MyGUI