/*************************************************************************** * GXml.cpp - XML class implementation * * ----------------------------------------------------------------------- * * copyright (C) 2010-2012 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GXml.cpp * @brief XML class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "GXml.hpp" #include "GXmlNode.hpp" #include "GXmlDocument.hpp" #include "GXmlText.hpp" #include "GXmlElement.hpp" #include "GXmlComment.hpp" #include "GXmlPI.hpp" #include "GException.hpp" #include "GTools.hpp" /* __ Method name definitions ____________________________________________ */ #define G_LOAD "GXml::load(std::string&)" #define G_SAVE "GXml::load(std::string&)" #define G_PARSE "GXml::parse(FILE*)" #define G_PROCESS "GXml::process(GXmlNode*, const std::string&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GXml::GXml(void) { // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] xml XML object. ***************************************************************************/ GXml::GXml(const GXml& xml) { // Initialise members init_members(); // Copy members copy_members(xml); // Return return; } /***********************************************************************//** * @brief Load constructor * * @param[in] filename XML file name. ***************************************************************************/ GXml::GXml(const std::string& filename) { // Initialise members init_members(); // Load XML file load(filename); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GXml::~GXml(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] xml XML object. ***************************************************************************/ GXml& GXml::operator= (const GXml& xml) { // Execute only if object is not identical if (this != &xml) { // Free members free_members(); // Initialise members init_members(); // Copy members copy_members(xml); } // endif: object was not identical // Return return *this; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear object. * * Reset object to a clean initial state. ***************************************************************************/ void GXml::clear(void) { // Free memory and initialise members free_members(); init_members(); // Return return; } /***********************************************************************//** * @brief Clone object ***************************************************************************/ GXml* GXml::clone(void) const { // Clone this image return new GXml(*this); } /***********************************************************************//** * @brief Append child node to XML document. * * @param[in] node Child node. ***************************************************************************/ void GXml::append(GXmlNode* node) { // Append node m_root.append(node); // Return return; } /***********************************************************************//** * @brief Load XML file. * * @param[in] filename Name of file to be loaded. * * @exception GException::file_not_found * XML file not found. * @exception GException::file_open_error * Unable to open XML file (read access requested). * * Loads XML file by reading all lines from the XML file. * Any environment variable present in the filename will be expanded. ***************************************************************************/ void GXml::load(const std::string& filename) { // Clear object clear(); // Expand environment variables std::string fname = expand_env(filename); // Check if file exists if (!file_exists(fname)) throw GException::file_not_found(G_LOAD, fname); // Open parameter file FILE* fptr = fopen(fname.c_str(), "r"); if (fptr == NULL) throw GException::file_open_error(G_LOAD, fname); // Parse file parse(fptr); // Close file fclose(fptr); // Return return; } /***********************************************************************//** * @brief Save XML file. * * @param[in] filename Name of file to be saved. * * @exception GException::file_open_error * Unable to open XML file (write access requested). * * Save XML document into file. ***************************************************************************/ void GXml::save(const std::string& filename) { // Open parameter file FILE* fptr = fopen(filename.c_str(), "w"); if (fptr == NULL) throw GException::file_open_error(G_SAVE, filename); // Write XML document m_root.write(fptr); // Close file fclose(fptr); // Return return; } /***********************************************************************//** * @brief Return number of children in document root ***************************************************************************/ int GXml::children(void) const { // Return number of document root children return m_root.children(); } /***********************************************************************//** * @brief Return pointer on document child node * * @param[in] index Index of node (0,1,2,...) ***************************************************************************/ GXmlNode* GXml::child(int index) const { // Return pointer return m_root.child(index); } /***********************************************************************//** * @brief Return number of child elements in document root ***************************************************************************/ int GXml::elements(void) const { // Return number return m_root.elements(); } /***********************************************************************//** * @brief Return number of child elements of given name in document root * * @param[in] name Name of child elements. ***************************************************************************/ int GXml::elements(const std::string& name) const { // Return number return m_root.elements(name); } /***********************************************************************//** * @brief Return pointer on child element * * @param[in] index Index of child element (0,1,2,...) ***************************************************************************/ GXmlElement* GXml::element(int index) const { // Return pointer return (static_cast(m_root.element(index))); } /***********************************************************************//** * @brief Return pointer on child element of a given name * * @param[in] name Name of child elements. * @param[in] index Index of child element (0,1,2,...) ***************************************************************************/ GXmlElement* GXml::element(const std::string& name, int index) const { // Return pointer return (static_cast(m_root.element(name, index))); } /***********************************************************************//** * @brief Print XML object ***************************************************************************/ std::string GXml::print(int indent) const { // Initialise result string std::string result; // Append header result.append("=== GXml ==="); // Append model result.append("\n"+m_root.print(0)); // Return result return result; } /***********************************************************************//** * @brief Print XML object ***************************************************************************/ std::string GXml::print(void) const { // Initialise result string std::string result = print(0); // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GXml::init_members(void) { // Initialise members m_root.clear(); // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] xml Object from which members which should be copied. ***************************************************************************/ void GXml::copy_members(const GXml& xml) { // Copy attributes m_root = xml.m_root; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GXml::free_members(void) { // Return return; } /***********************************************************************//** * @brief Parse XML file * * @param[in] fptr Pointer to file to be parsed. * * @exception GException::xml_syntax_error * XML syntax error. * * Parse the XML file and add nodes corresponding to the content to the * object. The XML file is split into segments, made either of text or of * tags. ***************************************************************************/ void GXml::parse(FILE* fptr) { // Initialise parser int c; bool in_markup = false; bool in_comment = false; std::string segment; GXmlNode* current = &m_root; // Main parsing loop while ((c = fgetc(fptr)) != EOF) { // Convert special characters into line feeds if (c == '\x85' || c == L'\x2028') { if (in_markup) throw GException::xml_syntax_error(G_PARSE, segment, "invalid character encountered"); else c = '\x0a'; } // If we are not within a markup and if a markup is reached then add // the text segment to the nodes and switch to in_markup mode if (in_markup == false) { // Markup start reached? if (c == '<') { // Add text segment to nodes (ignores empty segments) process_text(¤t, segment); // Prepare new segment and signal that we are within tag segment.clear(); segment.append(1, (char)c); in_markup = true; } // Markup stop encountered? else if (c == '>') { segment.append(1, (char)c); throw GException::xml_syntax_error(G_PARSE, segment, "unexpected closing bracket \">\" encountered"); } // ... otherwise add character to segment else segment.append(1, (char)c); } // If we are within a markup and if a markup end is reached then process // the markup and switch to not in_tag mode else { // Markup stop reached? if (c == '>') { // Append character to segment segment.append(1, (char)c); // If we are in comment then check if this is the end of // the comment if (in_comment) { int n = segment.length(); if (n > 2) { if (segment.compare(n-3,3,"-->") == 0) in_comment = false; } } // If we are not in the comment, then process markup if (!in_comment) { // Process markup process_markup(¤t, segment); // Prepare new segment and signal that we are not // within markup segment.clear(); in_markup = false; } } // Markup start encountered? else if (!in_comment && c == '<') { // Append character to segment segment.append(1, (char)c); // If we encounter an opening bracket then throw an exception throw GException::xml_syntax_error(G_PARSE, segment, "unexpected opening bracket \"<\" encountered"); } // ... otherwise add character to segment else { segment.append(1, (char)c); if (!in_comment && segment == "") == 0)) type = MT_COMMENT; // Check for declaration else if (n >= 7 && (segment.compare(0,6,"") == 0)) type = MT_DECLARATION; // Check for processing instruction else if (n >= 4 && (segment.compare(0,2,"") == 0)) type = MT_PROCESSING; // Check for empty element tag else if (n >= 3 && (segment.compare(0,1,"<") == 0) && (segment.compare(n-2,2,"/>") == 0)) type = MT_ELEMENT_EMPTY; // Check for element end tag else if (n >= 3 && (segment.compare(0,2,"") == 0)) type = MT_ELEMENT_END; // Check for element start tag else if (n >= 2 && (segment.compare(0,1,"<") == 0) && (segment.compare(n-1,1,">") == 0)) type = MT_ELEMENT_START; // Return type return type; }