00001 /*************************************************************************** 00002 * GXml.cpp - XML 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 GXml.cpp 00023 * @brief XML 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 "GUrlFile.hpp" 00035 #include "GUrlString.hpp" 00036 #include "GXml.hpp" 00037 #include "GXmlNode.hpp" 00038 #include "GXmlDocument.hpp" 00039 #include "GXmlText.hpp" 00040 #include "GXmlElement.hpp" 00041 #include "GXmlComment.hpp" 00042 #include "GXmlPI.hpp" 00043 00044 /* __ Method name definitions ____________________________________________ */ 00045 #define G_LOAD "GXml::load(std::string&)" 00046 #define G_PARSE "GXml::parse(GUrl&)" 00047 #define G_PROCESS "GXml::process(GXmlNode*, const 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 GXml::GXml(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] xml XML object. 00079 ***************************************************************************/ 00080 GXml::GXml(const GXml& xml) 00081 { 00082 // Initialise members 00083 init_members(); 00084 00085 // Copy members 00086 copy_members(xml); 00087 00088 // Return 00089 return; 00090 } 00091 00092 00093 /***********************************************************************//** 00094 * @brief XML document constructor 00095 * 00096 * @param[in] xml XML text string or file name. 00097 * 00098 * Constructs GXml object by either parsing a text string or a file. If the 00099 * @p xml argument starts with @p <?xml it is interpreted as a XML file and 00100 * parsed directly. Otherwise the constructor will interpret @p xml as a 00101 * filename, and opens the file for parsing. 00102 ***************************************************************************/ 00103 GXml::GXml(const std::string& xml) 00104 { 00105 // Initialise members 00106 init_members(); 00107 00108 // If the string is an XML text then parse it directly 00109 if (xml.compare(0, 5, "<?xml") == 0) { 00110 GUrlString url(xml); 00111 read(url); 00112 url.close(); 00113 } 00114 00115 // ... otherwise interpret the string as a filename 00116 else { 00117 load(xml); 00118 } 00119 00120 // Return 00121 return; 00122 } 00123 00124 00125 /***********************************************************************//** 00126 * @brief Destructor 00127 ***************************************************************************/ 00128 GXml::~GXml(void) 00129 { 00130 // Free members 00131 free_members(); 00132 00133 // Return 00134 return; 00135 } 00136 00137 00138 /*========================================================================== 00139 = = 00140 = Operators = 00141 = = 00142 ==========================================================================*/ 00143 00144 /***********************************************************************//** 00145 * @brief Assignment operator 00146 * 00147 * @param[in] xml XML object. 00148 * @return XML object. 00149 ***************************************************************************/ 00150 GXml& GXml::operator=(const GXml& xml) 00151 { 00152 // Execute only if object is not identical 00153 if (this != &xml) { 00154 00155 // Free members 00156 free_members(); 00157 00158 // Initialise members 00159 init_members(); 00160 00161 // Copy members 00162 copy_members(xml); 00163 00164 } // endif: object was not identical 00165 00166 // Return 00167 return *this; 00168 } 00169 00170 00171 /***********************************************************************//** 00172 * @brief Return pointer to child of XML document root element 00173 * 00174 * @param[in] index Node index [0,...,size()-1]. 00175 * @return Pointer to child of XML document root element. 00176 * 00177 * Returns a pointer to the child number @p index of the XML document root 00178 * element. An exception will be thrown if the @p index is not valid. 00179 ***************************************************************************/ 00180 GXmlNode* GXml::operator[](const int& index) 00181 { 00182 // Return pointer 00183 return m_root[index]; 00184 } 00185 00186 00187 /***********************************************************************//** 00188 * @brief Return pointer to child of XML document root element (const variant) 00189 * 00190 * @param[in] index Node index [0,...,size()-1]. 00191 * @return Pointer to child of XML document root element. 00192 * 00193 * Returns a pointer to the child number @p index of the XML document root 00194 * element. An exception will be thrown if the @p index is not valid. 00195 ***************************************************************************/ 00196 const GXmlNode* GXml::operator[](const int& index) const 00197 { 00198 // Return pointer 00199 return m_root[index]; 00200 } 00201 00202 00203 /*========================================================================== 00204 = = 00205 = Public methods = 00206 = = 00207 ==========================================================================*/ 00208 00209 /***********************************************************************//** 00210 * @brief Clear XML object 00211 * 00212 * Resets XML object to a clean initial state. 00213 ***************************************************************************/ 00214 void GXml::clear(void) 00215 { 00216 // Free memory and initialise members 00217 free_members(); 00218 init_members(); 00219 00220 // Return 00221 return; 00222 } 00223 00224 00225 /***********************************************************************//** 00226 * @brief Clone XML object 00227 * 00228 * @return Pointer to deep copy of XML object. 00229 ***************************************************************************/ 00230 GXml* GXml::clone(void) const 00231 { 00232 // Clone object 00233 return new GXml(*this); 00234 } 00235 00236 00237 /***********************************************************************//** 00238 * @brief Set child node in XML document root 00239 * 00240 * @param[in] index Child node index [0,...,size()-1]. 00241 * @param[in] node XML child node. 00242 * @return Pointer to deep copy of child node. 00243 * 00244 * Set @p node with @p index of XML document root. 00245 ***************************************************************************/ 00246 GXmlNode* GXml::set(const int& index, const GXmlNode& node) 00247 { 00248 // Set node and return pointer 00249 return (m_root.set(index, node)); 00250 } 00251 00252 00253 /***********************************************************************//** 00254 * @brief Append child node to XML document root 00255 * 00256 * @param[in] node Child node. 00257 * @return Pointer to appended child node. 00258 * 00259 * Appends node to XML document root by making a deep copy of the @p node. 00260 ***************************************************************************/ 00261 GXmlNode* GXml::append(const GXmlNode& node) 00262 { 00263 // Append node and return pointer 00264 return (m_root.append(node)); 00265 } 00266 00267 00268 /***********************************************************************//** 00269 * @brief Append child node to XML document root 00270 * 00271 * @param[in] segment XML child node. 00272 * @return Pointer to appended child node. 00273 * 00274 * Appends XML element that is constructed from a text @p segment. The text 00275 * segment is parsed and the element name and attributes are extracted using 00276 * the GXmlElement::parse_start() method. The method returns a pointer to the 00277 * XML element child node that has been appended. 00278 ***************************************************************************/ 00279 GXmlElement* GXml::append(const std::string& segment) 00280 { 00281 // Append node and return pointer 00282 return (m_root.append(segment)); 00283 } 00284 00285 00286 /***********************************************************************//** 00287 * @brief Insert child node into XML document root 00288 * 00289 * @param[in] index Child node index [0,...,size()-1]. 00290 * @param[in] node XML child node. 00291 * @return Pointer to inserted child node. 00292 * 00293 * Inserts the XML child @p node before the node with the specified @p index. 00294 * A deep copy of the node will be made and the pointer to this node will be 00295 * stored. 00296 ***************************************************************************/ 00297 GXmlNode* GXml::insert(const int& index, const GXmlNode& node) 00298 { 00299 // Insert node and return pointer 00300 return (m_root.insert(index, node)); 00301 } 00302 00303 00304 /***********************************************************************//** 00305 * @brief Remove child node from XML document root 00306 * 00307 * @param[in] index Child node index [0,...,size()-1]. 00308 * 00309 * Remove XML child node at @p index from the XML document root. 00310 ***************************************************************************/ 00311 void GXml::remove(const int& index) 00312 { 00313 // Remove node 00314 m_root.remove(index); 00315 00316 // Return 00317 return; 00318 } 00319 00320 00321 /***********************************************************************//** 00322 * @brief Reserve space for child nodes in XML document root 00323 * 00324 * @param[in] num Number of nodes. 00325 * 00326 * Reserves space for @p num nodes in the XML document root. 00327 ***************************************************************************/ 00328 void GXml::reserve(const int& num) 00329 { 00330 // Reservers space node 00331 m_root.reserve(num); 00332 00333 // Return 00334 return; 00335 } 00336 00337 00338 /***********************************************************************//** 00339 * @brief Append all XML child nodes from another XML node in the XML 00340 * document root 00341 * 00342 * @param[in] node XML child node. 00343 * 00344 * Append all XML child nodes found in @p node to the XML document root. 00345 * Nodes are copied deeply so that they live now on their on in the actual 00346 * object. 00347 ***************************************************************************/ 00348 void GXml::extend(const GXmlNode& node) 00349 { 00350 // Extend node 00351 m_root.extend(node); 00352 00353 // Return 00354 return; 00355 } 00356 00357 00358 /***********************************************************************//** 00359 * @brief Return number of child elements in XML document root 00360 * 00361 * @return Number of child elements in XML document root. 00362 * 00363 * Returns the number of GXmlElement child elements of the XML document root. 00364 * GXMLElement child elements are nodes of type NT_ELEMENT. 00365 ***************************************************************************/ 00366 int GXml::elements(void) const 00367 { 00368 // Return number 00369 return m_root.elements(); 00370 } 00371 00372 00373 /***********************************************************************//** 00374 * @brief Return number of child elements with a given name in XML 00375 * document root 00376 * 00377 * @param[in] name Name of child elements. 00378 * @return Number of child elements with a given @p name in XML 00379 * document root. 00380 * 00381 * Returns the number of GXMLElement child elements of the XML document root 00382 * that have a given @p name. GXmlElement child elements are nodes of type 00383 * NT_ELEMENT. 00384 ***************************************************************************/ 00385 int GXml::elements(const std::string& name) const 00386 { 00387 // Return number 00388 return m_root.elements(name); 00389 } 00390 00391 00392 /***********************************************************************//** 00393 * @brief Return pointer to child element 00394 * 00395 * @param[in] index Node index [0,...,elements()-1]. 00396 * @return Pointer to child element. 00397 * 00398 * Returns a pointer to the child number @p index of the XML document root. 00399 * An exception will be thrown if the @p index is not valid. 00400 ***************************************************************************/ 00401 GXmlElement* GXml::element(const int& index) 00402 { 00403 // Return pointer 00404 return m_root.element(index); 00405 } 00406 00407 00408 /***********************************************************************//** 00409 * @brief Return pointer to child element (const variant) 00410 * 00411 * @param[in] index Node index [0,...,elements()-1]. 00412 * @return Pointer to child element. 00413 * 00414 * Returns a pointer to the child number @p index of the XML document root. 00415 * An exception will be thrown if the @p index is not valid. 00416 ***************************************************************************/ 00417 const GXmlElement* GXml::element(const int& index) const 00418 { 00419 // Return pointer 00420 return m_root.element(index); 00421 } 00422 00423 00424 /***********************************************************************//** 00425 * @brief Return pointer to child element by hierarchy 00426 * 00427 * @param[in] name Child element hierarchy. 00428 * @return Pointer to child element. 00429 * 00430 * Returns a pointer to the child element described by a hierarchy of the 00431 * following syntax 00432 * 00433 * params > param[1] > value > struct 00434 * 00435 * The > symbols indicate subsequent hierarchy levels, the square brackets 00436 * provides the index in case that multiple tags with the same name exist 00437 * at a given hierarchy level. Omitting the index means that the first 00438 * tag with the specified name is accessed. 00439 ***************************************************************************/ 00440 GXmlElement* GXml::element(const std::string& name) 00441 { 00442 // Return pointer 00443 return m_root.element(name); 00444 } 00445 00446 00447 /***********************************************************************//** 00448 * @brief Return pointer to child element by hierarchy (const version) 00449 * 00450 * @param[in] name Child element hierarchy. 00451 * @return Pointer to child element. 00452 * 00453 * Returns a pointer to the child element described by a hierarchy of the 00454 * following syntax 00455 * 00456 * params > param[1] > value > struct 00457 * 00458 * The > symbols indicate subsequent hierarchy levels, the square brackets 00459 * provides the index in case that multiple tags with the same name exist 00460 * at a given hierarchy level. Omitting the index means that the first 00461 * tag with the specified name is accessed. 00462 ***************************************************************************/ 00463 const GXmlElement* GXml::element(const std::string& name) const 00464 { 00465 // Return pointer 00466 return m_root.element(name); 00467 } 00468 00469 00470 /***********************************************************************//** 00471 * @brief Return pointer to child element of a given name 00472 * 00473 * @param[in] name Name of child element. 00474 * @param[in] index Node index [0,...,elements()-1]. 00475 * @return Pointer to child element. 00476 * 00477 * Returns a pointer to the child number @p index with @p name of the XML 00478 * document root. An exception will be thrown if the @p index is not valid. 00479 ***************************************************************************/ 00480 GXmlElement* GXml::element(const std::string& name, const int& index) 00481 { 00482 // Return pointer 00483 return m_root.element(name, index); 00484 } 00485 00486 00487 /***********************************************************************//** 00488 * @brief Return pointer to child element of a given name (const variant) 00489 * 00490 * @param[in] name Name of child element. 00491 * @param[in] index Node index [0,...,elements()-1]. 00492 * @return Pointer to child element. 00493 * 00494 * Returns a pointer to the child number @p index with @p name of the XML 00495 * document root. An exception will be thrown if the @p index is not valid. 00496 ***************************************************************************/ 00497 const GXmlElement* GXml::element(const std::string& name, const int& index) const 00498 { 00499 // Return pointer 00500 return m_root.element(name, index); 00501 } 00502 00503 00504 /***********************************************************************//** 00505 * @brief Load XML document from file 00506 * 00507 * @param[in] filename File name. 00508 * 00509 * Loads a XML document from a file by reading from the file's Unified 00510 * Resource Locator (URL). The read() method is invoked for this purpose. 00511 * 00512 * The method uses the GUrlFile file opening constructor to open the URL. 00513 * This constructor will automatically expand any environment variables that 00514 * are present in the filename. 00515 * 00516 * @todo Ideally, we would like to extract the URL type from the filename 00517 * so that any kind of URL can be used for loading. 00518 ***************************************************************************/ 00519 void GXml::load(const GFilename& filename) 00520 { 00521 // Throw an exception if file does not exist 00522 if (!filename.exists()) { 00523 throw GException::file_open_error(G_LOAD, filename); 00524 } 00525 00526 // Open XML URL as file for reading 00527 GUrlFile url(filename.url().c_str(), "r"); 00528 00529 // Read XML document from URL 00530 read(url); 00531 00532 // Close URL 00533 url.close(); 00534 00535 // Store filename in XML document 00536 m_root.filename(filename); 00537 00538 // Return 00539 return; 00540 } 00541 00542 00543 /***********************************************************************//** 00544 * @brief Save XML document into file 00545 * 00546 * @param[in] filename File name. 00547 * 00548 * Saves the XML document into a file by writing into the file's Unified 00549 * Resource Locator (URL). The write() method is invoked for this purpose. 00550 * 00551 * The method uses the GUrlFile file opening constructor to open the URL. 00552 * This constructor will automatically expand any environment variables that 00553 * are present in the filename. 00554 * 00555 * @todo Ideally, we would like to extract the URL type from the filename 00556 * so that any kind of URL can be used for loading. 00557 ***************************************************************************/ 00558 void GXml::save(const GFilename& filename) const 00559 { 00560 // Open XML file for writing 00561 GUrlFile url(filename.url().c_str(), "w"); 00562 00563 // Store filename in XML document (circumvent const correctness) 00564 const_cast<GXmlDocument*>(&m_root)->filename(filename); 00565 00566 // Write XML document 00567 write(url, 0); 00568 00569 // Close file 00570 url.close(); 00571 00572 // Return 00573 return; 00574 } 00575 00576 00577 /***********************************************************************//** 00578 * @brief Read XML document from URL 00579 * 00580 * @param[in] url Unified Resource Locator. 00581 * 00582 * Reads in the XML document by parsing a Unified Resource Locator of any 00583 * type. 00584 ***************************************************************************/ 00585 void GXml::read(const GUrl& url) 00586 { 00587 // Clear object 00588 clear(); 00589 00590 // Parse URL 00591 parse(url); 00592 00593 // Return 00594 return; 00595 } 00596 00597 00598 /***********************************************************************//** 00599 * @brief Write XML document into URL 00600 * 00601 * @param[in] url Unified Resource Locator. 00602 * @param[in] indent Indentation (default = 0). 00603 * 00604 * Writes the XML document in a Unified Resource Locator. Formatting of the 00605 * document can be adapted using the @p indent parameter. 00606 ***************************************************************************/ 00607 void GXml::write(GUrl& url, const int& indent) const 00608 { 00609 // Write XML document 00610 m_root.write(url); 00611 00612 // Return 00613 return; 00614 } 00615 00616 00617 /***********************************************************************//** 00618 * @brief Print XML object 00619 * 00620 * @param[in] chatter Chattiness. 00621 * @param[in] indent Text indentation. 00622 * @return String containing XML object. 00623 ***************************************************************************/ 00624 std::string GXml::print(const GChatter& chatter, const int& indent) const 00625 { 00626 // Initialise result string 00627 std::string result; 00628 00629 // Continue only if chatter is not silent 00630 if (chatter != SILENT) { 00631 00632 // Append header 00633 result.append("=== GXml ==="); 00634 00635 // Append model 00636 result.append("\n"+m_root.print(chatter, 0)); 00637 00638 } // endif: chatter was not silent 00639 00640 // Return result 00641 return result; 00642 } 00643 00644 00645 /***********************************************************************//** 00646 * @brief Print XML object 00647 * 00648 * @param[in] chatter Chattiness. 00649 * @return String containing XML object. 00650 ***************************************************************************/ 00651 std::string GXml::print(const GChatter& chatter) const 00652 { 00653 // Set result string 00654 std::string result = print(chatter, 0); 00655 00656 // Return result 00657 return result; 00658 } 00659 00660 00661 /*========================================================================== 00662 = = 00663 = Private methods = 00664 = = 00665 ==========================================================================*/ 00666 00667 /***********************************************************************//** 00668 * @brief Initialise class members 00669 ***************************************************************************/ 00670 void GXml::init_members(void) 00671 { 00672 // Initialise members 00673 m_root.clear(); 00674 00675 // Return 00676 return; 00677 } 00678 00679 00680 /***********************************************************************//** 00681 * @brief Copy class members 00682 * 00683 * @param[in] xml Object from which members which should be copied. 00684 ***************************************************************************/ 00685 void GXml::copy_members(const GXml& xml) 00686 { 00687 // Copy attributes 00688 m_root = xml.m_root; 00689 00690 // Return 00691 return; 00692 } 00693 00694 00695 /***********************************************************************//** 00696 * @brief Delete class members 00697 ***************************************************************************/ 00698 void GXml::free_members(void) 00699 { 00700 // Return 00701 return; 00702 } 00703 00704 00705 /***********************************************************************//** 00706 * @brief Parse XML URL 00707 * 00708 * @param[in] url Unified Resource Locator. 00709 * 00710 * @exception GException::xml_syntax_error 00711 * XML syntax error. 00712 * 00713 * Parses either a XML file or a XML text string and creates all associated 00714 * nodes. The XML file is split into segments, made either of text or of 00715 * tags. 00716 ***************************************************************************/ 00717 void GXml::parse(const GUrl& url) 00718 { 00719 // Initialise parser 00720 int c; 00721 bool in_markup = false; 00722 bool in_comment = false; 00723 std::string segment; 00724 GXmlNode* current = &m_root; 00725 00726 // Main parsing loop 00727 while ((c = url.get_char()) != EOF) { 00728 00729 // Convert special characters into line feeds 00730 if (c == '\x85' || c == L'\x2028') { 00731 if (in_markup) { 00732 throw GException::xml_syntax_error(G_PARSE, segment, 00733 "invalid character encountered"); 00734 } 00735 else { 00736 c = '\x0a'; 00737 } 00738 } 00739 00740 // Skip all linefeeds (to avoid extra linefeeds in text segments) 00741 if (c == '\x0a') { 00742 continue; 00743 } 00744 00745 // If we are not within a markup and if a markup is reached then 00746 // add the text segment to the nodes and switch to in_markup mode 00747 if (in_markup == false) { 00748 00749 // Markup start reached? 00750 if (c == '<') { 00751 00752 // Add text segment to nodes (ignores empty segments) 00753 process_text(¤t, segment); 00754 00755 // Prepare new segment and signal that we are within tag 00756 segment.clear(); 00757 segment.append(1, (char)c); 00758 in_markup = true; 00759 00760 } 00761 00762 // Markup stop encountered? 00763 else if (c == '>') { 00764 segment.append(1, (char)c); 00765 throw GException::xml_syntax_error(G_PARSE, segment, 00766 "unexpected closing bracket \">\" encountered"); 00767 } 00768 00769 // ... otherwise add character to segment 00770 else { 00771 segment.append(1, (char)c); 00772 } 00773 } 00774 00775 // If we are within a markup and if a markup end is reached then 00776 // process the markup and switch to not in_tag mode 00777 else { 00778 00779 // Markup stop reached? 00780 if (c == '>') { 00781 00782 // Append character to segment 00783 segment.append(1, (char)c); 00784 00785 // If we are in comment then check if this is the end of 00786 // the comment 00787 if (in_comment) { 00788 int n = segment.length(); 00789 if (n > 2) { 00790 if (segment.compare(n-3,3,"-->") == 0) { 00791 in_comment = false; 00792 } 00793 } 00794 } 00795 00796 // If we are not in the comment, then process markup 00797 if (!in_comment) { 00798 00799 // Process markup 00800 process_markup(¤t, segment); 00801 00802 // Prepare new segment and signal that we are not 00803 // within markup 00804 segment.clear(); 00805 in_markup = false; 00806 } 00807 } 00808 00809 // Markup start encountered? 00810 else if (!in_comment && c == '<') { 00811 00812 // Append character to segment 00813 segment.append(1, (char)c); 00814 00815 // If we encounter an opening bracket then throw an exception 00816 throw GException::xml_syntax_error(G_PARSE, segment, 00817 "unexpected opening bracket \"<\" encountered"); 00818 } 00819 00820 // ... otherwise add character to segment 00821 else { 00822 segment.append(1, (char)c); 00823 if (!in_comment && segment == "<!--") { 00824 in_comment = true; 00825 } 00826 } 00827 } 00828 00829 } // endwhile: main parsing loop 00830 00831 // Process any pending segment 00832 if (segment.size() > 0) { 00833 if (in_markup) { 00834 process_markup(¤t, segment); 00835 } 00836 else { 00837 process_text(¤t, segment); 00838 } 00839 } 00840 00841 // Verify that we are back to the root node 00842 if (current != &m_root) { 00843 std::string message = "closing tag "; 00844 GXmlElement* element = dynamic_cast<GXmlElement*>(current); 00845 if (element != NULL) { 00846 message += "for GXmlElement \""+element->name()+"\""; 00847 } 00848 message += " is missing"; 00849 throw GException::xml_syntax_error(G_PARSE, "", message); 00850 } 00851 00852 // Return 00853 return; 00854 } 00855 00856 00857 /***********************************************************************//** 00858 * @brief Process markup segment 00859 * 00860 * @param[in] current Handle to current node. 00861 * @param[in] segment Segment string. 00862 * 00863 * Process markup segment. 00864 ***************************************************************************/ 00865 void GXml::process_markup(GXmlNode** current, const std::string& segment) 00866 { 00867 // Determine segment tag type 00868 MarkupType type = get_markuptype(segment); 00869 00870 // Do tag specific processing 00871 switch (type) { 00872 00873 // Handle element start tag 00874 case MT_ELEMENT_START: 00875 { 00876 // Create new element node, set its parent, append it to the 00877 // current node and make it the current node 00878 GXmlElement element(segment); 00879 element.parent(*current); 00880 (*current)->append(element); 00881 int last = (*current)->size() - 1; 00882 (*current) = (*(*current))[last]; 00883 } 00884 break; 00885 00886 // Handle element end tag 00887 case MT_ELEMENT_END: 00888 { 00889 // Check if we expect an element end tag 00890 if ((*current)->type() != GXmlNode::NT_ELEMENT) { 00891 throw GException::xml_syntax_error(G_PROCESS, segment, 00892 "unexpected element end tag encountered"); 00893 } 00894 00895 // Check if we have the correct end tag 00896 GXmlElement* element = (GXmlElement*)(*current); 00897 element->parse_stop(segment); 00898 00899 // Set current node pointer back to parent of the current node 00900 (*current) = element->parent(); 00901 } 00902 break; 00903 00904 // Append empty-element tag 00905 case MT_ELEMENT_EMPTY: 00906 { 00907 GXmlElement element(segment.substr(1,segment.length()-3)); 00908 element.parent(*current); 00909 (*current)->append(element); 00910 } 00911 break; 00912 00913 // Append comment markup 00914 case MT_COMMENT: 00915 { 00916 // Create a new comment node, set its parent and append it to the 00917 // current node 00918 GXmlComment comment(segment); 00919 comment.parent(*current); 00920 (*current)->append(comment); 00921 } 00922 break; 00923 00924 // Declaration markup 00925 case MT_DECLARATION: 00926 { 00927 // Verify if declaration tag is allowed 00928 if (*current != &m_root) { 00929 throw GException::xml_syntax_error(G_PROCESS, segment, 00930 "unexpected declaration markup encountered"); 00931 } 00932 if (!m_root.is_empty()) { 00933 throw GException::xml_syntax_error(G_PROCESS, segment, 00934 "declaration markup only allowed in first line"); 00935 } 00936 00937 // Create temporary element to read in declaration attributes 00938 GXmlElement* element = new GXmlElement(segment); 00939 size_t pos = 5; 00940 while (pos != std::string::npos) { 00941 element->parse_attribute(&pos, segment); 00942 } 00943 00944 // Set attribute values 00945 std::string version = element->attribute("version"); 00946 std::string encoding = element->attribute("encoding"); 00947 std::string standalone = element->attribute("standalone"); 00948 if (version.length() > 0) { 00949 m_root.version(version); 00950 } 00951 if (encoding.length() > 0) { 00952 m_root.encoding(encoding); 00953 } 00954 if (standalone.length() > 0) { 00955 m_root.standalone(standalone); 00956 } 00957 00958 // Delete temporary element 00959 delete element; 00960 } 00961 break; 00962 00963 // Processing tag 00964 case MT_PROCESSING: 00965 { 00966 // Create a new PI node, set its parent and append it to the 00967 // current node 00968 GXmlPI pi(segment); 00969 pi.parent(*current); 00970 (*current)->append(pi); 00971 } 00972 break; 00973 00974 // Invalid tag, throw an error 00975 case MT_INVALID: 00976 throw GException::xml_syntax_error(G_PROCESS, segment, "invalid tag"); 00977 break; 00978 } 00979 00980 // Return 00981 return; 00982 } 00983 00984 00985 /***********************************************************************//** 00986 * @brief Process text segment 00987 * 00988 * @param[in] current Handle to current node. 00989 * @param[in] segment Segment string. 00990 * 00991 * Process text segment. 00992 ***************************************************************************/ 00993 void GXml::process_text(GXmlNode** current, const std::string& segment) 00994 { 00995 // Continue only if text segment is not empty 00996 if (segment.size() > 0) { 00997 00998 // Continue only if non whitespace characters are found 00999 size_t pos = segment.find_first_not_of("\x20\x09\x0d\x0a\x85"); 01000 if (pos != std::string::npos) { 01001 01002 // Append node 01003 (*current)->append(GXmlText(segment)); 01004 01005 } // endif: there was not only whitespace 01006 01007 } // endif: segment was not empty 01008 01009 // Return 01010 return; 01011 } 01012 01013 01014 /***********************************************************************//** 01015 * @brief Get Markup type of segment 01016 * 01017 * @param[in] segment Segment for which Markup Type should be determined. 01018 * 01019 * Returns Markup Type of segment. 01020 ***************************************************************************/ 01021 GXml::MarkupType GXml::get_markuptype(const std::string& segment) const 01022 { 01023 // Initialise with invalid Markup Type 01024 MarkupType type = MT_INVALID; 01025 01026 // Get length of segment 01027 int n = segment.length(); 01028 01029 // Check for comment 01030 if (n >= 7 && (segment.compare(0,4,"<!--") == 0) && 01031 (segment.compare(n-3,3,"-->") == 0)) { 01032 type = MT_COMMENT; 01033 } 01034 01035 // Check for declaration 01036 else if (n >= 7 && (segment.compare(0,6,"<?xml ") == 0) && 01037 (segment.compare(n-2,2,"?>") == 0)) { 01038 type = MT_DECLARATION; 01039 } 01040 01041 // Check for processing instruction 01042 else if (n >= 4 && (segment.compare(0,2,"<?") == 0) && 01043 (segment.compare(n-2,2,"?>") == 0)) { 01044 type = MT_PROCESSING; 01045 } 01046 01047 // Check for empty element tag 01048 else if (n >= 3 && (segment.compare(0,1,"<") == 0) && 01049 (segment.compare(n-2,2,"/>") == 0)) { 01050 type = MT_ELEMENT_EMPTY; 01051 } 01052 01053 // Check for element end tag 01054 else if (n >= 3 && (segment.compare(0,2,"</") == 0) && 01055 (segment.compare(n-1,1,">") == 0)) { 01056 type = MT_ELEMENT_END; 01057 } 01058 01059 // Check for element start tag 01060 else if (n >= 2 && (segment.compare(0,1,"<") == 0) && 01061 (segment.compare(n-1,1,">") == 0)) { 01062 type = MT_ELEMENT_START; 01063 } 01064 01065 // Return type 01066 return type; 01067 }