diff options
Diffstat (limited to 'src/spreadsheet/debug_state_dumper.cpp')
-rw-r--r-- | src/spreadsheet/debug_state_dumper.cpp | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/src/spreadsheet/debug_state_dumper.cpp b/src/spreadsheet/debug_state_dumper.cpp new file mode 100644 index 0000000..c748924 --- /dev/null +++ b/src/spreadsheet/debug_state_dumper.cpp @@ -0,0 +1,460 @@ +/* -*- 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 "debug_state_dumper.hpp" +#include "check_dumper.hpp" +#include "document_impl.hpp" +#include "sheet_impl.hpp" +#include "ostream_utils.hpp" + +#include <ixion/formula_name_resolver.hpp> +#include <ixion/named_expressions_iterator.hpp> + +#include <fstream> +#include <algorithm> + +namespace orcus { namespace spreadsheet { namespace detail { + +namespace { + +void print_named_expressions(const ixion::model_context& cxt, ixion::named_expressions_iterator iter, std::ostream& os) +{ + auto resolver = ixion::formula_name_resolver::get(ixion::formula_name_resolver_t::excel_a1, &cxt); + + if (!resolver) + return; + + const ixion::abs_address_t origin{0, 0, 0}; + ixion::print_config config; + config.display_sheet = ixion::display_sheet_t::always; + + for (; iter.has(); iter.next()) + { + auto name = iter.get(); + + std::string exp = ixion::print_formula_tokens( + config, cxt, origin, *resolver, name.expression->tokens); + + os << "- name: " << *name.name << std::endl; + os << " origin: " << resolver->get_name(name.expression->origin, origin, true) << std::endl; + os << " expression: " << exp << std::endl; + } +} + +} // anonymous namespace + +doc_debug_state_dumper::doc_debug_state_dumper(const document_impl& doc) : m_doc(doc) +{ +} + +void doc_debug_state_dumper::dump(const fs::path& outdir) const +{ + dump_properties(outdir); + dump_styles(outdir); + dump_named_expressions(outdir); +} + +void doc_debug_state_dumper::dump_properties(const fs::path& outdir) const +{ + const fs::path outpath = outdir / "properties.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + of << "formula-grammar: " << m_doc.grammar << std::endl; + of << "origin-date: " << m_doc.origin_date << std::endl; + of << "output-precision: " << short(m_doc.doc_config.output_precision) << std::endl; +} + +void doc_debug_state_dumper::dump_styles(const fs::path& outdir) const +{ + const fs::path outpath = outdir / "styles.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + of << std::boolalpha; + + auto to_string = [](std::optional<bool> v) -> std::string + { + if (!v) + return "(unset)"; + + return *v ? "true" : "false"; + }; + + auto dump_xf = [&of,to_string](std::size_t i, const cell_format_t& xf) + { + of << " - id: " << i << std::endl + << " font: " << xf.font << std::endl + << " fill: " << xf.fill << std::endl + << " border: " << xf.border << std::endl + << " protection: " << xf.protection << std::endl + << " number-format: " << xf.number_format << std::endl + << " style-xf: " << xf.style_xf << std::endl + << " horizontal-alignment: " << xf.hor_align << std::endl + << " vertical-alignment: " << xf.ver_align << std::endl + << " apply-number-format: " << xf.apply_num_format << std::endl + << " apply-font: " << xf.apply_font << std::endl + << " apply-fill: " << xf.apply_fill << std::endl + << " apply-border: " << xf.apply_border << std::endl + << " apply-alignment: " << xf.apply_alignment << std::endl + << " apply-protection: " << xf.apply_protection << std::endl + << " wrap-text: " << to_string(xf.wrap_text) << std::endl + << " shrink-to-fit: " << to_string(xf.shrink_to_fit) << std::endl; + }; + + auto optional_value = [&of](std::string_view name, const auto& v, int level=2) + { + // v is of type std::optional<T>. + + constexpr char q = '"'; + constexpr const char* indent_unit_s = " "; + + std::string indent = indent_unit_s; + for (int i = 0; i < level - 1; ++i) + indent += indent_unit_s; + + of << indent << name << ": "; + + if (v) + { + std::ostringstream os; + os << *v; + std::string s = os.str(); + bool quote = s.find_first_of("#:-") != s.npos; + if (quote) + of << q << s << q; + else + of << s; + } + else + of << "(unset)"; + + of << std::endl; + }; + + auto dump_border = [&optional_value](const border_attrs_t& _attrs) + { + optional_value("style", _attrs.style, 3); + optional_value("color", _attrs.border_color, 3); + optional_value("width", _attrs.border_width, 3); + }; + + of << "cell-styles:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_cell_styles_count(); ++i) + { + const cell_style_t* cs = m_doc.styles_store.get_cell_style(i); + assert(cs); + + of << " - id: " << i << std::endl + << " name: " << cs->name << std::endl + << " display-name: " << cs->display_name << std::endl + << " parent: " << cs->parent_name << std::endl + << " xf: " << cs->xf << std::endl + << " builtin: " << cs->builtin << std::endl; + } + + of << "cell-style-formats:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_cell_style_formats_count(); ++i) + { + const cell_format_t* xf = m_doc.styles_store.get_cell_style_format(i); + assert(xf); + dump_xf(i, *xf); + } + + of << "cell-formats:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_cell_formats_count(); ++i) + { + const cell_format_t* xf = m_doc.styles_store.get_cell_format(i); + assert(xf); + dump_xf(i, *xf); + } + + of << "fonts:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_font_count(); ++i) + { + const font_t* font = m_doc.styles_store.get_font(i); + assert(font); + + of << " - id: " << i << std::endl; + optional_value("name", font->name, 2); + optional_value("name-asian", font->name_asian, 2); + optional_value("name-complex", font->name_complex, 2); + optional_value("size", font->size, 2); + optional_value("size-asian", font->size_asian, 2); + optional_value("size-complex", font->size_complex, 2); + optional_value("bold", font->bold, 2); + optional_value("bold-asian", font->bold_asian, 2); + optional_value("bold-complex", font->bold_complex, 2); + optional_value("italic", font->italic, 2); + optional_value("italic-asian", font->italic_asian, 2); + optional_value("italic-complex", font->italic_complex, 2); + optional_value("underline-style", font->underline_style, 2); + optional_value("underline-width", font->underline_width, 2); + optional_value("underline-mode", font->underline_mode, 2); + optional_value("underline-type", font->underline_type, 2); + optional_value("underline-color", font->underline_color, 2); + optional_value("color", font->color, 2); + optional_value("strikethrough-style", font->strikethrough_style, 2); + optional_value("strikethrough-width", font->strikethrough_width, 2); + optional_value("strikethrough-type", font->strikethrough_type, 2); + optional_value("strikethrough-text", font->strikethrough_text, 2); + } + + of << "fills:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_fill_count(); ++i) + { + const fill_t* fill = m_doc.styles_store.get_fill(i); + assert(fill); + + of << " - id: " << i << std::endl; + optional_value("pattern", fill->pattern_type, 2); + optional_value("fg-color", fill->fg_color, 2); + optional_value("bg-color", fill->bg_color, 2); + } + + of << "borders:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_border_count(); ++i) + { + const border_t* border = m_doc.styles_store.get_border(i); + assert(border); + + of << " - id: " << i << std::endl; + + of << " top:" << std::endl; + dump_border(border->top); + of << " bottom:" << std::endl; + dump_border(border->bottom); + of << " left:" << std::endl; + dump_border(border->left); + of << " right:" << std::endl; + dump_border(border->right); + of << " diagonal:" << std::endl; + dump_border(border->diagonal); + of << " diagonal-bl-tr:" << std::endl; + dump_border(border->diagonal_bl_tr); + of << " diagonal-tl-br:" << std::endl; + dump_border(border->diagonal_tl_br); + } + + of << "protections:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_protection_count(); ++i) + { + const protection_t* prot = m_doc.styles_store.get_protection(i); + assert(prot); + + of << " - id: " << i << std::endl; + optional_value("locked", prot->locked, 2); + optional_value("hidden", prot->hidden, 2); + optional_value("print-content", prot->print_content, 2); + optional_value("formula-hidden", prot->formula_hidden, 2); + } + + of << "number-formats:" << std::endl; + + for (std::size_t i = 0; i < m_doc.styles_store.get_number_format_count(); ++i) + { + const number_format_t* numfmt = m_doc.styles_store.get_number_format(i); + assert(numfmt); + + of << " - id: " << i << std::endl; + optional_value("identifier", numfmt->identifier, 2); + optional_value("format-string", numfmt->format_string, 2); + } +} + +void doc_debug_state_dumper::dump_named_expressions(const fs::path& outdir) const +{ + const fs::path outpath = outdir / "named-expressions.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + print_named_expressions(m_doc.context, m_doc.context.get_named_expressions_iterator(), of); +} + +sheet_debug_state_dumper::sheet_debug_state_dumper(const sheet_impl& sheet, std::string_view sheet_name) : + m_sheet(sheet), m_sheet_name(sheet_name) {} + +void sheet_debug_state_dumper::dump(const fs::path& outdir) const +{ + dump_cell_values(outdir); + dump_cell_formats(outdir); + dump_column_formats(outdir); + dump_row_formats(outdir); + dump_column_widths(outdir); + dump_row_heights(outdir); + dump_auto_filter(outdir); + dump_named_expressions(outdir); +} + +void sheet_debug_state_dumper::dump_cell_values(const fs::path& outdir) const +{ + check_dumper dumper{m_sheet, m_sheet_name}; + fs::path outpath = outdir / "cell-values.txt"; + std::ofstream of{outpath.native()}; + if (of) + dumper.dump(of); +} + +void sheet_debug_state_dumper::dump_cell_formats(const fs::path& outdir) const +{ + fs::path outpath = outdir / "cell-formats.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + std::vector<col_t> columns; + for (const auto& node : m_sheet.cell_formats) + columns.push_back(node.first); + + std::sort(columns.begin(), columns.end()); + + for (const col_t col : columns) + { + of << "column: " << col << std::endl; + + auto it = m_sheet.cell_formats.find(col); + assert(it != m_sheet.cell_formats.end()); + const segment_row_index_type& rows = *it->second; + + for (const auto& seg : rows.segment_range()) + { + // NB: end position is not inclusive. + of << " - rows: " << seg.start << '-' << (seg.end - 1) << std::endl; + of << " xf: " << seg.value << std::endl; + } + } +} + +void sheet_debug_state_dumper::dump_column_formats(const fs::path& outdir) const +{ + fs::path outpath = outdir / "column-formats.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + for (const auto& seg : m_sheet.column_formats.segment_range()) + { + of << "- columns: " << seg.start << '-' << (seg.end - 1) << std::endl; + of << " xf: " << seg.value << std::endl; + } +} + +void sheet_debug_state_dumper::dump_row_formats(const fs::path& outdir) const +{ + fs::path outpath = outdir / "row-formats.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + for (const auto& seg : m_sheet.row_formats.segment_range()) + { + of << "- rows: " << seg.start << '-' << (seg.end - 1) << std::endl; + of << " xf: " << seg.value << std::endl; + } +} + +void sheet_debug_state_dumper::dump_column_widths(const fs::path& outdir) const +{ + fs::path outpath = outdir / "column-widths.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + for (const auto& seg : m_sheet.col_widths.segment_range()) + { + of << "- columns: " << seg.start << '-' << (seg.end - 1) << std::endl; + of << " width: "; + + if (seg.value == get_default_column_width()) + of << "(default)"; + else + of << seg.value; + + of << std::endl; + } +} + +void sheet_debug_state_dumper::dump_row_heights(const fs::path& outdir) const +{ + fs::path outpath = outdir / "row-heights.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + for (const auto& seg : m_sheet.row_heights.segment_range()) + { + of << "- rows: " << seg.start << '-' << (seg.end - 1) << std::endl; + of << " height: "; + + if (seg.value == get_default_row_height()) + of << "(default)"; + else + of << seg.value; + + of << std::endl; + } +} + +void sheet_debug_state_dumper::dump_auto_filter(const fs::path& outdir) const +{ + if (!m_sheet.auto_filter_data) + return; + + fs::path outpath = outdir / "auto-filter.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + const auto_filter_t& data = *m_sheet.auto_filter_data; + + auto resolver = ixion::formula_name_resolver::get( + ixion::formula_name_resolver_t::excel_a1, nullptr); + + if (!resolver) + return; + + ixion::abs_address_t origin; + ixion::range_t name{data.range}; + name.set_absolute(false); + + of << "range: " << resolver->get_name(name, origin, false) << "\n"; + of << "columns:\n"; + + for (const auto& [col, cdata] : data.columns) + { + of << "- column: " << col << "\n"; + of << " match-values:\n"; + + for (const auto& v : cdata.match_values) + of << " - " << v << std::endl; + } +} + +void sheet_debug_state_dumper::dump_named_expressions(const fs::path& outdir) const +{ + const fs::path outpath = outdir / "named-expressions.yaml"; + std::ofstream of{outpath.native()}; + if (!of) + return; + + const ixion::model_context& cxt = m_sheet.doc.get_model_context(); + print_named_expressions(cxt, cxt.get_named_expressions_iterator(m_sheet.sheet_id), of); +} + +}}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |