LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/orcus/src/liborcus - xml_map_tree.cpp (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 1 284 0.4 %
Date: 2012-12-17 Functions: 2 40 5.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*************************************************************************
       2             :  *
       3             :  * Copyright (c) 2012 Kohei Yoshida
       4             :  *
       5             :  * Permission is hereby granted, free of charge, to any person
       6             :  * obtaining a copy of this software and associated documentation
       7             :  * files (the "Software"), to deal in the Software without
       8             :  * restriction, including without limitation the rights to use,
       9             :  * copy, modify, merge, publish, distribute, sublicense, and/or sell
      10             :  * copies of the Software, and to permit persons to whom the
      11             :  * Software is furnished to do so, subject to the following
      12             :  * conditions:
      13             :  *
      14             :  * The above copyright notice and this permission notice shall be
      15             :  * included in all copies or substantial portions of the Software.
      16             :  *
      17             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      18             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
      19             :  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      20             :  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      21             :  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      22             :  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      23             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      24             :  * OTHER DEALINGS IN THE SOFTWARE.
      25             :  *
      26             :  ************************************************************************/
      27             : 
      28             : #include "xml_map_tree.hpp"
      29             : #include "orcus/global.hpp"
      30             : 
      31             : #define ORCUS_DEBUG_XML_MAP_TREE 0
      32             : 
      33             : #if ORCUS_DEBUG_XML_MAP_TREE
      34             : #include <iostream>
      35             : #endif
      36             : 
      37             : using namespace std;
      38             : 
      39             : namespace orcus {
      40             : 
      41             : namespace {
      42             : 
      43             : class find_by_name : std::unary_function<xml_map_tree::linkable, bool>
      44             : {
      45             :     xmlns_id_t m_ns;
      46             :     pstring m_name;
      47             : public:
      48           0 :     find_by_name(xmlns_id_t ns, const pstring& name) : m_ns(ns), m_name(name) {}
      49           0 :     bool operator() (const xml_map_tree::linkable& e) const
      50             :     {
      51           0 :         return m_ns == e.ns && m_name == e.name;
      52             :     }
      53             : };
      54             : 
      55             : class xpath_parser
      56             : {
      57             :     const xmlns_context& m_cxt;
      58             :     const char* mp_char;
      59             :     const char* mp_end;
      60             : 
      61             :     enum token_type { element, attribute };
      62             :     token_type m_next_token_type;
      63             : 
      64             : public:
      65             : 
      66           0 :     struct token
      67             :     {
      68             :         xmlns_id_t ns;
      69             :         pstring name;
      70             :         bool attribute;
      71             : 
      72             :         token(xmlns_id_t _ns, const pstring& _name, bool _attribute) :
      73           0 :             ns(_ns), name(_name), attribute(_attribute)
      74             :         {
      75             : #if ORCUS_DEBUG_XML_MAP_TREE
      76             :             cout << "xpath_parser::token: (ns='" << (ns ? ns : "none") << "', name='" << name << "', attribute=" << attribute << ")" << endl;
      77             : #endif
      78             :         }
      79             : 
      80           0 :         token() : ns(XMLNS_UNKNOWN_ID), attribute(false) {}
      81             :         token(const token& r) : ns(r.ns), name(r.name), attribute(r.attribute) {}
      82             :     };
      83             : 
      84           0 :     xpath_parser(const xmlns_context& cxt, const char* p, size_t n) :
      85           0 :         m_cxt(cxt), mp_char(p), mp_end(p+n), m_next_token_type(element)
      86             :     {
      87           0 :         if (!n)
      88           0 :             throw xml_map_tree::xpath_error("empty path");
      89             : 
      90           0 :         if (*p != '/')
      91           0 :             throw xml_map_tree::xpath_error("first character must be '/'.");
      92             : 
      93           0 :         ++mp_char;
      94           0 :     }
      95             : 
      96           0 :     token next()
      97             :     {
      98           0 :         if (mp_char == mp_end)
      99             :             return token();
     100             : 
     101             :         const char* p0 = NULL;
     102             :         size_t len = 0;
     103             :         xmlns_id_t ns = XMLNS_UNKNOWN_ID;
     104             : 
     105           0 :         for (; mp_char != mp_end; ++mp_char, ++len)
     106             :         {
     107           0 :             if (!p0)
     108             :             {
     109             :                 p0 = mp_char;
     110             :                 len = 0;
     111             :             }
     112             : 
     113           0 :             switch (*mp_char)
     114             :             {
     115             :                 case '/':
     116             :                 {
     117             :                     // '/' encountered.  Next token is an element name.
     118           0 :                     if (m_next_token_type == attribute)
     119           0 :                         throw xml_map_tree::xpath_error("attribute name should not contain '/'.");
     120             : 
     121           0 :                     m_next_token_type = element;
     122           0 :                     ++mp_char; // skip the '/'.
     123             :                     return token(ns, pstring(p0, len), false);
     124             :                 }
     125             :                 case '@':
     126             :                 {
     127             :                     // '@' encountered.  Next token is an attribute name.
     128           0 :                     m_next_token_type = attribute;
     129           0 :                     ++mp_char; // skip the '@'.
     130             :                     return token(ns, pstring(p0, len), false);
     131             :                 }
     132             :                 case ':':
     133             :                 {
     134             :                     // What comes ':' is a namespace. Reset the name and
     135             :                     // convert the namespace to a proper ID.
     136             :                     pstring ns_name(p0, len);
     137           0 :                     ns = m_cxt.get(ns_name);
     138             :                     p0 = NULL; // reset the name.
     139             :                 }
     140           0 :                 break;
     141             :                 default:
     142             :                     ;
     143             :             }
     144             :         }
     145             : 
     146             :         // '/' has never been encountered.  It must be the last name in the path.
     147           0 :         return token(ns, pstring(p0, len), m_next_token_type == attribute);
     148             :     }
     149             : };
     150             : 
     151             : template<typename T>
     152             : void print_element_stack(ostream& os, const T& elem_stack)
     153             : {
     154             :     typename T::const_iterator it = elem_stack.begin(), it_end = elem_stack.end();
     155             :     for (; it != it_end; ++it)
     156             :     {
     157             :         const xml_map_tree::element& elem = **it;
     158             :         os << '/' << elem.name;
     159             :     }
     160             : }
     161             : 
     162             : }
     163             : 
     164           0 : xml_map_tree::xpath_error::xpath_error(const string& msg) : general_error(msg) {}
     165             : 
     166           0 : xml_map_tree::cell_position::cell_position() :
     167           0 :     row(-1), col(-1) {}
     168             : 
     169           0 : xml_map_tree::cell_position::cell_position(const pstring& _sheet, spreadsheet::row_t _row, spreadsheet::col_t _col) :
     170           0 :     sheet(_sheet), row(_row), col(_col) {}
     171             : 
     172           0 : xml_map_tree::cell_position::cell_position(const cell_position& r) :
     173           0 :     sheet(r.sheet), row(r.row), col(r.col) {}
     174             : 
     175           0 : xml_map_tree::element_position::element_position() :
     176           0 :     open_begin(NULL), open_end(NULL), close_begin(NULL), close_end(NULL) {}
     177             : 
     178           0 : xml_map_tree::cell_reference::cell_reference() {}
     179             : 
     180           0 : xml_map_tree::range_reference::range_reference(const cell_position& _pos) :
     181           0 :     pos(_pos), row_size(0) {}
     182             : 
     183           0 : xml_map_tree::linkable::linkable(xmlns_id_t _ns, const pstring& _name, linkable_node_type _node_type) :
     184           0 :     ns(_ns), name(_name), node_type(_node_type) {}
     185             : 
     186           0 : xml_map_tree::attribute::attribute(xmlns_id_t _ns, const pstring& _name, reference_type _ref_type) :
     187           0 :     linkable(_ns, _name, node_attribute), ref_type(_ref_type)
     188             : {
     189           0 :     switch (ref_type)
     190             :     {
     191             :         case reference_cell:
     192           0 :             cell_ref = new cell_reference;
     193           0 :         break;
     194             :         case reference_range_field:
     195           0 :             field_ref = new field_in_range;
     196           0 :         break;
     197             :         default:
     198           0 :             throw general_error("unexpected reference type in the constructor of attribute.");
     199             :     }
     200           0 : }
     201             : 
     202           0 : xml_map_tree::attribute::~attribute()
     203             : {
     204           0 :     switch (ref_type)
     205             :     {
     206             :         case reference_cell:
     207           0 :             delete cell_ref;
     208             :         break;
     209             :         case reference_range_field:
     210           0 :             delete field_ref;
     211           0 :         break;
     212             :         default:
     213           0 :             throw general_error("unexpected reference type in the destructor of attribute.");
     214             :     }
     215           0 : }
     216             : 
     217           0 : xml_map_tree::element::element(
     218             :     xmlns_id_t _ns, const pstring& _name, element_type _elem_type, reference_type _ref_type) :
     219             :     linkable(_ns, _name, node_element),
     220             :     elem_type(_elem_type),
     221             :     ref_type(_ref_type),
     222           0 :     range_parent(NULL)
     223             : {
     224           0 :     if (elem_type == element_unlinked)
     225             :     {
     226           0 :         child_elements = new element_store_type;
     227           0 :         return;
     228             :     }
     229             : 
     230           0 :     assert(elem_type == element_linked);
     231             : 
     232           0 :     switch (ref_type)
     233             :     {
     234             :         case reference_cell:
     235           0 :             cell_ref = new cell_reference;
     236           0 :         break;
     237             :         case reference_range_field:
     238           0 :             field_ref = new field_in_range;
     239           0 :         break;
     240             :         default:
     241           0 :             throw general_error("unexpected reference type in the constructor of element.");
     242             :     }
     243             : }
     244             : 
     245           0 : xml_map_tree::element::~element()
     246             : {
     247           0 :     if (elem_type == element_unlinked)
     248             :     {
     249           0 :         delete child_elements;
     250             :         return;
     251             :     }
     252             : 
     253           0 :     assert(elem_type == element_linked);
     254             : 
     255           0 :     switch (ref_type)
     256             :     {
     257             :         case reference_cell:
     258           0 :             delete cell_ref;
     259             :         break;
     260             :         case reference_range_field:
     261           0 :             delete field_ref;
     262           0 :         break;
     263             :         default:
     264           0 :             throw general_error("unexpected reference type in the destructor of element.");
     265             :     }
     266           0 : }
     267             : 
     268           0 : const xml_map_tree::element* xml_map_tree::element::get_child(xmlns_id_t _ns, const pstring& _name) const
     269             : {
     270           0 :     if (elem_type != element_unlinked)
     271             :         return NULL;
     272             : 
     273           0 :     assert(child_elements);
     274             : 
     275             :     element_store_type::const_iterator it =
     276           0 :         std::find_if(child_elements->begin(), child_elements->end(), find_by_name(_ns, _name));
     277             : 
     278           0 :     return it == child_elements->end() ? NULL : &(*it);
     279             : }
     280             : 
     281           0 : bool xml_map_tree::element::unlinked_attribute_anchor() const
     282             : {
     283           0 :     return elem_type == element_unlinked && ref_type == reference_unknown && !attributes.empty();
     284             : }
     285             : 
     286           0 : xml_map_tree::walker::walker(const xml_map_tree& parent) :
     287           0 :     m_parent(parent) {}
     288           0 : xml_map_tree::walker::walker(const xml_map_tree::walker& r) :
     289           0 :     m_parent(r.m_parent), m_stack(r.m_stack), m_unlinked_stack(r.m_unlinked_stack) {}
     290             : 
     291           0 : void xml_map_tree::walker::reset()
     292             : {
     293             :     m_stack.clear();
     294             :     m_unlinked_stack.clear();
     295           0 : }
     296             : 
     297           0 : const xml_map_tree::element* xml_map_tree::walker::push_element(xmlns_id_t ns, const pstring& name)
     298             : {
     299           0 :     if (!m_unlinked_stack.empty())
     300             :     {
     301             :         // We're still in the unlinked region.
     302           0 :         m_unlinked_stack.push_back(xml_name_t(ns, name));
     303           0 :         return NULL;
     304             :     }
     305             : 
     306           0 :     if (m_stack.empty())
     307             :     {
     308           0 :         if (!m_parent.mp_root)
     309             :         {
     310             :             // Tree is empty.
     311           0 :             m_unlinked_stack.push_back(xml_name_t(ns, name));
     312             :             return NULL;
     313             :         }
     314             : 
     315           0 :         const element* p = m_parent.mp_root;
     316           0 :         if (p->ns != ns || p->name != name)
     317             :         {
     318             :             // Names differ.
     319           0 :             m_unlinked_stack.push_back(xml_name_t(ns, name));
     320             :             return NULL;
     321             :         }
     322             : 
     323           0 :         m_stack.push_back(p);
     324           0 :         return p;
     325             :     }
     326             : 
     327           0 :     if (m_stack.back()->elem_type == element_unlinked)
     328             :     {
     329             :         // Check if the current element has a child of the same name.
     330           0 :         const element* p = m_stack.back()->get_child(ns, name);
     331           0 :         if (p)
     332             :         {
     333           0 :             m_stack.push_back(p);
     334           0 :             return p;
     335             :         }
     336             :     }
     337             : 
     338           0 :     m_unlinked_stack.push_back(xml_name_t(ns, name));
     339           0 :     return NULL;
     340             : }
     341             : 
     342           0 : const xml_map_tree::element* xml_map_tree::walker::pop_element(xmlns_id_t ns, const pstring& name)
     343             : {
     344           0 :     if (!m_unlinked_stack.empty())
     345             :     {
     346             :         // We're in the unlinked region.  Pop element from the unlinked stack.
     347           0 :         if (m_unlinked_stack.back().ns != ns || m_unlinked_stack.back().name != name)
     348           0 :             throw general_error("Closing element has a different name than the opening element. (unlinked stack)");
     349             : 
     350             :         m_unlinked_stack.pop_back();
     351             : 
     352           0 :         if (!m_unlinked_stack.empty())
     353             :             // We are still in the unlinked region.
     354             :             return NULL;
     355             : 
     356           0 :         return m_stack.empty() ? NULL : m_stack.back();
     357             :     }
     358             : 
     359           0 :     if (m_stack.empty())
     360           0 :         throw general_error("Element was popped while the stack was empty.");
     361             : 
     362           0 :     if (m_stack.back()->ns != ns || m_stack.back()->name != name)
     363           0 :         throw general_error("Closing element has a different name than the opening element. (linked stack)");
     364             : 
     365             :     m_stack.pop_back();
     366           0 :     return m_stack.empty() ? NULL : m_stack.back();
     367             : }
     368             : 
     369           0 : xml_map_tree::xml_map_tree(xmlns_repository& xmlns_repo) :
     370           0 :     m_xmlns_cxt(xmlns_repo.create_context()), mp_cur_range_ref(NULL), mp_root(NULL) {}
     371             : 
     372           0 : xml_map_tree::~xml_map_tree()
     373             : {
     374           0 :     std::for_each(m_field_refs.begin(), m_field_refs.end(), map_object_deleter<range_ref_map_type>());
     375           0 :     delete mp_root;
     376           0 : }
     377             : 
     378           0 : void xml_map_tree::set_namespace_alias(const pstring& alias, const pstring& uri)
     379             : {
     380             : #if ORCUS_DEBUG_XML_MAP_TREE
     381             :     cout << "xml_map_tree::set_namespace_alias: alias='" << alias << "', uri='" << uri << "'" << endl;
     382             : #endif
     383             :     // We need to turn the alias string persistent because the xmlns context
     384             :     // doesn't intern the alias strings.
     385           0 :     pstring alias_safe = m_names.intern(alias).first;
     386           0 :     m_xmlns_cxt.push(alias_safe, uri);
     387           0 : }
     388             : 
     389           0 : xmlns_id_t xml_map_tree::get_namespace(const pstring& alias) const
     390             : {
     391           0 :     return m_xmlns_cxt.get(alias);
     392             : }
     393             : 
     394           0 : void xml_map_tree::set_cell_link(const pstring& xpath, const cell_position& ref)
     395             : {
     396           0 :     if (xpath.empty())
     397           0 :         return;
     398             : 
     399             : #if ORCUS_DEBUG_XML_MAP_TREE
     400             :     cout << "xml_map_tree::set_cell_link: xpath='" << xpath << "' (ref=" << ref << ")" << endl;
     401             : #endif
     402             : 
     403             :     element_list_type elem_stack;
     404           0 :     linkable* node = get_element_stack(xpath, reference_cell, elem_stack);
     405           0 :     assert(node);
     406           0 :     assert(!elem_stack.empty());
     407             :     cell_reference* cell_ref = NULL;
     408           0 :     switch (node->node_type)
     409             :     {
     410             :         case node_element:
     411           0 :             assert(static_cast<element*>(node)->cell_ref);
     412             :             cell_ref = static_cast<element*>(node)->cell_ref;
     413             :         break;
     414             :         case node_attribute:
     415           0 :             assert(static_cast<attribute*>(node)->cell_ref);
     416             :             cell_ref = static_cast<attribute*>(node)->cell_ref;
     417             :         break;
     418             :         default:
     419           0 :             throw general_error("unknown node type returned from get_element_stack call in xml_map_tree::set_cell_link().");
     420             :     }
     421             : 
     422           0 :     cell_ref->pos = ref;
     423             : }
     424             : 
     425           0 : void xml_map_tree::start_range()
     426             : {
     427             :     m_cur_range_parent.clear();
     428           0 :     mp_cur_range_ref = NULL;
     429           0 : }
     430             : 
     431           0 : void xml_map_tree::append_range_field_link(const pstring& xpath, const cell_position& pos)
     432             : {
     433           0 :     if (xpath.empty())
     434           0 :         return;
     435             : 
     436             :     range_reference* range_ref = NULL;
     437           0 :     range_ref_map_type::iterator it = m_field_refs.lower_bound(pos);
     438           0 :     if (it == m_field_refs.end() || m_field_refs.key_comp()(pos, it->first))
     439             :     {
     440             :         // This reference does not exist yet.  Insert a new one.
     441             : 
     442             :         // Make sure the sheet name string is persistent.
     443             :         cell_position pos_safe = pos;
     444           0 :         pos_safe.sheet = m_names.intern(pos.sheet.get(), pos.sheet.size()).first;
     445             : 
     446           0 :         it = m_field_refs.insert(it, range_ref_map_type::value_type(pos_safe, new range_reference(pos_safe)));
     447             :     }
     448             : 
     449           0 :     range_ref = it->second;
     450           0 :     assert(range_ref);
     451             : 
     452           0 :     if (!mp_cur_range_ref)
     453           0 :         mp_cur_range_ref = range_ref;
     454             : 
     455             : #if ORCUS_DEBUG_XML_MAP_TREE
     456             :     cout << "xml_map_tree::append_range_field_link: " << xpath << " (ref=" << pos << ")" << endl;
     457             : #endif
     458             :     element_list_type elem_stack;
     459           0 :     linkable* node = get_element_stack(xpath, reference_range_field, elem_stack);
     460           0 :     if (elem_stack.size() < 2)
     461           0 :         throw xpath_error("Path of a range field link must be at least 2 levels.");
     462             : 
     463           0 :     switch (node->node_type)
     464             :     {
     465             :         case node_element:
     466             :         {
     467             :             element* p = static_cast<element*>(node);
     468           0 :             assert(p && p->ref_type == reference_range_field && p->field_ref);
     469           0 :             p->field_ref->ref = range_ref;
     470           0 :             p->field_ref->column_pos = range_ref->field_nodes.size();
     471             : 
     472           0 :             range_ref->field_nodes.push_back(p);
     473             :         }
     474           0 :         break;
     475             :         case node_attribute:
     476             :         {
     477             :             attribute* p = static_cast<attribute*>(node);
     478           0 :             assert(p && p->ref_type == reference_range_field && p->field_ref);
     479           0 :             p->field_ref->ref = range_ref;
     480           0 :             p->field_ref->column_pos = range_ref->field_nodes.size();
     481             : 
     482           0 :             range_ref->field_nodes.push_back(p);
     483             :         }
     484           0 :         break;
     485             :         default:
     486             :             ;
     487             :     }
     488             : 
     489             :     // Determine the deepest common element for all field link elements in the
     490             :     // current range reference.
     491           0 :     if (m_cur_range_parent.empty())
     492             :     {
     493             :         // First field link in this range.
     494             :         element_list_type::iterator it_end = elem_stack.end();
     495           0 :         if (node->node_type == node_element)
     496             :             --it_end; // Skip the linked element, which is used as a field in a range.
     497             : 
     498             :         --it_end; // Skip the next-up element, which is used to group a single record entry.
     499           0 :         m_cur_range_parent.assign(elem_stack.begin(), it_end);
     500             : #if ORCUS_DEBUG_XML_MAP_TREE
     501             :         print_element_stack(cout, m_cur_range_parent);
     502             :         cout << endl;
     503             : #endif
     504             :     }
     505             :     else
     506             :     {
     507             :         // Determine the deepest common element between the two.
     508             :         element_list_type::iterator it = elem_stack.begin(), it_end = elem_stack.end();
     509             :         element_list_type::iterator it_cur = m_cur_range_parent.begin(), it_cur_end = m_cur_range_parent.end();
     510           0 :         if (*it != *it_cur)
     511           0 :             throw xpath_error("Two field links in the same range reference start with different root elements.");
     512             : 
     513             :         ++it;
     514             :         ++it_cur;
     515             : 
     516           0 :         for (; it != it_end && it_cur != it_cur_end; ++it, ++it_cur)
     517             :         {
     518           0 :             if (*it == *it_cur)
     519           0 :                 continue;
     520             : 
     521             :             // The two elements differ.  Take their parent element as the new common element.
     522           0 :             m_cur_range_parent.assign(elem_stack.begin(), it); // current elemnt excluded.
     523             :             break;
     524             :         }
     525             : 
     526           0 :         if (m_cur_range_parent.empty())
     527           0 :             throw xpath_error("Two field links in the same range reference must at least share the first level of their paths.");
     528           0 :     }
     529             : }
     530             : 
     531           0 : void xml_map_tree::commit_range()
     532             : {
     533           0 :     if (!mp_cur_range_ref)
     534             :         // Nothing to commit.
     535           0 :         return;
     536             : 
     537             : #if ORCUS_DEBUG_XML_MAP_TREE
     538             :     cout << "parent element path for this range: ";
     539             :     element_list_type::iterator it = m_cur_range_parent.begin(), it_end = m_cur_range_parent.end();
     540             :     for (; it != it_end; ++it)
     541             :         cout << "/" << (**it).name;
     542             :     cout << endl;
     543             : #endif
     544             : 
     545           0 :     assert(!m_cur_range_parent.empty());
     546             :     // Mark the range parent element.
     547           0 :     m_cur_range_parent.back()->range_parent = mp_cur_range_ref;
     548             : }
     549             : 
     550           0 : const xml_map_tree::linkable* xml_map_tree::get_link(const pstring& xpath) const
     551             : {
     552           0 :     if (!mp_root)
     553             :         return NULL;
     554             : 
     555           0 :     if (xpath.empty())
     556             :         return NULL;
     557             : 
     558             : #if ORCUS_DEBUG_XML_MAP_TREE
     559             :     cout << "xml_map_tree::get_link: xpath = '" << xpath << "'" << endl;
     560             : #endif
     561             :     const linkable* cur_node = mp_root;
     562             : 
     563           0 :     xpath_parser parser(m_xmlns_cxt, xpath.get(), xpath.size());
     564             : 
     565             :     // Check the root element first.
     566           0 :     xpath_parser::token token = parser.next();
     567           0 :     if (cur_node->ns != token.ns || cur_node->name != token.name)
     568             :         // Root element name doesn't match.
     569             :         return NULL;
     570             : 
     571             : #if ORCUS_DEBUG_XML_MAP_TREE
     572             :     cout << "xml_map_tree::get_link: root = (ns=" << token.ns << ", name=" << token.name << ")" << endl;
     573             : #endif
     574           0 :     for (token = parser.next(); !token.name.empty(); token = parser.next())
     575             :     {
     576           0 :         if (token.attribute)
     577             :         {
     578             :             // The current node should be an element and should have an attribute of the same name.
     579           0 :             if (cur_node->node_type != node_element)
     580             :                 return NULL;
     581             : 
     582             :             const element* elem = static_cast<const element*>(cur_node);
     583             :             const attribute_store_type& attrs = elem->attributes;
     584             :             attribute_store_type::const_iterator it =
     585           0 :                 std::find_if(attrs.begin(), attrs.end(), find_by_name(token.ns, token.name));
     586             : 
     587           0 :             if (it == attrs.end())
     588             :                 // No such attribute exists.
     589             :                 return NULL;
     590             : 
     591             :             return &(*it);
     592             :         }
     593             : 
     594             :         // See if an element of this name exists below the current element.
     595             : 
     596           0 :         if (cur_node->node_type != node_element)
     597             :             return NULL;
     598             : 
     599             :         const element* elem = static_cast<const element*>(cur_node);
     600           0 :         if (elem->elem_type != element_unlinked)
     601             :             return NULL;
     602             : 
     603           0 :         if (!elem->child_elements)
     604             :             return NULL;
     605             : 
     606             :         element_store_type::const_iterator it =
     607             :             std::find_if(
     608             :                 elem->child_elements->begin(), elem->child_elements->end(),
     609           0 :                 find_by_name(token.ns, token.name));
     610             : 
     611           0 :         if (it == elem->child_elements->end())
     612             :             // No such child element exists.
     613             :             return NULL;
     614             : 
     615             :         cur_node = &(*it);
     616             :     }
     617             : 
     618           0 :     if (cur_node->node_type != node_element || static_cast<const element*>(cur_node)->elem_type == element_unlinked)
     619             :         // Non-leaf elements are not links.
     620             :         return NULL;
     621             : 
     622             :     return cur_node;
     623             : }
     624             : 
     625           0 : xml_map_tree::walker xml_map_tree::get_tree_walker() const
     626             : {
     627           0 :     return walker(*this);
     628             : }
     629             : 
     630           0 : xml_map_tree::range_ref_map_type& xml_map_tree::get_range_references()
     631             : {
     632           0 :     return m_field_refs;
     633             : }
     634             : 
     635           0 : pstring xml_map_tree::intern_string(const pstring& str) const
     636             : {
     637           0 :     return m_names.intern(str).first;
     638             : }
     639             : 
     640           0 : xml_map_tree::linkable* xml_map_tree::get_element_stack(
     641             :     const pstring& xpath, reference_type ref_type, element_list_type& elem_stack)
     642             : {
     643           0 :     assert(!xpath.empty());
     644           0 :     xpath_parser parser(m_xmlns_cxt,xpath.get(), xpath.size());
     645             : 
     646             :     element_list_type elem_stack_new;
     647             : 
     648             :     // Get the root element first.
     649           0 :     xpath_parser::token token = parser.next();
     650           0 :     if (mp_root)
     651             :     {
     652             :         // Make sure the root element's names are the same.
     653           0 :         if (mp_root->ns != token.ns || mp_root->name != token.name)
     654           0 :             throw xpath_error("path begins with inconsistent root level name.");
     655             :     }
     656             :     else
     657             :     {
     658             :         // First time the root element is encountered.
     659           0 :         if (token.attribute)
     660           0 :             throw xpath_error("root element cannot be an attribute.");
     661             : 
     662             :         mp_root = new element(
     663           0 :             token.ns, m_names.intern(token.name.get(), token.name.size()).first,
     664           0 :             element_unlinked, reference_unknown);
     665             :     }
     666             : 
     667           0 :     elem_stack_new.push_back(mp_root);
     668           0 :     element* cur_element = elem_stack_new.back();
     669           0 :     assert(cur_element);
     670           0 :     assert(cur_element->child_elements);
     671             : 
     672           0 :     token = parser.next();
     673           0 :     for (xpath_parser::token token_next = parser.next(); !token_next.name.empty(); token_next = parser.next())
     674             :     {
     675             :         // Check if the current element contains a child element of the same name.
     676           0 :         if (token.attribute)
     677           0 :             throw xpath_error("attribute must always be at the end of the path.");
     678             : 
     679           0 :         element_store_type& children = *cur_element->child_elements;
     680           0 :         element_store_type::iterator it = std::find_if(children.begin(), children.end(), find_by_name(token.ns, token.name));
     681           0 :         if (it == children.end())
     682             :         {
     683             :             // Insert a new element of this name.
     684             :             children.push_back(
     685             :                 new element(
     686           0 :                     token.ns, m_names.intern(token.name.get(), token.name.size()).first,
     687           0 :                     element_unlinked, reference_unknown));
     688           0 :             cur_element = &children.back();
     689             :         }
     690             :         else
     691           0 :             cur_element = &(*it);
     692             : 
     693           0 :         elem_stack_new.push_back(cur_element);
     694             :         token = token_next;
     695             :     }
     696             : 
     697           0 :     assert(cur_element);
     698             : 
     699             :     // Insert a leaf node.
     700             : 
     701             :     linkable* ret = NULL;
     702           0 :     if (token.attribute)
     703             :     {
     704             :         // This is an attribute.  Insert it into the current element.
     705             :         attribute_store_type& attrs = cur_element->attributes;
     706             : 
     707             :         // Check if an attribute of the same name already exists.
     708           0 :         attribute_store_type::iterator it = std::find_if(attrs.begin(), attrs.end(), find_by_name(token.ns, token.name));
     709           0 :         if (it != attrs.end())
     710           0 :             throw xpath_error("This attribute is already linked.  You can't link the same attribute twice.");
     711             : 
     712             :         attrs.push_back(
     713             :             new attribute(
     714           0 :                 token.ns, m_names.intern(token.name.get(), token.name.size()).first, ref_type));
     715             : 
     716           0 :         ret = &attrs.back();
     717             :     }
     718             :     else
     719             :     {
     720             :         // Check if an element of the same name already exists.
     721           0 :         element_store_type& children = *cur_element->child_elements;
     722           0 :         element_store_type::iterator it = std::find_if(children.begin(), children.end(), find_by_name(token.ns, token.name));
     723           0 :         if (it == children.end())
     724             :         {
     725             :             // No element of that name exists.
     726             :             children.push_back(
     727             :                 new element(
     728           0 :                     token.ns, m_names.intern(token.name.get(), token.name.size()).first,
     729           0 :                     element_linked, ref_type));
     730             : 
     731           0 :             elem_stack_new.push_back(&children.back());
     732           0 :             ret = &children.back();
     733             :         }
     734             :         else
     735             :         {
     736             :             // This element already exists.  Check if this is already linked.
     737             :             element& elem = *it;
     738           0 :             if (elem.ref_type != reference_unknown || elem.elem_type != element_unlinked)
     739           0 :                 throw xpath_error("This element is already linked.  You can't link the same element twice.");
     740             : 
     741             :             // Turn this existing non-linked element into a linked one.
     742           0 :             delete elem.child_elements;
     743           0 :             elem.elem_type = element_linked;
     744           0 :             elem.ref_type = ref_type;
     745           0 :             switch (ref_type)
     746             :             {
     747             :                 case reference_cell:
     748           0 :                     elem.cell_ref = new cell_reference;
     749           0 :                 break;
     750             :                 case reference_range_field:
     751           0 :                     elem.field_ref = new field_in_range;
     752           0 :                 break;
     753             :                 default:
     754           0 :                     throw general_error("Unknown reference type in xml_map_tree::get_element_stack.");
     755             :             }
     756             : 
     757           0 :             elem_stack_new.push_back(&elem);
     758           0 :             ret = &elem;
     759             :         }
     760             :     }
     761             : 
     762             :     elem_stack.swap(elem_stack_new);
     763             : 
     764           0 :     return ret;
     765             : }
     766             : 
     767           0 : std::ostream& operator<< (std::ostream& os, const xml_map_tree::cell_position& ref)
     768             : {
     769           0 :     os << "[sheet='" << ref.sheet << "' row=" << ref.row << " column=" << ref.col << "]";
     770           0 :     return os;
     771             : }
     772             : 
     773           0 : std::ostream& operator<< (std::ostream& os, const xml_map_tree::linkable& link)
     774             : {
     775           0 :     if (!link.ns_alias.empty())
     776           0 :         os << link.ns_alias << ':';
     777           0 :     os << link.name;
     778           0 :     return os;
     779             : }
     780             : 
     781           0 : bool operator< (const xml_map_tree::cell_position& left, const xml_map_tree::cell_position& right)
     782             : {
     783           0 :     if (left.sheet != right.sheet)
     784           0 :         return left.sheet < right.sheet;
     785             : 
     786           0 :     if (left.row != right.row)
     787           0 :         return left.row < right.row;
     788             : 
     789           0 :     return left.col < right.col;
     790             : }
     791             : 
     792          24 : }
     793             : 

Generated by: LCOV version 1.10