Cantera  2.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 // Copyright 2001 California Institute of Technology
8 
9 #include "cantera/base/config.h"
10 #ifdef HAS_SSTREAM
11 #include <sstream>
12 #endif
13 
14 #include <algorithm>
15 using namespace std;
16 
17 #include "cantera/base/xml.h"
18 #include "cantera/base/global.h"
20 #include <ctype.h>
21 #include <cstdlib>
22 
23 namespace Cantera
24 {
25 
26 
27 ////////////////////// exceptions ////////////////////////////
28 
29 //! Classs representing a generic XML error condition
30 class XML_Error : public CanteraError
31 {
32 protected:
33  //! Constructor
34  /*!
35  * Note, we don't actually post the error in this class.
36  * Therefore, this class can't be used externally. Therefore,
37  * it's a protected constructor.
38  *
39  * @param line Number number where the error occurred.
40  */
41  XML_Error(int line=0) :
42  m_line(line),
43  m_msg("Error in XML file") {
44  if (line > 0) {
45  m_msg += " at line " + int2str(line+1);
46  }
47  m_msg += ".\n";
48  }
49 
50  //! destructor
51  virtual ~XML_Error() throw() {
52  }
53 
54 protected:
55  //! Line number of the file
56  int m_line;
57 
58  //! String message for the error
59  std::string m_msg;
60 };
61 
62 //! Class representing a specific type of XML file formatting error
63 /*!
64  * An XML tag is not matched
65  */
66 class XML_TagMismatch : public XML_Error
67 {
68 public:
69 
70  //! Constructor
71  /*!
72  * An XML element must have the same opening and closing name.
73  *
74  * @param opentag String representing the opening of the XML bracket
75  * @param closetag String representing the closing of the XML bracket
76  * @param line Line number where the error occurred.
77  */
78  XML_TagMismatch(std::string opentag, std::string closetag,
79  int line=0) :
80  XML_Error(line) {
81  m_msg += "<" + opentag + "> paired with </" + closetag + ">.\n";
82  setError("XML_TagMismatch", m_msg);
83  }
84 
85  //! Destructor
86  virtual ~XML_TagMismatch() throw() {}
87 };
88 
89 //! Class representing a specific type of XML file formatting error
90 /*!
91  * An XML_Node doesn't have a required child node
92  */
93 class XML_NoChild : public XML_Error
94 {
95 public:
96 
97  //! Constructor
98  /*!
99  * An XML element doesn't have the required child node
100  *
101  * @param p XML_Node to write a string error message
102  * @param parent Namf of the parent node
103  * @param child Name of the required child node
104  * @param line Line number where the error occurred.
105  */
106  XML_NoChild(const XML_Node* p, std::string parent,
107  std::string child, int line=0) :
108  XML_Error(line) {
109  m_msg += " The XML Node \"" + parent +
110  "\", does not contain a required\n" +
111  " XML child node named \""
112  + child + "\".\n";
113 #ifdef HAS_SSTREAM
114  ostringstream ss(ostringstream::out);
115  p->write(ss,1);
116  m_msg += ss.str() + "\n";
117 #endif
118  setError("XML_NoChild", m_msg);
119  }
120 
121  //! Destructor
122  virtual ~XML_NoChild() throw() {}
123 };
124 
125 //! Class representing a specific type of XML file formatting error
126 /*!
127  * An XML_Node's units attribute has the wrong type of units.
128  */
130 {
131 public:
132 
133  //! Constructor
134  /*!
135  * Wrong units string.
136  *
137  * @param name Name of the current XML node
138  * @param units Units string in the "units" attribute
139  * @param line Line number where the error occurred.
140  */
141  XML_IllegalUnits(std::string name, std::string units, int line=0) :
142  XML_Error(line) {
143  m_msg += "Illegal units (" + units +
144  ") specified for node " + name + ".\n";
145  setError("XML_IllegalUnits", m_msg);
146  }
147 
148  //! Destructor
149  virtual ~XML_IllegalUnits() throw() {}
150 };
151 
152 
153 
154 //////////////////// XML_Reader methods ///////////////////////
155 
156 
157 XML_Reader::XML_Reader(std::istream& input) :
158  m_s(input),
159  m_line(0)
160 {
161 }
162 
163 //! Get a single character from the input stream.
164 /*!
165  * If the character
166  * is a new-line character, then increment the line count.
167  */
168 void XML_Reader::getchr(char& ch)
169 {
170  m_s.get(ch);
171  if (ch == '\n') {
172  m_line++;
173  }
174 }
175 
176 
177 // Returns string 'aline' stripped of leading and trailing white
178 // space.
179 // @todo why is this a class method?
180 std::string XML_Reader::strip(const std::string& aline) const
181 {
182  int len = static_cast<int>(aline.size());
183  int i, j, ll;
184  for (i = len-1; i >= 0; i--) {
185  ll = aline[i];
186  if (! isspace(ll)) {
187  break;
188  }
189  }
190  for (j = 0; j < i; j++) {
191  ll = aline[j];
192  if (! isspace(ll)) {
193  break;
194  }
195  }
196  // if (aline[j] != ' ' && aline[j] != '\n') break;
197  return aline.substr(j, i - j + 1);
198 }
199 
200 
201 /// Looks for a substring within 'aline' enclosed in double
202 /// quotes, and returns this substring (without the quotes) if
203 /// found. If not, an empty string is returned.
204 /// @todo why is this a class method?
205 std::string XML_Reader::inquotes(const std::string& aline) const
206 {
207  int len = static_cast<int>(aline.size());
208  int i, j;
209  for (i = len-1; i >= 0; i--)
210  if (aline[i] == '"') {
211  break;
212  }
213  for (j = 0; j < i; j++)
214  if (aline[j] == '"') {
215  break;
216  }
217  if (j == i) {
218  return "";
219  } else {
220  return aline.substr(j+1, i - j - 1);
221  }
222 }
223 
224 
225 //! Find the first position of a character, q, in string, s, which is not immediately preceded by the backslash character
226 /*!
227  * @param s Input string
228  * @param q Search for this character
229  * @param istart Defaults to 0
230  */
231 static string::size_type findUnbackslashed(std::string s, const char q,
232  std::string::size_type istart = 0)
233 {
234  string::size_type iloc, icurrent, len;
235  icurrent = istart;
236  len = s.size();
237  while (1) {
238  iloc = s.find(q, icurrent);
239  if (iloc == string::npos || iloc == 0) {
240  return iloc;
241  }
242  char cm1 = s[iloc-1];
243  if (cm1 == '\\') {
244  if (iloc >= (len -1)) {
245  return string::npos;
246  }
247  icurrent = iloc + 1;
248  } else {
249  return iloc;
250  }
251  }
252 }
253 
254 /*
255  * Searches a string for the first occurrence of a valid
256  * quoted string. Quotes can start with either a single
257  * quote or a double quote, but must also end with the same
258  * type. Quotes may be commented out by preceding with a
259  * backslash character, '\\'.
260  */
261 int XML_Reader::findQuotedString(const std::string& s, std::string& rstring) const
262 {
263  const char q1 = '\'';
264  const char q2 = '"';
265  rstring = "";
266  char qtype = ' ';
267  string::size_type iloc1, iloc2, ilocStart = 0;
268  iloc1 = findUnbackslashed(s, q1);
269  iloc2 = findUnbackslashed(s, q2);
270  if (iloc2 != string::npos) {
271  ilocStart = iloc2;
272  qtype = q2;
273  }
274  if (iloc1 != string::npos) {
275  if (iloc1 < ilocStart) {
276  ilocStart = iloc1;
277  qtype = q1;
278  }
279  }
280  if (qtype == ' ') {
281  return 0;
282  }
283 
284  iloc1 = findUnbackslashed(s, qtype, ilocStart+1);
285 
286  if (iloc1 == string::npos) {
287  return 0;
288  }
289  /*
290  * Define the return string by the two endpoints.
291  * Strip the surrounding quotes as well
292  */
293  rstring = s.substr(ilocStart + 1, iloc1 - 1);
294  /*
295  * Return the first character position past the quotes
296  */
297  return static_cast<int>(iloc1)+1;
298 }
299 
300 /*
301  * parseTag parses XML tags, i.e., the XML elements that are
302  * in between angle brackets.
303  */
304 void XML_Reader::parseTag(std::string tag, std::string& name,
305  std::map<std::string, std::string>& attribs) const
306 {
307  string::size_type iloc;
308  string attr, val;
309  string s = stripws(tag);
310  iloc = s.find(' ');
311  if (iloc != string::npos) {
312  name = s.substr(0, iloc);
313  s = stripws(s.substr(iloc+1,s.size()));
314  if (s[s.size()-1] == '/') {
315  name += "/";
316  }
317 
318  // get attributes
319  while (1) {
320  iloc = s.find('=');
321  if (iloc == string::npos) {
322  break;
323  }
324  attr = stripws(s.substr(0,iloc));
325  if (attr == "") {
326  break;
327  }
328  s = stripws(s.substr(iloc+1,s.size()));
329  iloc = findQuotedString(s, val);
330  attribs[attr] = val;
331  if (iloc != string::npos) {
332  if (iloc < s.size()) {
333  s = stripws(s.substr(iloc,s.size()));
334  } else {
335  break;
336  }
337  }
338  }
339  } else {
340  name = s;
341  }
342 }
343 
344 std::string XML_Reader::readTag(std::map<std::string, std::string>& attribs)
345 {
346  string name, tag = "";
347  bool incomment = false;
348  char ch = '-';
349  while (1) {
350  if (m_s.eof() || (getchr(ch), ch == '<')) {
351  break;
352  }
353  }
354  char ch1 = ' ', ch2 = ' ';
355  while (1) {
356  if (m_s.eof()) {
357  tag = "EOF";
358  break;
359  }
360  ch2 = ch1;
361  ch1 = ch;
362  getchr(ch);
363  if (ch == '-') {
364  if (ch1 == '-' && ch2 == '!') {
365  incomment = true;
366  tag = "-";
367  }
368  } else if (ch == '>') {
369  if (incomment) {
370  if (ch1 == '-' && ch2 == '-') {
371  break;
372  }
373  } else {
374  break;
375  }
376  }
377  if (isprint(ch)) {
378  tag += ch;
379  }
380  }
381  if (incomment) {
382  attribs.clear();
383  return tag;
384  } else {
385  parseTag(tag, name, attribs);
386  return name;
387  }
388 }
389 
391 {
392  string tag = "";
393  char ch, lastch;
394  ch = '\n';
395  bool front = true;
396  while (1) {
397  if (m_s.eof()) {
398  break;
399  }
400  lastch = ch;
401  getchr(ch);
402  if (ch == '\n') {
403  front = true;
404  } else if (ch != ' ') {
405  front = false;
406  }
407  if (ch == '<') {
408  m_s.putback(ch);
409  break;
410  }
411  if (front && lastch == ' ' && ch == ' ') {
412  ;
413  } else {
414  tag += ch;
415  }
416  }
417  return stripws(tag);
418 }
419 
420 
421 ////////////////////////// XML_Node /////////////////////////////////
422 
423 XML_Node::XML_Node(const char* cnm) :
424  m_name(""),
425  m_value(""),
426  m_parent(0),
427  m_root(0),
428  m_locked(false),
429  m_nchildren(0),
430  m_iscomment(false) ,
431  m_linenum(0)
432 {
433  if (! cnm) {
434  m_name = "--";
435  } else {
436  m_name = cnm;
437  }
438  m_root = this;
439 }
440 
441 // Default constructor for XML_Node, representing a tree structure
442 /*
443  * Constructor for an XML_Node, which is a node in a tree-like structure
444  * representing an XML file.
445  *
446  * @param nm Name of the node.
447  * The default name of the node is "--"
448  *
449  * @param parent Pointer to the parent for this node in the tree.
450  * A value of zero 0 indicates this is the top of the tree.
451  */
452 XML_Node::XML_Node(const std::string nm, XML_Node* const parent) :
453  m_name(nm),
454  m_value(""),
455  m_parent(parent),
456  m_root(0),
457  m_locked(false),
458  m_nchildren(0),
459  m_iscomment(false),
460  m_linenum(0)
461 {
462  if (!parent) {
463  m_root = this;
464  } else {
465  m_root = &(parent->root());
466  }
467 }
468 
469 // Copy constructor
470 /*
471  * @param right Object to be copied
472  */
474  m_name(""),
475  m_value(""),
476  m_parent(0),
477  m_root(0),
478  m_locked(false),
479  m_nchildren(0),
480  m_iscomment(right.m_iscomment),
481  m_linenum(right.m_linenum)
482 {
483  m_root = this;
484  m_name = right.m_name;
485  m_value = right.m_value;
486  right.copy(this);
487 }
488 
489 // Assignment operator for XML trees
490 /*
491  * @param right XML tree to copy
492  */
494 {
495  if (&right != this) {
496  int n = static_cast<int>(m_children.size());
497  for (int i = 0; i < n; i++) {
498  if (m_children[i]) {
499  if (m_children[i]->parent() == this) {
500  delete m_children[i];
501  m_children[i] = 0;
502  }
503  }
504  }
505  m_children.resize(0);
506  right.copy(this);
507  }
508  return *this;
509 }
510 
511 // Destructor for the object
513 {
514  if (m_locked)
515  throw CanteraError("XML_Node::~XML_Node",
516  "attempt to delete locked XML_Node "+name());
517  int n = static_cast<int>(m_children.size());
518  for (int i = 0; i < n; i++) {
519  if (m_children[i]) {
520  if (m_children[i]->parent() == this) {
521  delete m_children[i];
522  m_children[i] = 0;
523  }
524  }
525  }
526 }
527 
529 {
530  int n = static_cast<int>(m_children.size());
531  for (int i = 0; i < n; i++) {
532  if (m_children[i]) {
533  if (m_children[i]->parent() == this) {
534  delete m_children[i];
535  m_children[i] = 0;
536  }
537  }
538  }
539  m_value.clear();
540  m_childindex.clear();
541  m_attribs.clear();
542  m_children.clear();
543 
544  m_nchildren = 0;
545  m_iscomment = false;
546  m_linenum = 0;
547 
548 }
549 
550 // Add a child node to the current node containing a comment
551 /*
552  * Child node will have the name, "comment".
553  *
554  * @param comment Content of the comment
555  */
556 void XML_Node::addComment(const std::string& comment)
557 {
558  addChild("comment", comment);
559 }
560 
561 
562 //! Merge an existing node as a child node to the current node
563 /*!
564  * This will merge an XML_Node as a child to the current node.
565  * Note, this actually adds the node. Therefore, the current node is changed.
566  * There is no copy made of the child node. The child node should not be deleted in the future.
567  *
568  * @param node Reference to a child XML_Node object
569  *
570  * @return Returns a reference to the added child node
571  */
573 {
574  m_children.push_back(&node);
575  m_nchildren = static_cast<int>(m_children.size());
576  m_childindex.insert(pair<const std::string, XML_Node*>(node.name(), m_children.back()));
577  node.setRoot(root());
578  node.setParent(this);
579  return *m_children.back();
580 }
581 
582 // Add a child node to the current node by making a copy of an existing node tree
583 /*
584  * This will add an XML_Node as a child to the current node.
585  * Note, this actually adds the node. Therefore, node is changed.
586  * A copy is made of the underlying tree.
587  *
588  * @param node Reference to a child XML_Node object
589  *
590  * @return returns a reference to the added node
591  */
592 XML_Node& XML_Node::addChild(const XML_Node& node)
593 {
594  XML_Node* xx = new XML_Node(node);
595  m_children.push_back(xx);
596  m_nchildren = static_cast<int>(m_children.size());
597  m_childindex.insert(pair<const std::string, XML_Node*>(xx->name(), xx));
598  xx->setRoot(root());
599  xx->setParent(this);
600  return *m_children.back();
601 }
602 
603 // Add a new malloced child node to the current node with a specified name
604 /*
605  * This will add an XML_Node as a child to the current node.
606  * The node will be blank except for the specified name.
607  *
608  * @param sname Name of the new child
609  *
610  * @return Returns a reference to the added node
611  */
612 XML_Node& XML_Node::addChild(const std::string& sname)
613 {
614  XML_Node* xxx = new XML_Node(sname, this);
615  m_children.push_back(xxx);
616  m_nchildren = m_children.size();
617  m_childindex.insert(pair<const std::string, XML_Node*>(sname, xxx));
618  xxx->setRoot(root());
619  xxx->setParent(this);
620  return *m_children.back();
621 }
622 
623 XML_Node& XML_Node::addChild(const char* cstring)
624 {
625  return addChild(std::string(cstring));
626 }
627 
628 // Add a new malloced child node to the current xml node, and at the
629 // same time add a value to the child
630 /*
631  * Resulting XML string:
632  * <name>value</name>
633  *
634  * @param name Name of the child XML_Node object
635  * @param value Value of the XML_Node - string
636  * @return Returns a reference to the created child XML_Node object
637  */
638 XML_Node& XML_Node::addChild(const std::string& name, const std::string& value)
639 {
640  XML_Node& c = addChild(name);
641  c.addValue(value);
642  return c;
643 }
644 
645 // Add a child node to the current xml node, and at the
646 // same time add a formatted value to the child
647 /*
648  * This version supplies a formatting string (printf format)
649  * to the output of the value.
650  *
651  * Resulting XML string:
652  * <name>value</name>
653  *
654  * @param name Name of the child XML_Node object
655  * @param value Value of the XML_Node - double
656  * @param fmt Format of the output for value
657  *
658  * @return Returns a reference to the created child XML_Node object
659  */
660 XML_Node& XML_Node::addChild(const std::string& name, const doublereal value,
661  const std::string fmt)
662 {
663  XML_Node& c = addChild(name);
664  c.addValue(value, fmt);
665  return c;
666 }
667 
668 // Remove a child from this node's list of children
669 /*
670  * This function removes an XML_Node from the children of this node.
671  *
672  * @param node Pointer to the node to be removed. Note, this node
673  * isn't modified in any way.
674  */
675 void XML_Node::removeChild(const XML_Node* const node)
676 {
677  vector<XML_Node*>::iterator i;
678  i = find(m_children.begin(), m_children.end(), node);
679  m_children.erase(i);
680  m_nchildren = m_children.size();
681  m_childindex.erase(node->name());
682 }
683 
684 std::string XML_Node::id() const
685 {
686  if (hasAttrib("id")) {
687  return attrib("id");
688  }
689  return std::string("");
690 }
691 
692 // Modify the value for the current node
693 /*
694  * This functions fills in the m_value field of the current node
695  *
696  * @param val string Value that the node will be assigned
697  */
698 void XML_Node::addValue(const std::string& val)
699 {
700  m_value = val;
701  if (m_name == "comment") {
702  m_iscomment = true;
703  }
704 }
705 
706 // Modify the value for the current node
707 /*
708  * This functions fills in the m_value field of the current node
709  * with a formatted double value
710  *
711  * @param val double Value that the node will be assigned
712  * @param fmt Format of the printf string conversion of the double.
713  * Default is "%g" Must be less than 63 chars
714  */
715 void XML_Node::addValue(const doublereal val, const std::string fmt)
716 {
717  m_value = stripws(fp2str(val, fmt));
718 }
719 
720 // Return the value of an XML node as a string
721 /*
722  * This is a simple accessor routine
723  */
724 std::string XML_Node::value() const
725 {
726  return m_value;
727 }
728 
729 // Overloaded parenthesis operator returns the value of the Node
730 /*
731  * @return Returns the value of the node as a string.
732  */
733 std::string XML_Node::operator()() const
734 {
735  return m_value;
736 }
737 
738 // Return the value of an XML node as a double
739 /*
740  * This accesses the value string, and then tries to
741  * interpret it as a single double value.
742  */
743 doublereal XML_Node::fp_value() const
744 {
745  return atofCheck(m_value.c_str());
746 }
747 
748 // Return the value of an XML node as a single int
749 /*
750  * This accesses the value string, and then tries to
751  * interpret it as a single int value.
752  */
753 integer XML_Node::int_value() const
754 {
755  return std::atoi(m_value.c_str());
756 }
757 
758 // Return the value of an XML child node as a string
759 /*
760  * @param cname Name of the child node of the current
761  * node, for which you want the value
762  */
763 std::string XML_Node::value(const std::string& cname) const
764 {
765  return child(cname).value();
766 }
767 
768 // Overloaded parenthesis operator with one augment
769 // returns the value of an XML child node as a string
770 /*
771  * @param cname Name of the child node to the current
772  * node, for which you want the value
773  */
774 std::string XML_Node::operator()(std::string loc) const
775 {
776  return value(loc);
777 }
778 
779 // Add or modify an attribute of the current node
780 /*
781  * This functions fills in the m_value field of the current node
782  * with a string value
783  *
784  * @param attrib String name for the attribute to be assigned
785  * @param value String value that the attribute will have
786  */
787 void XML_Node::addAttribute(const std::string& attrib, const std::string& value)
788 {
790 }
791 
792 // Add or modify an attribute to the double, value
793 /*
794  * This functions fills in the attribute field, named attrib,
795  * with the double value, value. A formatting string is used.
796  *
797  * @param attrib String name for the attribute to be assigned
798  * @param value double Value that the node will be assigned
799  * @param fmt Format of the printf string conversion of the double.
800  * Default is "%g".
801  */
802 void XML_Node::addAttribute(const std::string& attrib,
803  const doublereal value, const std::string fmt)
804 {
805  m_attribs[attrib] = fp2str(value, fmt);
806 }
807 
808 // The operator[] is overloaded to provide a lookup capability
809 // on attributes for the current XML element.
810 /*
811  * For example
812  * xmlNode["id"]
813  * will return the value of the attribute "id" for the current
814  * XML element. It will return the blank std::string if there isn't
815  * an attribute with that name.
816  *
817  * @param attr attribute string to look up
818  *
819  * @return Returns a string representing the value of the attribute
820  * within the XML node. If there is no attribute
821  * with the given name, it returns the null string.
822  */
823 std::string XML_Node::operator[](const std::string& attr) const
824 {
825  return attrib(attr);
826 }
827 
828 // Function returns the value of an attribute
829 /*
830  * This function searches the attributes vector for the parameter
831  * std::string attribute. If a match is found, the attribute value
832  * is returned as a string. If no match is found, the empty string
833  * is returned.
834  *
835  * @param attr Std::String containing the attribute to be searched for.
836  *
837  * @return Returns If a match is found, the attribute value
838  * is returned as a string. If no match is found, the empty string
839  * is returned.
840  */
841 std::string XML_Node::attrib(const std::string& attr) const
842 {
843  std::map<std::string,std::string>::const_iterator i = m_attribs.find(attr);
844  if (i != m_attribs.end()) {
845  return i->second;
846  }
847  return "";
848 }
849 
850 // Returns a changeable value of the attributes map for the current node
851 /*
852  * Note this is a simple accessor routine. And, it is a private function.
853  * It's used in some internal copy and assignment routines
854  */
855 std::map<std::string,std::string>& XML_Node::attribs()
856 {
857  return m_attribs;
858 }
859 
860 const std::map<std::string,std::string>& XML_Node::attribsConst() const
861 {
862  return m_attribs;
863 }
864 
865 // Set the line number
866 /*
867  * @param n the member data m_linenum is set to n
868  */
869 void XML_Node::setLineNumber(const int n)
870 {
871  m_linenum = n;
872 }
873 
874 // Return the line number
875 /*
876  * @return returns the member data m_linenum
877  */
879 {
880  return m_linenum;
881 }
882 
883 // Returns a pointer to the parent node of the current node
885 {
886  return m_parent;
887 }
888 
889 // Sets the pointer for the parent node of the current node
890 /*
891  * @param p Pointer to the parent node
892  *
893  * @return Returns the pointer p
894  */
896 {
897  m_parent = p;
898  return p;
899 }
900 
901 // Tests whether the current node has a child node with a particular name
902 /*
903  * @param ch Name of the child node to test
904  *
905  * @return Returns true if the child node exists, false otherwise.
906  */
907 bool XML_Node::hasChild(const std::string ch) const
908 {
909  return (m_childindex.find(ch) != m_childindex.end());
910 }
911 
912 // Tests whether the current node has an attribute with a particular name
913 /*
914  * @param a Name of the attribute to test
915  *
916  * @return Returns true if the attribute exists, false otherwise.
917  */
918 bool XML_Node::hasAttrib(std::string a) const
919 {
920  return (m_attribs.find(a) != m_attribs.end());
921 }
922 
923 // Return a reference to the n'th child of the current node
924 /*
925  * @param n Number of the child to return
926  */
927 XML_Node& XML_Node::child(const size_t n) const
928 {
929  return *m_children[n];
930 }
931 
932 // Return an unchangeable reference to the vector of children of the current node
933 /*
934  * Each of the individual XML_Node child pointers, however,
935  * is to a changeable xml node object.
936  *
937  * @param n Number of the child to return
938  */
939 const std::vector<XML_Node*>& XML_Node::children() const
940 {
941  return m_children;
942 }
943 //=====================================================================================================================
944 // Return the number of children
945 /*
946  * @param discardComments Bool indicating whether we should ignore comments in the count. defaults to false
947  */
948 size_t XML_Node::nChildren(const bool discardComments) const
949 {
950  if (discardComments) {
951  size_t count = 0;
952  for (size_t i = 0; i < m_nchildren; i++) {
953  XML_Node* xc = m_children[i];
954  if (!(xc->isComment())) {
955  count++;
956  }
957  }
958  return count;
959  }
960  return m_nchildren;
961 }
962 //=====================================================================================================================
964 {
965  return m_iscomment;
966 }
967 //=====================================================================================================================
968 // Require that the current xml node have an attribute named
969 // by the first argument, a, and that this attribute have the
970 // the string value listed in the second argument, v.
971 /*
972  * @param a attribute name
973  * @param v required value of the attribute
974  *
975  * If the condition is not true, an exception is thrown
976  */
977 void XML_Node::_require(const std::string& a, const std::string& v) const
978 {
979  if (hasAttrib(a)) {
980  if (attrib(a) == v) {
981  return;
982  }
983  }
984  string msg="XML_Node "+name()+" is required to have an attribute named " + a +
985  " with the value \"" + v +"\", but instead the value is \"" + attrib(a);
986  throw CanteraError("XML_Node::require", msg);
987 }
988 
989 
990 // This routine carries out a search for an XML node based
991 // on both the xml element name and the attribute ID.
992 /*
993  * If exact matches are found for both fields, the pointer
994  * to the matching XML Node is returned.
995  *
996  * The ID attribute may be defaulted by setting it to "".
997  * In this case the pointer to the first xml element matching the name
998  * only is returned.
999  *
1000  * @param nameTarget Name of the XML Node that is being searched for
1001  * @param idTarget "id" attribute of the XML Node that the routine
1002  * looks for
1003  *
1004  * @return Returns the pointer to the XML node that fits the criteria
1005  *
1006  * @internal
1007  * This algorithm does a lateral search of first generation children
1008  * first before diving deeper into each tree branch.
1009  */
1011 findNameID(const std::string& nameTarget,
1012  const std::string& idTarget) const
1013 {
1014  XML_Node* scResult = 0;
1015  XML_Node* sc;
1016  std::string idattrib = id();
1017  if (name() == nameTarget) {
1018  if (idTarget == "" || idTarget == idattrib) {
1019  return const_cast<XML_Node*>(this);
1020  }
1021  }
1022  for (size_t n = 0; n < m_nchildren; n++) {
1023  sc = m_children[n];
1024  if (sc->name() == nameTarget) {
1025  if (idTarget == "") {
1026  return sc;
1027  }
1028  idattrib = sc->id();
1029  if (idTarget == idattrib) {
1030  return sc;
1031  }
1032  }
1033  }
1034  for (size_t n = 0; n < m_nchildren; n++) {
1035  sc = m_children[n];
1036  scResult = sc->findNameID(nameTarget, idTarget);
1037  if (scResult) {
1038  return scResult;
1039  }
1040  }
1041  return scResult;
1042 }
1043 //====================================================================================================================
1044 // This routine carries out a search for an XML node based
1045 // on both the xml element name and the attribute ID and an integer index.
1046 /*
1047  * If exact matches are found for all fields, the pointer
1048  * to the matching XML Node is returned. The search is only carried out on
1049  * the current element and the child elements of the current element.
1050  *
1051  * The "id" attribute may be defaulted by setting it to "".
1052  * In this case the pointer to the first xml element matching the name
1053  * only is returned.
1054  *
1055  * @param nameTarget Name of the XML Node that is being searched for
1056  * @param idTarget "id" attribute of the XML Node that the routine
1057  * looks for
1058  * @param index Integer describing the index. The index is an
1059  * attribute of the form index = "3"
1060  *
1061  * @return Returns the pointer to the XML node that fits the criteria
1062  *
1063  */
1064 XML_Node* XML_Node::findNameIDIndex(const std::string& nameTarget,
1065  const std::string& idTarget, const int index_i) const
1066 {
1067  XML_Node* scResult = 0;
1068  XML_Node* sc;
1069  std::string idattrib = id();
1070  std::string ii = attrib("index");
1071  std::string index_s = int2str(index_i);
1072  int iMax = -1000000;
1073  if (name() == nameTarget) {
1074  if (idTarget == "" || idTarget == idattrib) {
1075  if (index_s == ii) {
1076  return const_cast<XML_Node*>(this);
1077  }
1078  }
1079  }
1080  for (size_t n = 0; n < m_nchildren; n++) {
1081  sc = m_children[n];
1082  if (sc->name() == nameTarget) {
1083  ii = sc->attrib("index");
1084  int indexR = atoi(ii.c_str());
1085  idattrib = sc->id();
1086  if (idTarget == idattrib || idTarget == "") {
1087  if (index_s == ii) {
1088  return sc;
1089  }
1090  }
1091  if (indexR > iMax) {
1092  scResult = sc;
1093  iMax = indexR;
1094  }
1095  }
1096  }
1097 
1098  return scResult;
1099 }
1100 //====================================================================================================================
1101 // This routine carries out a recursive search for an XML node based
1102 // on the xml element attribute, "id" .
1103 /*
1104  * If exact match is found, the pointer
1105  * to the matching XML Node is returned. If not, 0 is returned.
1106  *
1107  * The ID attribute may be defaulted by setting it to "".
1108  * In this case the pointer to the first xml element matching the name
1109  * only is returned.
1110  *
1111  * @param id "id" attribute of the XML Node that the routine
1112  * looks for
1113  * @param depth Depth of the search.
1114  *
1115  * @return Returns the pointer to the XML node that fits the criteria
1116  *
1117  * @internal
1118  * This algorithm does a lateral search of first generation children
1119  * first before diving deeper into each tree branch.
1120  */
1121 XML_Node* XML_Node::findID(const std::string& id, const int depth) const
1122 {
1123  if (hasAttrib("id")) {
1124  if (attrib("id") == id) {
1125  return const_cast<XML_Node*>(this);
1126  }
1127  }
1128  if (depth > 0) {
1129  XML_Node* r = 0;
1130  for (size_t i = 0; i < nChildren(); i++) {
1131  r = m_children[i]->findID(id, depth-1);
1132  if (r != 0) {
1133  return r;
1134  }
1135  }
1136  }
1137  return 0;
1138 }
1139 
1140 // This routine carries out a recursive search for an XML node based
1141 // on an attribute of each XML node
1142 /*
1143  * If exact match is found with respect to the attribute name and
1144  * value of the attribute, the pointer
1145  * to the matching XML Node is returned. If not, 0 is returned.
1146  *
1147  *
1148  * @param attr Attribute of the XML Node that the routine
1149  * looks for
1150  * @param val Value of the attribute
1151  *
1152  * @return Returns the pointer to the XML node that fits the criteria
1153  *
1154  */
1155 XML_Node* XML_Node::findByAttr(const std::string& attr,
1156  const std::string& val, int depth) const
1157 {
1158  if (hasAttrib(attr)) {
1159  if (attrib(attr) == val) {
1160  return const_cast<XML_Node*>(this);
1161  }
1162  }
1163  if (depth > 0) {
1164  XML_Node* r = 0;
1165  size_t n = nChildren();
1166  for (size_t i = 0; i < n; i++) {
1167  r = m_children[i]->findByAttr(attr, val, depth - 1);
1168  if (r != 0) {
1169  return r;
1170  }
1171  }
1172  }
1173  return 0;
1174 }
1175 
1176 // This routine carries out a recursive search for an XML node based
1177 // on the name of the node.
1178 /*
1179  * If exact match is found with respect to XML_Node name, the pointer
1180  * to the matching XML Node is returned. If not, 0 is returned.
1181  * This is the non-const version of the routine.
1182  *
1183  * @param nm Name of the XML node
1184  *
1185  * @return Returns the pointer to the XML node that fits the criteria
1186  */
1187 XML_Node* XML_Node::findByName(const std::string& nm, int depth)
1188 {
1189  if (name() == nm) {
1190  return this;
1191  }
1192  if (depth > 0) {
1193  XML_Node* r = 0;
1194  for (size_t i = 0; i < nChildren(); i++) {
1195  r = m_children[i]->findByName(nm);
1196  if (r != 0) {
1197  return r;
1198  }
1199  }
1200  }
1201  return 0;
1202 }
1203 
1204 // This routine carries out a recursive search for an XML node based
1205 // on the name of the node.
1206 /*
1207  * If exact match is found with respect to XML_Node name, the pointer
1208  * to the matching XML Node is returned. If not, 0 is returned.
1209  * This is the const version of the routine.
1210  *
1211  * @param nm Name of the XML node
1212  *
1213  * @return Returns the pointer to the XML node that fits the criteria
1214  */
1215 const XML_Node* XML_Node::findByName(const std::string& nm, int depth) const
1216 {
1217  if (name() == nm) {
1218  return const_cast<XML_Node*>(this);
1219  }
1220  if (depth > 0) {
1221  const XML_Node* r = 0;
1222  for (size_t i = 0; i < nChildren(); i++) {
1223  r = m_children[i]->findByName(nm);
1224  if (r != 0) {
1225  return r;
1226  }
1227  }
1228  }
1229  return 0;
1230 }
1231 
1232 // Write the header to the xml file to the specified ostream
1233 /*
1234  * @param s ostream to write the output to
1235  */
1236 void XML_Node::writeHeader(std::ostream& s)
1237 {
1238  s << "<?xml version=\"1.0\"?>" << endl;
1239 }
1240 
1241 // Main routine to create an tree-like representation of an XML file
1242 /*
1243  * Given an input stream, this routine will read matched XML tags
1244  * representing the ctml file until an EOF is read from the file.
1245  * This routine is called by the root XML_Node object.
1246  *
1247  * @param f Input stream containing the ascii input file
1248  */
1249 void XML_Node::build(std::istream& f)
1250 {
1251  XML_Reader r(f);
1252  string nm, nm2, val;
1253  XML_Node* node = this;
1254  map<string, string> attribs;
1255  while (!f.eof()) {
1256  attribs.clear();
1257  nm = r.readTag(attribs);
1258 
1259  if (nm == "EOF") {
1260  break;
1261  }
1262  if (nm == "--" && m_name == "--" && m_root == this) {
1263  continue;
1264  }
1265  int lnum = r.m_line;
1266  if (nm[nm.size() - 1] == '/') {
1267  nm2 = nm.substr(0,nm.size()-1);
1268  node = &node->addChild(nm2);
1269  node->addValue("");
1270  node->attribs() = attribs;
1271  node->setLineNumber(lnum);
1272  node = node->parent();
1273  } else if (nm[0] != '/') {
1274  if (nm[0] != '!' && nm[0] != '-' && nm[0] != '?') {
1275  node = &node->addChild(nm);
1276  val = r.readValue();
1277  node->addValue(val);
1278  node->attribs() = attribs;
1279  node->setLineNumber(lnum);
1280  } else if (nm.substr(0,2) == "--") {
1281  if (nm.substr(nm.size()-2,2) == "--") {
1282  node->addComment(nm.substr(2,nm.size()-4));
1283  }
1284  }
1285  } else {
1286  if (node->name() != nm.substr(1,nm.size()-1)) {
1287  throw XML_TagMismatch(node->name(), nm.substr(1,nm.size()-1), lnum);
1288  }
1289  node = node->parent();
1290  }
1291  }
1292 }
1293 
1294 // Copy all of the information in the current XML_Node tree
1295 // into the destination XML_Node tree, doing a union operation as
1296 // we go
1297 /*
1298  * Note this is a const function because the current XML_Node and
1299  * its children isn't altered by this operation.
1300  *
1301  * @param node_dest This is the XML node to receive the information
1302  *
1303  */
1304 void XML_Node::copyUnion(XML_Node* const node_dest) const
1305 {
1306  XML_Node* sc, *dc;
1307  node_dest->addValue(m_value);
1308  if (m_name == "") {
1309  return;
1310  }
1311  map<string,string>::const_iterator b = m_attribs.begin();
1312  for (; b != m_attribs.end(); ++b) {
1313  if (! node_dest->hasAttrib(b->first)) {
1314  node_dest->addAttribute(b->first, b->second);
1315  }
1316  }
1317  const vector<XML_Node*> &vsc = node_dest->children();
1318  for (size_t n = 0; n < m_nchildren; n++) {
1319  sc = m_children[n];
1320  size_t ndc = node_dest->nChildren();
1321  dc = 0;
1322  if (! sc->m_iscomment) {
1323  for (size_t idc = 0; idc < ndc; idc++) {
1324  XML_Node* dcc = vsc[idc];
1325  if (dcc->name() == sc->name()) {
1326  if (sc->hasAttrib("id")) {
1327  if (sc->attrib("id") != dcc->attrib("id")) {
1328  break;
1329  }
1330  }
1331  if (sc->hasAttrib("name")) {
1332  if (sc->attrib("name") != dcc->attrib("name")) {
1333  break;
1334  }
1335  }
1336  if (sc->hasAttrib("model")) {
1337  if (sc->attrib("model") != dcc->attrib("model")) {
1338  break;
1339  }
1340  }
1341  if (sc->hasAttrib("title")) {
1342  if (sc->attrib("title") != dcc->attrib("title")) {
1343  break;
1344  }
1345  }
1346  dc = vsc[idc];
1347  }
1348  }
1349  }
1350  if (!dc) {
1351  (void) node_dest->addChild(sc->name());
1352  dc = vsc[ndc];
1353  }
1354  sc->copyUnion(dc);
1355  }
1356 }
1357 
1358 // Copy all of the information in the current XML_Node tree
1359 // into the destination XML_Node tree, doing a complete copy
1360 // as we go.
1361 /*
1362  * Note this is a const function because the current XML_Node and
1363  * its children isn't altered by this operation.
1364  *
1365  * @param node_dest This is the XML node to receive the information
1366  */
1367 void XML_Node::copy(XML_Node* const node_dest) const
1368 {
1369  XML_Node* sc, *dc;
1370  node_dest->addValue(m_value);
1371  node_dest->setName(m_name);
1372  node_dest->setLineNumber(m_linenum);
1373  if (m_name == "") {
1374  return;
1375  }
1376  map<string,string>::const_iterator b = m_attribs.begin();
1377  for (; b != m_attribs.end(); ++b) {
1378  node_dest->addAttribute(b->first, b->second);
1379  }
1380  const vector<XML_Node*> &vsc = node_dest->children();
1381 
1382  for (size_t n = 0; n < m_nchildren; n++) {
1383  sc = m_children[n];
1384  size_t ndc = node_dest->nChildren();
1385  // Here is where we do a malloc of the child node.
1386  (void) node_dest->addChild(sc->name());
1387  dc = vsc[ndc];
1388  sc->copy(dc);
1389  }
1390 }
1391 
1392 // Set the lock for this node
1394 {
1395  m_locked = true;
1396  for (size_t i = 0; i < m_nchildren; i++) {
1397  m_children[i]->lock();
1398  }
1399 }
1400 
1401 // Unset the lock for this node
1403 {
1404  m_locked = false;
1405  for (size_t i = 0; i < m_nchildren; i++) {
1406  m_children[i]->unlock();
1407  }
1408 }
1409 
1410 // Get a vector of pointers to XML_Node containing all of the children
1411 // of the current node which matches the input name
1412 /*
1413  * @param name Name of the XML_Node children to search on
1414  *
1415  * @param children output vector of pointers to XML_Node children
1416  * with the matching name
1417  */
1418 void XML_Node::getChildren(const std::string& nm,
1419  std::vector<XML_Node*>& children) const
1420 {
1421  for (size_t i = 0; i < nChildren(); i++) {
1422  if (child(i).name() == nm) {
1423  children.push_back(&child(i));
1424  }
1425  }
1426 }
1427 
1428 // Return a changeable reference to a child of the current node,
1429 // named by the argument
1430 /*
1431  * @param loc Name of the child to return
1432  */
1433 XML_Node& XML_Node::child(const std::string& aloc) const
1434 {
1435  string::size_type iloc;
1436  string cname;
1437  string loc = aloc;
1438  std::multimap<std::string,XML_Node*>::const_iterator i;
1439 
1440  while (1) {
1441  iloc = loc.find('/');
1442  if (iloc != string::npos) {
1443  cname = loc.substr(0,iloc);
1444  loc = loc.substr(iloc+1, loc.size());
1445  i = m_childindex.find(cname);
1446  if (i != m_childindex.end()) {
1447  return i->second->child(loc);
1448  } else {
1449  throw XML_NoChild(this, m_name, cname, lineNumber());
1450  }
1451  } else {
1452  i = m_childindex.find(loc);
1453  if (i != m_childindex.end()) {
1454  return *(i->second);
1455  } else {
1456  throw XML_NoChild(this, m_name, loc, lineNumber());
1457  }
1458  }
1459  }
1460 }
1461 
1462 /*
1463  * Write an XML subtree to an output stream. This is the
1464  * main recursive routine. It doesn't put a final endl
1465  * on. This is fixed up in the public method.
1466  */
1467 void XML_Node::write_int(std::ostream& s, int level, int numRecursivesAllowed) const
1468 {
1469 
1470  if (m_name == "") {
1471  return;
1472  }
1473 
1474  string indent(level, ' ');
1475  if (m_iscomment) {
1476  /*
1477  * In the comment section, we test to see if there
1478  * already is a space beginning and ending the comment.
1479  * If there already is one, we don't add another one.
1480  */
1481  s << endl << indent << "<!--";
1482  if (! isspace(m_value[0])) {
1483  s << " ";
1484  }
1485  s << m_value;
1486  int ll = static_cast<int>(m_value.size()) - 1;
1487  if (! isspace(m_value[ll])) {
1488  s << " ";
1489  }
1490  s << "-->";
1491  return;
1492  }
1493 
1494  s << indent << "<" << m_name;
1495  map<string,string>::const_iterator b = m_attribs.begin();
1496  for (; b != m_attribs.end(); ++b) {
1497  s << " " << b->first << "=\"" << b->second << "\"";
1498  }
1499  if (m_value == "" && m_nchildren == 0) {
1500  s << "/>";
1501  } else {
1502  s << ">";
1503 
1504  if (m_value != "") {
1505  string vv = m_value;
1506  string::size_type ieol = vv.find('\n');
1507  if (ieol != string::npos) {
1508  while (1 > 0) {
1509  ieol = vv.find('\n');
1510  if (ieol != string::npos) {
1511  if (ieol == 0) {
1512  s << endl << indent << " ";
1513  } else {
1514  size_t jf = ieol - 1;
1515  for (int j = 0; j < (int) ieol; j++) {
1516  if (! isspace(vv[j])) {
1517  jf = j;
1518  break;
1519  }
1520  }
1521  s << endl << indent << " " << vv.substr(jf,ieol-jf);
1522  }
1523  vv = vv.substr(ieol+1);
1524  } else {
1525  int lll = static_cast<int>(vv.size()) - 1;
1526  if (lll >= 0) {
1527  int jf = lll;
1528  for (int j = 0; j < lll; j++) {
1529  if (! isspace(vv[j])) {
1530  jf = j;
1531  break;
1532  }
1533  }
1534  if (jf < lll) {
1535  s << endl << indent << " " << vv.substr(jf);
1536  }
1537  }
1538  break;
1539  }
1540  }
1541  s << endl << indent;
1542  } else {
1543  bool doSpace = true;
1544  bool doNewLine = false;
1545  int ll = static_cast<int>(m_value.size()) - 1;
1546  if (ll > 25) {
1547  doNewLine = true;
1548  }
1549  if (m_name == "floatArray") {
1550  doNewLine = true;
1551  }
1552  if (doNewLine) {
1553  doSpace = false;
1554  }
1555 
1556  if (doNewLine) {
1557  s << endl << indent << " ";
1558  }
1559  /*
1560  * Put spaces around a raw value field for readability
1561  */
1562  if (doSpace && (! isspace(m_value[0]))) {
1563  s << " ";
1564  }
1565  /*
1566  * Write out the value
1567  */
1568  s << m_value;
1569 
1570  if (doSpace && (! isspace(m_value[ll]))) {
1571  s << " ";
1572  }
1573  if (doNewLine) {
1574  s << endl << indent;
1575  }
1576  }
1577  }
1578  if (numRecursivesAllowed > 0) {
1579  for (size_t i = 0; i < m_nchildren; i++) {
1580  s << endl;
1581  m_children[i]->write_int(s,level + 2, numRecursivesAllowed - 1);
1582  }
1583  }
1584  if (m_nchildren > 0) {
1585  s << endl << indent;
1586  }
1587  s << "</" << m_name << ">";
1588  }
1589 }
1590 
1591 /*
1592  * Write an XML subtree to an output stream. This is a
1593  * wrapper around the static routine write_int(). All this
1594  * does is add an endl on to the output stream. write_int() is
1595  * fine, but the last endl wasn't being written.
1596  * It also checks for the special name "--". If found and we
1597  * are at the root of the xml tree, then the block
1598  * is skipped and the children are processed. "--" is used
1599  * to denote the top of the tree.
1600  */
1601 void XML_Node::write(std::ostream& s, const int level, int numRecursivesAllowed) const
1602 {
1603  if (m_name == "--" && m_root == this) {
1604  for (size_t i = 0; i < m_nchildren; i++) {
1605  m_children[i]->write_int(s,level, numRecursivesAllowed-1);
1606  s << endl;
1607  }
1608  } else {
1609  write_int(s, level, numRecursivesAllowed);
1610  s << endl;
1611  }
1612 }
1613 
1615 {
1616  return *m_root;
1617 }
1618 
1619 void XML_Node::setRoot(const XML_Node& root)
1620 {
1621  m_root = const_cast<XML_Node*>(&root);
1622  for (size_t i = 0; i < m_nchildren; i++) {
1623  m_children[i]->setRoot(root);
1624  }
1625 }
1626 
1628  const std::string& idtarget)
1629 {
1630  XML_Node* scResult = 0;
1631  XML_Node* sc;
1632  if (!root) {
1633  return 0;
1634  }
1635  string idattrib;
1636  string rname = root->name();
1637  if (rname == "phase") {
1638  if (idtarget == "") {
1639  return root;
1640  }
1641  idattrib = root->id();
1642  if (idtarget == idattrib) {
1643  return root;
1644  } else {
1645  return 0;
1646  }
1647  }
1648 
1649  const vector<XML_Node*> &vsc = root->children();
1650  for (size_t n = 0; n < root->nChildren(); n++) {
1651  sc = vsc[n];
1652  if (sc->name() == "phase") {
1653  if (idtarget == "") {
1654  return sc;
1655  }
1656  idattrib = sc->id();
1657  if (idtarget == idattrib) {
1658  return sc;
1659  }
1660  }
1661  }
1662  for (size_t n = 0; n < root->nChildren(); n++) {
1663  sc = vsc[n];
1664  if (sc->name() != "phase") {
1665  scResult = findXMLPhase(sc, idtarget);
1666  if (scResult) {
1667  return scResult;
1668  }
1669  }
1670  }
1671  return scResult;
1672 }
1673 
1674 }
1675 
1676