1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Remere's Map Editor is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#ifndef OTML_H
19#define OTML_H
20
21#include <sstream>
22#include <fstream>
23#include <string>
24#include <vector>
25#include <exception>
26#include <memory>
27#include <algorithm>
28#include <cmath>
29#include <boost/algorithm/string.hpp>
30#include <boost/tokenizer.hpp>
31
32class OTMLNode;
33class OTMLDocument;
34class OTMLParser;
35class OTMLEmitter;
36
37#ifdef __GXX_EXPERIMENTAL_CXX0X__
38typedef std::shared_ptr<OTMLNode> OTMLNodePtr;
39typedef std::enable_shared_from_this<OTMLNode> OTMLNodeEnableSharedFromThis;
40typedef std::shared_ptr<OTMLDocument> OTMLDocumentPtr;
41typedef std::weak_ptr<OTMLNode> OTMLNodeWeakPtr;
42#else
43 #include <boost/shared_ptr.hpp>
44 #include <boost/enable_shared_from_this.hpp>
45typedef boost::shared_ptr<OTMLNode> OTMLNodePtr;
46typedef boost::enable_shared_from_this<OTMLNode> OTMLNodeEnableSharedFromThis;
47typedef boost::shared_ptr<OTMLDocument> OTMLDocumentPtr;
48typedef boost::weak_ptr<OTMLNode> OTMLNodeWeakPtr;
49#endif
50
51typedef std::vector<OTMLNodePtr> OTMLNodeList;
52
53namespace otml_util {
54 template <typename T, typename R>
55 bool cast(const T& in, R& out) {
56 std::stringstream ss;
57 ss << in;
58 ss >> out;
59 return !!ss && ss.eof();
60 }
61
62 template <typename T>
63 bool cast(const T& in, std::string& out) {
64 std::stringstream ss;
65 ss << in;
66 out = ss.str();
67 return true;
68 }
69
70 template <>
71 inline bool cast(const std::string& in, std::string& out) {
72 out = in;
73 return true;
74 }
75
76 template <>
77 inline bool cast(const std::string& in, bool& b) {
78 if (in == "true") {
79 b = true;
80 } else if (in == "false") {
81 b = false;
82 } else {
83 return false;
84 }
85 return true;
86 }
87
88 template <>
89 inline bool cast(const std::string& in, char& c) {
90 if (in.length() != 1) {
91 return false;
92 }
93 c = in[0];
94 return true;
95 }
96
97 template <>
98 inline bool cast(const std::string& in, long& l) {
99 if (in.find_first_not_of("-0123456789") != std::string::npos) {
100 return false;
101 }
102 std::size_t t = in.find_last_of('-');
103 if (t != std::string::npos && t != 0) {
104 return false;
105 }
106 l = atol(in.c_str());
107 return true;
108 }
109
110 template <>
111 inline bool cast(const std::string& in, int& i) {
112 long l;
113 if (cast(in, l)) {
114 i = l;
115 return true;
116 }
117 return false;
118 }
119
120 template <>
121 inline bool cast(const std::string& in, double& d) {
122 if (in.find_first_not_of("-0123456789.") != std::string::npos) {
123 return false;
124 }
125 std::size_t t = in.find_last_of('-');
126 if (t != std::string::npos && t != 0) {
127 return false;
128 }
129 t = in.find_first_of('.');
130 if (t != std::string::npos && (t == 0 || t == in.length() - 1 || in.find_first_of('.', t + 1) != std::string::npos)) {
131 return false;
132 }
133 d = atof(in.c_str());
134 return true;
135 }
136
137 template <>
138 inline bool cast(const bool& in, std::string& out) {
139 out = (in ? "true" : "false");
140 return true;
141 }
142
143 class BadCast : public std::bad_cast {
144 public:
145 virtual ~BadCast() throw() { }
146 virtual const char* what() {
147 return "failed to cast value";
148 }
149 };
150
151 template <typename R, typename T>
152 R safeCast(const T& t) {
153 R r;
154 if (!cast(t, r)) {
155 throw BadCast();
156 }
157 return r;
158 }
159};
160
161class OTMLException : public std::exception {
162public:
163 OTMLException(const std::string& error) :
164 m_what(error) { }
165 OTMLException(const OTMLNodePtr& node, const std::string& error);
166 OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line = -1);
167 virtual ~OTMLException() throw() {};
168
169 virtual const char* what() const throw() {
170 return m_what.c_str();
171 }
172
173protected:
174 std::string m_what;
175};
176
177class OTMLNode : public OTMLNodeEnableSharedFromThis {
178public:
179 virtual ~OTMLNode() { }
180
181 static OTMLNodePtr create(std::string tag = "", bool unique = false);
182 static OTMLNodePtr create(std::string tag, std::string value);
183
184 std::string tag() const {
185 return m_tag;
186 }
187 int size() const {
188 return m_children.size();
189 }
190 OTMLNodePtr parent() const {
191 return m_parent.lock();
192 }
193 std::string source() const {
194 return m_source;
195 }
196 std::string rawValue() const {
197 return m_value;
198 }
199
200 bool isUnique() const {
201 return m_unique;
202 }
203 bool isNull() const {
204 return m_null;
205 }
206
207 bool hasTag() const {
208 return !m_tag.empty();
209 }
210 bool hasValue() const {
211 return !m_value.empty();
212 }
213 bool hasChildren() const;
214 bool hasChildAt(const std::string& childTag) {
215 return !!get(childTag);
216 }
217 bool hasChildAtIndex(int childIndex) {
218 return !!getIndex(childIndex);
219 }
220
221 void setTag(std::string tag) {
222 m_tag = tag;
223 }
224 void setValue(const std::string& value) {
225 m_value = value;
226 }
227 void setNull(bool null) {
228 m_null = null;
229 }
230 void setUnique(bool unique) {
231 m_unique = unique;
232 }
233 void setParent(const OTMLNodePtr& parent) {
234 m_parent = parent;
235 }
236 void setSource(const std::string& source) {
237 m_source = source;
238 }
239
240 OTMLNodePtr get(const std::string& childTag) const;
241 OTMLNodePtr getIndex(int childIndex) const;
242
243 OTMLNodePtr at(const std::string& childTag);
244 OTMLNodePtr atIndex(int childIndex);
245
246 void addChild(const OTMLNodePtr& newChild);
247 bool removeChild(const OTMLNodePtr& oldChild);
248 bool replaceChild(const OTMLNodePtr& oldChild, const OTMLNodePtr& newChild);
249 void merge(const OTMLNodePtr& node);
250 void copy(const OTMLNodePtr& node);
251 void clear();
252
253 OTMLNodeList children() const;
254 OTMLNodePtr clone() const;
255
256 template <typename T>
257 T value();
258 template <typename T>
259 T valueAt(const std::string& childTag);
260 template <typename T>
261 T valueAtIndex(int childIndex);
262 template <typename T>
263 T valueAt(const std::string& childTag, const T& def);
264 template <typename T>
265 T valueAtIndex(int childIndex, const T& def);
266
267 template <typename T>
268 void write(const T& v);
269 template <typename T>
270 void writeAt(const std::string& childTag, const T& v);
271 template <typename T>
272 void writeIn(const T& v);
273
274 virtual std::string emit();
275
276protected:
277 OTMLNode() :
278 m_unique(false), m_null(false) { }
279
280 OTMLNodeList m_children;
281 OTMLNodeWeakPtr m_parent;
282 std::string m_tag;
283 std::string m_value;
284 std::string m_source;
285 bool m_unique;
286 bool m_null;
287};
288
289class OTMLDocument : public OTMLNode {
290public:
291 virtual ~OTMLDocument() { }
292 static OTMLDocumentPtr create();
293 static OTMLDocumentPtr parse(const std::string& fileName);
294 static OTMLDocumentPtr parse(std::istream& in, const std::string& source);
295 std::string emit();
296 bool save(const std::string& fileName);
297
298private:
299 OTMLDocument() { }
300};
301
302class OTMLParser {
303public:
304 OTMLParser(OTMLDocumentPtr doc, std::istream& in) :
305 currentDepth(0), currentLine(0),
306 doc(doc), currentParent(doc),
307 in(in) { }
308 void parse();
309
310private:
311 std::string getNextLine();
312 int getLineDepth(const std::string& line, bool multilining = false);
313 void parseLine(std::string line);
314 void parseNode(const std::string& data);
315
316 int currentDepth;
317 int currentLine;
318 OTMLDocumentPtr doc;
319 OTMLNodePtr currentParent;
320 OTMLNodePtr previousNode;
321 std::istream& in;
322};
323
324class OTMLEmitter {
325public:
326 static std::string emitNode(const OTMLNodePtr& node, int currentDepth = -1);
327};
328
329inline OTMLException::OTMLException(const OTMLNodePtr& node, const std::string& error) {
330 std::stringstream ss;
331 ss << "OTML error";
332 if (!node->source().empty()) {
333 ss << " in '" << node->source() << "'";
334 }
335 ss << ": " << error;
336 m_what = ss.str();
337}
338
339inline OTMLException::OTMLException(const OTMLDocumentPtr& doc, const std::string& error, int line) {
340 std::stringstream ss;
341 ss << "OTML error";
342 if (doc && !doc->source().empty()) {
343 ss << " in '" << doc->source() << "'";
344 if (line >= 0) {
345 ss << " at line " << line;
346 }
347 }
348 ss << ": " << error;
349 m_what = ss.str();
350}
351
352inline OTMLNodePtr OTMLNode::create(std::string tag, bool unique) {
353 OTMLNodePtr node(new OTMLNode);
354 node->setTag(tag);
355 node->setUnique(unique);
356 return node;
357}
358
359inline OTMLNodePtr OTMLNode::create(std::string tag, std::string value) {
360 OTMLNodePtr node(new OTMLNode);
361 node->setTag(tag);
362 node->setValue(value);
363 node->setUnique(true);
364 return node;
365}
366
367inline bool OTMLNode::hasChildren() const {
368 int count = 0;
369 for (OTMLNodeList::const_iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
370 const OTMLNodePtr& child = *it;
371 if (!child->isNull()) {
372 count++;
373 }
374 }
375 return count > 0;
376}
377
378inline OTMLNodePtr OTMLNode::get(const std::string& childTag) const {
379 for (OTMLNodeList::const_iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
380 const OTMLNodePtr& child = *it;
381 if (child->tag() == childTag && !child->isNull()) {
382 return child;
383 }
384 }
385 return OTMLNodePtr();
386}
387
388inline OTMLNodePtr OTMLNode::getIndex(int childIndex) const {
389 if (childIndex < size() && childIndex >= 0) {
390 return m_children[childIndex];
391 }
392 return OTMLNodePtr();
393}
394
395inline OTMLNodePtr OTMLNode::at(const std::string& childTag) {
396 OTMLNodePtr res;
397 for (OTMLNodeList::iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
398 const OTMLNodePtr& child = *it;
399 if (child->tag() == childTag && !child->isNull()) {
400 res = child;
401 break;
402 }
403 }
404 if (!res) {
405 std::stringstream ss;
406 ss << "child node with tag '" << childTag << "' not found";
407 throw OTMLException(shared_from_this(), ss.str());
408 }
409 return res;
410}
411
412inline OTMLNodePtr OTMLNode::atIndex(int childIndex) {
413 if (childIndex >= size() || childIndex < 0) {
414 std::stringstream ss;
415 ss << "child node with index '" << childIndex << "' not found";
416 throw OTMLException(shared_from_this(), ss.str());
417 }
418 return m_children[childIndex];
419}
420
421inline void OTMLNode::addChild(const OTMLNodePtr& newChild) {
422 if (newChild->hasTag()) {
423 for (OTMLNodeList::iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
424 const OTMLNodePtr& node = *it;
425 if (node->tag() == newChild->tag() && (node->isUnique() || newChild->isUnique())) {
426 newChild->setUnique(true);
427
428 if (node->hasChildren() && newChild->hasChildren()) {
429 OTMLNodePtr tmpNode = node->clone();
430 tmpNode->merge(newChild);
431 newChild->copy(tmpNode);
432 }
433
434 replaceChild(node, newChild);
435 OTMLNodeList::iterator it = m_children.begin();
436 while (it != m_children.end()) {
437 OTMLNodePtr node = (*it);
438 if (node != newChild && node->tag() == newChild->tag()) {
439 node->setParent(OTMLNodePtr());
440 it = m_children.erase(it);
441 } else {
442 ++it;
443 }
444 }
445 return;
446 }
447 }
448 }
449 m_children.push_back(newChild);
450 newChild->setParent(shared_from_this());
451}
452
453inline bool OTMLNode::removeChild(const OTMLNodePtr& oldChild) {
454 OTMLNodeList::iterator it = std::find(m_children.begin(), m_children.end(), oldChild);
455 if (it != m_children.end()) {
456 m_children.erase(it);
457 oldChild->setParent(OTMLNodePtr());
458 return true;
459 }
460 return false;
461}
462
463inline bool OTMLNode::replaceChild(const OTMLNodePtr& oldChild, const OTMLNodePtr& newChild) {
464 OTMLNodeList::iterator it = std::find(m_children.begin(), m_children.end(), oldChild);
465 if (it != m_children.end()) {
466 oldChild->setParent(OTMLNodePtr());
467 newChild->setParent(shared_from_this());
468 it = m_children.erase(it);
469 m_children.insert(it, newChild);
470 return true;
471 }
472 return false;
473}
474
475inline void OTMLNode::copy(const OTMLNodePtr& node) {
476 setTag(node->tag());
477 setValue(node->rawValue());
478 setUnique(node->isUnique());
479 setNull(node->isNull());
480 setSource(node->source());
481 clear();
482 for (OTMLNodeList::iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
483 const OTMLNodePtr& child = *it;
484 addChild(child->clone());
485 }
486}
487
488inline void OTMLNode::merge(const OTMLNodePtr& node) {
489 for (OTMLNodeList::iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
490 const OTMLNodePtr& child = *it;
491 addChild(child->clone());
492 }
493 setTag(node->tag());
494 setSource(node->source());
495}
496
497inline void OTMLNode::clear() {
498 for (OTMLNodeList::iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
499 const OTMLNodePtr& child = *it;
500 child->setParent(OTMLNodePtr());
501 }
502 m_children.clear();
503}
504
505inline OTMLNodeList OTMLNode::children() const {
506 OTMLNodeList children;
507 for (OTMLNodeList::const_iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
508 const OTMLNodePtr& child = *it;
509 if (!child->isNull()) {
510 children.push_back(child);
511 }
512 }
513 return children;
514}
515
516inline OTMLNodePtr OTMLNode::clone() const {
517 OTMLNodePtr myClone(new OTMLNode);
518 myClone->setTag(m_tag);
519 myClone->setValue(m_value);
520 myClone->setUnique(m_unique);
521 myClone->setNull(m_null);
522 myClone->setSource(m_source);
523 for (OTMLNodeList::const_iterator it = m_children.begin(), end = m_children.end(); it != end; ++it) {
524 const OTMLNodePtr& child = *it;
525 myClone->addChild(child->clone());
526 }
527 return myClone;
528}
529
530inline std::string OTMLNode::emit() {
531 return OTMLEmitter::emitNode(shared_from_this(), 0);
532}
533
534template <>
535inline std::string OTMLNode::value() {
536 std::string value = m_value;
537 if (boost::starts_with(value, "\"") && boost::ends_with(value, "\"")) {
538 value = value.substr(1, value.length() - 2);
539 boost::replace_all(value, "\\\\", "\\");
540 boost::replace_all(value, "\\\"", "\"");
541 boost::replace_all(value, "\\t", "\t");
542 boost::replace_all(value, "\\n", "\n");
543 boost::replace_all(value, "\\'", "\'");
544 }
545 return value;
546}
547
548template <typename T>
549T OTMLNode::value() {
550 T ret;
551 if (!otml_util::cast(m_value, ret)) {
552 throw OTMLException(shared_from_this(), "failed to cast node value");
553 }
554 return ret;
555}
556
557template <typename T>
558T OTMLNode::valueAt(const std::string& childTag) {
559 OTMLNodePtr node = at(childTag);
560 return node->value<T>();
561}
562
563template <typename T>
564T OTMLNode::valueAtIndex(int childIndex) {
565 OTMLNodePtr node = atIndex(childIndex);
566 return node->value<T>();
567}
568
569template <typename T>
570T OTMLNode::valueAt(const std::string& childTag, const T& def) {
571 if (OTMLNodePtr node = get(childTag)) {
572 if (!node->isNull()) {
573 return node->value<T>();
574 }
575 }
576 return def;
577}
578
579template <typename T>
580T OTMLNode::valueAtIndex(int childIndex, const T& def) {
581 if (OTMLNodePtr node = getIndex(childIndex)) {
582 return node->value<T>();
583 }
584 return def;
585}
586
587template <typename T>
588void OTMLNode::write(const T& v) {
589 m_value = otml_util::safeCast<std::string>(v);
590}
591
592template <typename T>
593void OTMLNode::writeAt(const std::string& childTag, const T& v) {
594 OTMLNodePtr child = OTMLNode::create(childTag);
595 child->setUnique(true);
596 child->write<T>(v);
597 addChild(child);
598}
599
600template <typename T>
601void OTMLNode::writeIn(const T& v) {
602 OTMLNodePtr child = OTMLNode::create();
603 child->write<T>(v);
604 addChild(child);
605}
606
607inline OTMLDocumentPtr OTMLDocument::create() {
608 OTMLDocumentPtr doc(new OTMLDocument);
609 doc->setTag("doc");
610 return doc;
611}
612
613inline OTMLDocumentPtr OTMLDocument::parse(const std::string& fileName) {
614 std::ifstream fin(fileName.c_str());
615 if (!fin.good()) {
616 std::stringstream ss;
617 ss << "failed to open file " << fileName;
618 throw OTMLException(ss.str());
619 }
620 return parse(fin, fileName);
621}
622
623inline OTMLDocumentPtr OTMLDocument::parse(std::istream& in, const std::string& source) {
624 OTMLDocumentPtr doc(new OTMLDocument);
625 doc->setSource(source);
626 OTMLParser parser(doc, in);
627 parser.parse();
628 return doc;
629}
630
631inline std::string OTMLDocument::emit() {
632 return OTMLEmitter::emitNode(shared_from_this()) + "\n";
633}
634
635inline bool OTMLDocument::save(const std::string& fileName) {
636 m_source = fileName;
637 std::ofstream fout(fileName.c_str());
638 if (fout.good()) {
639 fout << emit();
640 fout.close();
641 return true;
642 }
643 return false;
644}
645
646inline std::string OTMLEmitter::emitNode(const OTMLNodePtr& node, int currentDepth) {
647 std::stringstream ss;
648 if (currentDepth >= 0) {
649 for (int i = 0; i < currentDepth; ++i) {
650 ss << " ";
651 }
652 if (node->hasTag()) {
653 ss << node->tag();
654 if (node->hasValue() || node->isUnique() || node->isNull()) {
655 ss << ":";
656 }
657 } else {
658 ss << "-";
659 }
660 if (node->isNull()) {
661 ss << " ~";
662 } else if (node->hasValue()) {
663 ss << " ";
664 std::string value = node->rawValue();
665 if (value.find("\n") != std::string::npos) {
666 if (value[value.length() - 1] == '\n' && value[value.length() - 2] == '\n') {
667 ss << "|+";
668 } else if (value[value.length() - 1] == '\n') {
669 ss << "|";
670 } else {
671 ss << "|-";
672 }
673 for (std::size_t pos = 0; pos < value.length(); ++pos) {
674 ss << "\n";
675 for (int i = 0; i < currentDepth + 1; ++i) {
676 ss << " ";
677 }
678 while (pos < value.length()) {
679 if (value[pos] == '\n') {
680 break;
681 }
682 ss << value[pos++];
683 }
684 }
685 } else {
686 ss << value;
687 }
688 }
689 }
690 for (int i = 0; i < node->size(); ++i) {
691 if (currentDepth >= 0 || i != 0) {
692 ss << "\n";
693 }
694 ss << emitNode(node->atIndex(i), currentDepth + 1);
695 }
696 return ss.str();
697}
698
699inline void OTMLParser::parse() {
700 if (!in.good()) {
701 throw OTMLException(doc, "cannot read from input stream");
702 }
703 while (!in.eof()) {
704 parseLine(getNextLine());
705 }
706}
707
708inline std::string OTMLParser::getNextLine() {
709 currentLine++;
710 std::string line;
711 std::getline(in, line);
712 return line;
713}
714
715inline int OTMLParser::getLineDepth(const std::string& line, bool multilining) {
716 std::size_t spaces = 0;
717 while (line[spaces] == ' ') {
718 spaces++;
719 }
720
721 int depth = spaces / 2;
722 if (!multilining || depth <= currentDepth) {
723 if (line[spaces] == '\t') {
724 throw OTMLException(doc, "indentation with tabs are not allowed", currentLine);
725 }
726 if (spaces % 2 != 0) {
727 throw OTMLException(doc, "must indent every 2 spaces", currentLine);
728 }
729 }
730 return depth;
731}
732
733inline void OTMLParser::parseLine(std::string line) {
734 int depth = getLineDepth(line);
735 if (depth == -1) {
736 return;
737 }
738 boost::trim(line);
739 if (line.empty()) {
740 return;
741 }
742 if (line.substr(0, 2) == "//") {
743 return;
744 }
745 if (depth == currentDepth + 1) {
746 currentParent = previousNode;
747 } else if (depth < currentDepth) {
748 for (int i = 0; i < currentDepth - depth; ++i) {
749 currentParent = currentParent->parent();
750 }
751 } else if (depth != currentDepth) {
752 throw OTMLException(doc, "invalid indentation depth, are you indenting correctly?", currentLine);
753 }
754 currentDepth = depth;
755 parseNode(line);
756}
757
758inline void OTMLParser::parseNode(const std::string& data) {
759 std::string tag;
760 std::string value;
761 std::size_t dotsPos = data.find_first_of(':');
762 int nodeLine = currentLine;
763 if (!data.empty() && data[0] == '-') {
764 value = data.substr(1);
765 boost::trim(value);
766 } else if (dotsPos != std::string::npos) {
767 tag = data.substr(0, dotsPos);
768 if (data.size() > dotsPos + 1) {
769 value = data.substr(dotsPos + 1);
770 }
771 } else {
772 tag = data;
773 }
774 boost::trim(tag);
775 boost::trim(value);
776 if (value == "|" || value == "|-" || value == "|+") {
777 std::string multiLineData;
778 do {
779 size_t lastPos = in.tellg();
780 std::string line = getNextLine();
781 int depth = getLineDepth(line, true);
782 if (depth > currentDepth) {
783 multiLineData += line.substr((currentDepth + 1) * 2);
784 } else {
785 boost::trim(line);
786 if (!line.empty()) {
787 in.seekg(lastPos, std::ios::beg);
788 currentLine--;
789 break;
790 }
791 }
792 multiLineData += "\n";
793 } while (!in.eof());
794 if (value == "|" || value == "|-") {
795 int lastPos = multiLineData.length();
796 while (multiLineData[--lastPos] == '\n') {
797 multiLineData.erase(lastPos, 1);
798 }
799
800 if (value == "|") {
801 multiLineData.append("\n");
802 }
803 }
804 value = multiLineData;
805 }
806 OTMLNodePtr node = OTMLNode::create(tag);
807 node->setUnique(dotsPos != std::string::npos);
808 node->setTag(tag);
809 node->setSource(doc->source() + ":" + otml_util::safeCast<std::string>(nodeLine));
810 if (value == "~") {
811 node->setNull(true);
812 } else {
813 if (boost::starts_with(value, "[") && boost::ends_with(value, "]")) {
814 typedef boost::tokenizer<boost::escaped_list_separator<char>> Tokenizer;
815 std::string tmp = value.substr(1, value.length() - 2);
816 Tokenizer tok(tmp);
817 for (Tokenizer::iterator it = tok.begin(), end = tok.end(); it != end; ++it) {
818 std::string v = *it;
819 boost::trim(v);
820 node->writeIn(v);
821 }
822 } else {
823 node->setValue(value);
824 }
825 }
826
827 currentParent->addChild(node);
828 previousNode = node;
829}
830
831#endif
832