Cantera  2.4.0
xml.cpp
Go to the documentation of this file.
1 /**
2  * @file xml.cpp
3  * Classes providing support for XML data files. These classes
4  * implement only those aspects of XML required to read, write, and
5  * manipulate CTML data files.
6  */
7 
8 // This file is part of Cantera. See License.txt in the top-level directory or
9 // at http://www.cantera.org/license.txt for license and copyright information.
10 
11 #include "cantera/base/xml.h"
13 #include "cantera/base/global.h"
14 #include "cantera/base/utilities.h"
15 
16 #include <sstream>
17 #include <fstream>
18 
19 using namespace std;
20 
21 namespace Cantera
22 {
23 ////////////////////// exceptions ////////////////////////////
24 
25 //! Classs representing a generic XML error condition
26 class XML_Error : public CanteraError
27 {
28 protected:
29  //! Constructor
30  /*!
31  * Note, we don't actually post the error in this class. Therefore, this
32  * class can't be used externally. Therefore, it's a protected constructor.
33  *
34  * @param file Name of the XML file being processed
35  * @param line Number number where the error occurred.
36  */
37  XML_Error(const std::string& file, int line) {
38  m_msg = fmt::format("Error in XML file '{}' at line {}.\n", file, line);
39  }
40 
41  virtual std::string getMessage() const {
42  return m_msg;
43  }
44 
45  //! destructor
46  virtual ~XML_Error() throw() {
47  }
48 
49  //! String message for the error
50  std::string m_msg;
51 };
52 
53 //! Class representing a specific type of XML file formatting error
54 /*!
55  * An XML tag is not matched
56  */
57 class XML_TagMismatch : public XML_Error
58 {
59 public:
60  //! Constructor
61  /*!
62  * An XML element must have the same opening and closing name.
63  *
64  * @param opentag String representing the opening of the XML bracket
65  * @param closetag String representing the closing of the XML bracket
66  * @param filename Name of the XML file being processed
67  * @param line Line number where the error occurred.
68  */
69  XML_TagMismatch(const std::string& opentag, const std::string& closetag,
70  const std::string& filename, int line) :
71  XML_Error(filename, line) {
72  m_msg += fmt::format("<{}> paired with </{}>.\n", opentag, closetag);
73  }
74 
75  virtual std::string getClass() const {
76  return "XML_TagMismatch";
77  }
78 };
79 
80 //! Class representing a specific type of XML file formatting error
81 /*!
82  * An XML_Node doesn't have a required child node
83  */
84 class XML_NoChild : public XML_Error
85 {
86 public:
87  //! Constructor
88  /*!
89  * An XML element doesn't have the required child node
90  *
91  * @param p XML_Node to write a string error message
92  * @param parent Name of the parent node
93  * @param child Name of the required child node
94  * @param filename Name of the XML file being processed
95  * @param line Line number where the error occurred.
96  */
97  XML_NoChild(const XML_Node* p, const std::string& parent,
98  std::string child, const std::string& filename, int line) :
99  XML_Error(filename, line) {
100  m_msg += fmt::format("The XML Node <{}> does not contain a required "
101  "child node named <{}>.\nExisting children are named:\n",
102  parent, child);
103  for (auto cnode : p->children()) {
104  m_msg += fmt::format(" <{}>\n", cnode->name());
105  }
106  }
107 
108  virtual std::string getClass() const {
109  return "XML_NoChild";
110  }
111 };
112 
113 //////////////////// XML_Reader methods ///////////////////////
114 
115 XML_Reader::XML_Reader(std::istream& input) :
116  m_s(input),
117  m_line(0)
118 {
119 }
120 
121 void XML_Reader::getchr(char& ch)
122 {
123  m_s.get(ch);
124  if (ch == '\n') {
125  m_line++;
126  }
127 }
128 
129 //! Find the first position of a character, q, in string, s, which is not
130 //! immediately preceded by the backslash character
131 /*!
132  * @param s Input string
133  * @param q Search for this character
134  * @param istart Defaults to 0
135  */
136 static string::size_type findUnbackslashed(const std::string& s, const char q,
137  std::string::size_type istart = 0)
138 {
139  size_t icurrent = istart;
140  while (true) {
141  size_t iloc = s.find(q, icurrent);
142  if (iloc == string::npos || iloc == 0) {
143  return iloc;
144  }
145  char cm1 = s[iloc-1];
146  if (cm1 == '\\') {
147  if (iloc >= (s.size() -1)) {
148  return string::npos;
149  }
150  icurrent = iloc + 1;
151  } else {
152  return iloc;
153  }
154  }
155 }
156 
157 int XML_Reader::findQuotedString(const std::string& s, std::string& rstring) const
158 {
159  const char q1 = '\'';
160  const char q2 = '"';
161  rstring = "";
162  char qtype = ' ';
163  string::size_type iloc1, iloc2, ilocStart = 0;
164  iloc1 = findUnbackslashed(s, q1);
165  iloc2 = findUnbackslashed(s, q2);
166  if (iloc2 != string::npos) {
167  ilocStart = iloc2;
168  qtype = q2;
169  }
170  if (iloc1 != string::npos && iloc1 < ilocStart) {
171  ilocStart = iloc1;
172  qtype = q1;
173  }
174  if (qtype == ' ') {
175  return 0;
176  }
177 
178  iloc1 = findUnbackslashed(s, qtype, ilocStart+1);
179 
180  if (iloc1 == string::npos) {
181  return 0;
182  }
183 
184  // Define the return string by the two endpoints. Strip the surrounding
185  // quotes as well
186  rstring = s.substr(ilocStart + 1, iloc1 - 1);
187 
188  // Return the first character position past the quotes
189  return static_cast<int>(iloc1)+1;
190 }
191 
192 void XML_Reader::parseTag(const std::string& tag, std::string& name,
193  std::map<std::string, std::string>& attribs) const
194 {
195  string s = trimCopy(tag);
196  size_t iloc = s.find(' ');
197  if (iloc != string::npos) {
198  name = s.substr(0, iloc);
199  s = trimCopy(s.substr(iloc+1,s.size()));
200  if (s[s.size()-1] == '/') {
201  name += "/";
202  }
203 
204  // get attributes
205  while (true) {
206  iloc = s.find('=');
207  if (iloc == string::npos) {
208  break;
209  }
210  string attr = trimCopy(s.substr(0,iloc));
211  if (attr == "") {
212  break;
213  }
214  s = trimCopy(s.substr(iloc+1,s.size()));
215  string val;
216  iloc = findQuotedString(s, val);
217  attribs[attr] = val;
218  if (iloc != string::npos) {
219  if (iloc < s.size()) {
220  s = trimCopy(s.substr(iloc,s.size()));
221  } else {
222  break;
223  }
224  }
225  }
226  } else {
227  name = s;
228  }
229 }
230 
231 std::string XML_Reader::readTag(std::map<std::string, std::string>& attribs)
232 {
233  string tag = "";
234  bool incomment = false;
235  char ch = '-';
236  while (true) {
237  if (m_s.eof() || (getchr(ch), ch == '<')) {
238  break;
239  }
240  }
241  char ch1 = ' ', ch2 = ' ';
242  while (true) {
243  if (m_s.eof()) {
244  tag = "EOF";
245  break;
246  }
247  ch2 = ch1;
248  ch1 = ch;
249  getchr(ch);
250  if (ch == '-') {
251  if (ch1 == '-' && ch2 == '!') {
252  incomment = true;
253  tag = "-";
254  }
255  } else if (ch == '>') {
256  if (incomment) {
257  if (ch1 == '-' && ch2 == '-') {
258  break;
259  }
260  } else {
261  break;
262  }
263  }
264  if (isprint(ch)) {
265  tag += ch;
266  }
267  }
268  if (incomment) {
269  attribs.clear();
270  return tag;
271  } else {
272  string name;
273  parseTag(tag, name, attribs);
274  return name;
275  }
276 }
277 
279 {
280  string tag = "";
281  char ch = '\n';
282  bool front = true;
283  while (true) {
284  if (m_s.eof()) {
285  break;
286  }
287  char lastch = ch;
288  getchr(ch);
289  if (ch == '\n') {
290  front = true;
291  } else if (ch != ' ') {
292  front = false;
293  }
294  if (ch == '<') {
295  m_s.putback(ch);
296  break;
297  }
298  if (front && lastch == ' ' && ch == ' ') {
299  ;
300  } else {
301  tag += ch;
302  }
303  }
304  return trimCopy(tag);
305 }
306 
307 ////////////////////////// XML_Node /////////////////////////////////
308 
309 XML_Node::XML_Node(const std::string& nm, XML_Node* const parent_) :
310  m_name(nm),
311  m_parent(parent_),
312  m_root(0),
313  m_locked(false),
314  m_iscomment(false),
315  m_linenum(0)
316 {
317  if (!parent_) {
318  m_root = this;
319  } else {
320  m_root = &parent_->root();
321  }
322 }
323 
324 XML_Node::XML_Node(const XML_Node& right) :
325  m_parent(0),
326  m_root(0),
327  m_locked(false),
328  m_iscomment(right.m_iscomment),
329  m_linenum(right.m_linenum)
330 {
331  m_root = this;
332  m_name = right.m_name;
333  m_value = right.m_value;
334  right.copy(this);
335 }
336 
337 XML_Node& XML_Node::operator=(const XML_Node& right)
338 {
339  if (&right != this) {
340  for (size_t i = 0; i < m_children.size(); i++) {
341  if (m_children[i] && m_children[i]->parent() == this) {
342  delete m_children[i];
343  m_children[i] = 0;
344  }
345  }
346  m_children.resize(0);
347  right.copy(this);
348  }
349  return *this;
350 }
351 
352 XML_Node::~XML_Node()
353 {
354  if (m_locked) {
355  writelog("XML_Node::~XML_Node: deleted a locked XML_Node: "+name());
356  }
357  for (size_t i = 0; i < m_children.size(); i++) {
358  if (m_children[i] && m_children[i]->parent() == this) {
359  delete m_children[i];
360  m_children[i] = 0;
361  }
362  }
363 }
364 
366 {
367  for (size_t i = 0; i < m_children.size(); i++) {
368  if (m_children[i] && m_children[i]->parent() == this) {
369  delete m_children[i];
370  m_children[i] = 0;
371  }
372  }
373  m_value.clear();
374  m_childindex.clear();
375  m_attribs.clear();
376  m_children.clear();
377 
378  m_iscomment = false;
379  m_linenum = 0;
380 }
381 
382 void XML_Node::addComment(const std::string& comment)
383 {
384  addChild("comment", comment);
385 }
386 
388 {
389  m_children.push_back(&node);
390  m_childindex.insert({node.name(), m_children.back()});
391  node.setRoot(root());
392  node.setParent(this);
393  return *m_children.back();
394 }
395 
396 XML_Node& XML_Node::addChild(const XML_Node& node)
397 {
398  return mergeAsChild(*(new XML_Node(node)));
399 }
400 
401 XML_Node& XML_Node::addChild(const std::string& sname)
402 {
403  return mergeAsChild(*(new XML_Node(sname, this)));
404 }
405 
406 XML_Node& XML_Node::addChild(const std::string& name, const std::string& value)
407 {
408  XML_Node& c = addChild(name);
409  c.addValue(value);
410  return c;
411 }
412 
413 XML_Node& XML_Node::addChild(const std::string& name, const doublereal value,
414  const std::string& fmt)
415 {
416  XML_Node& c = addChild(name);
417  c.addValue(value, fmt);
418  return c;
419 }
420 
421 void XML_Node::removeChild(const XML_Node* const node)
422 {
423  auto i = find(m_children.begin(), m_children.end(), node);
424  m_children.erase(i);
425  m_childindex.erase(node->name());
426 }
427 
428 std::string XML_Node::id() const
429 {
430  if (hasAttrib("id")) {
431  return attrib("id");
432  }
433  return "";
434 }
435 
436 void XML_Node::addValue(const std::string& val)
437 {
438  m_value = val;
439  if (m_name == "comment") {
440  m_iscomment = true;
441  }
442 }
443 
444 void XML_Node::addValue(const doublereal val, const std::string& fmt)
445 {
446  m_value = trimCopy(fmt::sprintf(fmt, val));
447 }
448 
449 std::string XML_Node::value() const
450 {
451  return m_value;
452 }
453 
454 doublereal XML_Node::fp_value() const
455 {
456  return fpValueCheck(m_value);
457 }
458 
459 integer XML_Node::int_value() const
460 {
461  return std::atoi(m_value.c_str());
462 }
463 
464 std::string XML_Node::value(const std::string& cname) const
465 {
466  return child(cname).value();
467 }
468 
469 std::string XML_Node::operator()(const std::string& loc) const
470 {
471  return value(loc);
472 }
473 
474 void XML_Node::addAttribute(const std::string& attrib, const std::string& value)
475 {
477 }
478 
479 void XML_Node::addAttribute(const std::string& attrib,
480  const doublereal vvalue, const std::string& fmt)
481 {
482  m_attribs[attrib] = fmt::sprintf(fmt, vvalue);
483 }
484 
485 void XML_Node::addAttribute(const std::string& aattrib, const int vvalue)
486 {
487  m_attribs[aattrib] = fmt::format("{}", vvalue);
488 }
489 
490 void XML_Node::addAttribute(const std::string& aattrib, const size_t vvalue)
491 {
492  m_attribs[aattrib] = fmt::format("{}", vvalue);
493 }
494 
495 std::string XML_Node::operator[](const std::string& attr) const
496 {
497  return attrib(attr);
498 }
499 
500 std::string XML_Node::attrib(const std::string& attr) const
501 {
502  return getValue<string,string>(m_attribs, attr, "");
503 }
504 
505 std::map<std::string,std::string>& XML_Node::attribs()
506 {
507  return m_attribs;
508 }
509 
510 const std::map<std::string,std::string>& XML_Node::attribsConst() const
511 {
512  return m_attribs;
513 }
514 
515 void XML_Node::setLineNumber(const int n)
516 {
517  m_linenum = n;
518 }
519 
521 {
522  return m_linenum;
523 }
524 
526 {
527  return m_parent;
528 }
529 
531 {
532  m_parent = p;
533  return p;
534 }
535 
536 bool XML_Node::hasChild(const std::string& ch) const
537 {
538  return (m_childindex.find(ch) != m_childindex.end());
539 }
540 
541 bool XML_Node::hasAttrib(const std::string& a) const
542 {
543  return (m_attribs.find(a) != m_attribs.end());
544 }
545 
546 XML_Node& XML_Node::child(const size_t n) const
547 {
548  return *m_children[n];
549 }
550 
551 const std::vector<XML_Node*>& XML_Node::children() const
552 {
553  return m_children;
554 }
555 
556 size_t XML_Node::nChildren(const bool discardComments) const
557 {
558  if (discardComments) {
559  size_t count = 0;
560  for (size_t i = 0; i < m_children.size(); i++) {
561  XML_Node* xc = m_children[i];
562  if (!(xc->isComment())) {
563  count++;
564  }
565  }
566  return count;
567  }
568  return m_children.size();
569 }
570 
572 {
573  return m_iscomment;
574 }
575 
576 void XML_Node::_require(const std::string& a, const std::string& v) const
577 {
578  if (hasAttrib(a) && attrib(a) == v) {
579  return;
580  }
581  string msg="XML_Node "+name()+" is required to have an attribute named " + a +
582  " with the value \"" + v +"\", but instead the value is \"" + attrib(a);
583  throw CanteraError("XML_Node::require", msg);
584 }
585 
586 XML_Node* XML_Node::findNameID(const std::string& nameTarget,
587  const std::string& idTarget) const
588 {
589  XML_Node* scResult = 0;
590  std::string idattrib = id();
591  if (name() == nameTarget && (idTarget == "" || idTarget == idattrib)) {
592  return const_cast<XML_Node*>(this);
593  }
594  for (size_t n = 0; n < m_children.size(); n++) {
595  XML_Node* sc = m_children[n];
596  if (sc->name() == nameTarget) {
597  if (idTarget == "") {
598  return sc;
599  }
600  idattrib = sc->id();
601  if (idTarget == idattrib) {
602  return sc;
603  }
604  }
605  }
606  for (size_t n = 0; n < m_children.size(); n++) {
607  XML_Node* sc = m_children[n];
608  scResult = sc->findNameID(nameTarget, idTarget);
609  if (scResult) {
610  return scResult;
611  }
612  }
613  return scResult;
614 }
615 
616 XML_Node* XML_Node::findNameIDIndex(const std::string& nameTarget,
617  const std::string& idTarget, const int index_i) const
618 {
619  XML_Node* scResult = 0;
620  std::string idattrib = id();
621  std::string ii = attrib("index");
622  std::string index_s = fmt::format("{}", index_i);
623  int iMax = -1000000;
624  if (name() == nameTarget && (idTarget == "" || idTarget == idattrib) && index_s == ii) {
625  return const_cast<XML_Node*>(this);
626  }
627  for (size_t n = 0; n < m_children.size(); n++) {
628  XML_Node* sc = m_children[n];
629  if (sc->name() == nameTarget) {
630  ii = sc->attrib("index");
631  int indexR = atoi(ii.c_str());
632  idattrib = sc->id();
633  if ((idTarget == idattrib || idTarget == "") && index_s == ii) {
634  return sc;
635  }
636  if (indexR > iMax) {
637  scResult = sc;
638  iMax = indexR;
639  }
640  }
641  }
642  return scResult;
643 }
644 
645 XML_Node* XML_Node::findID(const std::string& id_, const int depth) const
646 {
647  if (hasAttrib("id") && attrib("id") == id_) {
648  return const_cast<XML_Node*>(this);
649  }
650  if (depth > 0) {
651  for (size_t i = 0; i < nChildren(); i++) {
652  XML_Node* r = m_children[i]->findID(id_, depth-1);
653  if (r != 0) {
654  return r;
655  }
656  }
657  }
658  return 0;
659 }
660 
661 XML_Node* XML_Node::findByAttr(const std::string& attr,
662  const std::string& val, int depth) const
663 {
664  if (hasAttrib(attr) && attrib(attr) == val) {
665  return const_cast<XML_Node*>(this);
666  }
667  if (depth > 0) {
668  size_t n = nChildren();
669  for (size_t i = 0; i < n; i++) {
670  XML_Node* r = m_children[i]->findByAttr(attr, val, depth - 1);
671  if (r != 0) {
672  return r;
673  }
674  }
675  }
676  return 0;
677 }
678 
679 XML_Node* XML_Node::findByName(const std::string& nm, int depth)
680 {
681  if (name() == nm) {
682  return this;
683  }
684  if (depth > 0) {
685  for (size_t i = 0; i < nChildren(); i++) {
686  XML_Node* r = m_children[i]->findByName(nm);
687  if (r != 0) {
688  return r;
689  }
690  }
691  }
692  return 0;
693 }
694 
695 const XML_Node* XML_Node::findByName(const std::string& nm, int depth) const
696 {
697  if (name() == nm) {
698  return const_cast<XML_Node*>(this);
699  }
700  if (depth > 0) {
701  for (size_t i = 0; i < nChildren(); i++) {
702  XML_Node* r = m_children[i]->findByName(nm);
703  if (r != 0) {
704  return r;
705  }
706  }
707  }
708  return 0;
709 }
710 
711 void XML_Node::writeHeader(std::ostream& s)
712 {
713  s << "<?xml version=\"1.0\"?>" << endl;
714 }
715 
716 void XML_Node::build(const std::string& filename)
717 {
718  ifstream fin(filename);
719  if (!fin) {
720  throw CanteraError("XML_Node::build",
721  "Unable to open file '{}' for reading.", filename);
722  }
723  build(fin, filename);
724 }
725 
726 void XML_Node::build(std::istream& f, const std::string& filename)
727 {
728  m_filename = filename;
729  XML_Reader r(f);
730  XML_Node* node = this;
731  bool first = true;
732  while (!f.eof()) {
733  map<string, string> node_attribs;
734  string nm = r.readTag(node_attribs);
735 
736  if (nm == "EOF") {
737  break;
738  }
739  if (nm == "--" && m_name == "--" && m_root == this) {
740  continue;
741  }
742  int lnum = r.m_line;
743  if (nm[nm.size() - 1] == '/') {
744  string nm2 = nm.substr(0,nm.size()-1);
745  if (first) {
746  node->setName(nm2);
747  first = false;
748  } else {
749  node = &node->addChild(nm2);
750  }
751  node->addValue("");
752  node->attribs() = node_attribs;
753  node->setLineNumber(lnum);
754  node = node->parent();
755  } else if (nm[0] != '/') {
756  if (nm[0] != '!' && nm[0] != '-' && nm[0] != '?') {
757  if (first) {
758  node->setName(nm);
759  first = false;
760  } else {
761  node = &node->addChild(nm);
762  }
763  node->addValue(r.readValue());
764  node->attribs() = node_attribs;
765  node->setLineNumber(lnum);
766  } else if (nm.substr(0,2) == "--") {
767  if (nm.substr(nm.size()-2,2) == "--") {
768  node->addComment(nm.substr(2,nm.size()-4));
769  }
770  }
771  } else {
772  if (node->name() != nm.substr(1,nm.size()-1)) {
773  throw XML_TagMismatch(node->name(), nm.substr(1,nm.size()-1),
774  root().m_filename, lnum);
775  }
776  node = node->parent();
777  }
778  }
779 }
780 
781 void XML_Node::copyUnion(XML_Node* const node_dest) const
782 {
783  node_dest->addValue(m_value);
784  if (m_name == "") {
785  return;
786  }
787  for (const auto& attr : m_attribs) {
788  if (!node_dest->hasAttrib(attr.first)) {
789  node_dest->addAttribute(attr.first, attr.second);
790  }
791  }
792  const vector<XML_Node*> &vsc = node_dest->children();
793  for (size_t n = 0; n < m_children.size(); n++) {
794  XML_Node* sc = m_children[n];
795  size_t ndc = node_dest->nChildren();
796  XML_Node* dc = 0;
797  if (! sc->m_iscomment) {
798  for (size_t idc = 0; idc < ndc; idc++) {
799  XML_Node* dcc = vsc[idc];
800  if (dcc->name() == sc->name()) {
801  if (sc->hasAttrib("id") && sc->attrib("id") != dcc->attrib("id")) {
802  break;
803  }
804  if (sc->hasAttrib("name") && sc->attrib("name") != dcc->attrib("name")) {
805  break;
806  }
807  if (sc->hasAttrib("model") && sc->attrib("model") != dcc->attrib("model")) {
808  break;
809  }
810  if (sc->hasAttrib("title") && sc->attrib("title") != dcc->attrib("title")) {
811  break;
812  }
813  dc = vsc[idc];
814  }
815  }
816  }
817  if (!dc) {
818  node_dest->addChild(sc->name());
819  dc = vsc[ndc];
820  }
821  sc->copyUnion(dc);
822  }
823 }
824 
825 void XML_Node::copy(XML_Node* const node_dest) const
826 {
827  node_dest->addValue(m_value);
828  node_dest->setName(m_name);
829  node_dest->setLineNumber(m_linenum);
830  if (m_name == "") {
831  return;
832  }
833  for (const auto& attr : m_attribs) {
834  node_dest->addAttribute(attr.first, attr.second);
835  }
836  const vector<XML_Node*> &vsc = node_dest->children();
837 
838  for (size_t n = 0; n < m_children.size(); n++) {
839  XML_Node* sc = m_children[n];
840  size_t ndc = node_dest->nChildren();
841  // Here is where we create the child node.
842  node_dest->addChild(sc->name());
843  XML_Node* dc = vsc[ndc];
844  sc->copy(dc);
845  }
846 }
847 
849 {
850  m_locked = true;
851  for (size_t i = 0; i < m_children.size(); i++) {
852  m_children[i]->lock();
853  }
854 }
855 
857 {
858  m_locked = false;
859  for (size_t i = 0; i < m_children.size(); i++) {
860  m_children[i]->unlock();
861  }
862 }
863 
864 std::vector<XML_Node*> XML_Node::getChildren(const std::string& nm) const
865 {
866  std::vector<XML_Node*> children_;
867  for (size_t i = 0; i < nChildren(); i++) {
868  if (caseInsensitiveEquals(child(i).name(), nm)) {
869  children_.push_back(&child(i));
870  }
871  }
872  return children_;
873 }
874 
875 XML_Node& XML_Node::child(const std::string& aloc) const
876 {
877  string loc = aloc;
878  while (true) {
879  size_t iloc = loc.find('/');
880  if (iloc != string::npos) {
881  string cname = loc.substr(0,iloc);
882  loc = loc.substr(iloc+1, loc.size());
883  auto i = m_childindex.find(cname);
884  if (i != m_childindex.end()) {
885  return i->second->child(loc);
886  } else {
887  throw XML_NoChild(this, m_name, cname, root().m_filename,
888  lineNumber());
889  }
890  } else {
891  auto i = m_childindex.find(loc);
892  if (i != m_childindex.end()) {
893  return *(i->second);
894  } else {
895  throw XML_NoChild(this, m_name, loc, root().m_filename,
896  lineNumber());
897  }
898  }
899  }
900 }
901 
902 void XML_Node::write_int(std::ostream& s, int level, int numRecursivesAllowed) const
903 {
904  if (m_name == "") {
905  return;
906  }
907 
908  string indent(level, ' ');
909  if (m_iscomment) {
910  // In the comment section, we test to see if there already is a space
911  // beginning and ending the comment. If there already is one, we don't
912  // add another one.
913  s << endl << indent << "<!--";
914  if (! isspace(m_value[0])) {
915  s << " ";
916  }
917  s << m_value;
918  if (! isspace(m_value[m_value.size()-1])) {
919  s << " ";
920  }
921  s << "-->";
922  return;
923  }
924 
925  s << indent << "<" << m_name;
926  for (const auto& attr : m_attribs) {
927  s << " " << attr.first << "=\"" << attr.second << "\"";
928  }
929  if (m_value == "" && m_children.empty()) {
930  s << "/>";
931  } else {
932  s << ">";
933 
934  if (m_value != "") {
935  string vv = m_value;
936  string::size_type ieol = vv.find('\n');
937  if (ieol != string::npos) {
938  while (true) {
939  ieol = vv.find('\n');
940  if (ieol != string::npos) {
941  if (ieol == 0) {
942  s << endl << indent << " ";
943  } else {
944  size_t jf = ieol - 1;
945  for (int j = 0; j < (int) ieol; j++) {
946  if (! isspace(vv[j])) {
947  jf = j;
948  break;
949  }
950  }
951  s << endl << indent << " " << vv.substr(jf,ieol-jf);
952  }
953  vv = vv.substr(ieol+1);
954  } else {
955  size_t lll = vv.size() - 1;
956  if (lll != npos) {
957  size_t jf = lll;
958  for (size_t j = 0; j < lll; j++) {
959  if (! isspace(vv[j])) {
960  jf = j;
961  break;
962  }
963  }
964  if (jf < lll) {
965  s << endl << indent << " " << vv.substr(jf);
966  }
967  }
968  break;
969  }
970  }
971  s << endl << indent;
972  } else {
973  bool doSpace = true;
974  bool doNewLine = false;
975  size_t ll = m_value.size() - 1;
976  if (ll > 25) {
977  doNewLine = true;
978  }
979  if (m_name == "floatArray") {
980  doNewLine = true;
981  }
982  if (doNewLine) {
983  doSpace = false;
984  }
985 
986  if (doNewLine) {
987  s << endl << indent << " ";
988  }
989 
990  // Put spaces around a raw value field for readability
991  if (doSpace && (! isspace(m_value[0]))) {
992  s << " ";
993  }
994 
995  // Write out the value
996  s << m_value;
997 
998  if (doSpace && (! isspace(m_value[ll]))) {
999  s << " ";
1000  }
1001  if (doNewLine) {
1002  s << endl << indent;
1003  }
1004  }
1005  }
1006  if (numRecursivesAllowed > 0) {
1007  for (size_t i = 0; i < m_children.size(); i++) {
1008  s << endl;
1009  m_children[i]->write_int(s,level + 2, numRecursivesAllowed - 1);
1010  }
1011  }
1012  if (!m_children.empty()) {
1013  s << endl << indent;
1014  }
1015  s << "</" << m_name << ">";
1016  }
1017 }
1018 
1019 void XML_Node::write(std::ostream& s, const int level, int numRecursivesAllowed) const
1020 {
1021  write_int(s, level, numRecursivesAllowed);
1022  s << endl;
1023 }
1024 
1026 {
1027  return *m_root;
1028 }
1029 
1030 void XML_Node::setRoot(const XML_Node& newRoot)
1031 {
1032  m_root = const_cast<XML_Node*>(&newRoot);
1033  for (size_t i = 0; i < m_children.size(); i++) {
1034  m_children[i]->setRoot(newRoot);
1035  }
1036 }
1037 
1039  const std::string& idtarget)
1040 {
1041  XML_Node* scResult = 0;
1042  if (!root) {
1043  return 0;
1044  }
1045  if (root->name() == "phase") {
1046  if (idtarget == "") {
1047  return root;
1048  }
1049  if (idtarget == root->id()) {
1050  return root;
1051  }
1052  }
1053 
1054  const vector<XML_Node*> &vsc = root->children();
1055  for (size_t n = 0; n < root->nChildren(); n++) {
1056  XML_Node* sc = vsc[n];
1057  if (sc->name() == "phase") {
1058  if (idtarget == "") {
1059  return sc;
1060  }
1061  if (idtarget == sc->id()) {
1062  return sc;
1063  }
1064  }
1065  }
1066  for (size_t n = 0; n < root->nChildren(); n++) {
1067  XML_Node* sc = vsc[n];
1068  scResult = findXMLPhase(sc, idtarget);
1069  if (scResult) {
1070  return scResult;
1071  }
1072  }
1073  return scResult;
1074 }
1075 
1076 }
void setName(const std::string &name_)
Sets the name of the XML node.
Definition: xml.h:378
Class representing a specific type of XML file formatting error.
Definition: xml.cpp:57
static string::size_type findUnbackslashed(const std::string &s, const char q, std::string::size_type istart=0)
Find the first position of a character, q, in string, s, which is not immediately preceded by the bac...
Definition: xml.cpp:136
std::vector< XML_Node * > getChildren(const std::string &name) const
Get a vector of pointers to XML_Node containing all of the children of the current node which match t...
Definition: xml.cpp:864
std::vector< XML_Node * > m_children
Vector of pointers to child nodes.
Definition: xml.h:693
std::string trimCopy(const std::string &input)
Trim.
XML_Error(const std::string &file, int line)
Constructor.
Definition: xml.cpp:37
std::string readTag(std::map< std::string, std::string > &attribs)
Reads an XML tag into a string.
Definition: xml.cpp:231
std::string name() const
Returns the name of the XML node.
Definition: xml.h:370
XML_Node * findXMLPhase(XML_Node *root, const std::string &idtarget)
Search an XML_Node tree for a named phase XML_Node.
Definition: xml.cpp:1038
virtual std::string getClass() const
Method overridden by derived classes to indicate their type.
Definition: xml.cpp:108
virtual ~XML_Error()
destructor
Definition: xml.cpp:46
const XML_Node * findByName(const std::string &nm, int depth=100000) const
This routine carries out a recursive search for an XML node based on the name of the node...
Definition: xml.cpp:695
void clear()
Clear the current node and everything under it.
Definition: xml.cpp:365
Various templated functions that carry out common vector operations (see Templated Utility Functions)...
void copyUnion(XML_Node *const node_dest) const
Copy all of the information in the current XML_Node tree into the destination XML_Node tree...
Definition: xml.cpp:781
std::string operator()(const std::string &cname) const
The Overloaded parenthesis operator with one augment returns the value of an XML child node as a stri...
Definition: xml.cpp:469
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
virtual std::string getMessage() const
Method overridden by derived classes to format the error message.
Definition: xml.cpp:41
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:153
integer int_value() const
Return the value of an XML node as a single int.
Definition: xml.cpp:459
void unlock()
Unset the lock for this node and all of its children.
Definition: xml.cpp:856
std::map< std::string, std::string > & attribs()
Returns a changeable value of the attributes map for the current node.
Definition: xml.cpp:505
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:97
XML_TagMismatch(const std::string &opentag, const std::string &closetag, const std::string &filename, int line)
Constructor.
Definition: xml.cpp:69
STL namespace.
void write(std::ostream &s, const int level=0, int numRecursivesAllowed=60000) const
Write an XML subtree to an output stream.
Definition: xml.cpp:1019
This file contains definitions for utility functions and text for modules, inputfiles, logs, textlogs, (see Input File Handling, Diagnostic Output, and Writing messages to the screen).
std::map< std::string, std::string > m_attribs
Storage of attributes for a node.
Definition: xml.h:671
bool hasAttrib(const std::string &a) const
Tests whether the current node has an attribute with a particular name.
Definition: xml.cpp:541
bool isComment() const
Boolean function indicating whether a comment.
Definition: xml.cpp:571
XML_Node * setParent(XML_Node *const p)
Sets the pointer for the parent node of the current node.
Definition: xml.cpp:530
std::string m_value
Value of the XML node.
Definition: xml.h:652
void addValue(const std::string &val)
Modify the value for the current node.
Definition: xml.cpp:436
Class XML_Reader reads an XML file into an XML_Node object.
Definition: xml.h:27
virtual std::string getClass() const
Method overridden by derived classes to indicate their type.
Definition: xml.cpp:75
void _require(const std::string &a, const std::string &v) const
Require that the current XML node have an attribute named by the first argument, a, and that this attribute have the the string value listed in the second argument, v.
Definition: xml.cpp:576
void addComment(const std::string &comment)
Add a child node to the current node containing a comment.
Definition: xml.cpp:382
const std::vector< XML_Node * > & children() const
Return an unchangeable reference to the vector of children of the current node.
Definition: xml.cpp:551
int findQuotedString(const std::string &aline, std::string &rstring) const
Searches a string for the first occurrence of a valid quoted string.
Definition: xml.cpp:157
bool m_locked
Lock for this node.
Definition: xml.h:690
XML_Node * m_root
Pointer to the root XML_Node for the current node.
Definition: xml.h:683
std::string m_filename
Name of the file from which this XML node was read.
Definition: xml.h:656
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:848
bool m_iscomment
True if the current node is a comment node.
Definition: xml.h:696
XML_Node(const std::string &nm="--", XML_Node *const parent=0)
Constructor for XML_Node, representing a tree structure.
Definition: xml.cpp:309
Classes providing support for XML data files.
bool caseInsensitiveEquals(const std::string &input, const std::string &test)
Case insensitive equality predicate.
XML_Node * parent() const
Returns a pointer to the parent node of the current node.
Definition: xml.cpp:525
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
std::string value() const
Return the value of an XML node as a string.
Definition: xml.cpp:449
void build(const std::string &filename)
Populate the XML tree from an input file.
Definition: xml.cpp:716
std::string m_msg
String message for the error.
Definition: xml.cpp:50
XML_Node & mergeAsChild(XML_Node &node)
Merge an existing node as a child node to the current node.
Definition: xml.cpp:387
XML_Node * m_parent
Pointer to the parent XML_Node for the current node.
Definition: xml.h:677
void addAttribute(const std::string &attrib, const std::string &value)
Add or modify an attribute of the current node.
Definition: xml.cpp:474
void copy(XML_Node *const node_dest) const
Copy all of the information in the current XML_Node tree into the destination XML_Node tree...
Definition: xml.cpp:825
int lineNumber() const
Return the line number.
Definition: xml.cpp:520
void setLineNumber(const int n)
Set the line number.
Definition: xml.cpp:515
void parseTag(const std::string &tag, std::string &name, std::map< std::string, std::string > &attribs) const
parseTag parses XML tags, i.e., the XML elements that are in between angle brackets.
Definition: xml.cpp:192
XML_Node * findNameID(const std::string &nameTarget, const std::string &idTarget) const
This routine carries out a recursive search for an XML node based on both the XML element name and th...
Definition: xml.cpp:586
void setRoot(const XML_Node &root)
Set the root XML_Node value within the current node.
Definition: xml.cpp:1030
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:536
int m_line
Line count.
Definition: xml.h:89
XML_Node & child(const size_t n) const
Return a changeable reference to the n&#39;th child of the current node.
Definition: xml.cpp:546
XML_Node & root() const
Return the root of the current XML_Node tree.
Definition: xml.cpp:1025
const std::map< std::string, std::string > & attribsConst() const
Returns an unchangeable value of the attributes map for the current node.
Definition: xml.cpp:510
std::string m_name
XML node name of the node.
Definition: xml.h:639
XML_Node * findNameIDIndex(const std::string &nameTarget, const std::string &idTarget, const int index) const
This routine carries out a search for an XML node based on both the XML element name and the attribut...
Definition: xml.cpp:616
std::string readValue()
Return the value portion of an XML element.
Definition: xml.cpp:278
void removeChild(const XML_Node *const node)
Remove a child from this node&#39;s list of children.
Definition: xml.cpp:421
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:500
std::string operator[](const std::string &attr) const
The operator[] is overloaded to provide a lookup capability on attributes for the current XML element...
Definition: xml.cpp:495
std::string id() const
Return the id attribute, if present.
Definition: xml.cpp:428
Contains declarations for string manipulation functions within Cantera.
Definition: fmt.h:29
void writeHeader(std::ostream &s)
Write the header to the XML file to the specified ostream.
Definition: xml.cpp:711
XML_Node * findByAttr(const std::string &attr, const std::string &val, int depth=100000) const
This routine carries out a recursive search for an XML node based on an attribute of each XML node...
Definition: xml.cpp:661
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:8
Classs representing a generic XML error condition.
Definition: xml.cpp:26
void write_int(std::ostream &s, int level=0, int numRecursivesAllowed=60000) const
Write an XML subtree to an output stream.
Definition: xml.cpp:902
doublereal fpValueCheck(const std::string &val)
Translate a string into one doublereal value, with error checking.
doublereal fp_value() const
Return the value of an XML node as a single double.
Definition: xml.cpp:454
int m_linenum
The member data m_linenum.
Definition: xml.h:702
size_t nChildren(bool discardComments=false) const
Return the number of children.
Definition: xml.cpp:556
XML_NoChild(const XML_Node *p, const std::string &parent, std::string child, const std::string &filename, int line)
Constructor.
Definition: xml.cpp:97
std::multimap< std::string, XML_Node * > m_childindex
Map containing an index between the node name and the pointer to the node.
Definition: xml.h:665
XML_Node * findID(const std::string &id, const int depth=100) const
This routine carries out a recursive search for an XML node based on the XML element attribute...
Definition: xml.cpp:645
std::istream & m_s
Input stream containing the XML file.
Definition: xml.h:85
Class representing a specific type of XML file formatting error.
Definition: xml.cpp:84
void getchr(char &ch)
Read a single character from the input stream and returns it.
Definition: xml.cpp:121