src/xml/GXmlElement.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *          GXmlElement.cpp - XML element node class implementation        *
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 GXmlElement.cpp
00023  * @brief XML element node 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 "GFilename.hpp"
00034 #include "GXmlDocument.hpp"
00035 #include "GXmlElement.hpp"
00036 
00037 /* __ Method name definitions ____________________________________________ */
00038 #define G_PARSE_START                "GXmlElement::parse_start(std::string&)"
00039 #define G_PARSE_STOP                  "GXmlElement::parse_stop(std::string&)"
00040 #define G_PARSE_ATTRIBUTE            "GXmlElement::parse_attribute(size_t*, "\
00041                                                               "std::string&)"
00042 
00043 /* __ Constants __________________________________________________________ */
00044 const int g_indent = 2;                      //!< Indent for XML file writing
00045 
00046 /* __ Macros _____________________________________________________________ */
00047 
00048 /* __ Coding definitions _________________________________________________ */
00049 
00050 /* __ Debug definitions __________________________________________________ */
00051 
00052 
00053 /*==========================================================================
00054  =                                                                         =
00055  =                         Constructors/destructors                        =
00056  =                                                                         =
00057  ==========================================================================*/
00058 
00059 /***********************************************************************//**
00060  * @brief Void constructor
00061  ***************************************************************************/
00062 GXmlElement::GXmlElement(void) : GXmlNode()
00063 {
00064     // Initialise members
00065     init_members();
00066 
00067     // Return
00068     return;
00069 }
00070 
00071 
00072 /***********************************************************************//**
00073  * @brief Copy constructor
00074  *
00075  * @param[in] node XML element.
00076  ***************************************************************************/
00077 GXmlElement::GXmlElement(const GXmlElement& node) : GXmlNode(node)
00078 {
00079     // Initialise members
00080     init_members();
00081 
00082     // Copy members
00083     copy_members(node);
00084 
00085     // Return
00086     return;
00087 }
00088 
00089 
00090 /***********************************************************************//**
00091  * @brief Segment constructor
00092  *
00093  * @param[in] segment XML segment.
00094  *
00095  * Constructs a XML element from a text @p segment. The text segment is
00096  * parsed and the element name and attributes are extracted using the
00097  * parse_start() method.
00098  ***************************************************************************/
00099 GXmlElement::GXmlElement(const std::string& segment) : GXmlNode()
00100 {
00101     // Initialise members
00102     init_members();
00103 
00104     // Parse start element
00105     parse_start(segment);
00106 
00107     // Return
00108     return;
00109 }
00110 
00111 
00112 /***********************************************************************//**
00113  * @brief Destructor
00114  ***************************************************************************/
00115 GXmlElement::~GXmlElement(void)
00116 {
00117     // Free members
00118     free_members();
00119 
00120     // Return
00121     return;
00122 }
00123 
00124 
00125 /*==========================================================================
00126  =                                                                         =
00127  =                                Operators                                =
00128  =                                                                         =
00129  ==========================================================================*/
00130 
00131 /***********************************************************************//**
00132  * @brief Assignment operator
00133  *
00134  * @param[in] node XML element.
00135  * @return XML element.
00136  ***************************************************************************/
00137 GXmlElement& GXmlElement::operator=(const GXmlElement& node)
00138 {
00139     // Execute only if object is not identical
00140     if (this != &node) {
00141 
00142         // Copy base class members
00143         this->GXmlNode::operator=(node);
00144 
00145         // Free members
00146         free_members();
00147 
00148         // Initialise members
00149         init_members();
00150 
00151         // Copy members
00152         copy_members(node);
00153 
00154     } // endif: object was not identical
00155 
00156     // Return
00157     return *this;
00158 }
00159 
00160 
00161 /*==========================================================================
00162  =                                                                         =
00163  =                             Public methods                              =
00164  =                                                                         =
00165  ==========================================================================*/
00166  
00167  /***********************************************************************//**
00168  * @brief Clear XML element
00169  *
00170  * Resets the XML element to a clean initial state.
00171  ***************************************************************************/
00172 void GXmlElement::clear(void)
00173 {
00174     // Free class members (base and derived classes, derived class first)
00175     free_members();
00176     this->GXmlNode::free_members();
00177 
00178     // Initialise members
00179     this->GXmlNode::init_members();
00180     init_members();
00181 
00182     // Return
00183     return;
00184 }
00185 
00186 
00187 /***********************************************************************//**
00188  * @brief Clone XML element
00189  *
00190  * @return Pointer to deep copy of XML element.
00191  ***************************************************************************/
00192 GXmlElement* GXmlElement::clone(void) const
00193 {
00194     // Clone element
00195     return new GXmlElement(*this);
00196 }
00197 
00198 
00199 /***********************************************************************//**
00200  * @brief Return attribute value
00201  *
00202  * @param[in] name Attribute name.
00203  * @return String containing attribute value.
00204  *
00205  * Returns the value of the attribute @p name. If the requested attribute was
00206  * not found an empty string is returned.
00207  ***************************************************************************/
00208 std::string GXmlElement::attribute(const std::string& name) const
00209 {
00210     // Initialise empty value (i.e. attribute not found)
00211     std::string value = "";
00212 
00213     // Search attribute value in list of attributes
00214     for (int i = 0; i < m_attr.size(); ++i) {
00215         if (m_attr[i]->name() == name) {
00216             value = m_attr[i]->value();
00217             break;
00218         }
00219     }
00220 
00221     // Return value
00222     return value;
00223 }
00224 
00225 
00226 /***********************************************************************//**
00227  * @brief Set attribute value
00228  *
00229  * @param[in] name Attribute name.
00230  * @param[in] value Attribute value.
00231  *
00232  * Sets an attribute of the element. If the attribute name exists the value
00233  * is modified. If the attribute does not yet exist it is created and
00234  * added to the list of attributes.
00235  *
00236  * Note that this logical assures that only one attribute with a given name
00237  * will exist in the element.
00238  ***************************************************************************/
00239 void GXmlElement::attribute(const std::string& name, const std::string& value)
00240 {
00241     // Initialise attribute NULL pointer
00242     GXmlAttribute* attr = NULL;
00243 
00244     // Search attribute name in list of attributes
00245     for (int i = 0; i < m_attr.size(); ++i) {
00246         if (m_attr[i]->name() == name) {
00247             attr = m_attr[i];
00248             break;
00249         }
00250     }
00251 
00252     // If no attribute with specified name has been found then add a new
00253     // attribute to the list of attributes
00254     if (attr == NULL) {
00255         attr = new GXmlAttribute;
00256         attr->name(name);
00257         m_attr.push_back(attr);
00258     }
00259 
00260     // Set or update value of attribute
00261     attr->value(value);
00262 
00263     // Return
00264     return;
00265 }
00266 
00267 
00268 /***********************************************************************//**
00269  * @brief Check if element has a given attribute
00270  *
00271  * @param[in] name Attribute name.
00272  * @return True if attribute exists, false otherwise.
00273  *
00274  * Checks whether the element contains an attribute with @p name. If the
00275  * attribute was found true is returned, false otherwise.
00276  ***************************************************************************/
00277 bool GXmlElement::has_attribute(const std::string& name) const
00278 {
00279     // Initialise found flag
00280     bool found = false;
00281 
00282     // Search attribute value in list of attributes
00283     for (int i = 0; i < m_attr.size(); ++i) {
00284         if (m_attr[i]->name() == name) {
00285             found = true;
00286             break;
00287         }
00288     }
00289 
00290     // Return found flag
00291     return found;
00292 }
00293 
00294 
00295 /***********************************************************************//**
00296  * @brief Remove attribute from element
00297  *
00298  * @param[in] name Attribute name.
00299  *
00300  * Remove the attribute with @p name from the XML element. If the requested
00301  * attribute was not found the method does nothing.
00302  ***************************************************************************/
00303 void GXmlElement::remove_attribute(const std::string& name)
00304 {
00305     // Do nothing if there are no attributes
00306     if (!m_attr.empty()) {
00307 
00308         // Store number of attributes.
00309         int num = m_attr.size();
00310 
00311         // Search attribute name in list of attributes and erase attribute
00312         // when it has been found. Note that if several attributes with the
00313         // same name exist (which should never be the case!), only the
00314         // first attribute is removed
00315         for (int i = 0; i < num; ++i) {
00316             if (m_attr[i]->name() == name) {
00317                 m_attr.erase(m_attr.begin() + i);
00318                 break;
00319             }
00320         }
00321 
00322     } // endif: there were attributes
00323 
00324     // Return
00325     return;
00326 }
00327 
00328 
00329 /***********************************************************************//**
00330  * @brief Write element into URL
00331  *
00332  * @param[in] url Unified Resource Locator.
00333  * @param[in] indent Text indentation.
00334  *
00335  * Writes the element into a Unified Resource Locator.
00336  ***************************************************************************/
00337 void GXmlElement::write(GUrl& url, const int& indent) const
00338 {
00339     // Prepend indentation
00340     for (int k = 0; k < indent; ++k) {
00341         url.printf(" ");
00342     }
00343 
00344     // Write element name into URL
00345     url.printf("<%s", m_name.c_str());
00346 
00347     // Write attributes into URL
00348     for (int k = 0; k < m_attr.size(); ++k) {
00349         m_attr[k]->write(url);
00350     }
00351 
00352     // If there are no children then write an empty tag
00353     if (is_empty()) {
00354         url.printf(" />\n");
00355     }
00356 
00357     // ... otherwise finish start tag, write children and write end tag
00358     else {
00359 
00360         // Case A: The element contains a single text leaf
00361         if ((m_nodes.size() == 1) && (m_nodes[0]->type() == NT_TEXT)) {
00362 
00363             // Finish start tag
00364             url.printf(">");
00365 
00366             // Write text leaf
00367             m_nodes[0]->write(url, 0);
00368 
00369             // Write end tag
00370             url.printf("</%s>\n", m_name.c_str());
00371         }
00372         
00373         // Case B: ... otherwise it contains markup
00374         else {
00375 
00376             // Finish start tag
00377             url.printf(">\n");
00378 
00379             // Write children in file
00380             for (int i = 0; i < m_nodes.size(); ++i) {
00381                 m_nodes[i]->write(url, indent+g_indent);
00382                 if (m_nodes[i]->type() == NT_TEXT) {
00383                     url.printf("\n");
00384                 }
00385             }
00386 
00387             // Write end tag
00388             for (int k = 0; k < indent; ++k) {
00389                 url.printf(" ");
00390             }
00391             url.printf("</%s>\n", m_name.c_str());
00392         
00393         } // endelse: element contained markup
00394         
00395     } // endelse: finished start tag
00396 
00397     // Return
00398     return;
00399 }
00400 
00401 
00402 /***********************************************************************//**
00403  * @brief Print XML element
00404  *
00405  * @param[in] chatter Chattiness.
00406  * @param[in] indent Text indentation.
00407  * @return String containing XML element
00408  ***************************************************************************/
00409 std::string GXmlElement::print(const GChatter& chatter,
00410                                const int&      indent) const
00411 {
00412     // Allocate result string
00413     std::string result;
00414 
00415     // Continue only if chatter is not silent
00416     if (chatter != SILENT) {
00417 
00418         // Initialise result string
00419         result = gammalib::fill(" ", indent);
00420 
00421         // Append element to string
00422         result.append("GXmlElement::"+m_name);
00423         for (int k = 0; k < m_attr.size(); ++k) {
00424             result.append(m_attr[k]->print(chatter));
00425         }
00426 
00427         // Append children
00428         for (int i = 0; i < m_nodes.size(); ++i) {
00429             result.append("\n" + m_nodes[i]->print(chatter, indent+g_indent));
00430         }
00431 
00432     } // endif: chatter was not silent
00433 
00434     // Return
00435     return result;
00436 }
00437 
00438 
00439 /*==========================================================================
00440  =                                                                         =
00441  =                             Private methods                             =
00442  =                                                                         =
00443  ==========================================================================*/
00444 
00445 /***********************************************************************//**
00446  * @brief Initialise class members
00447  ***************************************************************************/
00448 void GXmlElement::init_members(void)
00449 {
00450     // Initialise members
00451     m_name.clear();
00452     m_attr.clear();
00453 
00454     // Return
00455     return;
00456 }
00457 
00458 
00459 /***********************************************************************//**
00460  * @brief Copy class members
00461  *
00462  * @param[in] node XML element.
00463  *
00464  * This method copies all class members. XML attributes are cloned.
00465  ***************************************************************************/
00466 void GXmlElement::copy_members(const GXmlElement& node)
00467 {
00468     // Copy members
00469     m_name   = node.m_name;
00470 
00471     // Copy attribute container
00472     m_attr.clear();
00473     for (int i = 0; i < node.m_attr.size(); ++i) {
00474         m_attr.push_back((node.m_attr[i]->clone()));
00475     }
00476 
00477     // Return
00478     return;
00479 }
00480 
00481 
00482 /***********************************************************************//**
00483  * @brief Delete class members
00484  *
00485  * As container classes that hold pointers need to handle themselves the
00486  * proper deallocation of memory, we loop here over all pointers and make
00487  * sure that we deallocate the associated nodes.
00488  ***************************************************************************/
00489 void GXmlElement::free_members(void)
00490 {
00491     // Free attributes
00492     for (int i = 0; i < m_attr.size(); ++i) {
00493         delete m_attr[i];
00494         m_attr[i] = NULL;
00495     }
00496 
00497     // Return
00498     return;
00499 }
00500 
00501 
00502 /***********************************************************************//**
00503  * @brief Parse element start segment string
00504  *
00505  * @param[in] segment Segment string.
00506  *
00507  * @exception GException::xml_syntax_error
00508  *            XML syntax error.
00509  *
00510  * Parse the segment string and set class members based on the information
00511  * that is found. The method also performs syntax checking. It does not
00512  * require brackets to be set.
00513  ***************************************************************************/
00514 void GXmlElement::parse_start(const std::string& segment)
00515 {
00516     // Initialize position check
00517     std::size_t pos_start = 0;
00518 
00519     // Get length of segment
00520     int n = segment.length();
00521 
00522     // Throw an error is segment is empty
00523     if (n < 1) {
00524         throw GException::xml_syntax_error(G_PARSE_START, segment,
00525                           "no element name specified");
00526     }
00527 
00528     // If string starts with brackets then check that the brackets are
00529     // valid comment brackets
00530     if (segment[0] == '<') {
00531         if (n < 2 || (segment.compare(0,1,"<") != 0) ||
00532                      (segment.compare(n-1,1,">") != 0)) {
00533             throw GException::xml_syntax_error(G_PARSE_START, segment,
00534                                                "invalid tag brackets");
00535         }
00536         pos_start = 1;
00537     } // endif: there were brackets
00538 
00539     // Extract element name
00540     std::size_t pos = segment.find_first_of("\x20\x09\x0d\x0a>", 1);
00541     if (pos == pos_start) {
00542         throw GException::xml_syntax_error(G_PARSE_START, segment,
00543                           "no whitespace allowed before element name");
00544     }
00545     if (pos == std::string::npos) {
00546         if (pos_start == 1) {
00547             throw GException::xml_syntax_error(G_PARSE_START, segment,
00548                               "element name not found");
00549         }
00550     }
00551     m_name = segment.substr(pos_start, pos-pos_start);
00552 
00553     // Extract attributes
00554     while (pos != std::string::npos) {
00555         parse_attribute(&pos, segment);
00556     }
00557 
00558     // Return
00559     return;
00560 }
00561 
00562 
00563 /***********************************************************************//**
00564  * @brief Parse element stop segment string
00565  *
00566  * @param[in] segment Segment string.
00567  *
00568  * @exception GException::xml_syntax_error
00569  *            XML syntax error.
00570  *
00571  * Parse the stop segment string and verify the syntax.
00572  ***************************************************************************/
00573 void GXmlElement::parse_stop(const std::string& segment)
00574 {
00575     // Get length of segment
00576     int n = segment.length();
00577 
00578     // Check on existence of brackets
00579     if (n < 3 || (segment.compare(0,2,"</") != 0) ||
00580                  (segment.compare(n-1,1,">") != 0)) {
00581         throw GException::xml_syntax_error(G_PARSE_STOP, segment,
00582                           "incorrect or missing tag brackets");
00583     }
00584 
00585     // Extract and verify element name
00586     size_t pos = segment.find_first_of("\x20\x09\x0d\x0a>", 2);
00587     if (pos == 2) {
00588         throw GException::xml_syntax_error(G_PARSE_STOP, segment,
00589                           "no whitespace allowed after \"</\"");
00590     }
00591     if (pos == std::string::npos) {
00592         throw GException::xml_syntax_error(G_PARSE_STOP, segment,
00593                           "element name not found");
00594     }
00595     std::string name = segment.substr(2, pos-2);
00596     if (name != m_name) {
00597         throw GException::xml_syntax_error(G_PARSE_STOP, segment,
00598                           "invalid name in element stop tag"
00599                           " (found \""+name+"\", expected \""+m_name+"\"");
00600     }
00601 
00602     // Verify that no further characters exist in element stop tag
00603     size_t pos2 = segment.find_first_of("\x20\x09\x0d\x0a>", pos);
00604     if (pos2 != n-1) {
00605         throw GException::xml_syntax_error(G_PARSE_STOP, segment,
00606                           "invalid characters found after element name");
00607     }
00608 
00609     // Return
00610     return;
00611 }
00612 
00613 
00614 /***********************************************************************//**
00615  * @brief Parse element attribute
00616  *
00617  * @param[in] pos Start position in string.
00618  * @param[in] segment Segment string.
00619  *
00620  * @exception GException::xml_syntax_error
00621  *            XML syntax error.
00622  *
00623  * Parse the segment string for one attribute, and if attribute was found,
00624  * attach it to element.
00625  *
00626  * @todo Verify XML validity of attribute name and value
00627  ***************************************************************************/
00628 void GXmlElement::parse_attribute(size_t* pos, const std::string& segment)
00629 {
00630     // Main loop
00631     do {
00632         // Get substring for error message
00633         std::string error = segment.substr(*pos, segment.length()-*pos);
00634 
00635         // Find first character of name substring
00636         std::size_t pos_name_start = segment.find_first_not_of("\x20\x09\x0d\x0a/>?", *pos);
00637         if (pos_name_start == std::string::npos) {
00638             *pos = std::string::npos;
00639             continue;
00640         }
00641 
00642         // Find end of name substring
00643         std::size_t pos_name_end = segment.find_first_of("\x20\x09\x0d\x0a=", pos_name_start);
00644         if (pos_name_end == std::string::npos) {
00645             throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00646                               "invalid or missing attribute name");
00647         }
00648 
00649         // Find '=' character
00650         std::size_t pos_equal = segment.find_first_of("=", pos_name_end);
00651         if (pos_equal == std::string::npos) {
00652             throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00653                               "\"=\" sign not found for attribute");
00654         }
00655 
00656         // Find start of value substring
00657         std::size_t pos_value_start = segment.find_first_of("\x22\x27", pos_equal);
00658         if (pos_value_start == std::string::npos) {
00659             throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00660                               "invalid or missing attribute value start hyphen");
00661         }
00662 
00663         // Save hyphen character and step forward one character
00664         std::string hyphen = segment.substr(pos_value_start, 1);
00665         pos_value_start++;
00666         if (pos_value_start >= segment.length()) {
00667             throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00668                               "invalid or missing attribute value");
00669         }
00670 
00671         // Find end of value substring
00672         std::size_t pos_value_end = segment.find_first_of(hyphen, pos_value_start);
00673         if (pos_value_end == std::string::npos) {
00674             throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00675                               "invalid or missing attribute value end hyphen");
00676         }
00677 
00678         // Get name substring
00679         std::size_t n_name = pos_name_end - pos_name_start;
00680         if (n_name < 1) {
00681             throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00682                               "invalid or missing attribute name");
00683         }
00684         std::string name = segment.substr(pos_name_start, n_name);
00685 
00686         //@todo Check XML validity of attribute name
00687 
00688         // Get value substring length
00689         std::size_t n_value = pos_value_end - pos_value_start;
00690         //if (n_value < 0) {
00691         //    throw GException::xml_syntax_error(G_PARSE_ATTRIBUTE, error,
00692         //                      "invalid or missing attribute value");
00693         //}
00694         std::string value = segment.substr(pos_value_start-1, n_value+2);
00695 
00696         //@todo Check XML validity of attribute value
00697 
00698         // Allocate, set and append new attribute to element
00699         GXmlAttribute* attr  = new GXmlAttribute(name, value);
00700         m_attr.push_back(attr);
00701 
00702         // Update segment pointer
00703         pos_value_end++;
00704         if (pos_value_end >= segment.length()) {
00705             pos_value_end = std::string::npos;
00706         }
00707         *pos = pos_value_end;
00708 
00709     } while (0);
00710 
00711     // Return
00712     return;
00713 }

Generated on Tue Jan 24 12:37:25 2017 for GammaLib by  doxygen 1.4.7