/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "orcus/spreadsheet/factory.hpp" #include #include #include #include #include #include #include #include "factory_pivot.hpp" #include "factory_shared_strings.hpp" #include "factory_sheet.hpp" #include "global_settings.hpp" #include #include #include #include #include #include #include namespace orcus { namespace spreadsheet { namespace { class import_ref_resolver : public iface::import_reference_resolver { document& m_doc; const ixion::formula_name_resolver* m_resolver; public: import_ref_resolver(document& doc) : m_doc(doc), m_resolver(nullptr) {} void set_formula_ref_context(formula_ref_context_t cxt) { m_resolver = m_doc.get_formula_name_resolver(cxt); } virtual src_address_t resolve_address(std::string_view address) override { if (!m_resolver) throw std::runtime_error("import_ref_resolver::resolve_address: formula resolver is null!"); ixion::formula_name_t name = m_resolver->resolve(address, ixion::abs_address_t()); if (name.type != ixion::formula_name_t::cell_reference) { std::ostringstream os; os << address << " is not a valid cell address."; throw orcus::invalid_arg_error(os.str()); } auto addr = std::get(name.value); src_address_t ret; ret.sheet = addr.sheet; ret.column = addr.column; ret.row = addr.row; return ret; } virtual src_range_t resolve_range(std::string_view range) override { if (!m_resolver) throw std::runtime_error("import_ref_resolver::resolve_range: formula resolver is null!"); ixion::formula_name_t name = m_resolver->resolve(range, ixion::abs_address_t()); switch (name.type) { case ixion::formula_name_t::range_reference: { auto v = std::get(name.value); src_range_t ret; ret.first.sheet = v.first.sheet; ret.first.column = v.first.column; ret.first.row = v.first.row; ret.last.sheet = v.last.sheet; ret.last.column = v.last.column; ret.last.row = v.last.row; return ret; } case ixion::formula_name_t::cell_reference: { // Single cell address is still considered a valid "range". auto addr = std::get(name.value); src_address_t cell; cell.sheet = addr.sheet; cell.column = addr.column; cell.row = addr.row; src_range_t ret; ret.first = cell; ret.last = cell; return ret; } default: ; } std::ostringstream os; os << "'" << range << "' is not a valid range address."; throw orcus::invalid_arg_error(os.str()); } }; class import_global_named_exp : public iface::import_named_expression { document& m_doc; std::string_view m_name; ixion::abs_address_t m_base; ixion::formula_tokens_t m_tokens; void define(std::string_view name, std::string_view expression, formula_ref_context_t ref_cxt) { string_pool& sp = m_doc.get_string_pool(); m_name = sp.intern(name).first; const ixion::formula_name_resolver* resolver = m_doc.get_formula_name_resolver(ref_cxt); assert(resolver); ixion::model_context& cxt = m_doc.get_model_context(); m_tokens = ixion::parse_formula_string(cxt, m_base, *resolver, expression); } public: import_global_named_exp(document& doc) : m_doc(doc), m_base(0, 0, 0) {} virtual ~import_global_named_exp() override {} virtual void set_base_position(const src_address_t& pos) override { m_base.sheet = pos.sheet; m_base.row = pos.row; m_base.column = pos.column; } virtual void set_named_expression(std::string_view name, std::string_view expression) override { define(name, expression, formula_ref_context_t::global); } virtual void set_named_range(std::string_view name, std::string_view range) override { define(name, range, formula_ref_context_t::named_range); } virtual void commit() override { ixion::model_context& cxt = m_doc.get_model_context(); cxt.set_named_expression(std::string{m_name}, m_base, std::move(m_tokens)); m_name = std::string_view{}; m_base.sheet = 0; m_base.row = 0; m_base.column = 0; } }; using sheet_ifaces_type = std::vector>; } // anonymous namespace import_factory_config::import_factory_config() = default; import_factory_config::import_factory_config(const import_factory_config& other) = default; import_factory_config::~import_factory_config() = default; import_factory_config& import_factory_config::operator=(const import_factory_config& other) = default; struct import_factory::impl { std::shared_ptr m_config; import_factory& m_envelope; document& m_doc; view* m_view; character_set_t m_charset; import_global_settings m_global_settings; import_pivot_cache_def m_pc_def; import_pivot_cache_records m_pc_records; import_ref_resolver m_ref_resolver; import_global_named_exp m_global_named_exp; import_styles m_styles; detail::import_shared_strings shared_strings; sheet_ifaces_type m_sheets; bool m_recalc_formula_cells; formula_error_policy_t m_error_policy; impl(import_factory& envelope, document& doc) : m_config(std::make_shared()), m_envelope(envelope), m_doc(doc), m_view(nullptr), m_charset(character_set_t::unspecified), m_global_settings(envelope, doc), m_pc_def(doc), m_pc_records(doc), m_ref_resolver(doc), m_global_named_exp(doc), m_styles(m_config, doc.get_styles(), doc.get_string_pool()), shared_strings(doc.get_string_pool(), doc.get_model_context(), doc.get_styles(), doc.get_shared_strings()), m_recalc_formula_cells(false), m_error_policy(formula_error_policy_t::fail) { } }; import_factory::import_factory(document& doc) : mp_impl(std::make_unique(*this, doc)) {} import_factory::import_factory(document& doc, view& view_store) : mp_impl(std::make_unique(*this, doc)) { // Store the optional view store. mp_impl->m_view = &view_store; } import_factory::~import_factory() {} iface::import_global_settings* import_factory::get_global_settings() { return &mp_impl->m_global_settings; } iface::import_shared_strings* import_factory::get_shared_strings() { return &mp_impl->shared_strings; } iface::import_styles* import_factory::get_styles() { return &mp_impl->m_styles; } iface::import_named_expression* import_factory::get_named_expression() { return &mp_impl->m_global_named_exp; } iface::import_reference_resolver* import_factory::get_reference_resolver(formula_ref_context_t cxt) { mp_impl->m_ref_resolver.set_formula_ref_context(cxt); return &mp_impl->m_ref_resolver; } iface::import_pivot_cache_definition* import_factory::create_pivot_cache_definition( pivot_cache_id_t cache_id) { mp_impl->m_pc_def.create_cache(cache_id); return &mp_impl->m_pc_def; } iface::import_pivot_cache_records* import_factory::create_pivot_cache_records( orcus::spreadsheet::pivot_cache_id_t cache_id) { pivot_collection& pcs = mp_impl->m_doc.get_pivot_collection(); pivot_cache* pc = pcs.get_cache(cache_id); if (!pc) return nullptr; mp_impl->m_pc_records.set_cache(pc); return &mp_impl->m_pc_records; } iface::import_sheet* import_factory::append_sheet(sheet_t sheet_index, std::string_view name) { assert(sheet_index == static_cast(mp_impl->m_doc.get_sheet_count())); sheet* sh = mp_impl->m_doc.append_sheet(name); if (!sh) return nullptr; sheet_view* sv = nullptr; if (mp_impl->m_view) sv = mp_impl->m_view->get_or_create_sheet_view(sheet_index); mp_impl->m_sheets.push_back( std::make_unique(mp_impl->m_doc, *sh, sv)); import_sheet* p = mp_impl->m_sheets.back().get(); p->set_character_set(mp_impl->m_charset); p->set_fill_missing_formula_results(!mp_impl->m_recalc_formula_cells); p->set_formula_error_policy(mp_impl->m_error_policy); return p; } iface::import_sheet* import_factory::get_sheet(std::string_view name) { sheet_t si = mp_impl->m_doc.get_sheet_index(name); if (si == ixion::invalid_sheet) return nullptr; return mp_impl->m_sheets.at(si).get(); } iface::import_sheet* import_factory::get_sheet(sheet_t sheet_index) { if (sheet_index < 0 || size_t(sheet_index) >= mp_impl->m_sheets.size()) return nullptr; return mp_impl->m_sheets[sheet_index].get(); } void import_factory::finalize() { mp_impl->m_doc.finalize_import(); if (mp_impl->m_recalc_formula_cells) mp_impl->m_doc.recalc_formula_cells(); } void import_factory::set_config(const import_factory_config& config) { // NB: update the object state. *mp_impl->m_config = config; } void import_factory::set_default_row_size(row_t row_size) { range_size_t ss = mp_impl->m_doc.get_sheet_size(); ss.rows = row_size; mp_impl->m_doc.set_sheet_size(ss); } void import_factory::set_default_column_size(col_t col_size) { range_size_t ss = mp_impl->m_doc.get_sheet_size(); ss.columns = col_size; mp_impl->m_doc.set_sheet_size(ss); } void import_factory::set_character_set(character_set_t charset) { mp_impl->m_charset = charset; for (std::unique_ptr& sheet : mp_impl->m_sheets) sheet->set_character_set(charset); } character_set_t import_factory::get_character_set() const { return mp_impl->m_charset; } void import_factory::set_recalc_formula_cells(bool b) { mp_impl->m_recalc_formula_cells = b; } void import_factory::set_formula_error_policy(formula_error_policy_t policy) { mp_impl->m_error_policy = policy; } struct export_factory::impl { const document& m_doc; std::vector> m_sheets; std::unordered_map m_sheet_index_map; impl(const document& doc) : m_doc(doc) {} export_sheet* get_sheet(std::string_view name) { auto it = m_sheet_index_map.find(name); if (it != m_sheet_index_map.end()) { // Instance for this sheet already exists. sheet_t sheet_pos = it->second; assert(size_t(sheet_pos) < m_sheets.size()); return m_sheets[sheet_pos].get(); } const sheet* sh = m_doc.get_sheet(name); if (!sh) return nullptr; sheet_t sheet_pos = m_sheets.size(); m_sheets.emplace_back(std::make_unique(m_doc, *sh)); m_sheet_index_map.insert( std::make_pair(name, sheet_pos)); return m_sheets[sheet_pos].get(); } }; export_factory::export_factory(const document& doc) : mp_impl(std::make_unique(doc)) {} export_factory::~export_factory() {} const iface::export_sheet* export_factory::get_sheet(std::string_view sheet_name) const { return mp_impl->get_sheet(sheet_name); } }} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */