xml.cpp

Go to the documentation of this file.
00001 /**
00002  * @file xml.cpp
00003  * Classes providing support for XML data files. These classes
00004  * implement only those aspects of XML required to read, write, and
00005  * manipulate CTML data files.
00006  */
00007 
00008 /*
00009  * $Revision: 387 $
00010  * $Date: 2010-01-17 13:17:55 -0500 (Sun, 17 Jan 2010) $
00011  */
00012 
00013 // Copyright 2001  California Institute of Technology
00014 
00015 
00016 // turn off warnings under Windows
00017 #ifdef WIN32
00018 #pragma warning(disable:4786)
00019 #pragma warning(disable:4503)
00020 #pragma warning(disable:4996)
00021 #endif
00022 
00023 #include "config.h"
00024 #ifdef HAS_SSTREAM
00025 #include <sstream>
00026 #endif
00027 
00028 #include <algorithm>
00029 using namespace std;
00030 
00031 #include "xml.h"
00032 #include "global.h"
00033 #include "stringUtils.h"
00034 #include <ctype.h>
00035 #include <cstdlib>
00036 
00037 namespace Cantera {
00038 
00039 
00040   ////////////////////// exceptions ////////////////////////////
00041 
00042   //! Classs representing a generic XML error condition
00043   class XML_Error : public CanteraError {
00044   protected:
00045     //! Constructor
00046     /*!
00047      * Note, we don't actually post the error in this class.
00048      * Therefore, this class can't be used externally. Therefore,
00049      * it's a protected constructor.
00050      *
00051      * @param line Number number where the error occurred.
00052      */
00053     XML_Error(int line=0) :
00054       m_line(line)
00055     {
00056       m_msg = "Error in XML file";
00057       if (line > 0) {
00058         m_msg += " at line " + int2str(line+1);
00059       }
00060       m_msg += ".\n";
00061     }
00062 
00063     //! destructor
00064     virtual ~XML_Error() throw() {
00065     }
00066 
00067   protected:
00068     //! Line number of the file
00069     int m_line;
00070 
00071     //! String message for the error
00072     std::string m_msg;
00073   };
00074     
00075   //! Class representing a specific type of XML file formatting error
00076   /*!
00077    * An XML tag is not matched
00078    */
00079   class XML_TagMismatch : public XML_Error {
00080   public:
00081 
00082     //! Constructor
00083     /*!
00084      *  An XML element must have the same opening and closing name.
00085      *
00086      *  @param  opentag     String representing the opening of the XML bracket
00087      *  @param  closetag    String representing the closing of the XML bracket 
00088      *  @param  line        Line number where the error occurred.
00089      */
00090     XML_TagMismatch(std::string opentag, std::string closetag, 
00091                     int line=0) :
00092       XML_Error(line)
00093     {
00094       m_msg += "<" + opentag + "> paired with </" + closetag + ">.\n";
00095       setError("XML_TagMismatch", m_msg);
00096     }
00097 
00098     //! Destructor
00099     virtual ~XML_TagMismatch() throw() {}
00100   };
00101 
00102   //! Class representing a specific type of XML file formatting error
00103   /*!
00104    * An XML_Node doesn't have a required child node
00105    */
00106   class XML_NoChild : public XML_Error {
00107   public:
00108 
00109     //! Constructor
00110     /*!
00111      *  An XML element doesn't have the required child node
00112      *
00113      *  @param  p           XML_Node to write a string error message
00114      *  @param  parent      Namf of the parent node
00115      *  @param  child       Name of the required child node
00116      *  @param  line        Line number where the error occurred.
00117      */
00118     XML_NoChild(const XML_Node* p, std::string parent, 
00119                 std::string child, int line=0) : 
00120       XML_Error(line)
00121     {
00122       m_msg += "           The XML Node \"" + parent + 
00123         "\", does not contain a required\n" +
00124         "           XML child node named \"" 
00125         + child + "\".\n";
00126 #ifdef HAS_SSTREAM
00127       ostringstream ss(ostringstream::out);
00128       p->write(ss,1);
00129       m_msg += ss.str() + "\n";
00130 #endif
00131       setError("XML_NoChild", m_msg);
00132     }
00133 
00134     //! Destructor
00135     virtual ~XML_NoChild() throw() {}
00136   };
00137 
00138   //! Class representing a specific type of XML file formatting error
00139   /*!
00140    * An XML_Node's units attribute has the wrong type of units.
00141    */
00142   class XML_IllegalUnits : public XML_Error {
00143   public:
00144 
00145     //! Constructor
00146     /*!
00147      *  Wrong units string.
00148      *
00149      *  @param  name        Name of the current XML node
00150      *  @param  units       Units string in the "units" attribute
00151      *  @param  line        Line number where the error occurred.
00152      */
00153     XML_IllegalUnits(std::string name, std::string units, int line=0) :
00154       XML_Error(line)
00155     {
00156       m_msg += "Illegal units (" + units + 
00157         ") specified for node " + name + ".\n";
00158       setError("XML_IllegalUnits", m_msg);
00159     }
00160 
00161     //! Destructor
00162     virtual ~XML_IllegalUnits() throw() {}
00163   };
00164 
00165 
00166 
00167   //////////////////// XML_Reader methods ///////////////////////
00168 
00169 
00170   XML_Reader::XML_Reader(std::istream& input) : 
00171     m_s(input), 
00172     m_line(0) 
00173   {
00174   }
00175   
00176   //! Get a single character from the input stream.
00177   /*! 
00178    *  If the character
00179    *  is a new-line character, then increment the line count.
00180    */
00181   void XML_Reader::getchr(char& ch) {
00182     m_s.get(ch);
00183     if (ch == '\n') {
00184       m_line++;
00185     } 
00186   }
00187 
00188 
00189   // Returns string 'aline' stripped of leading and trailing white
00190   // space.
00191   // @todo why is this a class method?
00192   std::string XML_Reader::strip(const std::string& aline) const {
00193     int len = static_cast<int>(aline.size());
00194     int i, j, ll;
00195     for (i = len-1; i >= 0; i--) {
00196       ll = aline[i];
00197       if (! isspace(ll)) break;
00198     }
00199     for (j = 0;  j < i; j++) {
00200       ll = aline[j];
00201       if (! isspace(ll)) break;
00202     }
00203     //  if (aline[j] != ' ' && aline[j] != '\n') break;
00204     return aline.substr(j, i - j + 1);
00205   }
00206 
00207 
00208   /// Looks for a substring within 'aline' enclosed in double
00209   /// quotes, and returns this substring (without the quotes) if
00210   /// found.  If not, an empty string is returned.
00211   /// @todo why is this a class method?
00212   std::string XML_Reader::inquotes(const std::string& aline) const {
00213     int len = static_cast<int>(aline.size());
00214     int i, j;
00215     for (i = len-1; i >= 0; i--) 
00216       if (aline[i] == '"') break;
00217     for (j = 0;  j < i; j++) 
00218       if (aline[j] == '"') break;
00219     if (j == i) return "";
00220     else return aline.substr(j+1, i - j - 1);
00221   }
00222 
00223   /**
00224    * Find the first position of a character, q, in string s,
00225    * which is not immediately preceded by the backslash character
00226    * '\'
00227    */
00228   static string::size_type findUnbackslashed(std::string s, const char q,
00229                                              std::string::size_type istart = 0) {
00230     string::size_type iloc, icurrent, len;
00231     icurrent = istart;
00232     len = s.size();
00233     while (1) {
00234       iloc = s.find(q, icurrent);
00235       if (iloc == string::npos || iloc == 0) {
00236         return iloc;
00237       }
00238       char cm1 = s[iloc-1];
00239       if (cm1 == '\\') {
00240         if (iloc >= (len -1)) return string::npos;
00241         icurrent = iloc + 1;
00242       } else {
00243         return iloc;
00244       }
00245     }
00246   }
00247 
00248   /*
00249    *  Searches a string for the first occurrence of a valid
00250    *  quoted string. Quotes can start with either a single
00251    *  quote or a double quote, but must also end with the same
00252    *  type. Quotes may be commented out by preceding with a
00253    *  backslash character, '\\'. 
00254    */
00255   int XML_Reader::findQuotedString(const std::string& s, std::string &rstring) const {
00256     const char q1 = '\'';
00257     const char q2 = '"';
00258     rstring = "";
00259     char qtype = ' ';
00260     string::size_type iloc1, iloc2, ilocStart = 0;
00261     iloc1 = findUnbackslashed(s, q1);
00262     iloc2 = findUnbackslashed(s, q2);
00263     if (iloc2 != string::npos) {
00264       ilocStart = iloc2;
00265       qtype = q2;
00266     }
00267     if (iloc1 != string::npos) {
00268       if (iloc1 < ilocStart) {
00269         ilocStart = iloc1;
00270         qtype = q1;
00271       }
00272     }
00273     if (qtype == ' ') return 0;
00274 
00275     iloc1 = findUnbackslashed(s, qtype, ilocStart+1);
00276 
00277     if (iloc1 == string::npos) {
00278       return 0;
00279     }
00280     /*
00281      * Define the return string by the two endpoints.
00282      * Strip the surrounding quotes as well
00283      */
00284     rstring = s.substr(ilocStart + 1, iloc1 - 1);
00285     /*
00286      * Return the first character position past the quotes
00287      */
00288     return static_cast<int>(iloc1)+1;   
00289   }
00290     
00291   /*
00292    * parseTag parses XML tags, i.e., the XML elements that are
00293    * inbetween angle brackets.
00294    */
00295   void XML_Reader::parseTag(std::string tag, std::string& name, 
00296                             std::map<std::string, std::string>& attribs) const {
00297     string::size_type iloc;
00298     string attr, val;
00299     string s = strip(tag);
00300     iloc = s.find(' ');
00301     if (iloc != string::npos) {
00302       name = s.substr(0, iloc);
00303       s = strip(s.substr(iloc+1,s.size()));
00304       if (s[s.size()-1] == '/') {
00305         name += "/";
00306       }
00307 
00308       // get attributes
00309       while (1) {
00310         iloc = s.find('=');
00311         if (iloc == string::npos) break; 
00312         attr = strip(s.substr(0,iloc));
00313         if (attr == "") break;
00314         s = strip(s.substr(iloc+1,s.size()));
00315         iloc = findQuotedString(s, val);
00316         attribs[attr] = val;
00317         if (iloc != string::npos) {
00318           if (iloc < s.size()) 
00319             s = strip(s.substr(iloc,s.size()));
00320           else
00321             break;
00322         }
00323       }
00324     }
00325     else {
00326       name = s;
00327     }
00328   }
00329     
00330   std::string XML_Reader::readTag(std::map<std::string, std::string>& attribs) {
00331     string name, tag = "";
00332     bool incomment = false;
00333     char ch  = '-';
00334     while (1) {
00335       if (m_s.eof() || (getchr(ch), ch == '<')) break;
00336     }
00337     char ch1 = ' ', ch2 = ' ';
00338     while (1) {
00339       if (m_s.eof()) { tag = "EOF"; break;}
00340       ch2 = ch1;
00341       ch1 = ch;
00342       getchr(ch);
00343       if (ch == '-') {
00344         if (ch1 == '-' && ch2 == '!') {
00345           incomment = true;
00346           tag = "-";
00347         }
00348       }
00349       else if (ch == '>') {
00350         if (incomment) {
00351           if (ch1 == '-' && ch2 == '-') break;
00352         }
00353         else
00354           break;
00355       }
00356       if (isprint(ch)) tag += ch;
00357     }
00358     if (incomment) {
00359       attribs.clear();
00360       return tag;
00361     }
00362     else {
00363       parseTag(tag, name, attribs);
00364       return name;
00365     }
00366   }
00367 
00368   std::string XML_Reader::readValue() {
00369     string tag = "";
00370     char ch, lastch;
00371     ch = '\n';
00372     bool front = true;
00373     while (1) {
00374       if (m_s.eof()) break;
00375       lastch = ch;
00376       getchr(ch);
00377       if (ch == '\n') 
00378         front = true;
00379       else if (ch != ' ') 
00380         front = false;
00381       if (ch == '<') {
00382         m_s.putback(ch); 
00383         break;
00384       }
00385       if (front && lastch == ' ' && ch == ' ') ;
00386       else tag += ch;
00387     }
00388     return strip(tag);
00389   }
00390 
00391 
00392   //////////////////////////  XML_Node  /////////////////////////////////
00393 
00394   XML_Node::XML_Node(const char * cnm)  
00395     : m_value(""), 
00396       m_parent(0),
00397       m_locked(false),
00398       m_nchildren(0), 
00399       m_iscomment(false) 
00400   {
00401     if (! cnm) {
00402       m_name = "--";
00403     } else {
00404       m_name = cnm;
00405     }
00406     m_root = this;
00407   }
00408 
00409   // Default constructor for XML_Node, representing a tree structure
00410   /*
00411    *  Constructor for an XML_Node, which is a node in a tree-like structure
00412    *  representing an XML file.
00413    *
00414    *  @param nm  Name of the node.
00415    *             The default name of the node is "--"
00416    *
00417    *  @param p pointer to the root for this node in the tree.
00418    *           The default is 0 indicating this is the top of the tree.
00419    */
00420   XML_Node::XML_Node(const std::string nm, XML_Node * const p) 
00421     : m_name(nm),
00422       m_value(""), 
00423       m_parent(p),
00424       m_locked(false),
00425       m_nchildren(0), 
00426       m_iscomment(false) 
00427   {
00428     if (!p) m_root = this;
00429     else m_root = &p->root();
00430   }
00431 
00432   // Copy constructor
00433   /*
00434    * @param right   Object to be copied
00435    */
00436   XML_Node::XML_Node(const XML_Node &right) :
00437     m_name(""), 
00438     m_value(""),
00439     m_parent(0),
00440     m_locked(false),
00441     m_nchildren(0), 
00442     m_iscomment(false) 
00443   {
00444     m_root = this;
00445     right.copy(this);
00446   }
00447 
00448   // Assignment operator for XML trees
00449   /*
00450    *  @param right    XML tree to copy
00451    */
00452   XML_Node & XML_Node::operator=(const XML_Node &right)
00453   {
00454     if (&right != this) {
00455       int n = static_cast<int>(m_children.size());
00456       for (int i = 0; i < n; i++) {
00457         if (m_children[i]) {
00458           if (m_children[i]->parent() == this) {
00459             delete m_children[i];
00460             m_children[i] = 0;
00461           }
00462         }
00463       }
00464       m_children.resize(0);
00465       right.copy(this);
00466     }
00467     return *this;
00468   }
00469 
00470   // Destructor for the object
00471   XML_Node::~XML_Node() {
00472     if (m_locked) 
00473       throw CanteraError("XML_Node::~XML_Node",
00474                          "attempt to delete locked XML_Node "+name());
00475     int n = static_cast<int>(m_children.size());
00476     for (int i = 0; i < n; i++) {
00477       if (m_children[i]) {
00478         if (m_children[i]->parent() == this) {
00479           delete m_children[i];
00480           m_children[i] = 0;
00481         }
00482       }
00483     }
00484   }
00485 
00486   void XML_Node::clear() {
00487     int n = static_cast<int>(m_children.size());
00488     for (int i = 0; i < n; i++) {
00489       if (m_children[i]) {
00490         if (m_children[i]->parent() == this) {
00491           delete m_children[i];
00492           m_children[i] = 0;
00493         }
00494       }
00495     }
00496     m_value.clear();
00497     m_childindex.clear();
00498     m_attribs.clear();
00499     m_children.clear();
00500 
00501     m_nchildren = 0;
00502     m_iscomment = false;
00503     m_linenum = 0;
00504 
00505   }
00506 
00507   // Add a child node to the current node containing a comment
00508   /*
00509    *  Child node will have the name, "comment".
00510    *
00511    *  @param comment Content of the comment
00512    */
00513   void XML_Node::addComment(const std::string &comment) {
00514     addChild("comment", comment);
00515   }
00516 
00517   // Add a child node to the current node
00518   /*
00519    * This will add an XML_Node as a child to the current node.
00520    * Note, this actually adds the node. Therefore, node is changed.
00521    * There is no copy made of the child node.
00522    *
00523    *  @param node  Reference to a child XML_Node object
00524    *
00525    *  @return returns a reference to the added node
00526    */
00527   XML_Node& XML_Node::addChild(XML_Node& node) {
00528     m_children.push_back(&node);
00529     m_nchildren = static_cast<int>(m_children.size());
00530     m_childindex[node.name()] = m_children.back();
00531     node.setRoot(root());
00532     node.setParent(this);
00533     return *m_children.back();
00534   }
00535 
00536   // Add a child node to the current node with a specified name
00537   /*
00538    * This will add an XML_Node as a child to the current node.
00539    * The node will be blank except for the specified name.
00540    *
00541    *  @param sname    Name of the new child
00542    *
00543    *  @return         Returns a reference to the added node
00544    */
00545   XML_Node& XML_Node::addChild(const std::string &sname) { 
00546     XML_Node *xxx = new XML_Node(sname, this);
00547     m_children.push_back(xxx);
00548     m_nchildren = static_cast<int>(m_children.size());
00549     m_childindex[sname] = m_children.back();
00550     xxx->setRoot(root());
00551     xxx->setParent(this);
00552     return *m_children.back();
00553   }
00554 
00555   //    Add a child node to the current xml node, and at the
00556   //    same time add a value to the child
00557   /*
00558    *    Resulting XML string:
00559    *      <name>value</name>
00560    *
00561    *   @param   name       Name of the child XML_Node object
00562    *   @param   value      Value of the XML_Node - string
00563    *   @return  Returns a reference to the created child XML_Node object
00564    */
00565   XML_Node& XML_Node::addChild(const std::string &name, const std::string &value) {
00566     XML_Node& c = addChild(name);
00567     c.addValue(value);
00568     return c;
00569   }
00570 
00571   //     Add a child node to the current xml node, and at the
00572   //     same time add a formatted value to the child
00573   /*
00574    *  This version supplies a formatting string (printf format)
00575    *  to the output of the value.
00576    *
00577    *    Resulting XML string:
00578    *      <name>value</name>
00579    *
00580    *   @param   name       Name of the child XML_Node object
00581    *   @param   value      Value of the XML_Node - double
00582    *   @param   fmt        Format of the output for value
00583    *
00584    *   @return  Returns a reference to the created child XML_Node object
00585    */
00586   XML_Node& XML_Node::addChild(const std::string &name, const doublereal value,
00587                                const std::string fmt) {
00588     XML_Node& c = addChild(name);
00589     c.addValue(value, fmt);
00590     return c;
00591   }
00592 
00593   //  Remove a child from this node's list of children
00594   /*
00595    *  This function removes an XML_Node from the children of this node.
00596    *
00597    * @param  node  Pointer to the node to be removed. Note, this node
00598    *               isn't modified in any way.
00599    */
00600   void XML_Node::removeChild(const XML_Node * const node) {
00601     vector<XML_Node*>::iterator i;
00602     i = find(m_children.begin(), m_children.end(), node);
00603     m_children.erase(i);
00604     m_nchildren = static_cast<int>(m_children.size());
00605     m_childindex.erase(node->name());
00606   }
00607 
00608   std::string XML_Node::id() const {
00609     if (hasAttrib("id")) return attrib("id");
00610     return std::string("");
00611   }
00612 
00613   // Modify the value for the current node
00614   /*
00615    * This functions fills in the m_value field of the current node
00616    *
00617    * @param val  string Value that the node will be assigned
00618    */
00619   void XML_Node::addValue(const std::string &val) { 
00620     m_value = val;
00621     if (m_name == "comment") m_iscomment = true;
00622   }
00623 
00624   //    Modify the value for the current node
00625   /*
00626    * This functions fills in the m_value field of the current node
00627    * with a formatted double value
00628    *
00629    * @param val  double Value that the node will be assigned
00630    * @param fmt  Format of the printf string conversion of the double.
00631    *             Default is "%g" Must be less than 63 chars
00632    */
00633   void XML_Node::addValue(const doublereal val, const std::string fmt) {
00634     m_value = stripws(fp2str(val, fmt));
00635   }
00636 
00637   // Return the value of an XML node as a string
00638   /*
00639    *  This is a simple accessor routine
00640    */
00641   std::string XML_Node::value() const {
00642     return m_value;
00643   }
00644 
00645   // Overloaded parenthesis operator returns the value of the Node
00646   /*
00647    *  @return  Returns the value of the node as a string.
00648    */
00649   std::string XML_Node::operator()() const {
00650     return m_value;
00651   }
00652 
00653   // Return the value of an XML node as a double
00654   /*
00655    *  This accesses the value string, and then tries to 
00656    *  interpret it as a single double value.
00657    */
00658   doublereal  XML_Node::fp_value() const { 
00659     return atofCheck(m_value.c_str()); 
00660   }
00661 
00662   // Return the value of an XML node as a single int
00663   /*
00664    *  This accesses the value string, and then tries to 
00665    *  interpret it as a single int value.
00666    */
00667   integer XML_Node::int_value() const {
00668     return std::atoi(m_value.c_str()); 
00669   }
00670 
00671   //  Return the value of an XML child node as a string
00672   /*
00673    *  @param cname  Name of the child node of the current
00674    *                node, for which you want the value
00675    */
00676   std::string XML_Node::value(const std::string &cname) const { 
00677     return child(cname).value();
00678   }
00679 
00680   //  Overloaded parenthesis operator with one augment 
00681   //  returns the value of an XML child node as a string
00682   /*
00683    *  @param cname  Name of the child node to the current
00684    *                node, for which you want the value
00685    */
00686   std::string XML_Node::operator()(std::string loc) const {
00687     return value(loc); 
00688   }
00689   
00690   // Add or modify an attribute of the current node
00691   /*
00692    * This functions fills in the m_value field of the current node
00693    * with a string value
00694    *
00695    * @param attrib  String name for the attribute to be assigned
00696    * @param value   String value that the attribute will have
00697    */
00698   void XML_Node::addAttribute(const std::string & attrib, const std::string & value) {
00699     m_attribs[attrib] = value;
00700   }
00701 
00702   // Add or modify an attribute to the double, value
00703   /*
00704    * This functions fills in the attribute field, named attrib,
00705    * with the double value, value. A formatting string is used.
00706    *
00707    * @param attrib  String name for the attribute to be assigned
00708    * @param value   double Value that the node will be assigned
00709    * @param fmt     Format of the printf string conversion of the double.
00710    *                Default is "%g". 
00711    */
00712   void XML_Node::addAttribute(const std::string & attrib, 
00713                               const doublereal value, const std::string fmt) {
00714     m_attribs[attrib] = fp2str(value, fmt);
00715   }
00716     
00717   //  The operator[] is overloaded to provide a lookup capability
00718   //  on attributes for the current XML element.
00719   /*
00720    * For example
00721    *     xmlNode["id"] 
00722    * will return the value of the attribute "id" for the current
00723    * XML element. It will return the blank std::string if there isn't
00724    * an attribute with that name.
00725    *
00726    * @param attr  attribute string to look up
00727    * 
00728    * @return  Returns a string representing the value of the attribute
00729    *          within the XML node. If there is no attribute
00730    *          with the given name, it returns the null string.
00731    */
00732   std::string XML_Node::operator[](const std::string & attr) const {
00733     return attrib(attr);
00734   }
00735 
00736   // Function returns the value of an attribute
00737   /*
00738    * This function searches the attibutes vector for the parameter 
00739    * std::string attribute. If a match is found, the attribute value
00740    * is returned as a string. If no match is found, the empty string
00741    * is returned.
00742    *
00743    * @param attr  Std::String containing the attribute to be searched for.
00744    *
00745    * @return Returns  If a match is found, the attribute value
00746    *                  is returned as a string. If no match is found, the empty string
00747    *                  is returned.
00748    */
00749   std::string XML_Node::attrib(const std::string & attr) const { 
00750     std::map<std::string,std::string>::const_iterator i = m_attribs.find(attr);
00751     if (i != m_attribs.end()) return i->second;
00752     return ""; 
00753   }
00754 
00755   //  Returns a changeable value of the attributes map for the current node
00756   /*
00757    *  Note this is a simple accessor routine. And, it is a private function.
00758    *  It's used in some internal copy and assignment routines
00759    */
00760   std::map<std::string,std::string>& XML_Node::attribs() { 
00761     return m_attribs;
00762   }
00763 
00764   // Set the line number 
00765   /*
00766    *  @param n   the member data m_linenum is set to n
00767    */
00768   void XML_Node::setLineNumber(const int n) {
00769     m_linenum = n;
00770   }
00771 
00772   // Return the line number 
00773   /*
00774    *  @return  returns the member data m_linenum
00775    */
00776   int XML_Node::lineNumber() const {
00777     return m_linenum;
00778   }
00779 
00780   // Returns a pointer to the parent node of the current node
00781   XML_Node* XML_Node::parent() const {
00782     return m_parent;
00783   }
00784 
00785   // Sets the pointer for the parent node of the current node
00786   /*
00787    * @param p Pointer to the parent node
00788    *
00789    * @return  Returns the pointer p
00790    */
00791   XML_Node* XML_Node::setParent(XML_Node * const p) { 
00792     m_parent = p;
00793     return p; 
00794   }
00795 
00796   // Tests whether the current node has a child node with a particular name
00797   /*
00798    * @param ch  Name of the child node to test
00799    *
00800    * @return Returns true if the child node exists, false otherwise.
00801    */
00802   bool XML_Node::hasChild(const std::string ch) const {
00803     return (m_childindex.find(ch) != m_childindex.end());
00804   }
00805 
00806   // Tests whether the current node has an attribute with a particular name
00807   /*
00808    * @param a  Name of the attribute to test
00809    *
00810    * @return Returns true if the attribute exists, false otherwise.
00811    */
00812   bool XML_Node::hasAttrib(std::string a) const {
00813     return (m_attribs.find(a) != m_attribs.end());
00814   }
00815   
00816   // Return a reference to the n'th child of the current node
00817   /*
00818    *  @param n  Number of the child to return
00819    */
00820   XML_Node& XML_Node::child(const int n) const {
00821     return *m_children[n]; 
00822   }
00823 
00824   // Return an unchangeable reference to the vector of children of the current node
00825   /*
00826    * Each of the individual XML_Node child pointers, however,
00827    *  is to a changeable xml node object.
00828    *
00829    *  @param n  Number of the child to return
00830    */
00831   const std::vector<XML_Node*>& XML_Node::children() const { 
00832     return m_children; 
00833   }
00834 
00835   // return the number of children
00836   /*
00837    *
00838    */
00839   int XML_Node::nChildren() const { 
00840     return m_nchildren;
00841   }
00842  
00843   //    Require that the current xml node have an attribute named
00844   //    by the first argument, a, and that this attribute have the
00845   //    the string value listed in the second argument, v.
00846   /*
00847    *   @param a  attribute name
00848    *   @param v  required value of the attribute
00849    *
00850    *  If the condition is not true, an exception is thrown
00851    */
00852   void XML_Node::_require(const std::string &a, const std::string &v) const {
00853     if (hasAttrib(a)) {
00854       if (attrib(a) == v) return;
00855     }
00856     string msg="XML_Node "+name()+" is required to have an attribute named " + a + 
00857       " with the value \"" + v +"\", but instead the value is \"" + attrib(a);
00858     throw CanteraError("XML_Node::require", msg);
00859   }
00860 
00861 
00862   //  This routine carries out a search for an XML node based
00863   //  on both the xml element name and the attribute ID.
00864   /*
00865    * If exact matches are found for both fields, the pointer
00866    * to the matching XML Node is returned.
00867    * 
00868    * The ID attribute may be defaulted by setting it to "".
00869    * In this case the pointer to the first xml element matching the name
00870    * only is returned.
00871    *
00872    *  @param nameTarget  Name of the XML Node that is being searched for
00873    *  @param idTarget    "id" attribute of the XML Node that the routine
00874    *                     looks for
00875    *
00876    *  @return   Returns the pointer to the XML node that fits the criteria
00877    *
00878    * @internal
00879    * This algorithm does a lateral search of first generation children
00880    * first before diving deeper into each tree branch.
00881    */
00882   XML_Node* XML_Node::
00883   findNameID(const std::string & nameTarget, 
00884              const std::string & idTarget) const {
00885     XML_Node *scResult = 0;
00886     XML_Node *sc;
00887     std::string idattrib = id();
00888     int n;
00889     if (name() == nameTarget) {
00890       if (idTarget == "" || idTarget == idattrib) {
00891         return const_cast<XML_Node*>(this);
00892       }
00893     }
00894     for (n = 0; n < m_nchildren; n++) {
00895       sc = m_children[n];
00896       if (sc->name() == nameTarget) {
00897         if (idTarget == "") return sc;
00898         idattrib = sc->id();
00899         if (idTarget == idattrib) return sc;
00900       }
00901     }
00902     for (n = 0; n < m_nchildren; n++) {
00903       sc = m_children[n];
00904       scResult = sc->findNameID(nameTarget, idTarget);
00905       if (scResult) return scResult;
00906     }
00907     return scResult;
00908   }
00909     
00910   //   This routine carries out a recursive search for an XML node based
00911   //   on the xml element attribute ID.
00912   /*
00913    * If exact match is found, the pointer
00914    * to the matching XML Node is returned. If not, 0 is returned.
00915    * 
00916    * The ID attribute may be defaulted by setting it to "".
00917    * In this case the pointer to the first xml element matching the name
00918    * only is returned.
00919    *
00920    *  @param id       "id" attribute of the XML Node that the routine
00921    *                  looks for
00922    *  @param depth    Depth of the search.
00923    *
00924    *  @return         Returns the pointer to the XML node that fits the criteria
00925    *
00926    * @internal
00927    * This algorithm does a lateral search of first generation children
00928    * first before diving deeper into each tree branch.
00929    */
00930   XML_Node* XML_Node::findID(const std::string & id, const int depth) const {
00931     if (hasAttrib("id")) {
00932       if (attrib("id") == id) {
00933         return const_cast<XML_Node*>(this);
00934       }
00935     }
00936     if (depth > 0) {
00937       XML_Node* r = 0;
00938       int n = nChildren();
00939       for (int i = 0; i < n; i++) {
00940         r = m_children[i]->findID(id, depth-1);
00941         if (r != 0) return r;
00942       }
00943     }
00944     return 0;
00945   }
00946 
00947   //  This routine carries out a recursive search for an XML node based
00948   //  on an attribute of each XML node
00949   /*
00950    * If exact match is found with respect to the attribute name and
00951    * value of the attribute, the pointer
00952    * to the matching XML Node is returned. If not, 0 is returned.
00953    * 
00954    *
00955    *  @param attr     Attribute of the XML Node that the routine
00956    *                  looks for
00957    *  @param val      Value of the attribute
00958    *
00959    *  @return         Returns the pointer to the XML node that fits the criteria
00960    *
00961    */
00962   XML_Node* XML_Node::findByAttr(const std::string& attr, 
00963                                  const std::string& val) const {
00964     if (hasAttrib(attr)) {
00965       if (attrib(attr) == val) {
00966         return const_cast<XML_Node*>(this);
00967       }
00968     }
00969     XML_Node* r = 0;
00970     int n = nChildren();
00971     for (int i = 0; i < n; i++) {
00972       r = m_children[i]->findByAttr(attr, val);
00973       if (r != 0) return r;
00974     }
00975     return 0;
00976   }
00977 
00978   // This routine carries out a recursive search for an XML node based
00979   // on the name of the node.
00980   /*
00981    * If exact match is found with respect to XML_Node name, the pointer
00982    * to the matching XML Node is returned. If not, 0 is returned.
00983    * This is the non-const version of the routine.
00984    *
00985    *  @param nm       Name of the XML node
00986    *
00987    *  @return         Returns the pointer to the XML node that fits the criteria
00988    */
00989   XML_Node* XML_Node::findByName(const std::string& nm) {
00990     if (name() == nm) {
00991       return this;
00992     }
00993     XML_Node* r = 0;
00994     int n = nChildren();
00995     for (int i = 0; i < n; i++) {
00996       r = m_children[i]->findByName(nm);
00997       if (r != 0) return r;
00998     }
00999     return 0;
01000   }
01001 
01002   // This routine carries out a recursive search for an XML node based
01003   // on the name of the node.
01004   /*
01005    * If exact match is found with respect to XML_Node name, the pointer
01006    * to the matching XML Node is returned. If not, 0 is returned.
01007    * This is the const version of the routine.
01008    *
01009    *  @param nm       Name of the XML node
01010    *
01011    *  @return         Returns the pointer to the XML node that fits the criteria
01012    */
01013   const XML_Node* XML_Node::findByName(const std::string& nm) const {
01014     if (name() == nm) {
01015       return const_cast<XML_Node*>(this);
01016     }
01017     const XML_Node* r = 0;
01018     int n = nChildren();
01019     for (int i = 0; i < n; i++) {
01020       r = m_children[i]->findByName(nm);
01021       if (r != 0) return r;
01022     }
01023     return 0;
01024   }
01025 
01026   // Write the header to the xml file to the specified ostream
01027   /*
01028    *   @param s   ostream to write the output to
01029    */
01030   void XML_Node::writeHeader(std::ostream& s) {
01031     s << "<?xml version=\"1.0\"?>" << endl;
01032   }
01033 
01034   // Main routine to create an tree-like representation of an XML file
01035   /*
01036    *   Given an input stream, this routine will read matched XML tags
01037    *   representing the ctml file until an EOF is read from the file. 
01038    *   This routine is called by the root XML_Node object.
01039    *
01040    * @param f   Input stream containing the ascii input file
01041    */
01042   void XML_Node::build(std::istream& f) {
01043     XML_Reader r(f);
01044     string nm, nm2, val;
01045     XML_Node* node = this;
01046     map<string, string> attribs;
01047     while (!f.eof()) {
01048       attribs.clear();
01049       nm = r.readTag(attribs);
01050 
01051       if (nm == "EOF") break;
01052       if (nm == "--" && m_name == "--" && m_root == this) {
01053         continue;
01054       }
01055       int lnum = r.m_line;
01056       if (nm[nm.size() - 1] == '/') {
01057         nm2 = nm.substr(0,nm.size()-1);
01058         node = &node->addChild(nm2);
01059         node->addValue("");
01060         node->attribs() = attribs;
01061         node->setLineNumber(lnum);
01062         node = node->parent();
01063       }
01064       else if (nm[0] != '/') {
01065         if (nm[0] != '!' && nm[0] != '-' && nm[0] != '?') {
01066           node = &node->addChild(nm);
01067           val = r.readValue();
01068           node->addValue(val);
01069           node->attribs() = attribs;
01070           node->setLineNumber(lnum);
01071         }
01072         else if (nm.substr(0,2) == "--") {
01073           if (nm.substr(nm.size()-2,2) == "--") {
01074             node->addComment(nm.substr(2,nm.size()-4));
01075           }
01076         }
01077       }
01078       else {
01079         if (node->name() != nm.substr(1,nm.size()-1)) 
01080           throw XML_TagMismatch(node->name(), 
01081                                 nm.substr(1,nm.size()-1), lnum);
01082         node = node->parent();
01083       }
01084     }
01085   } 
01086 
01087   // Copy all of the information in the current XML_Node tree
01088   // into the destination XML_Node tree, doing a union operation as
01089   // we go
01090   /*
01091    *  Note this is a const function becuase the current XML_Node and
01092    *  its children isn't altered by this operation.
01093    *
01094    *  @param node_dest  This is the XML node to receive the information
01095    *
01096    */
01097   void XML_Node::copyUnion(XML_Node * const node_dest) const {
01098     XML_Node *sc, *dc;
01099     int ndc, idc;
01100     node_dest->addValue(m_value);
01101     if (m_name == "") return;
01102     map<string,string>::const_iterator b = m_attribs.begin();
01103     for (; b != m_attribs.end(); ++b) {
01104       if (! node_dest->hasAttrib(b->first)) {
01105         node_dest->addAttribute(b->first, b->second);
01106       }
01107     }
01108     const vector<XML_Node*> &vsc = node_dest->children();
01109     for (int n = 0; n < m_nchildren; n++) {
01110       sc = m_children[n];
01111       ndc = node_dest->nChildren();
01112       dc = 0;
01113       if (! sc->m_iscomment) {
01114         for (idc = 0; idc < ndc; idc++) {
01115           XML_Node *dcc = vsc[idc];
01116           if (dcc->name() == sc->name()) {
01117             if (sc->hasAttrib("id")) {
01118               if (sc->attrib("id") != dcc->attrib("id")) break;
01119             }
01120             if (sc->hasAttrib("name")) {
01121               if (sc->attrib("name") != dcc->attrib("name")) break;
01122             }
01123             if (sc->hasAttrib("model")) {
01124               if (sc->attrib("model") != dcc->attrib("model")) break;
01125             }
01126             if (sc->hasAttrib("title")) {
01127               if (sc->attrib("title") != dcc->attrib("title")) break;
01128             }
01129             dc = vsc[idc];
01130           }
01131         }
01132       }
01133       if (!dc) {
01134         (void) node_dest->addChild(sc->name());
01135         dc = vsc[ndc];
01136       }
01137       sc->copyUnion(dc);
01138     }
01139   }
01140 
01141   // Copy all of the information in the current XML_Node tree
01142   // into the destination XML_Node tree, doing a complete copy 
01143   // as we go.
01144   /*
01145    *  Note this is a const function becuase the current XML_Node and
01146    *  its children isn't altered by this operation.
01147    *
01148    *  @param node_dest  This is the XML node to receive the information
01149    */
01150   void XML_Node::copy(XML_Node * const node_dest) const {
01151     XML_Node *sc, *dc;
01152     int ndc;
01153     node_dest->addValue(m_value);
01154     node_dest->setName(m_name);
01155     if (m_name == "") return;
01156     map<string,string>::const_iterator b = m_attribs.begin();
01157     for (; b != m_attribs.end(); ++b) {
01158       node_dest->addAttribute(b->first, b->second);
01159     }
01160     const vector<XML_Node*> &vsc = node_dest->children();
01161 
01162     for (int n = 0; n < m_nchildren; n++) {
01163       sc = m_children[n];
01164       ndc = node_dest->nChildren();
01165       (void) node_dest->addChild(sc->name());
01166       dc = vsc[ndc];
01167       sc->copy(dc);
01168     }
01169   }
01170 
01171   // Set the lock for this node
01172   void XML_Node::lock() {
01173     m_locked = true;
01174     for (int i = 0; i < m_nchildren; i++) {
01175       m_children[i]->lock();
01176     }
01177   }
01178 
01179   // Unset the lock for this node
01180   void XML_Node::unlock() {
01181     m_locked = false;
01182     for (int i = 0; i < m_nchildren; i++) {
01183       m_children[i]->unlock();
01184     }
01185   }
01186 
01187   // Get a vector of pointers to XML_Node containing all of the children
01188   // of the current node which matches the input name
01189   /*
01190    *  @param name   Name of the XML_Node children to search on
01191    *
01192    * @param children  output vector of pointers to XML_Node children
01193    *                  with the matching name
01194    */
01195   void XML_Node::getChildren(const std::string &nm, 
01196                              std::vector<XML_Node*>& children) const {
01197     int i, n = nChildren();
01198     for (i = 0; i < n; i++) {
01199       if (child(i).name() == nm) {
01200         children.push_back(&child(i));
01201       } 
01202     }
01203   }
01204 
01205   // Return a changeable reference to a child of the current node, 
01206   // named by the argument
01207   /*
01208    *  @param loc  Name of the child to return
01209    */
01210   XML_Node& XML_Node::child(const std::string &aloc) const {
01211     string::size_type iloc;
01212     string cname;
01213     string loc = aloc;
01214     std::map<std::string,XML_Node*>::const_iterator i;
01215 
01216     while (1) {
01217       iloc = loc.find('/');
01218       if (iloc != string::npos) {
01219         cname = loc.substr(0,iloc);
01220         loc = loc.substr(iloc+1, loc.size());
01221         i = m_childindex.find(cname);
01222         if (i != m_childindex.end()) return i->second->child(loc);
01223         else {
01224           throw XML_NoChild(this, m_name, cname, lineNumber());
01225         }
01226       }
01227       else {
01228         i = m_childindex.find(loc);
01229         if (i != m_childindex.end()) return *(i->second);
01230         else {
01231           throw XML_NoChild(this, m_name, loc, lineNumber());
01232         }
01233       }
01234     }
01235   }
01236     
01237   /*
01238    * Write an XML subtree to an output stream. This is the
01239    * main recursive routine. It doesn't put a final endl
01240    * on. This is fixed up in the public method.
01241    */
01242   void XML_Node::write_int(std::ostream& s, int level) const {
01243 
01244     if (m_name == "") return;
01245 
01246     string indent(level, ' ');
01247     if (m_iscomment) {
01248       /*
01249        * In the comment section, we test to see if there
01250        * already is a space beginning and ending the comment.
01251        * If there already is one, we don't add another one.
01252        */
01253       s << endl << indent << "<!--";
01254       if (! isspace(m_value[0])) {
01255         s << " ";
01256       }
01257       s << m_value;
01258       int ll = static_cast<int>(m_value.size()) - 1;
01259       if (! isspace(m_value[ll])) {
01260         s << " ";
01261       }
01262       s << "-->";
01263       return;
01264     }
01265         
01266     s << indent << "<" << m_name;
01267     map<string,string>::const_iterator b = m_attribs.begin();
01268     for (; b != m_attribs.end(); ++b) {
01269       s << " " << b->first << "=\"" << b->second << "\"";
01270     }
01271     if (m_value == "" && m_nchildren == 0) {
01272       s << "/>";
01273     }
01274     else {
01275       s << ">";
01276             
01277       if (m_value != "") {
01278         string vv = m_value;
01279         string::size_type ieol = vv.find('\n');
01280         if (ieol != string::npos) { 
01281           while (1 > 0) {
01282             ieol = vv.find('\n');
01283             if (ieol != string::npos) {
01284               if (ieol == 0) {
01285                 s << endl << indent << "  ";
01286               } else {
01287                 int jf = ieol - 1;
01288                 for (int j = 0; j < (int) ieol; j++) {
01289                   if (! isspace(vv[j])) {
01290                     jf = j;
01291                     break;
01292                   }
01293                 }
01294                 s << endl << indent << "  " << vv.substr(jf,ieol-jf);
01295               }
01296               vv = vv.substr(ieol+1);
01297             }
01298             else {
01299               int lll = static_cast<int>(vv.size()) - 1;
01300               if (lll >= 0) {
01301                 int jf = lll;
01302                 for (int j = 0; j < lll; j++) {
01303                   if (! isspace(vv[j])) {
01304                     jf = j;
01305                     break;
01306                   }
01307                 }
01308                 if (jf < lll) {
01309                   s << endl << indent << "  " << vv.substr(jf);
01310                 }
01311               }
01312               break;
01313             }
01314           }
01315           s << endl << indent;
01316         }
01317         else {
01318           bool doSpace = true;
01319           bool doNewLine = false;
01320           int ll = static_cast<int>(m_value.size()) - 1;
01321           if (ll > 15) {
01322             doNewLine = true;
01323           }
01324           if (m_name == "floatArray") {
01325             doNewLine = true;
01326           }
01327           if (doNewLine) doSpace = false;
01328                 
01329           if (doNewLine) {
01330             s << endl << indent << "  ";
01331           }
01332           /*
01333            * Put spaces around a raw value field for readability
01334            */
01335           if (doSpace && (! isspace(m_value[0]))) {
01336             s << " ";
01337           }
01338           /*
01339            * Write out the value
01340            */
01341           s << m_value;
01342                 
01343           if (doSpace && (! isspace(m_value[ll]))) {
01344             s << " ";
01345           }
01346           if (doNewLine) {
01347             s << endl << indent;
01348           }
01349         }
01350       }
01351       int i;
01352       for (i = 0; i < m_nchildren; i++) {
01353         s << endl;
01354         m_children[i]->write_int(s,level + 2);
01355       }
01356       if (m_nchildren > 0) s << endl << indent;
01357       s << "</" << m_name << ">";
01358     }
01359   }
01360 
01361   /*
01362    * Write an XML subtree to an output stream. This is a 
01363    * wrapper around the static routine write_int(). All this
01364    * does is add an endl on to the output stream. write_int() is
01365    * fine, but the last endl wasn't being written.
01366    * It also checks for the special name "--". If found and we
01367    * are at the root of the xml tree, then the block
01368    * is skipped and the children are processed. "--" is used
01369    * to denote the top of the tree.
01370    */
01371   void XML_Node::write(std::ostream& s, const int level) const {
01372     if (m_name == "--" && m_root == this) {
01373       for (int i = 0; i < m_nchildren; i++) {
01374         m_children[i]->write_int(s,level);
01375         s << endl;
01376       }
01377     } else {
01378       write_int(s, level);
01379       s << endl;
01380     }
01381   }
01382 
01383   XML_Node& XML_Node::root() const { 
01384     return *m_root; 
01385   }
01386  
01387   void XML_Node::setRoot(const XML_Node& root) {
01388      m_root = const_cast<XML_Node*>(&root); 
01389   }
01390         
01391   XML_Node * findXMLPhase(XML_Node *root, 
01392                           const std::string &idtarget) {
01393     XML_Node *scResult = 0;
01394     XML_Node *sc;
01395     if (!root) return 0;
01396     string idattrib;
01397     string rname = root->name();
01398     if (rname == "phase") {
01399       if (idtarget == "") return root;
01400       idattrib = root->id();
01401       if (idtarget == idattrib) return root;
01402       else return               0;
01403     }
01404 
01405     const vector<XML_Node*> &vsc = root->children();
01406     int n;
01407     for (n = 0; n < root->nChildren(); n++) {
01408       sc = vsc[n];
01409       if (sc->name() == "phase") {
01410         if (idtarget == "") return sc;
01411         idattrib = sc->id();
01412         if (idtarget == idattrib) return sc;
01413       }
01414     }
01415     for (n = 0; n < root->nChildren(); n++) {
01416       sc = vsc[n];
01417       if  (sc->name() != "phase") {
01418         scResult = findXMLPhase(sc, idtarget);
01419         if (scResult) return scResult;
01420       }
01421     }
01422     return scResult;
01423   }
01424 
01425 }
01426 
01427 
Generated by  doxygen 1.6.3