LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/orcus/src/liborcus - orcus_xml.cpp (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 1 247 0.4 %
Date: 2012-12-17 Functions: 2 32 6.2 %
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 "orcus/orcus_xml.hpp"
      29             : #include "orcus/global.hpp"
      30             : #include "orcus/sax_ns_parser.hpp"
      31             : #include "orcus/spreadsheet/import_interface.hpp"
      32             : #include "orcus/spreadsheet/export_interface.hpp"
      33             : #include "orcus/xml_namespace.hpp"
      34             : 
      35             : #include "xml_map_tree.hpp"
      36             : 
      37             : #define ORCUS_DEBUG_XML 0
      38             : 
      39             : #if ORCUS_DEBUG_XML
      40             : #include <iostream>
      41             : #endif
      42             : 
      43             : #include <vector>
      44             : #include <boost/ptr_container/ptr_vector.hpp>
      45             : #include <fstream>
      46             : 
      47             : using namespace std;
      48             : 
      49             : namespace orcus {
      50             : 
      51             : namespace {
      52             : 
      53           0 : class xml_data_sax_handler
      54             : {
      55           0 :     struct scope
      56             :     {
      57             :         xmlns_id_t ns;
      58             :         pstring name;
      59             :         const char* element_open_begin;
      60             :         const char* element_open_end;
      61             : 
      62             :         xml_map_tree::element_type type;
      63             : 
      64             :         scope(xmlns_id_t _ns, const pstring& _name) :
      65             :             ns(_ns), name(_name),
      66             :             element_open_begin(NULL), element_open_end(NULL),
      67           0 :             type(xml_map_tree::element_unknown) {}
      68             :     };
      69             : 
      70             :     vector<sax_ns_parser_attribute> m_attrs;
      71             :     vector<scope> m_scopes;
      72             : 
      73             :     spreadsheet::iface::import_factory& m_factory;
      74             :     xml_map_tree::const_element_list_type& m_link_positions;
      75             :     const xml_map_tree& m_map_tree;
      76             :     xml_map_tree::walker m_map_tree_walker;
      77             : 
      78             :     const xml_map_tree::element* mp_current_elem;
      79             : 
      80             :     bool m_in_range_ref:1;
      81             : 
      82             : private:
      83             : 
      84           0 :     const sax_ns_parser_attribute* find_attr_by_name(xmlns_id_t ns, const pstring& name)
      85             :     {
      86             :         vector<sax_ns_parser_attribute>::const_iterator it = m_attrs.begin(), it_end = m_attrs.end();
      87           0 :         for (; it != it_end; ++it)
      88             :         {
      89           0 :             if (it->ns == ns && it->name == name)
      90             :                 return &(*it);
      91             :         }
      92             :         return NULL;
      93             :     }
      94             : 
      95           0 :     void set_single_link_cell(const xml_map_tree::cell_reference& ref, const pstring& val)
      96             :     {
      97           0 :         spreadsheet::iface::import_sheet* sheet = m_factory.get_sheet(ref.pos.sheet.get(), ref.pos.sheet.size());
      98           0 :         if (sheet)
      99           0 :             sheet->set_auto(ref.pos.row, ref.pos.col, val.get(), val.size());
     100           0 :     }
     101             : 
     102           0 :     void set_field_link_cell(const xml_map_tree::field_in_range& field, const pstring& val)
     103             :     {
     104           0 :         assert(field.ref);
     105           0 :         assert(!field.ref->pos.sheet.empty());
     106             : 
     107           0 :         if (field.column_pos == 0)
     108           0 :             ++field.ref->row_size;
     109             : 
     110             :         const xml_map_tree::cell_position& pos = field.ref->pos;
     111           0 :         spreadsheet::iface::import_sheet* sheet = m_factory.get_sheet(pos.sheet.get(), pos.sheet.size());
     112           0 :         if (sheet)
     113             :             sheet->set_auto(
     114             :                pos.row + field.ref->row_size,
     115             :                pos.col + field.column_pos,
     116           0 :                val.get(), val.size());
     117           0 :     }
     118             : 
     119             : public:
     120           0 :     xml_data_sax_handler(
     121             :        spreadsheet::iface::import_factory& factory,
     122             :        xml_map_tree::const_element_list_type& link_positions,
     123             :        const xml_map_tree& map_tree) :
     124             :         m_factory(factory),
     125             :         m_link_positions(link_positions),
     126             :         m_map_tree(map_tree),
     127             :         m_map_tree_walker(map_tree.get_tree_walker()),
     128             :         mp_current_elem(NULL),
     129           0 :         m_in_range_ref(false) {}
     130             : 
     131             :     void declaration()
     132             :     {
     133             :         m_attrs.clear();
     134             :     }
     135             : 
     136           0 :     void start_element(const sax_ns_parser_element& elem)
     137             :     {
     138           0 :         m_scopes.push_back(scope(elem.ns, elem.name));
     139             :         scope& cur = m_scopes.back();
     140           0 :         cur.element_open_begin = elem.begin_pos;
     141           0 :         cur.element_open_end = elem.end_pos;
     142             : 
     143           0 :         mp_current_elem = m_map_tree_walker.push_element(elem.ns, elem.name);
     144           0 :         if (mp_current_elem)
     145             :         {
     146             :             // Go through all linked attributes that belong to this element,
     147             :             // and see if they exist in this content xml.
     148             :             const xml_map_tree::attribute_store_type& linked_attrs = mp_current_elem->attributes;
     149             :             xml_map_tree::attribute_store_type::const_iterator it = linked_attrs.begin(), it_end = linked_attrs.end();
     150           0 :             for (; it != it_end; ++it)
     151             :             {
     152             :                 const xml_map_tree::attribute& linked_attr = *it;
     153           0 :                 const sax_ns_parser_attribute* p = find_attr_by_name(linked_attr.ns, linked_attr.name);
     154           0 :                 if (!p)
     155           0 :                     continue;
     156             : 
     157             :                 // This attribute is linked. Import its value.
     158             : 
     159           0 :                 pstring val_trimmed = p->value.trim();
     160           0 :                 switch (linked_attr.ref_type)
     161             :                 {
     162             :                     case xml_map_tree::reference_cell:
     163           0 :                         set_single_link_cell(*linked_attr.cell_ref, val_trimmed);
     164             :                     break;
     165             :                     case xml_map_tree::reference_range_field:
     166           0 :                         set_field_link_cell(*linked_attr.field_ref, val_trimmed);
     167             :                     break;
     168             :                     default:
     169             :                         ;
     170             :                 }
     171             : 
     172             :                 // Record the namespace alias used in the content stream.
     173           0 :                 linked_attr.ns_alias = m_map_tree.intern_string(p->ns_alias);
     174             :             }
     175             : 
     176           0 :             if (mp_current_elem->range_parent)
     177           0 :                 m_in_range_ref = true;
     178             :         }
     179             :         m_attrs.clear();
     180           0 :     }
     181             : 
     182           0 :     void end_element(const sax_ns_parser_element& elem)
     183             :     {
     184           0 :         assert(!m_scopes.empty());
     185             : 
     186           0 :         if (mp_current_elem)
     187             :         {
     188             :             // Store the end element position in stream for linked elements.
     189             :             const scope& cur = m_scopes.back();
     190           0 :             if (mp_current_elem->ref_type == xml_map_tree::reference_cell ||
     191             :                 mp_current_elem->range_parent ||
     192           0 :                 (!m_in_range_ref && mp_current_elem->unlinked_attribute_anchor()))
     193             :             {
     194             :                 // either single link element, parent of range link elements,
     195             :                 // or an unlinked attribute anchor outside linked ranges.
     196           0 :                 mp_current_elem->stream_pos.open_begin = cur.element_open_begin;
     197           0 :                 mp_current_elem->stream_pos.open_end = cur.element_open_end;
     198           0 :                 mp_current_elem->stream_pos.close_begin = elem.begin_pos;
     199           0 :                 mp_current_elem->stream_pos.close_end = elem.end_pos;
     200           0 :                 m_link_positions.push_back(mp_current_elem);
     201             :             }
     202             : 
     203           0 :             if (mp_current_elem->range_parent)
     204           0 :                 m_in_range_ref = false;
     205             : 
     206             :             // Record the namespace alias used in the content stream.
     207           0 :             mp_current_elem->ns_alias = m_map_tree.intern_string(elem.ns_alias);
     208             :         }
     209             : 
     210             :         m_scopes.pop_back();
     211           0 :         mp_current_elem = m_map_tree_walker.pop_element(elem.ns, elem.name);
     212           0 :     }
     213             : 
     214           0 :     void characters(const pstring& val)
     215             :     {
     216           0 :         if (!mp_current_elem)
     217             :             return;
     218             : 
     219           0 :         pstring val_trimmed = val.trim();
     220           0 :         if (val_trimmed.empty())
     221             :             return;
     222             : 
     223           0 :         switch (mp_current_elem->ref_type)
     224             :         {
     225             :             case xml_map_tree::reference_cell:
     226           0 :                 set_single_link_cell(*mp_current_elem->cell_ref, val_trimmed);
     227             :             break;
     228             :             case xml_map_tree::reference_range_field:
     229           0 :                 set_field_link_cell(*mp_current_elem->field_ref, val_trimmed);
     230             :             break;
     231             :             default:
     232             :                 ;
     233             :         }
     234             :     }
     235             : 
     236           0 :     void attribute(const pstring& /*name*/, const pstring& /*val*/)
     237             :     {
     238             :         // Ignore attributes in XML declaration.
     239           0 :     }
     240             : 
     241           0 :     void attribute(const sax_ns_parser_attribute& at)
     242             :     {
     243           0 :         m_attrs.push_back(at);
     244           0 :     }
     245             : };
     246             : 
     247             : /**
     248             :  * Used in write_range_reference_group().
     249             :  */
     250             : struct scope : boost::noncopyable
     251             : {
     252             :     const xml_map_tree::element& element;
     253             :     xml_map_tree::element_store_type::const_iterator current_child_pos;
     254             :     xml_map_tree::element_store_type::const_iterator end_child_pos;
     255             :     bool opened:1;
     256             : 
     257             :     scope(const xml_map_tree::element& _elem) :
     258           0 :         element(_elem), opened(false)
     259             :     {
     260           0 :         current_child_pos = end_child_pos;
     261             : 
     262           0 :         if (element.elem_type == xml_map_tree::element_unlinked)
     263             :         {
     264           0 :             current_child_pos = element.child_elements->begin();
     265           0 :             end_child_pos = element.child_elements->end();
     266             :         }
     267             :     }
     268             : };
     269             : 
     270             : typedef boost::ptr_vector<scope> scopes_type;
     271             : 
     272           0 : void write_opening_element(
     273             :     ostream& os, const xml_map_tree::element& elem, const xml_map_tree::range_reference& ref,
     274             :     const spreadsheet::iface::export_sheet& sheet, spreadsheet::row_t current_row, bool self_close)
     275             : {
     276           0 :     if (elem.attributes.empty())
     277             :     {
     278             :         // This element has no linked attributes. Just write the element name and be done with it.
     279           0 :         os << '<' << elem << '>';
     280           0 :         return;
     281             :     }
     282             : 
     283             :     // Element has one or more linked attributes.
     284             : 
     285           0 :     os << '<' << elem;
     286             : 
     287             :     xml_map_tree::attribute_store_type::const_iterator it = elem.attributes.begin(), it_end = elem.attributes.end();
     288           0 :     for (; it != it_end; ++it)
     289             :     {
     290             :         const xml_map_tree::attribute& attr = *it;
     291           0 :         if (attr.ref_type != xml_map_tree::reference_range_field)
     292             :             // In theory this should never happen but it won't hurt to check.
     293           0 :             continue;
     294             : 
     295           0 :         os << ' ' << attr << "=\"";
     296           0 :         sheet.write_string(os, ref.pos.row + 1 + current_row, ref.pos.col + attr.field_ref->column_pos);
     297           0 :         os << "\"";
     298             :     }
     299             : 
     300           0 :     if (self_close)
     301           0 :         os << '/';
     302             : 
     303           0 :     os << '>';
     304             : }
     305             : 
     306           0 : void write_opening_element(
     307             :     ostream& os, const xml_map_tree::element& elem, const spreadsheet::iface::export_factory& fact, bool self_close)
     308             : {
     309           0 :     os << '<' << elem;
     310             :     xml_map_tree::attribute_store_type::const_iterator it = elem.attributes.begin(), it_end = elem.attributes.end();
     311           0 :     for (; it != it_end; ++it)
     312             :     {
     313             :         const xml_map_tree::attribute& attr = *it;
     314           0 :         if (attr.ref_type != xml_map_tree::reference_cell)
     315             :             // We should only see single linked cell here, as all
     316             :             // field links are handled by the range parent above.
     317           0 :             continue;
     318             : 
     319           0 :         const xml_map_tree::cell_position& pos = attr.cell_ref->pos;
     320             : 
     321             :         const spreadsheet::iface::export_sheet* sheet =
     322           0 :             fact.get_sheet(pos.sheet.get(), pos.sheet.size());
     323           0 :         if (!sheet)
     324           0 :             continue;
     325             : 
     326           0 :         os << ' ' << attr << "=\"";
     327           0 :         sheet->write_string(os, pos.row, pos.col);
     328           0 :         os << "\"";
     329             :     }
     330             : 
     331           0 :     if (self_close)
     332           0 :         os << '/';
     333             : 
     334           0 :     os << '>';
     335           0 : }
     336             : 
     337             : /**
     338             :  * Write to the output stream a single range reference.
     339             :  *
     340             :  * @param os output stream.
     341             :  * @param root root map tree element representing the root of a single range
     342             :  *             reference.
     343             :  * @param ref range reference data.
     344             :  * @param factory export factory instance.
     345             :  */
     346           0 : void write_range_reference_group(
     347             :    ostream& os, const xml_map_tree::element& root, const xml_map_tree::range_reference& ref,
     348             :    const spreadsheet::iface::export_factory& factory)
     349             : {
     350           0 :     const spreadsheet::iface::export_sheet* sheet = factory.get_sheet(ref.pos.sheet.get(), ref.pos.sheet.size());
     351           0 :     if (!sheet)
     352           0 :         return;
     353             : 
     354             :     scopes_type scopes;
     355           0 :     for (spreadsheet::row_t current_row = 0; current_row < ref.row_size; ++current_row)
     356             :     {
     357           0 :         scopes.push_back(new scope(root)); // root element
     358             : 
     359           0 :         while (!scopes.empty())
     360             :         {
     361             :             bool new_scope = false;
     362             : 
     363           0 :             scope& cur_scope = scopes.back();
     364             : 
     365             :             // Self-closing element has no child elements nor content.
     366             :             bool self_close =
     367             :                 (cur_scope.current_child_pos == cur_scope.end_child_pos) &&
     368           0 :                 (cur_scope.element.ref_type != xml_map_tree::reference_range_field);
     369             : 
     370           0 :             if (!cur_scope.opened)
     371             :             {
     372             :                 // Write opening element of this scope only on the 1st entrance.
     373           0 :                 write_opening_element(os, cur_scope.element, ref, *sheet, current_row, self_close);
     374           0 :                 cur_scope.opened = true;
     375             :             }
     376             : 
     377           0 :             if (self_close)
     378             :             {
     379           0 :                 scopes.pop_back();
     380           0 :                 continue;
     381             :             }
     382             : 
     383             :             // Go though all child elements.
     384           0 :             for (; cur_scope.current_child_pos != cur_scope.end_child_pos; ++cur_scope.current_child_pos)
     385             :             {
     386             :                 const xml_map_tree::element& child_elem = *cur_scope.current_child_pos;
     387           0 :                 if (child_elem.elem_type == xml_map_tree::element_unlinked)
     388             :                 {
     389             :                     // This is a non-leaf element.  Push a new scope with this
     390             :                     // element and re-start the loop.
     391             :                     ++cur_scope.current_child_pos;
     392           0 :                     scopes.push_back(new scope(child_elem));
     393             :                     new_scope = true;
     394             :                     break;
     395             :                 }
     396             : 
     397             :                 // This is a leaf element.  This must be a field link element.
     398           0 :                 if (child_elem.ref_type == xml_map_tree::reference_range_field)
     399             :                 {
     400           0 :                     write_opening_element(os, child_elem, ref, *sheet, current_row, false);
     401           0 :                     sheet->write_string(os, ref.pos.row + 1 + current_row, ref.pos.col + child_elem.field_ref->column_pos);
     402           0 :                     os << "</" << child_elem << ">";
     403             :                 }
     404             :             }
     405             : 
     406           0 :             if (new_scope)
     407             :                 // Re-start the loop with a new scope.
     408           0 :                 continue;
     409             : 
     410             :             // Write content of this element before closing it (if it's linked).
     411           0 :             if (scopes.back().element.ref_type == xml_map_tree::reference_range_field)
     412             :                 sheet->write_string(
     413           0 :                     os, ref.pos.row + 1 + current_row, ref.pos.col + scopes.back().element.field_ref->column_pos);
     414             : 
     415             :             // Close this element for good, and exit the current scope.
     416           0 :             os << "</" << scopes.back().element << ">";
     417           0 :             scopes.pop_back();
     418             :         }
     419           0 :     }
     420             : }
     421             : 
     422             : /**
     423             :  * Write to an output stream the sub-structure comprising one or more range
     424             :  * references.
     425             :  *
     426             :  * @param os output stream
     427             :  * @param elem_top topmost element in the range reference sub-structure.
     428             :  */
     429           0 : void write_range_reference(ostream& os, const xml_map_tree::element& elem_top, const spreadsheet::iface::export_factory& factory)
     430             : {
     431             :     // Top element is expected to have one or more child elements, and each
     432             :     // child element represents a separate database range.
     433           0 :     if (elem_top.elem_type != xml_map_tree::element_unlinked)
     434             :         return;
     435             : 
     436           0 :     assert(elem_top.child_elements);
     437             : 
     438           0 :     if (elem_top.child_elements->empty())
     439             :         return;
     440             : 
     441             :     // TODO: For now, we assume that there is only one child element under the
     442             :     // range ref parent.
     443             :     write_range_reference_group(
     444           0 :        os, *elem_top.child_elements->begin(), *elem_top.range_parent, factory);
     445             : }
     446             : 
     447             : struct less_by_opening_elem_pos : std::binary_function<xml_map_tree::element*, xml_map_tree::element*, bool>
     448             : {
     449           0 :     bool operator() (const xml_map_tree::element* left, const xml_map_tree::element* right) const
     450             :     {
     451           0 :         return left->stream_pos.open_begin < right->stream_pos.open_begin;
     452             :     }
     453             : };
     454             : 
     455             : }
     456             : 
     457           0 : struct orcus_xml_impl
     458             : {
     459             :     spreadsheet::iface::import_factory* mp_import_factory;
     460             :     spreadsheet::iface::export_factory* mp_export_factory;
     461             : 
     462             :     /** original xml data stream. */
     463             :     string m_data_strm;
     464             : 
     465             :     /** xml namespace repository for the whole session. */
     466             :     xmlns_repository& m_ns_repo;
     467             : 
     468             :     /** xml namespace context  */
     469             :     xmlns_context m_ns_cxt_map;
     470             : 
     471             :     /** xml element tree that represents all mapped paths. */
     472             :     xml_map_tree m_map_tree;
     473             : 
     474             :     /**
     475             :      * Positions of all linked elements, single and range reference alike.
     476             :      * Stored link elements must be sorted in order of stream positions, and
     477             :      * as such, no linked elements should be nested; there should never be a
     478             :      * linked element inside the substructure of another linked element.
     479             :      */
     480             :     xml_map_tree::const_element_list_type m_link_positions;
     481             : 
     482             :     xml_map_tree::cell_position m_cur_range_ref;
     483             : 
     484           0 :     orcus_xml_impl(xmlns_repository& ns_repo) : m_ns_repo(ns_repo), m_ns_cxt_map(ns_repo.create_context()), m_map_tree(m_ns_repo) {}
     485             : };
     486             : 
     487           0 : orcus_xml::orcus_xml(xmlns_repository& ns_repo, spreadsheet::iface::import_factory* im_fact, spreadsheet::iface::export_factory* ex_fact) :
     488           0 :     mp_impl(new orcus_xml_impl(ns_repo))
     489             : {
     490           0 :     mp_impl->mp_import_factory = im_fact;
     491           0 :     mp_impl->mp_export_factory = ex_fact;
     492           0 : }
     493             : 
     494           0 : orcus_xml::~orcus_xml()
     495             : {
     496           0 :     delete mp_impl;
     497           0 : }
     498             : 
     499           0 : void orcus_xml::set_namespace_alias(const pstring& alias, const pstring& uri)
     500             : {
     501           0 :     mp_impl->m_map_tree.set_namespace_alias(alias, uri);
     502           0 : }
     503             : 
     504           0 : void orcus_xml::set_cell_link(const pstring& xpath, const pstring& sheet, spreadsheet::row_t row, spreadsheet::col_t col)
     505             : {
     506           0 :     pstring sheet_safe = mp_impl->m_map_tree.intern_string(sheet);
     507           0 :     mp_impl->m_map_tree.set_cell_link(xpath, xml_map_tree::cell_position(sheet_safe, row, col));
     508           0 : }
     509             : 
     510           0 : void orcus_xml::start_range(const pstring& sheet, spreadsheet::row_t row, spreadsheet::col_t col)
     511             : {
     512           0 :     pstring sheet_safe = mp_impl->m_map_tree.intern_string(sheet);
     513           0 :     mp_impl->m_cur_range_ref = xml_map_tree::cell_position(sheet_safe, row, col);
     514           0 :     mp_impl->m_map_tree.start_range();
     515           0 : }
     516             : 
     517           0 : void orcus_xml::append_field_link(const pstring& xpath)
     518             : {
     519           0 :     mp_impl->m_map_tree.append_range_field_link(xpath, mp_impl->m_cur_range_ref);
     520           0 : }
     521             : 
     522           0 : void orcus_xml::commit_range()
     523             : {
     524           0 :     mp_impl->m_cur_range_ref = xml_map_tree::cell_position();
     525           0 :     mp_impl->m_map_tree.commit_range();
     526           0 : }
     527             : 
     528           0 : void orcus_xml::append_sheet(const pstring& name)
     529             : {
     530           0 :     if (name.empty())
     531           0 :         return;
     532             : 
     533           0 :     mp_impl->mp_import_factory->append_sheet(name.get(), name.size());
     534             : }
     535             : 
     536           0 : void orcus_xml::read_file(const char* filepath)
     537             : {
     538             : #if ORCUS_DEBUG_XML
     539             :     cout << "reading file " << filepath << endl;
     540             : #endif
     541           0 :     string& strm = mp_impl->m_data_strm;
     542           0 :     load_file_content(filepath, strm);
     543           0 :     if (strm.empty())
     544           0 :         return;
     545             : 
     546             :     // Insert the range headers and reset the row size counters.
     547           0 :     xml_map_tree::range_ref_map_type& range_refs = mp_impl->m_map_tree.get_range_references();
     548             :     xml_map_tree::range_ref_map_type::iterator it_ref = range_refs.begin(), it_ref_end = range_refs.end();
     549           0 :     for (; it_ref != it_ref_end; ++it_ref)
     550             :     {
     551             :         const xml_map_tree::cell_position& ref = it_ref->first;
     552           0 :         xml_map_tree::range_reference& range_ref = *it_ref->second;
     553           0 :         range_ref.row_size = 0; // Reset the row offset.
     554             : 
     555             :         spreadsheet::iface::import_sheet* sheet =
     556           0 :             mp_impl->mp_import_factory->get_sheet(ref.sheet.get(), ref.sheet.size());
     557             : 
     558           0 :         if (!sheet)
     559           0 :             continue;
     560             : 
     561             :         xml_map_tree::const_linkable_list_type::const_iterator it = range_ref.field_nodes.begin(), it_end = range_ref.field_nodes.end();
     562           0 :         spreadsheet::row_t row = ref.row;
     563           0 :         spreadsheet::col_t col = ref.col;
     564           0 :         for (; it != it_end; ++it)
     565             :         {
     566           0 :             const xml_map_tree::linkable& e = **it;
     567           0 :             ostringstream os;
     568           0 :             if (e.ns)
     569           0 :                 os << mp_impl->m_ns_repo.get_short_name(e.ns) << ':';
     570           0 :             os << e.name;
     571           0 :             string s = os.str();
     572           0 :             if (!s.empty())
     573           0 :                 sheet->set_auto(row, col++, &s[0], s.size());
     574           0 :         }
     575             :     }
     576             : 
     577             :     // Parse the content xml.
     578           0 :     xmlns_context ns_cxt = mp_impl->m_ns_repo.create_context(); // new ns context for the content xml stream.
     579             :     xml_data_sax_handler handler(
     580           0 :        *mp_impl->mp_import_factory, mp_impl->m_link_positions, mp_impl->m_map_tree);
     581             : 
     582           0 :     sax_ns_parser<xml_data_sax_handler> parser(strm.c_str(), strm.size(), ns_cxt, handler);
     583           0 :     parser.parse();
     584             : }
     585             : 
     586             : #if ORCUS_DEBUG_XML
     587             : 
     588             : namespace {
     589             : 
     590             : void dump_links(const xml_map_tree::const_element_list_type& links)
     591             : {
     592             :     cout << "link count: " << links.size() << endl;
     593             : }
     594             : 
     595             : }
     596             : 
     597             : #endif
     598             : 
     599           0 : void orcus_xml::write_file(const char* filepath)
     600             : {
     601           0 :     if (!mp_impl->mp_export_factory)
     602             :         // We can't export data witout export factory.
     603             :         return;
     604             : 
     605           0 :     if (mp_impl->m_data_strm.empty())
     606             :         // Original xml stream is missing.  We need it.
     607             :         return;
     608             : 
     609             :     xml_map_tree::const_element_list_type& links = mp_impl->m_link_positions;
     610           0 :     if (links.empty())
     611             :         // nothing to write.
     612             :         return;
     613             : 
     614             :     // Sort all link position by opening element positions.
     615           0 :     std::sort(links.begin(), links.end(), less_by_opening_elem_pos());
     616             : 
     617             : #if ORCUS_DEBUG_XML
     618             :     cout << "writing to " << filepath << endl;
     619             : #endif
     620           0 :     ofstream file(filepath);
     621             : 
     622           0 :     if (!file)
     623           0 :         throw general_error("Failed to create output file.");
     624             : 
     625           0 :     spreadsheet::iface::export_factory& fact = *mp_impl->mp_export_factory;
     626             :     xml_map_tree::const_element_list_type::const_iterator it = links.begin(), it_end = links.end();
     627             : 
     628             : #if ORCUS_DEBUG_XML
     629             :     dump_links(links);
     630             : #endif
     631             : 
     632           0 :     const char* begin_pos = &mp_impl->m_data_strm[0];
     633           0 :     for (; it != it_end; ++it)
     634             :     {
     635           0 :         const xml_map_tree::element& elem = **it;
     636           0 :         if (elem.ref_type == xml_map_tree::reference_cell)
     637             :         {
     638             :             // Single cell link
     639           0 :             const xml_map_tree::cell_position& pos = elem.cell_ref->pos;
     640             : 
     641             :             const spreadsheet::iface::export_sheet* sheet =
     642           0 :                 fact.get_sheet(pos.sheet.get(), pos.sheet.size());
     643           0 :             if (!sheet)
     644           0 :                 continue;
     645             : 
     646           0 :             const char* open_begin = elem.stream_pos.open_begin;
     647           0 :             const char* close_begin = elem.stream_pos.close_begin;
     648           0 :             const char* close_end = elem.stream_pos.close_end;
     649             : 
     650           0 :             assert(open_begin > begin_pos);
     651           0 :             file << pstring(begin_pos, open_begin-begin_pos); // stream since last linked element.
     652             : 
     653           0 :             write_opening_element(file, elem, fact, false);
     654           0 :             sheet->write_string(file, pos.row, pos.col);
     655           0 :             file << pstring(close_begin, close_end-close_begin); // closing element.
     656             :             begin_pos = close_end;
     657             :         }
     658           0 :         else if (elem.range_parent)
     659             :         {
     660             :             // Range link
     661             :             const xml_map_tree::range_reference& ref = *elem.range_parent;
     662             :             const xml_map_tree::cell_position& pos = ref.pos;
     663             : 
     664             :             const spreadsheet::iface::export_sheet* sheet =
     665           0 :                 fact.get_sheet(pos.sheet.get(), pos.sheet.size());
     666           0 :             if (!sheet)
     667           0 :                 continue;
     668             : 
     669           0 :             const char* open_begin = elem.stream_pos.open_begin;
     670           0 :             const char* close_begin = elem.stream_pos.close_begin;
     671           0 :             const char* close_end = elem.stream_pos.close_end;
     672             : 
     673           0 :             assert(open_begin > begin_pos);
     674           0 :             file << pstring(begin_pos, open_begin-begin_pos); // stream since last linked element.
     675             : 
     676           0 :             write_opening_element(file, elem, fact, false);
     677           0 :             write_range_reference(file, elem, fact);
     678           0 :             file << pstring(close_begin, close_end-close_begin); // closing element.
     679             :             begin_pos = close_end;
     680             :         }
     681           0 :         else if (elem.unlinked_attribute_anchor())
     682             :         {
     683             :             // Element is not linked but has one or more attributes that are
     684             :             // linked.  Here, only write the opening element with attributes.
     685             : 
     686           0 :             const char* open_begin = elem.stream_pos.open_begin;
     687           0 :             const char* open_end = elem.stream_pos.open_end;
     688             : 
     689           0 :             bool self_close = open_begin == elem.stream_pos.close_begin;
     690             : 
     691           0 :             assert(open_begin > begin_pos);
     692           0 :             file << pstring(begin_pos, open_begin-begin_pos); // stream since last linked element.
     693             : 
     694           0 :             write_opening_element(file, elem, fact, self_close);
     695             :             begin_pos = open_end;
     696             :         }
     697             :         else
     698           0 :             throw general_error("Non-link element type encountered.");
     699             :     }
     700             : 
     701             :     // Flush the remaining stream.
     702           0 :     const char* strm_end = &mp_impl->m_data_strm[mp_impl->m_data_strm.size()-1];
     703           0 :     file << pstring(begin_pos, strm_end-begin_pos);
     704             : }
     705             : 
     706          24 : }

Generated by: LCOV version 1.10