00001 /*************************************************************************** 00002 * GXmlNode.cpp - Abstract XML node base class * 00003 * ----------------------------------------------------------------------- * 00004 * copyright (C) 2010-2016 by Juergen Knoedlseder * 00005 * ----------------------------------------------------------------------- * 00006 * * 00007 * This program is free software: you can redistribute it and/or modify * 00008 * it under the terms of the GNU General Public License as published by * 00009 * the Free Software Foundation, either version 3 of the License, or * 00010 * (at your option) any later version. * 00011 * * 00012 * This program is distributed in the hope that it will be useful, * 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00015 * GNU General Public License for more details. * 00016 * * 00017 * You should have received a copy of the GNU General Public License * 00018 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 00019 * * 00020 ***************************************************************************/ 00021 /** 00022 * @file GXmlNode.cpp 00023 * @brief Abstract XML node base class implementation 00024 * @author Juergen Knoedlseder 00025 */ 00026 00027 /* __ Includes ___________________________________________________________ */ 00028 #ifdef HAVE_CONFIG_H 00029 #include <config.h> 00030 #endif 00031 #include "GTools.hpp" 00032 #include "GException.hpp" 00033 #include "GXmlNode.hpp" 00034 #include "GXmlElement.hpp" 00035 #include "GXmlDocument.hpp" 00036 00037 /* __ Method name definitions ____________________________________________ */ 00038 #define G_ACCESS "GXmlNode::operator[](int&)" 00039 #define G_SET "GXmlNode::set(int&, GXmlNode&)" 00040 #define G_APPEND1 "GXmlNode::append(std::string&)" 00041 #define G_APPEND2 "GXmlNode::append(GXmlNode&)" 00042 #define G_INSERT "GXmlNode::insert(int&, GXmlNode&)" 00043 #define G_REMOVE "GXmlNode::remove(int&)" 00044 #define G_ELEMENT1 "GXmlNode* GXmlNode::element(int&)" 00045 #define G_ELEMENT2 "GXmlNode* GXmlNode::element(std::string&)" 00046 #define G_ELEMENT3 "GXmlNode* GXmlNode::element(std::string&, int&)" 00047 #define G_EXTRACT_INDEX "GXmlNode::extract_index(std::string&)" 00048 00049 /* __ Macros _____________________________________________________________ */ 00050 00051 /* __ Coding definitions _________________________________________________ */ 00052 00053 /* __ Debug definitions __________________________________________________ */ 00054 00055 00056 /*========================================================================== 00057 = = 00058 = Constructors/destructors = 00059 = = 00060 ==========================================================================*/ 00061 00062 /***********************************************************************//** 00063 * @brief Void constructor 00064 ***************************************************************************/ 00065 GXmlNode::GXmlNode(void) 00066 { 00067 // Initialise members 00068 init_members(); 00069 00070 // Return 00071 return; 00072 } 00073 00074 00075 /***********************************************************************//** 00076 * @brief Copy constructor 00077 * 00078 * @param[in] node XML node. 00079 ***************************************************************************/ 00080 GXmlNode::GXmlNode(const GXmlNode& node) 00081 { 00082 // Initialise members 00083 init_members(); 00084 00085 // Copy members 00086 copy_members(node); 00087 00088 // Return 00089 return; 00090 } 00091 00092 00093 /***********************************************************************//** 00094 * @brief Destructor 00095 ***************************************************************************/ 00096 GXmlNode::~GXmlNode(void) 00097 { 00098 // Free members 00099 free_members(); 00100 00101 // Return 00102 return; 00103 } 00104 00105 00106 /*========================================================================== 00107 = = 00108 = Operators = 00109 = = 00110 ==========================================================================*/ 00111 00112 /***********************************************************************//** 00113 * @brief Assignment operator 00114 * 00115 * @param[in] node XML node. 00116 * @return XML node. 00117 ***************************************************************************/ 00118 GXmlNode& GXmlNode::operator=(const GXmlNode& node) 00119 { 00120 // Execute only if object is not identical 00121 if (this != &node) { 00122 00123 // Free members 00124 free_members(); 00125 00126 // Initialise members 00127 init_members(); 00128 00129 // Copy members 00130 copy_members(node); 00131 00132 } // endif: object was not identical 00133 00134 // Return 00135 return *this; 00136 } 00137 00138 00139 /***********************************************************************//** 00140 * @brief Return pointer to XML child node 00141 * 00142 * @param[in] index Child node index [0,...,size()-1]. 00143 * @return Pointer to XML child node at @p index. 00144 * 00145 * @exception GException::out_of_range 00146 * Child node index is out of range. 00147 * 00148 * Returns a pointer to the XML child node with the specified @p index. 00149 ***************************************************************************/ 00150 GXmlNode* GXmlNode::operator[](const int& index) 00151 { 00152 // Compile option: raise exception if index is out of range 00153 #if defined(G_RANGE_CHECK) 00154 if (index < 0 || index >= size()) { 00155 throw GException::out_of_range(G_ACCESS, index, 0, size()-1); 00156 } 00157 #endif 00158 00159 // Return pointer 00160 return m_nodes[index]; 00161 } 00162 00163 00164 /***********************************************************************//** 00165 * @brief Return pointer to XML child node (const version) 00166 * 00167 * @param[in] index Child node index [0,...,size()-1]. 00168 * @return Pointer to XML child node at @p index. 00169 * 00170 * @exception GException::out_of_range 00171 * Child node index is out of range. 00172 * 00173 * Returns a const pointer to the XML child node with the specified @p index. 00174 ***************************************************************************/ 00175 const GXmlNode* GXmlNode::operator[](const int& index) const 00176 { 00177 // Compile option: raise exception if index is out of range 00178 #if defined(G_RANGE_CHECK) 00179 if (index < 0 || index >= size()) { 00180 throw GException::out_of_range(G_ACCESS, index, 0, size()-1); 00181 } 00182 #endif 00183 00184 // Return pointer 00185 return m_nodes[index]; 00186 } 00187 00188 00189 /*========================================================================== 00190 = = 00191 = Public methods = 00192 = = 00193 ==========================================================================*/ 00194 00195 /***********************************************************************//** 00196 * @brief Set XML child node 00197 * 00198 * @param[in] index Child node index [0,...,size()-1]. 00199 * @param[in] node XML child node. 00200 * @return Pointer to deep copy of child node 00201 * 00202 * @exception GException::invalid_argument 00203 * Not allowed to append root node. 00204 * @exception GException::invalid_value 00205 * Not allowed to append to a text, comment or PI node. 00206 * @exception GException::out_of_range 00207 * Child node index is out of range. 00208 * 00209 * Set XML child node. A deep copy of the node will be made and the pointer 00210 * to this node will be stored. 00211 ***************************************************************************/ 00212 GXmlNode* GXmlNode::set(const int& index, const GXmlNode& node) 00213 { 00214 // Make sure that node to append is not a root node as only one root node 00215 // is allowed to exist in a document. 00216 if (node.type() == NT_DOCUMENT) { 00217 std::string msg = "Invalid attempt to append root note (GXmlDocument)" 00218 " to a XML node. There can only be one root node in" 00219 " an XML document."; 00220 throw GException::invalid_argument(G_SET, msg); 00221 } 00222 00223 // Make sure that the current node is not a text node as nothing can be 00224 // appended to a text node. 00225 if (this->type() == NT_TEXT) { 00226 std::string msg = "Invalid attempt to append a XML node to a text node" 00227 " (GXmlText). Nothing can be appended to a text node."; 00228 throw GException::invalid_value(G_SET, msg); 00229 } 00230 00231 // Make sure that the current node is not a PI node as nothing can be 00232 // appended to a PI node. 00233 if (this->type() == NT_PI) { 00234 std::string msg = "Invalid attempt to append a XML node to a processing" 00235 " instruction node (GXmlPI). Nothing can be appended" 00236 " to a processing instruction node."; 00237 throw GException::invalid_value(G_SET, msg); 00238 } 00239 00240 // Make sure that the current node is not a comment node as nothing can be 00241 // appended to a PI node. 00242 if (this->type() == NT_COMMENT) { 00243 std::string msg = "Invalid attempt to append a XML node to a comment" 00244 " node (GXmlComment). Nothing can be appended" 00245 " to a processing instruction node."; 00246 throw GException::invalid_value(G_SET, msg); 00247 } 00248 00249 // Compile option: raise exception if index is out of range 00250 #if defined(G_RANGE_CHECK) 00251 if (index < 0 || index >= size()) { 00252 throw GException::out_of_range(G_SET, index, 0, size()-1); 00253 } 00254 #endif 00255 00256 // Delete any existing node 00257 if (m_nodes[index] != NULL) delete m_nodes[index]; 00258 00259 // Assign new child node by cloning 00260 m_nodes[index] = node.clone(); 00261 00262 // Return pointer 00263 return m_nodes[index]; 00264 } 00265 00266 00267 /***********************************************************************//** 00268 * @brief Append XML child node 00269 * 00270 * @param[in] node XML child node. 00271 * @return Pointer to appended child node 00272 * 00273 * @exception GException::invalid_argument 00274 * Not allowed to append root node. 00275 * @exception GException::invalid_value 00276 * Not allowed to append to a text, comment or PI node. 00277 * 00278 * Appends XML child node by making a deep copy of the node and storing its 00279 * pointer. 00280 ***************************************************************************/ 00281 GXmlNode* GXmlNode::append(const GXmlNode& node) 00282 { 00283 // Make sure that node to append is not a root node as only one root node 00284 // is allowed to exist in a document. 00285 if (node.type() == NT_DOCUMENT) { 00286 std::string msg = "Invalid attempt to append root note (GXmlDocument)" 00287 " to a XML node. There can only be one root node in" 00288 " an XML document."; 00289 throw GException::invalid_argument(G_APPEND2, msg); 00290 } 00291 00292 // Make sure that the current node is not a text node as nothing can be 00293 // appended to a text node. 00294 if (this->type() == NT_TEXT) { 00295 std::string msg = "Invalid attempt to append a XML node to a text node" 00296 " (GXmlText). Nothing can be appended to a text node."; 00297 throw GException::invalid_value(G_APPEND2, msg); 00298 } 00299 00300 // Make sure that the current node is not a PI node as nothing can be 00301 // appended to a PI node. 00302 if (this->type() == NT_PI) { 00303 std::string msg = "Invalid attempt to append a XML node to a processing" 00304 " instruction node (GXmlPI). Nothing can be appended" 00305 " to a processing instruction node."; 00306 throw GException::invalid_value(G_APPEND2, msg); 00307 } 00308 00309 // Make sure that the current node is not a comment node as nothing can be 00310 // appended to a PI node. 00311 if (this->type() == NT_COMMENT) { 00312 std::string msg = "Invalid attempt to append a XML node to a comment" 00313 " node (GXmlComment). Nothing can be appended" 00314 " to a processing instruction node."; 00315 throw GException::invalid_value(G_APPEND2, msg); 00316 } 00317 00318 // Clone child node 00319 GXmlNode* ptr = node.clone(); 00320 00321 // Append deep copy of child node 00322 m_nodes.push_back(ptr); 00323 00324 // Return pointer 00325 return ptr; 00326 } 00327 00328 00329 /***********************************************************************//** 00330 * @brief Append XML element child node 00331 * 00332 * @param[in] segment XML child node. 00333 * @return Pointer to appended child node 00334 * 00335 * @exception GException::invalid_value 00336 * Not allowed to append to a text, comment or PI node. 00337 * 00338 * Appends XML element that is constructed from a text @p segment. The text 00339 * segment is parsed and the element name and attributes are extracted using 00340 * the GXmlElement::parse_start() method. The method returns a pointer to the 00341 * XML element child node that has been appended. 00342 ***************************************************************************/ 00343 GXmlElement* GXmlNode::append(const std::string& segment) 00344 { 00345 // Make sure that the current node is not a text node as nothing can be 00346 // appended to a text node. 00347 if (this->type() == NT_TEXT) { 00348 std::string msg = "Invalid attempt to append the text segment \""+ 00349 segment+"\" to a text node (GXmlText). Nothing can" 00350 " be appended to a text node."; 00351 throw GException::invalid_value(G_APPEND1, msg); 00352 } 00353 00354 // Make sure that the current node is not a PI node as nothing can be 00355 // appended to a PI node. 00356 if (this->type() == NT_PI) { 00357 std::string msg = "Invalid attempt to append a XML node to a processing" 00358 " instruction node (GXmlPI). Nothing can be appended" 00359 " to a processing instruction node."; 00360 throw GException::invalid_value(G_APPEND1, msg); 00361 } 00362 00363 // Make sure that the current node is not a comment node as nothing can be 00364 // appended to a PI node. 00365 if (this->type() == NT_COMMENT) { 00366 std::string msg = "Invalid attempt to append a XML node to a comment" 00367 " node (GXmlComment). Nothing can be appended" 00368 " to a processing instruction node."; 00369 throw GException::invalid_value(G_APPEND1, msg); 00370 } 00371 00372 // Create a new XML child element from the text segment 00373 GXmlElement* ptr = new GXmlElement(segment); 00374 00375 // Append child element to container 00376 m_nodes.push_back(ptr); 00377 00378 // Return child element 00379 return ptr; 00380 } 00381 00382 00383 /***********************************************************************//** 00384 * @brief Insert XML child node 00385 * 00386 * @param[in] index Child node index [0,...,size()-1]. 00387 * @param[in] node XML child node. 00388 * @return Pointer to inserted child node 00389 * 00390 * @exception GException::invalid_argument 00391 * Not allowed to append root node. 00392 * @exception GException::invalid_value 00393 * Not allowed to append to a text, comment or PI node. 00394 * @exception GException::out_of_range 00395 * Child node index is out of range. 00396 * 00397 * Inserts the XML child @p node before the node with the specified @p index. 00398 * A deep copy of the node will be made and the pointer to this node will be 00399 * stored. 00400 ***************************************************************************/ 00401 GXmlNode* GXmlNode::insert(const int& index, const GXmlNode& node) 00402 { 00403 // Make sure that node to append is not a root node as only one root node 00404 // is allowed to exist in a document. 00405 if (node.type() == NT_DOCUMENT) { 00406 std::string msg = "Invalid attempt to append root note (GXmlDocument)" 00407 " to a XML node. There can only be one root node in" 00408 " an XML document."; 00409 throw GException::invalid_argument(G_INSERT, msg); 00410 } 00411 00412 // Make sure that the current node is not a text node as nothing can be 00413 // appended to a text node. 00414 if (this->type() == NT_TEXT) { 00415 std::string msg = "Invalid attempt to append a XML node to a text node" 00416 " (GXmlText). Nothing can be appended to a text node."; 00417 throw GException::invalid_value(G_INSERT, msg); 00418 } 00419 00420 // Make sure that the current node is not a PI node as nothing can be 00421 // appended to a PI node. 00422 if (this->type() == NT_PI) { 00423 std::string msg = "Invalid attempt to append a XML node to a processing" 00424 " instruction node (GXmlPI). Nothing can be appended" 00425 " to a processing instruction node."; 00426 throw GException::invalid_value(G_INSERT, msg); 00427 } 00428 00429 // Make sure that the current node is not a comment node as nothing can be 00430 // appended to a PI node. 00431 if (this->type() == NT_COMMENT) { 00432 std::string msg = "Invalid attempt to append a XML node to a comment" 00433 " node (GXmlComment). Nothing can be appended" 00434 " to a processing instruction node."; 00435 throw GException::invalid_value(G_INSERT, msg); 00436 } 00437 00438 // Compile option: raise exception if index is out of range 00439 #if defined(G_RANGE_CHECK) 00440 if (is_empty()) { 00441 if (index > 0) { 00442 throw GException::out_of_range(G_INSERT, index, 0, size()-1); 00443 } 00444 } 00445 else { 00446 if (index < 0 || index >= size()) { 00447 throw GException::out_of_range(G_INSERT, index, 0, size()-1); 00448 } 00449 } 00450 #endif 00451 00452 // Clone child node 00453 GXmlNode* ptr = node.clone(); 00454 00455 // Inserts deep copy of child node 00456 m_nodes.insert(m_nodes.begin()+index, ptr); 00457 00458 // Return pointer 00459 return ptr; 00460 } 00461 00462 00463 /***********************************************************************//** 00464 * @brief Remove XML child node 00465 * 00466 * @param[in] index Child node index [0,...,size()-1]. 00467 * 00468 * @exception GException::out_of_range 00469 * Child node index is out of range. 00470 * 00471 * Remove XML child node at @p index. 00472 ***************************************************************************/ 00473 void GXmlNode::remove(const int& index) 00474 { 00475 // Compile option: raise exception if index is out of range 00476 #if defined(G_RANGE_CHECK) 00477 if (index < 0 || index >= size()) { 00478 throw GException::out_of_range(G_REMOVE, index, 0, size()-1); 00479 } 00480 #endif 00481 00482 // Delete node 00483 delete m_nodes[index]; 00484 00485 // Erase child node from container 00486 m_nodes.erase(m_nodes.begin() + index); 00487 00488 // Return 00489 return; 00490 } 00491 00492 00493 /***********************************************************************//** 00494 * @brief Append all XML child nodes from another XML node 00495 * 00496 * @param[in] node XML node. 00497 * 00498 * Append all XML child nodes found in @p node to the actual object. Nodes 00499 * are copied deeply so that they live now on their on in the actual object. 00500 ***************************************************************************/ 00501 void GXmlNode::extend(const GXmlNode& node) 00502 { 00503 // Do nothing if node container is empty 00504 if (!node.is_empty()) { 00505 00506 // Get size. Note that we extract the size first to avoid an 00507 // endless loop that arises when a container is appended to 00508 // itself. 00509 int num = node.size(); 00510 00511 // Reserve enough space 00512 reserve(size() + num); 00513 00514 // Loop over all child nodes and append pointers to deep copies 00515 for (int i = 0; i < num; ++i) { 00516 m_nodes.push_back(node[i]->clone()); 00517 } 00518 00519 } // endif: node container was not empty 00520 00521 // Return 00522 return; 00523 } 00524 00525 00526 /***********************************************************************//** 00527 * @brief Return filename of XML file 00528 * 00529 * @return Filename. 00530 * 00531 * Returns the file name of the XML file by moving up the XML file hierarchy 00532 * to the root. In case that the XML node cannot move up to the root XML 00533 * node an empty file name is returned (this may happen if the XML node 00534 * is an orphan node). The file name will also be empty if the XML file has 00535 * not been read from disk or written to disk. 00536 ***************************************************************************/ 00537 GFilename GXmlNode::filename(void) const 00538 { 00539 // Initialise empty filename 00540 GFilename filename; 00541 00542 // Move up the XML document hierarchy until the root node has been 00543 // found 00544 GXmlNode* parent = this->parent(); 00545 while (parent != NULL) { 00546 00547 // Cast parent into a root node 00548 GXmlDocument* root = dynamic_cast<GXmlDocument*>(parent); 00549 00550 // If the parent is a root node, recover the filename and exit the 00551 // loop 00552 if (root != NULL) { 00553 filename = root->filename(); 00554 break; 00555 } 00556 00557 // ... otherwise move up the XML tree 00558 else { 00559 parent = parent->parent(); 00560 } 00561 00562 } // endwhile: move up to the root document 00563 00564 // Return filename 00565 return filename; 00566 } 00567 00568 00569 /***********************************************************************//** 00570 * @brief Return number of GXMLElement children of node 00571 * 00572 * @return Number of child elements. 00573 * 00574 * Returns the number of GXMLElement child elements of the XML node. 00575 * GXMLElement child elements are nodes of type NT_ELEMENT. 00576 ***************************************************************************/ 00577 int GXmlNode::elements(void) const 00578 { 00579 // Compute number of child elements in node 00580 int elements = 0; 00581 for (int i = 0; i < m_nodes.size(); ++i) { 00582 if (m_nodes[i]->type() == NT_ELEMENT) { 00583 elements++; 00584 } 00585 } 00586 00587 // Return number of child elements 00588 return elements; 00589 } 00590 00591 00592 /***********************************************************************//** 00593 * @brief Return number of GXMLElement children with a given name 00594 * 00595 * @param[in] name Name of GXMLElement elements. 00596 * @return Number of child elements with a given @p name. 00597 * 00598 * Returns the number of GXMLElement child elements of the XML node that 00599 * have a given @p name. GXMLElement child elements are nodes of type 00600 * NT_ELEMENT. 00601 ***************************************************************************/ 00602 int GXmlNode::elements(const std::string& name) const 00603 { 00604 // Compute number of child elements in node 00605 int elements = 0; 00606 for (int i = 0; i < m_nodes.size(); ++i) { 00607 if (m_nodes[i]->type() == NT_ELEMENT) { 00608 if (static_cast<GXmlElement*>(m_nodes[i])->name() == name) { 00609 elements++; 00610 } 00611 } 00612 } 00613 00614 // Return number of child elements 00615 return elements; 00616 } 00617 00618 00619 /***********************************************************************//** 00620 * @brief Return pointer to GXMLElement child 00621 * 00622 * @param[in] index Node index [0,...,elements()-1]. 00623 * @return Pointer to child element (NULL if element does not exist). 00624 * 00625 * @exception GException::out_of_range 00626 * Child element index is out of range. 00627 * 00628 * Returns a pointer to the child number @p index of the XML node. An 00629 * exception will be thrown if the @p index is not valid. 00630 ***************************************************************************/ 00631 GXmlElement* GXmlNode::element(const int& index) 00632 { 00633 // If index is outside boundary then throw an error 00634 if (index < 0 || index >= elements()) { 00635 throw GException::out_of_range(G_ELEMENT1, index, 0, elements()-1); 00636 } 00637 00638 // Get the requested child element 00639 GXmlElement* element = NULL; 00640 int elements = 0; 00641 for (int i = 0; i < m_nodes.size(); ++i) { 00642 GXmlElement* src = dynamic_cast<GXmlElement*>(m_nodes[i]); 00643 if (src != NULL) { 00644 if (elements == index) { 00645 element = src; 00646 break; 00647 } 00648 elements++; 00649 } 00650 } 00651 00652 // Return child element 00653 return element; 00654 } 00655 00656 00657 /***********************************************************************//** 00658 * @brief Return pointer to GXMLElement child (const variant) 00659 * 00660 * @param[in] index Node index [0,...,elements()-1]. 00661 * @return Pointer to child element (NULL if element does not exist). 00662 * 00663 * @exception GException::out_of_range 00664 * Child element index is out of range. 00665 * 00666 * Returns a pointer to the child number @p index of the XML node. An 00667 * exception will be thrown if the @p index is not valid. 00668 ***************************************************************************/ 00669 const GXmlElement* GXmlNode::element(const int& index) const 00670 { 00671 // If index is outside boundary then throw an error 00672 if (index < 0 || index >= elements()) { 00673 throw GException::out_of_range(G_ELEMENT1, index, 0, elements()-1); 00674 } 00675 00676 // Get the requested child element 00677 const GXmlElement* element = NULL; 00678 int elements = 0; 00679 for (int i = 0; i < m_nodes.size(); ++i) { 00680 const GXmlElement* src = dynamic_cast<const GXmlElement*>(m_nodes[i]); 00681 if (src != NULL) { 00682 if (elements == index) { 00683 element = src; 00684 break; 00685 } 00686 elements++; 00687 } 00688 } 00689 00690 // Return child element 00691 return element; 00692 } 00693 00694 00695 /***********************************************************************//** 00696 * @brief Return pointer on child walking down a hierarchy of tags 00697 * 00698 * @param[in] name Child element hierarchy. 00699 * @return Pointer to child element (NULL if element does not exist). 00700 * 00701 * @exception GException::invalid_argument 00702 * Hierarchy string invalid. 00703 * 00704 * Returns a pointer to the child element described by a hierarchy of the 00705 * following syntax 00706 * 00707 * params > param[1] > value > struct 00708 * 00709 * The > symbols indicate subsequent hierarchy levels, the square brackets 00710 * provides the index in case that multiple tags with the same name exist 00711 * at a given hierarchy level. Omitting the index means that the first 00712 * tag with the specified name is accessed. 00713 * 00714 * If the specified element does not exist the method returns a NULL pointer. 00715 ***************************************************************************/ 00716 GXmlElement* GXmlNode::element(const std::string& name) 00717 { 00718 // Initialise child node pointer 00719 GXmlElement* element = NULL; 00720 00721 // Split name into tags 00722 std::vector<std::string> tags = gammalib::split(name, ">"); 00723 00724 // Walk down the hierarchy 00725 GXmlNode* current = this; 00726 for (int i = 0; i < tags.size(); ++i) { 00727 00728 // Get tag name and index 00729 std::string tag = gammalib::strip_whitespace(tags[i]); 00730 int index = extract_index(tag); 00731 00732 // Break if the requested node does not exist 00733 int n = current->elements(tag); 00734 if (n < 1 || index < 0 || index >= n) { 00735 element = NULL; 00736 break; 00737 } 00738 00739 // Get node 00740 element = current->element(tag, index); 00741 current = element; 00742 00743 // Break if node has not been found 00744 if (current == NULL) { 00745 element = NULL; 00746 break; 00747 } 00748 00749 } // endfor: walked down hierarchy 00750 00751 // Return child element 00752 return element; 00753 } 00754 00755 00756 /***********************************************************************//** 00757 * @brief Return pointer on child walking down a hierarchy of tags (const 00758 * version) 00759 * 00760 * @param[in] name Child element hierarchy. 00761 * @return Pointer to child element (NULL if element does not exist). 00762 * 00763 * @exception GException::invalid_argument 00764 * Hierarchy string invalid. 00765 * 00766 * Returns a pointer to the child element described by a hierarchy of the 00767 * following syntax 00768 * 00769 * params > param[1] > value > struct 00770 * 00771 * The > symbols indicate subsequent hierarchy levels, the square brackets 00772 * provides the index in case that multiple tags with the same name exist 00773 * at a given hierarchy level. Omitting the index means that the first 00774 * tag with the specified name is accessed. 00775 * 00776 * If the specified element does not exist the method returns a NULL pointer. 00777 ***************************************************************************/ 00778 const GXmlElement* GXmlNode::element(const std::string& name) const 00779 { 00780 // Initialise child node pointer 00781 const GXmlElement* element = NULL; 00782 00783 // Split name into tags 00784 std::vector<std::string> tags = gammalib::split(name, ">"); 00785 00786 // Walk down the hierarchy 00787 const GXmlNode* current = this; 00788 for (int i = 0; i < tags.size(); ++i) { 00789 00790 // Get tag name and index 00791 std::string tag = gammalib::strip_whitespace(tags[i]); 00792 int index = extract_index(tag); 00793 00794 // Break if the requested node does not exist 00795 int n = current->elements(tag); 00796 if (n < 1 || index < 0 || index >= n) { 00797 element = NULL; 00798 break; 00799 } 00800 00801 // Get node 00802 element = current->element(tag, index); 00803 current = element; 00804 00805 // Break if node has not been found 00806 if (current == NULL) { 00807 element = NULL; 00808 break; 00809 } 00810 00811 } // endfor: walked down hierarchy 00812 00813 // Return child element 00814 return element; 00815 } 00816 00817 00818 /***********************************************************************//** 00819 * @brief Return pointer on GXMLElement child of a given name 00820 * 00821 * @param[in] name Name of child element. 00822 * @param[in] index Node index [0,...,elements()-1]. 00823 * @return Pointer to child element (NULL if element does not exist). 00824 * 00825 * @exception GException::xml_name_not_found 00826 * Child element name not found. 00827 * @exception GException::out_of_range 00828 * Child element index is out of range. 00829 * 00830 * Returns a pointer to the child number @p index with @p name of the XML 00831 * node. An exception will be thrown if the @p index is not valid. 00832 ***************************************************************************/ 00833 GXmlElement* GXmlNode::element(const std::string& name, const int& index) 00834 { 00835 // Determine number of child elements 00836 int n = elements(name); 00837 00838 // Signal if no children exist 00839 if (n < 1) { 00840 throw GException::xml_name_not_found(G_ELEMENT3, name); 00841 } 00842 00843 // If index is outside boundary then throw an error 00844 if (index < 0 || index >= n) { 00845 throw GException::out_of_range(G_ELEMENT3, index, 0, n-1); 00846 } 00847 00848 // Get the requested child element 00849 GXmlElement* element = NULL; 00850 int elements = 0; 00851 for (int i = 0; i < m_nodes.size(); ++i) { 00852 GXmlElement* src = dynamic_cast<GXmlElement*>(m_nodes[i]); 00853 if (src != NULL) { 00854 if (src->name() == name) { 00855 if (elements == index) { 00856 element = src; 00857 break; 00858 } 00859 elements++; 00860 } 00861 } 00862 } 00863 00864 // Return child element 00865 return element; 00866 } 00867 00868 00869 /***********************************************************************//** 00870 * @brief Return pointer on GXMLElement child of a given name (const variant) 00871 * 00872 * @param[in] name Name of child element. 00873 * @param[in] index Node index [0,...,elements()-1]. 00874 * @return Pointer to child element (NULL if element does not exist). 00875 * 00876 * @exception GException::xml_name_not_found 00877 * Child element name not found. 00878 * @exception GException::out_of_range 00879 * Child element index is out of range. 00880 * 00881 * Returns a pointer to the child number @p index with @p name of the XML 00882 * node. An exception will be thrown if the @p index is not valid. 00883 ***************************************************************************/ 00884 const GXmlElement* GXmlNode::element(const std::string& name, const int& index) const 00885 { 00886 // Determine number of child elements 00887 int n = elements(name); 00888 00889 // Signal if no children exist 00890 if (n < 1) { 00891 throw GException::xml_name_not_found(G_ELEMENT3, name); 00892 } 00893 00894 // If index is outside boundary then throw an error 00895 if (index < 0 || index >= n) { 00896 throw GException::out_of_range(G_ELEMENT3, index, 0, n-1); 00897 } 00898 00899 // Get the requested child element 00900 const GXmlElement* element = NULL; 00901 int elements = 0; 00902 for (int i = 0; i < m_nodes.size(); ++i) { 00903 const GXmlElement* src = dynamic_cast<const GXmlElement*>(m_nodes[i]); 00904 if (src != NULL) { 00905 if (src->name() == name) { 00906 if (elements == index) { 00907 element = src; 00908 break; 00909 } 00910 elements++; 00911 } 00912 } 00913 } 00914 00915 // Return child element 00916 return element; 00917 } 00918 00919 00920 /***********************************************************************//** 00921 * @brief Print XML node in string 00922 * 00923 * @param[in] chatter Chattiness (defaults to NORMAL). 00924 * @return String containing XML information. 00925 ***************************************************************************/ 00926 std::string GXmlNode::print(const GChatter& chatter) const 00927 { 00928 // Set result string 00929 std::string result = print(chatter, 0); 00930 00931 // Return 00932 return result; 00933 } 00934 00935 00936 /*========================================================================== 00937 = = 00938 = Private methods = 00939 = = 00940 ==========================================================================*/ 00941 00942 /***********************************************************************//** 00943 * @brief Initialise class members 00944 ***************************************************************************/ 00945 void GXmlNode::init_members(void) 00946 { 00947 // Initialise members 00948 m_nodes.clear(); 00949 m_parent = NULL; 00950 00951 // Return 00952 return; 00953 } 00954 00955 00956 /***********************************************************************//** 00957 * @brief Copy class members 00958 * 00959 * @param[in] node XML node. 00960 * 00961 * @todo Is copying the parent node pointer correct? 00962 ***************************************************************************/ 00963 void GXmlNode::copy_members(const GXmlNode& node) 00964 { 00965 // Copy members 00966 m_parent = node.m_parent; 00967 00968 // Copy nodes 00969 m_nodes.clear(); 00970 for (int i = 0; i < node.m_nodes.size(); ++i) { 00971 m_nodes.push_back((node.m_nodes[i]->clone())); 00972 } 00973 00974 // Return 00975 return; 00976 } 00977 00978 00979 /***********************************************************************//** 00980 * @brief Delete class members 00981 * 00982 * As container classes that hold pointers need to handle themselves the 00983 * proper deallocation of memory, we loop here over all pointers and make 00984 * sure that we deallocate the associated nodes. 00985 ***************************************************************************/ 00986 void GXmlNode::free_members(void) 00987 { 00988 // Free memory for all nodes 00989 for (int i = 0; i < m_nodes.size(); ++i) { 00990 delete m_nodes[i]; 00991 m_nodes[i] = NULL; 00992 } 00993 00994 // Return 00995 return; 00996 } 00997 00998 00999 /***********************************************************************//** 01000 * @brief Extract index from tag 01001 * 01002 * @param[in,out] tag Tag. 01003 * @return Index. 01004 * 01005 * @exception GException::invalid_argument 01006 * Tag string invalid. 01007 * 01008 * Extracts the index from a tag string of the following syntax 01009 * 01010 * param[1] 01011 * 01012 * The value within the squared brackets provides the index of the element. 01013 * If the squared brackets are omitted, an index of 0 will be returned. 01014 ***************************************************************************/ 01015 int GXmlNode::extract_index(std::string& tag) const 01016 { 01017 // Initialise index 01018 int index = 0; 01019 01020 // Search for squared brackets, extract index, and strip brackets 01021 // from tag 01022 size_t start = tag.find_first_of("["); 01023 if (start != std::string::npos) { 01024 size_t stop = tag.find_first_of("]", start); 01025 if (stop == std::string::npos) { 01026 std::string msg = "Tag specifier \""+tag+"\" is missing " 01027 "a ] bracket."; 01028 throw GException::invalid_argument(G_EXTRACT_INDEX, msg); 01029 } 01030 else { 01031 size_t length = stop - start - 1; 01032 if (length < 1) { 01033 std::string msg = "Index value is missing for \""+tag+"\"."; 01034 throw GException::invalid_argument(G_EXTRACT_INDEX, msg); 01035 } 01036 else { 01037 index = gammalib::toint(tag.substr(start+1, length)); 01038 tag = tag.substr(0, start); 01039 } 01040 } 01041 } 01042 01043 // Return index 01044 return index; 01045 }