/** * @file xml.cpp * Classes providing support for XML data files. These classes * implement only those aspects of XML required to read, write, and * manipulate CTML data files. */ // Copyright 2001 California Institute of Technology // turn off warnings under Windows #ifdef WIN32 #pragma warning(disable:4786) #pragma warning(disable:4503) #pragma warning(disable:4996) #endif #include "config.h" #ifdef HAS_SSTREAM #include #endif #include using namespace std; #include "xml.h" #include "global.h" #include "stringUtils.h" #include #include namespace Cantera { ////////////////////// exceptions //////////////////////////// //! Classs representing a generic XML error condition class XML_Error : public CanteraError { protected: //! Constructor /*! * Note, we don't actually post the error in this class. * Therefore, this class can't be used externally. Therefore, * it's a protected constructor. * * @param line Number number where the error occurred. */ XML_Error(int line=0) : m_line(line), m_msg(0) { m_msg = "Error in XML file"; if (line > 0) { m_msg += " at line " + int2str(line+1); } m_msg += ".\n"; } //! destructor virtual ~XML_Error() throw() { } protected: //! Line number of the file int m_line; //! String message for the error std::string m_msg; }; //! Class representing a specific type of XML file formatting error /*! * An XML tag is not matched */ class XML_TagMismatch : public XML_Error { public: //! Constructor /*! * An XML element must have the same opening and closing name. * * @param opentag String representing the opening of the XML bracket * @param closetag String representing the closing of the XML bracket * @param line Line number where the error occurred. */ XML_TagMismatch(std::string opentag, std::string closetag, int line=0) : XML_Error(line) { m_msg += "<" + opentag + "> paired with .\n"; setError("XML_TagMismatch", m_msg); } //! Destructor virtual ~XML_TagMismatch() throw() {} }; //! Class representing a specific type of XML file formatting error /*! * An XML_Node doesn't have a required child node */ class XML_NoChild : public XML_Error { public: //! Constructor /*! * An XML element doesn't have the required child node * * @param p XML_Node to write a string error message * @param parent Namf of the parent node * @param child Name of the required child node * @param line Line number where the error occurred. */ XML_NoChild(const XML_Node* p, std::string parent, std::string child, int line=0) : XML_Error(line) { m_msg += " The XML Node \"" + parent + "\", does not contain a required\n" + " XML child node named \"" + child + "\".\n"; #ifdef HAS_SSTREAM ostringstream ss(ostringstream::out); p->write(ss,1); m_msg += ss.str() + "\n"; #endif setError("XML_NoChild", m_msg); } //! Destructor virtual ~XML_NoChild() throw() {} }; //! Class representing a specific type of XML file formatting error /*! * An XML_Node's units attribute has the wrong type of units. */ class XML_IllegalUnits : public XML_Error { public: //! Constructor /*! * Wrong units string. * * @param name Name of the current XML node * @param units Units string in the "units" attribute * @param line Line number where the error occurred. */ XML_IllegalUnits(std::string name, std::string units, int line=0) : XML_Error(line) { m_msg += "Illegal units (" + units + ") specified for node " + name + ".\n"; setError("XML_IllegalUnits", m_msg); } //! Destructor virtual ~XML_IllegalUnits() throw() {} }; //////////////////// XML_Reader methods /////////////////////// XML_Reader::XML_Reader(std::istream& input) : m_s(input), m_line(0) { } //! Get a single character from the input stream. /*! * If the character * is a new-line character, then increment the line count. */ void XML_Reader::getchr(char& ch) { m_s.get(ch); if (ch == '\n') { m_line++; } } // Returns string 'aline' stripped of leading and trailing white // space. // @todo why is this a class method? std::string XML_Reader::strip(const std::string& aline) const { int len = static_cast(aline.size()); int i, j, ll; for (i = len-1; i >= 0; i--) { ll = aline[i]; if (! isspace(ll)) break; } for (j = 0; j < i; j++) { ll = aline[j]; if (! isspace(ll)) break; } // if (aline[j] != ' ' && aline[j] != '\n') break; return aline.substr(j, i - j + 1); } /// Looks for a substring within 'aline' enclosed in double /// quotes, and returns this substring (without the quotes) if /// found. If not, an empty string is returned. /// @todo why is this a class method? std::string XML_Reader::inquotes(const std::string& aline) const { int len = static_cast(aline.size()); int i, j; for (i = len-1; i >= 0; i--) if (aline[i] == '"') break; for (j = 0; j < i; j++) if (aline[j] == '"') break; if (j == i) return ""; else return aline.substr(j+1, i - j - 1); } /** * Find the first position of a character, q, in string s, * which is not immediately preceded by the backslash character * '\' */ static string::size_type findUnbackslashed(std::string s, const char q, std::string::size_type istart = 0) { string::size_type iloc, icurrent, len; icurrent = istart; len = s.size(); while (1) { iloc = s.find(q, icurrent); if (iloc == string::npos || iloc == 0) { return iloc; } char cm1 = s[iloc-1]; if (cm1 == '\\') { if (iloc >= (len -1)) return string::npos; icurrent = iloc + 1; } else { return iloc; } } } /* * Searches a string for the first occurrence of a valid * quoted string. Quotes can start with either a single * quote or a double quote, but must also end with the same * type. Quotes may be commented out by preceding with a * backslash character, '\\'. */ int XML_Reader::findQuotedString(const std::string& s, std::string &rstring) const { const char q1 = '\''; const char q2 = '"'; rstring = ""; char qtype = ' '; string::size_type iloc1, iloc2, ilocStart = 0; iloc1 = findUnbackslashed(s, q1); iloc2 = findUnbackslashed(s, q2); if (iloc2 != string::npos) { ilocStart = iloc2; qtype = q2; } if (iloc1 != string::npos) { if (iloc1 < ilocStart) { ilocStart = iloc1; qtype = q1; } } if (qtype == ' ') return 0; iloc1 = findUnbackslashed(s, qtype, ilocStart+1); if (iloc1 == string::npos) { return 0; } /* * Define the return string by the two endpoints. * Strip the surrounding quotes as well */ rstring = s.substr(ilocStart + 1, iloc1 - 1); /* * Return the first character position past the quotes */ return static_cast(iloc1)+1; } /* * parseTag parses XML tags, i.e., the XML elements that are * inbetween angle brackets. */ void XML_Reader::parseTag(std::string tag, std::string& name, std::map& attribs) const { string::size_type iloc; string attr, val; string s = strip(tag); iloc = s.find(' '); if (iloc != string::npos) { name = s.substr(0, iloc); s = strip(s.substr(iloc+1,s.size())); if (s[s.size()-1] == '/') { name += "/"; } // get attributes while (1) { iloc = s.find('='); if (iloc == string::npos) break; attr = strip(s.substr(0,iloc)); if (attr == "") break; s = strip(s.substr(iloc+1,s.size())); iloc = findQuotedString(s, val); attribs[attr] = val; if (iloc != string::npos) { if (iloc < s.size()) s = strip(s.substr(iloc,s.size())); else break; } } } else { name = s; } } std::string XML_Reader::readTag(std::map& attribs) { string name, tag = ""; bool incomment = false; char ch = '-'; while (1) { if (m_s.eof() || (getchr(ch), ch == '<')) break; } char ch1 = ' ', ch2 = ' '; while (1) { if (m_s.eof()) { tag = "EOF"; break;} ch2 = ch1; ch1 = ch; getchr(ch); if (ch == '-') { if (ch1 == '-' && ch2 == '!') { incomment = true; tag = "-"; } } else if (ch == '>') { if (incomment) { if (ch1 == '-' && ch2 == '-') break; } else break; } if (isprint(ch)) tag += ch; } if (incomment) { attribs.clear(); return tag; } else { parseTag(tag, name, attribs); return name; } } std::string XML_Reader::readValue() { string tag = ""; char ch, lastch; ch = '\n'; bool front = true; while (1) { if (m_s.eof()) break; lastch = ch; getchr(ch); if (ch == '\n') front = true; else if (ch != ' ') front = false; if (ch == '<') { m_s.putback(ch); break; } if (front && lastch == ' ' && ch == ' ') ; else tag += ch; } return strip(tag); } ////////////////////////// XML_Node ///////////////////////////////// XML_Node::XML_Node(const char * cnm) : m_name(""), m_value(""), m_parent(0), m_locked(false), m_nchildren(0), m_iscomment(false) { if (! cnm) { m_name = "--"; } else { m_name = cnm; } m_root = this; } // Default constructor for XML_Node, representing a tree structure /* * Constructor for an XML_Node, which is a node in a tree-like structure * representing an XML file. * * @param nm Name of the node. * The default name of the node is "--" * * @param p pointer to the root for this node in the tree. * The default is 0 indicating this is the top of the tree. */ XML_Node::XML_Node(const std::string nm, XML_Node * const p) : m_name(nm), m_value(""), m_parent(p), m_locked(false), m_nchildren(0), m_iscomment(false) { if (!p) m_root = this; else m_root = &p->root(); } // Copy constructor /* * @param right Object to be copied */ XML_Node::XML_Node(const XML_Node &right) : m_name(""), m_value(""), m_parent(0), m_locked(false), m_nchildren(0), m_iscomment(false) { m_root = this; right.copy(this); } // Assignment operator for XML trees /* * @param right XML tree to copy */ XML_Node & XML_Node::operator=(const XML_Node &right) { if (&right != this) { int n = static_cast(m_children.size()); for (int i = 0; i < n; i++) { if (m_children[i]) { if (m_children[i]->parent() == this) { delete m_children[i]; m_children[i] = 0; } } } m_children.resize(0); right.copy(this); } return *this; } // Destructor for the object XML_Node::~XML_Node() { if (m_locked) throw CanteraError("XML_Node::~XML_Node", "attempt to delete locked XML_Node "+name()); int n = static_cast(m_children.size()); for (int i = 0; i < n; i++) { if (m_children[i]) { if (m_children[i]->parent() == this) { delete m_children[i]; m_children[i] = 0; } } } } void XML_Node::clear() { int n = static_cast(m_children.size()); for (int i = 0; i < n; i++) { if (m_children[i]) { if (m_children[i]->parent() == this) { delete m_children[i]; m_children[i] = 0; } } } m_value.clear(); m_childindex.clear(); m_attribs.clear(); m_children.clear(); m_nchildren = 0; m_iscomment = false; m_linenum = 0; } // Add a child node to the current node containing a comment /* * Child node will have the name, "comment". * * @param comment Content of the comment */ void XML_Node::addComment(const std::string &comment) { addChild("comment", comment); } // Add a child node to the current node /* * This will add an XML_Node as a child to the current node. * Note, this actually adds the node. Therefore, node is changed. * There is no copy made of the child node. * * @param node Reference to a child XML_Node object * * @return returns a reference to the added node */ XML_Node& XML_Node::addChild(XML_Node& node) { m_children.push_back(&node); m_nchildren = static_cast(m_children.size()); m_childindex[node.name()] = m_children.back(); node.setRoot(root()); node.setParent(this); return *m_children.back(); } // Add a child node to the current node with a specified name /* * This will add an XML_Node as a child to the current node. * The node will be blank except for the specified name. * * @param sname Name of the new child * * @return Returns a reference to the added node */ XML_Node& XML_Node::addChild(const std::string &sname) { XML_Node *xxx = new XML_Node(sname, this); m_children.push_back(xxx); m_nchildren = static_cast(m_children.size()); m_childindex[sname] = m_children.back(); xxx->setRoot(root()); xxx->setParent(this); return *m_children.back(); } // Add a child node to the current xml node, and at the // same time add a value to the child /* * Resulting XML string: * value * * @param name Name of the child XML_Node object * @param value Value of the XML_Node - string * @return Returns a reference to the created child XML_Node object */ XML_Node& XML_Node::addChild(const std::string &name, const std::string &value) { XML_Node& c = addChild(name); c.addValue(value); return c; } // Add a child node to the current xml node, and at the // same time add a formatted value to the child /* * This version supplies a formatting string (printf format) * to the output of the value. * * Resulting XML string: * value * * @param name Name of the child XML_Node object * @param value Value of the XML_Node - double * @param fmt Format of the output for value * * @return Returns a reference to the created child XML_Node object */ XML_Node& XML_Node::addChild(const std::string &name, const doublereal value, const std::string fmt) { XML_Node& c = addChild(name); c.addValue(value, fmt); return c; } // Remove a child from this node's list of children /* * This function removes an XML_Node from the children of this node. * * @param node Pointer to the node to be removed. Note, this node * isn't modified in any way. */ void XML_Node::removeChild(const XML_Node * const node) { vector::iterator i; i = find(m_children.begin(), m_children.end(), node); m_children.erase(i); m_nchildren = static_cast(m_children.size()); m_childindex.erase(node->name()); } std::string XML_Node::id() const { if (hasAttrib("id")) return attrib("id"); return std::string(""); } // Modify the value for the current node /* * This functions fills in the m_value field of the current node * * @param val string Value that the node will be assigned */ void XML_Node::addValue(const std::string &val) { m_value = val; if (m_name == "comment") m_iscomment = true; } // Modify the value for the current node /* * This functions fills in the m_value field of the current node * with a formatted double value * * @param val double Value that the node will be assigned * @param fmt Format of the printf string conversion of the double. * Default is "%g" Must be less than 63 chars */ void XML_Node::addValue(const doublereal val, const std::string fmt) { m_value = stripws(fp2str(val, fmt)); } // Return the value of an XML node as a string /* * This is a simple accessor routine */ std::string XML_Node::value() const { return m_value; } // Overloaded parenthesis operator returns the value of the Node /* * @return Returns the value of the node as a string. */ std::string XML_Node::operator()() const { return m_value; } // Return the value of an XML node as a double /* * This accesses the value string, and then tries to * interpret it as a single double value. */ doublereal XML_Node::fp_value() const { return atofCheck(m_value.c_str()); } // Return the value of an XML node as a single int /* * This accesses the value string, and then tries to * interpret it as a single int value. */ integer XML_Node::int_value() const { return std::atoi(m_value.c_str()); } // Return the value of an XML child node as a string /* * @param cname Name of the child node of the current * node, for which you want the value */ std::string XML_Node::value(const std::string &cname) const { return child(cname).value(); } // Overloaded parenthesis operator with one augment // returns the value of an XML child node as a string /* * @param cname Name of the child node to the current * node, for which you want the value */ std::string XML_Node::operator()(std::string loc) const { return value(loc); } // Add or modify an attribute of the current node /* * This functions fills in the m_value field of the current node * with a string value * * @param attrib String name for the attribute to be assigned * @param value String value that the attribute will have */ void XML_Node::addAttribute(const std::string & attrib, const std::string & value) { m_attribs[attrib] = value; } // Add or modify an attribute to the double, value /* * This functions fills in the attribute field, named attrib, * with the double value, value. A formatting string is used. * * @param attrib String name for the attribute to be assigned * @param value double Value that the node will be assigned * @param fmt Format of the printf string conversion of the double. * Default is "%g". */ void XML_Node::addAttribute(const std::string & attrib, const doublereal value, const std::string fmt) { m_attribs[attrib] = fp2str(value, fmt); } // The operator[] is overloaded to provide a lookup capability // on attributes for the current XML element. /* * For example * xmlNode["id"] * will return the value of the attribute "id" for the current * XML element. It will return the blank std::string if there isn't * an attribute with that name. * * @param attr attribute string to look up * * @return Returns a string representing the value of the attribute * within the XML node. If there is no attribute * with the given name, it returns the null string. */ std::string XML_Node::operator[](const std::string & attr) const { return attrib(attr); } // Function returns the value of an attribute /* * This function searches the attibutes vector for the parameter * std::string attribute. If a match is found, the attribute value * is returned as a string. If no match is found, the empty string * is returned. * * @param attr Std::String containing the attribute to be searched for. * * @return Returns If a match is found, the attribute value * is returned as a string. If no match is found, the empty string * is returned. */ std::string XML_Node::attrib(const std::string & attr) const { std::map::const_iterator i = m_attribs.find(attr); if (i != m_attribs.end()) return i->second; return ""; } // Returns a changeable value of the attributes map for the current node /* * Note this is a simple accessor routine. And, it is a private function. * It's used in some internal copy and assignment routines */ std::map& XML_Node::attribs() { return m_attribs; } // Set the line number /* * @param n the member data m_linenum is set to n */ void XML_Node::setLineNumber(const int n) { m_linenum = n; } // Return the line number /* * @return returns the member data m_linenum */ int XML_Node::lineNumber() const { return m_linenum; } // Returns a pointer to the parent node of the current node XML_Node* XML_Node::parent() const { return m_parent; } // Sets the pointer for the parent node of the current node /* * @param p Pointer to the parent node * * @return Returns the pointer p */ XML_Node* XML_Node::setParent(XML_Node * const p) { m_parent = p; return p; } // Tests whether the current node has a child node with a particular name /* * @param ch Name of the child node to test * * @return Returns true if the child node exists, false otherwise. */ bool XML_Node::hasChild(const std::string ch) const { return (m_childindex.find(ch) != m_childindex.end()); } // Tests whether the current node has an attribute with a particular name /* * @param a Name of the attribute to test * * @return Returns true if the attribute exists, false otherwise. */ bool XML_Node::hasAttrib(std::string a) const { return (m_attribs.find(a) != m_attribs.end()); } // Return a reference to the n'th child of the current node /* * @param n Number of the child to return */ XML_Node& XML_Node::child(const int n) const { return *m_children[n]; } // Return an unchangeable reference to the vector of children of the current node /* * Each of the individual XML_Node child pointers, however, * is to a changeable xml node object. * * @param n Number of the child to return */ const std::vector& XML_Node::children() const { return m_children; } // return the number of children /* * */ int XML_Node::nChildren() const { return m_nchildren; } // Require that the current xml node have an attribute named // by the first argument, a, and that this attribute have the // the string value listed in the second argument, v. /* * @param a attribute name * @param v required value of the attribute * * If the condition is not true, an exception is thrown */ void XML_Node::_require(const std::string &a, const std::string &v) const { if (hasAttrib(a)) { if (attrib(a) == v) return; } string msg="XML_Node "+name()+" is required to have an attribute named " + a + " with the value \"" + v +"\", but instead the value is \"" + attrib(a); throw CanteraError("XML_Node::require", msg); } // This routine carries out a search for an XML node based // on both the xml element name and the attribute ID. /* * If exact matches are found for both fields, the pointer * to the matching XML Node is returned. * * The ID attribute may be defaulted by setting it to "". * In this case the pointer to the first xml element matching the name * only is returned. * * @param nameTarget Name of the XML Node that is being searched for * @param idTarget "id" attribute of the XML Node that the routine * looks for * * @return Returns the pointer to the XML node that fits the criteria * * @internal * This algorithm does a lateral search of first generation children * first before diving deeper into each tree branch. */ XML_Node* XML_Node:: findNameID(const std::string & nameTarget, const std::string & idTarget) const { XML_Node *scResult = 0; XML_Node *sc; std::string idattrib = id(); int n; if (name() == nameTarget) { if (idTarget == "" || idTarget == idattrib) { return const_cast(this); } } for (n = 0; n < m_nchildren; n++) { sc = m_children[n]; if (sc->name() == nameTarget) { if (idTarget == "") return sc; idattrib = sc->id(); if (idTarget == idattrib) return sc; } } for (n = 0; n < m_nchildren; n++) { sc = m_children[n]; scResult = sc->findNameID(nameTarget, idTarget); if (scResult) return scResult; } return scResult; } // This routine carries out a recursive search for an XML node based // on the xml element attribute ID. /* * If exact match is found, the pointer * to the matching XML Node is returned. If not, 0 is returned. * * The ID attribute may be defaulted by setting it to "". * In this case the pointer to the first xml element matching the name * only is returned. * * @param id "id" attribute of the XML Node that the routine * looks for * @param depth Depth of the search. * * @return Returns the pointer to the XML node that fits the criteria * * @internal * This algorithm does a lateral search of first generation children * first before diving deeper into each tree branch. */ XML_Node* XML_Node::findID(const std::string & id, const int depth) const { if (hasAttrib("id")) { if (attrib("id") == id) { return const_cast(this); } } if (depth > 0) { XML_Node* r = 0; int n = nChildren(); for (int i = 0; i < n; i++) { r = m_children[i]->findID(id, depth-1); if (r != 0) return r; } } return 0; } // This routine carries out a recursive search for an XML node based // on an attribute of each XML node /* * If exact match is found with respect to the attribute name and * value of the attribute, the pointer * to the matching XML Node is returned. If not, 0 is returned. * * * @param attr Attribute of the XML Node that the routine * looks for * @param val Value of the attribute * * @return Returns the pointer to the XML node that fits the criteria * */ XML_Node* XML_Node::findByAttr(const std::string& attr, const std::string& val) const { if (hasAttrib(attr)) { if (attrib(attr) == val) { return const_cast(this); } } XML_Node* r = 0; int n = nChildren(); for (int i = 0; i < n; i++) { r = m_children[i]->findByAttr(attr, val); if (r != 0) return r; } return 0; } // This routine carries out a recursive search for an XML node based // on the name of the node. /* * If exact match is found with respect to XML_Node name, the pointer * to the matching XML Node is returned. If not, 0 is returned. * This is the non-const version of the routine. * * @param nm Name of the XML node * * @return Returns the pointer to the XML node that fits the criteria */ XML_Node* XML_Node::findByName(const std::string& nm) { if (name() == nm) { return this; } XML_Node* r = 0; int n = nChildren(); for (int i = 0; i < n; i++) { r = m_children[i]->findByName(nm); if (r != 0) return r; } return 0; } // This routine carries out a recursive search for an XML node based // on the name of the node. /* * If exact match is found with respect to XML_Node name, the pointer * to the matching XML Node is returned. If not, 0 is returned. * This is the const version of the routine. * * @param nm Name of the XML node * * @return Returns the pointer to the XML node that fits the criteria */ const XML_Node* XML_Node::findByName(const std::string& nm) const { if (name() == nm) { return const_cast(this); } const XML_Node* r = 0; int n = nChildren(); for (int i = 0; i < n; i++) { r = m_children[i]->findByName(nm); if (r != 0) return r; } return 0; } // Write the header to the xml file to the specified ostream /* * @param s ostream to write the output to */ void XML_Node::writeHeader(std::ostream& s) { s << "" << endl; } // Main routine to create an tree-like representation of an XML file /* * Given an input stream, this routine will read matched XML tags * representing the ctml file until an EOF is read from the file. * This routine is called by the root XML_Node object. * * @param f Input stream containing the ascii input file */ void XML_Node::build(std::istream& f) { XML_Reader r(f); string nm, nm2, val; XML_Node* node = this; map attribs; while (!f.eof()) { attribs.clear(); nm = r.readTag(attribs); if (nm == "EOF") break; if (nm == "--" && m_name == "--" && m_root == this) { continue; } int lnum = r.m_line; if (nm[nm.size() - 1] == '/') { nm2 = nm.substr(0,nm.size()-1); node = &node->addChild(nm2); node->addValue(""); node->attribs() = attribs; node->setLineNumber(lnum); node = node->parent(); } else if (nm[0] != '/') { if (nm[0] != '!' && nm[0] != '-' && nm[0] != '?') { node = &node->addChild(nm); val = r.readValue(); node->addValue(val); node->attribs() = attribs; node->setLineNumber(lnum); } else if (nm.substr(0,2) == "--") { if (nm.substr(nm.size()-2,2) == "--") { node->addComment(nm.substr(2,nm.size()-4)); } } } else { if (node->name() != nm.substr(1,nm.size()-1)) throw XML_TagMismatch(node->name(), nm.substr(1,nm.size()-1), lnum); node = node->parent(); } } } // Copy all of the information in the current XML_Node tree // into the destination XML_Node tree, doing a union operation as // we go /* * Note this is a const function becuase the current XML_Node and * its children isn't altered by this operation. * * @param node_dest This is the XML node to receive the information * */ void XML_Node::copyUnion(XML_Node * const node_dest) const { XML_Node *sc, *dc; int ndc, idc; node_dest->addValue(m_value); if (m_name == "") return; map::const_iterator b = m_attribs.begin(); for (; b != m_attribs.end(); ++b) { if (! node_dest->hasAttrib(b->first)) { node_dest->addAttribute(b->first, b->second); } } const vector &vsc = node_dest->children(); for (int n = 0; n < m_nchildren; n++) { sc = m_children[n]; ndc = node_dest->nChildren(); dc = 0; if (! sc->m_iscomment) { for (idc = 0; idc < ndc; idc++) { XML_Node *dcc = vsc[idc]; if (dcc->name() == sc->name()) { if (sc->hasAttrib("id")) { if (sc->attrib("id") != dcc->attrib("id")) break; } if (sc->hasAttrib("name")) { if (sc->attrib("name") != dcc->attrib("name")) break; } if (sc->hasAttrib("model")) { if (sc->attrib("model") != dcc->attrib("model")) break; } if (sc->hasAttrib("title")) { if (sc->attrib("title") != dcc->attrib("title")) break; } dc = vsc[idc]; } } } if (!dc) { (void) node_dest->addChild(sc->name()); dc = vsc[ndc]; } sc->copyUnion(dc); } } // Copy all of the information in the current XML_Node tree // into the destination XML_Node tree, doing a complete copy // as we go. /* * Note this is a const function becuase the current XML_Node and * its children isn't altered by this operation. * * @param node_dest This is the XML node to receive the information */ void XML_Node::copy(XML_Node * const node_dest) const { XML_Node *sc, *dc; int ndc; node_dest->addValue(m_value); node_dest->setName(m_name); if (m_name == "") return; map::const_iterator b = m_attribs.begin(); for (; b != m_attribs.end(); ++b) { node_dest->addAttribute(b->first, b->second); } const vector &vsc = node_dest->children(); for (int n = 0; n < m_nchildren; n++) { sc = m_children[n]; ndc = node_dest->nChildren(); (void) node_dest->addChild(sc->name()); dc = vsc[ndc]; sc->copy(dc); } } // Set the lock for this node void XML_Node::lock() { m_locked = true; for (int i = 0; i < m_nchildren; i++) { m_children[i]->lock(); } } // Unset the lock for this node void XML_Node::unlock() { m_locked = false; for (int i = 0; i < m_nchildren; i++) { m_children[i]->unlock(); } } // Get a vector of pointers to XML_Node containing all of the children // of the current node which matches the input name /* * @param name Name of the XML_Node children to search on * * @param children output vector of pointers to XML_Node children * with the matching name */ void XML_Node::getChildren(const std::string &nm, std::vector& children) const { int i, n = nChildren(); for (i = 0; i < n; i++) { if (child(i).name() == nm) { children.push_back(&child(i)); } } } // Return a changeable reference to a child of the current node, // named by the argument /* * @param loc Name of the child to return */ XML_Node& XML_Node::child(const std::string &aloc) const { string::size_type iloc; string cname; string loc = aloc; std::map::const_iterator i; while (1) { iloc = loc.find('/'); if (iloc != string::npos) { cname = loc.substr(0,iloc); loc = loc.substr(iloc+1, loc.size()); i = m_childindex.find(cname); if (i != m_childindex.end()) return i->second->child(loc); else { throw XML_NoChild(this, m_name, cname, lineNumber()); } } else { i = m_childindex.find(loc); if (i != m_childindex.end()) return *(i->second); else { throw XML_NoChild(this, m_name, loc, lineNumber()); } } } } /* * Write an XML subtree to an output stream. This is the * main recursive routine. It doesn't put a final endl * on. This is fixed up in the public method. */ void XML_Node::write_int(std::ostream& s, int level) const { if (m_name == "") return; string indent(level, ' '); if (m_iscomment) { /* * In the comment section, we test to see if there * already is a space beginning and ending the comment. * If there already is one, we don't add another one. */ s << endl << indent << ""; return; } s << indent << "<" << m_name; map::const_iterator b = m_attribs.begin(); for (; b != m_attribs.end(); ++b) { s << " " << b->first << "=\"" << b->second << "\""; } if (m_value == "" && m_nchildren == 0) { s << "/>"; } else { s << ">"; if (m_value != "") { string vv = m_value; string::size_type ieol = vv.find('\n'); if (ieol != string::npos) { while (1 > 0) { ieol = vv.find('\n'); if (ieol != string::npos) { if (ieol == 0) { s << endl << indent << " "; } else { int jf = ieol - 1; for (int j = 0; j < (int) ieol; j++) { if (! isspace(vv[j])) { jf = j; break; } } s << endl << indent << " " << vv.substr(jf,ieol-jf); } vv = vv.substr(ieol+1); } else { int lll = static_cast(vv.size()) - 1; if (lll >= 0) { int jf = lll; for (int j = 0; j < lll; j++) { if (! isspace(vv[j])) { jf = j; break; } } if (jf < lll) { s << endl << indent << " " << vv.substr(jf); } } break; } } s << endl << indent; } else { bool doSpace = true; bool doNewLine = false; int ll = static_cast(m_value.size()) - 1; if (ll > 15) { doNewLine = true; } if (m_name == "floatArray") { doNewLine = true; } if (doNewLine) doSpace = false; if (doNewLine) { s << endl << indent << " "; } /* * Put spaces around a raw value field for readability */ if (doSpace && (! isspace(m_value[0]))) { s << " "; } /* * Write out the value */ s << m_value; if (doSpace && (! isspace(m_value[ll]))) { s << " "; } if (doNewLine) { s << endl << indent; } } } int i; for (i = 0; i < m_nchildren; i++) { s << endl; m_children[i]->write_int(s,level + 2); } if (m_nchildren > 0) s << endl << indent; s << ""; } } /* * Write an XML subtree to an output stream. This is a * wrapper around the static routine write_int(). All this * does is add an endl on to the output stream. write_int() is * fine, but the last endl wasn't being written. * It also checks for the special name "--". If found and we * are at the root of the xml tree, then the block * is skipped and the children are processed. "--" is used * to denote the top of the tree. */ void XML_Node::write(std::ostream& s, const int level) const { if (m_name == "--" && m_root == this) { for (int i = 0; i < m_nchildren; i++) { m_children[i]->write_int(s,level); s << endl; } } else { write_int(s, level); s << endl; } } XML_Node& XML_Node::root() const { return *m_root; } void XML_Node::setRoot(const XML_Node& root) { m_root = const_cast(&root); } XML_Node * findXMLPhase(XML_Node *root, const std::string &idtarget) { XML_Node *scResult = 0; XML_Node *sc; if (!root) return 0; string idattrib; string rname = root->name(); if (rname == "phase") { if (idtarget == "") return root; idattrib = root->id(); if (idtarget == idattrib) return root; else return 0; } const vector &vsc = root->children(); int n; for (n = 0; n < root->nChildren(); n++) { sc = vsc[n]; if (sc->name() == "phase") { if (idtarget == "") return sc; idattrib = sc->id(); if (idtarget == idattrib) return sc; } } for (n = 0; n < root->nChildren(); n++) { sc = vsc[n]; if (sc->name() != "phase") { scResult = findXMLPhase(sc, idtarget); if (scResult) return scResult; } } return scResult; } }