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