diff options
Diffstat (limited to '')
-rw-r--r-- | src/spreadsheet/factory.cpp | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/src/spreadsheet/factory.cpp b/src/spreadsheet/factory.cpp new file mode 100644 index 0000000..9cd7884 --- /dev/null +++ b/src/spreadsheet/factory.cpp @@ -0,0 +1,410 @@ +/* -*- 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 <orcus/spreadsheet/shared_strings.hpp> +#include <orcus/spreadsheet/styles.hpp> +#include <orcus/spreadsheet/sheet.hpp> +#include <orcus/spreadsheet/document.hpp> +#include <orcus/spreadsheet/view.hpp> +#include <orcus/exception.hpp> +#include <orcus/string_pool.hpp> + +#include "factory_pivot.hpp" +#include "factory_shared_strings.hpp" +#include "factory_sheet.hpp" +#include "global_settings.hpp" + +#include <ixion/formula_name_resolver.hpp> +#include <ixion/formula_tokens.hpp> +#include <ixion/formula.hpp> +#include <ixion/model_context.hpp> +#include <sstream> +#include <iostream> +#include <unordered_map> + +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<ixion::address_t>(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<ixion::range_t>(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<ixion::address_t>(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<std::unique_ptr<import_sheet>>; + +} // 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<import_factory_config> 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<import_factory_config>()), + 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<impl>(*this, doc)) {} + +import_factory::import_factory(document& doc, view& view_store) : + mp_impl(std::make_unique<impl>(*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<sheet_t>(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<import_sheet>(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<import_sheet>& 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<std::unique_ptr<export_sheet>> m_sheets; + std::unordered_map<std::string_view, sheet_t> 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<export_sheet>(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<impl>(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: */ |