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 : }
|