diff --git a/AGENTS.md b/AGENTS.md index de871673b..12c0b49ba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,20 +35,34 @@ bytes ─▶ magic/open_strategy ─▶ DecodedFile ─▶ Document ─▶ Eleme ### The element-adapter pattern (every engine follows it) -- An **`ElementRegistry`**: a flat `std::vector` (id = index + 1); each - `Element` holds `parent`/`first_child`/`last_child`/`prev`/`next` ids and a - `type`, plus side maps for per-type payloads. Builders: `create_element` / - `create_*_element` / `append_child`. Minimal example: - `oldms/presentation/ppt_element_registry.*`. +**The machinery is shared — do not write it again.** `internal/common/element_registry.hpp` +and `internal/common/element_adapter.hpp` hold it; an engine writes only what +its own format has. Compact example: `rtf/rtf_element_registry.*` + +`rtf/rtf_document.cpp`. + +- An **`ElementRegistry`**: `internal::ElementRegistry` is the flat + store (id = index + 1, a `std::deque` so `create_element_` can hand back a + reference), with `element_at`, `append_child`, `link_child` and + `check_element_id`. The engine derives `struct RegistryElement final : + ElementNode` — adding a field only if it has one, as odf/ooxml add the + `pugi::xml_node` — and declares its per-type payloads as `SideTable` + (hashed) or `SortedSideTable` (binary search, for payloads written in + id order). Both carry their own bounds check, so an accessor is + `return m_texts.at(id);` and nothing else. Only the `create_*_element` and + the secondary-chain `append_*` are per engine. - An **`ElementIdentifier` is opaque, and 64 bits wide**: registry engines use it as index + 1, `csv` packs a kind, a row and a column into its bits. An engine - wanting a narrower id narrows it inside its own store (`odf`'s - `ElementRegistry::StoredId`) and widens at the boundary. -- An **`ElementAdapter`**: one class implementing `abstract::ElementAdapter` (tree - navigation by id) and, via multiple inheritance, the per-type adapters it - supports (`SlideAdapter`, `ParagraphAdapter`, …). Each `*_adapter(id)` returns - `this` when the element is that type, else `nullptr`. Compact example: - `oldms/presentation/ppt_document.cpp`. + wanting a narrower id passes it as `internal::ElementRegistry`'s `Id` + (`odf::StoredId`) and widens at the boundary. +- An **`ElementAdapter`**: `internal::RegistryElementAdapter` + implements the tree navigation over the registry, and its base + `internal::ElementAdapter` inherits the per-type adapters named in + the pack and answers **every** `*_adapter(id)` hook from them — an engine + writes no hook of its own, it only lists the adapters. `element_is_unique` and + `element_is_self_locatable` default to true and `element_is_editable` to + false; override only where that is wrong (odf, ooxml text). An engine with no + registry (`csv`) derives from `internal::ElementAdapter` and writes its own + navigation. `ElementType` is the shared enum in `src/odr/document_element.hpp`. @@ -64,7 +78,7 @@ producer's layout recorded — odf's `text:soft-page-break` — are not parsed. |------|------| | `src/odr/*.hpp` | **Public API**: `file`, `document`, `document_element`, `html`, `style`, `quantity` (`Measure`), `odr`. | | `src/odr/internal/abstract/` | Core interfaces: `File`/`DecodedFile`, `Document` + `ElementAdapter`, `Filesystem`, `Archive`, `HtmlService`. | -| `src/odr/internal/common/` | Reusable impls: `Path`/`AbsPath`, base `Document`, filesystem, `style`, table cursor/range, temp files. | +| `src/odr/internal/common/` | Reusable impls: `Path`/`AbsPath`, base `Document`, the shared `ElementRegistry` + `ElementAdapter`, filesystem, `style`, table cursor/range, temp files. | | `src/odr/internal/util/` | Helpers: `byte_stream_util`, `string_util`, `stream_util`, `document_util`. | | `src/odr/internal/magic.*`, `open_strategy.*` | File-type detection + open/dispatch. | | `src/odr/internal/file_type_table.*` | **The** per-`FileType` table: extensions, MIME types, category, document type, `FileTypeCapabilities`. Every public lookup in `odr.hpp` is a thin forward into it — extend the table, not the lookups. | @@ -247,9 +261,10 @@ Dispatch `release.yml` against main, publish the draft that appears — alias is claimed twice, or if the declared capabilities exceed what the engines actually do. 2. For documents: subclass `internal::Document`; in its constructor build an - `ElementRegistry` and an `ElementAdapter` (pattern above). It defaults to - read-only — override `is_editable`/`is_savable`/`save` only for an engine - that can write. + `ElementRegistry` (derived from `internal::ElementRegistry`) and an + `ElementAdapter` (derived from `internal::RegistryElementAdapter`) — pattern + above, and neither is written from scratch. It defaults to read-only — + override `is_editable`/`is_savable`/`save` only for an engine that can write. 3. Implement the per-element adapters you can populate; the **generic HTML renderer then works for free**. 4. Register the factory (e.g. `oldms_file.cpp::document()` switches on diff --git a/src/odr/internal/common/element_adapter.hpp b/src/odr/internal/common/element_adapter.hpp new file mode 100644 index 000000000..624d31cff --- /dev/null +++ b/src/odr/internal/common/element_adapter.hpp @@ -0,0 +1,197 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace odr::internal { + +/// Answers the `*_adapter(id)` hooks for the @p Adapters it is given, and +/// inherits them: a hook whose adapter is in the pack returns `this` for its +/// element type, the rest keep the abstract nullptr. +template +class ElementAdapter : public abstract::ElementAdapter, public Adapters... { +public: + [[nodiscard]] bool element_is_unique( + [[maybe_unused]] const ElementIdentifier element_id) const override { + return true; + } + [[nodiscard]] bool element_is_self_locatable( + [[maybe_unused]] const ElementIdentifier element_id) const override { + return true; + } + [[nodiscard]] bool element_is_editable( + [[maybe_unused]] const ElementIdentifier element_id) const override { + return false; + } + [[nodiscard]] DocumentPath + element_document_path(const ElementIdentifier element_id) const override { + return util::document::extract_path(*this, element_id, null_element_id); + } + [[nodiscard]] ElementIdentifier + element_navigate_path(const ElementIdentifier element_id, + const DocumentPath &path) const override { + return util::document::navigate_path(*this, element_id, path); + } + + [[nodiscard]] const abstract::TextRootAdapter * + text_root_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::SlideAdapter * + slide_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::PageAdapter * + page_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::SheetAdapter * + sheet_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::SheetCellAdapter * + sheet_cell_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::MasterPageAdapter * + master_page_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::LineBreakAdapter * + line_break_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::ParagraphAdapter * + paragraph_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::SpanAdapter * + span_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::TextAdapter * + text_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::LinkAdapter * + link_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::BookmarkAdapter * + bookmark_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::ListAdapter * + list_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::ListItemAdapter * + list_item_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::TableAdapter * + table_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::TableColumnAdapter * + table_column_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::TableRowAdapter * + table_row_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::TableCellAdapter * + table_cell_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::FrameAdapter * + frame_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::RectAdapter * + rect_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::LineAdapter * + line_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::CircleAdapter * + circle_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + [[nodiscard]] const abstract::CustomShapeAdapter * + custom_shape_adapter(const ElementIdentifier element_id) const override { + return adapter_( + element_id); + } + [[nodiscard]] const abstract::ImageAdapter * + image_adapter(const ElementIdentifier element_id) const override { + return adapter_(element_id); + } + +private: + template + [[nodiscard]] const Adapter * + adapter_(const ElementIdentifier element_id) const { + if constexpr ((std::is_same_v || ...)) { + return element_type(element_id) == type ? this : nullptr; + } else { + return nullptr; + } + } +}; + +/// Navigates the tree of a registry whose `element_at(id)` yields the links. +template +class RegistryElementAdapter : public ElementAdapter { +public: + explicit RegistryElementAdapter(Registry ®istry) : m_registry(®istry) {} + + [[nodiscard]] ElementType + element_type(const ElementIdentifier element_id) const override { + return m_registry->element_at(element_id).type; + } + + [[nodiscard]] ElementIdentifier + element_parent(const ElementIdentifier element_id) const override { + return m_registry->element_at(element_id).parent_id; + } + [[nodiscard]] ElementIdentifier + element_first_child(const ElementIdentifier element_id) const override { + return m_registry->element_at(element_id).first_child_id; + } + [[nodiscard]] ElementIdentifier + element_last_child(const ElementIdentifier element_id) const override { + return m_registry->element_at(element_id).last_child_id; + } + [[nodiscard]] ElementIdentifier + element_previous_sibling(const ElementIdentifier element_id) const override { + return m_registry->element_at(element_id).previous_sibling_id; + } + [[nodiscard]] ElementIdentifier + element_next_sibling(const ElementIdentifier element_id) const override { + return m_registry->element_at(element_id).next_sibling_id; + } + +protected: + Registry *m_registry{nullptr}; +}; + +} // namespace odr::internal diff --git a/src/odr/internal/common/element_registry.hpp b/src/odr/internal/common/element_registry.hpp new file mode 100644 index 000000000..cc07aaada --- /dev/null +++ b/src/odr/internal/common/element_registry.hpp @@ -0,0 +1,202 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace odr::internal { + +/// The tree links every registry element carries, stored @p Id wide. +template struct ElementNode { + Id parent_id{null_element_id}; + Id first_child_id{null_element_id}; + Id last_child_id{null_element_id}; + Id previous_sibling_id{null_element_id}; + Id next_sibling_id{null_element_id}; + ElementType type{ElementType::none}; +}; + +/// A per-type payload keyed by element id, hashed, filled in any order. +/// Prefer @ref SortedSideTable for a payload written when its element is +/// created: it is a binary search over a narrow-keyed array instead. +template class SideTable final { +public: + T &emplace(const ElementIdentifier id, T value) { + return m_entries.insert_or_assign(id, std::move(value)).first->second; + } + + [[nodiscard]] T *find(const ElementIdentifier id) { + return find_(m_entries, id); + } + [[nodiscard]] const T *find(const ElementIdentifier id) const { + return find_(m_entries, id); + } + + [[nodiscard]] T &at(const ElementIdentifier id) { return at_(*this, id); } + [[nodiscard]] const T &at(const ElementIdentifier id) const { + return at_(*this, id); + } + +private: + std::unordered_map m_entries; + + /// One body for both constnesses: the argument carries the const and the + /// deduced return takes it on. + template + static auto *find_(Entries &entries, const ElementIdentifier id) { + const auto it = entries.find(id); + return it != std::end(entries) ? &it->second : nullptr; + } + + template + static auto &at_(Self &self, const ElementIdentifier id) { + auto *entry = self.find(id); + if (entry == nullptr) { + throw std::out_of_range("SideTable::at: identifier not found"); + } + return *entry; + } +}; + +/// A per-type payload appended as its elements are created, so the ids only +/// grow and a lookup is a binary search. `emplace` refuses one out of order, +/// and hands out a reference — hence a deque. +template +class SortedSideTable final { +public: + T &emplace(const ElementIdentifier id, T value) { + if (!m_entries.empty() && m_entries.back().first >= id) { + throw std::invalid_argument( + "SortedSideTable::emplace: identifier out of order"); + } + return m_entries.emplace_back(static_cast(id), std::move(value)).second; + } + + [[nodiscard]] T *find(const ElementIdentifier id) { + return find_(m_entries, id); + } + [[nodiscard]] const T *find(const ElementIdentifier id) const { + return find_(m_entries, id); + } + + [[nodiscard]] T &at(const ElementIdentifier id) { return at_(*this, id); } + [[nodiscard]] const T &at(const ElementIdentifier id) const { + return at_(*this, id); + } + + [[nodiscard]] auto begin() const noexcept { return m_entries.begin(); } + [[nodiscard]] auto end() const noexcept { return m_entries.end(); } + [[nodiscard]] auto begin() noexcept { return m_entries.begin(); } + [[nodiscard]] auto end() noexcept { return m_entries.end(); } + +private: + using Entry = std::pair; + + std::deque m_entries; + + /// One body for both constnesses: the argument carries the const and the + /// deduced return takes it on. + template + static auto *find_(Entries &entries, const ElementIdentifier id) { + const auto it = std::ranges::lower_bound(entries, id, {}, &Entry::first); + return it != std::end(entries) && it->first == id ? &it->second : nullptr; + } + + template + static auto &at_(Self &self, const ElementIdentifier id) { + auto *entry = self.find(id); + if (entry == nullptr) { + throw std::out_of_range("SortedSideTable::at: identifier not found"); + } + return *entry; + } +}; + +/// The flat store an engine builds its element tree in: an id is the index +/// plus one, and @p ElementT derives from @ref ElementNode for the links. +/// +/// A deque, not a vector: `create_element_` hands back a reference the parser +/// holds on to, and a vector both invalidates it and peaks holding two copies. +template +class ElementRegistry { +public: + using Element = ElementT; + + [[nodiscard]] std::size_t size() const noexcept { return m_elements.size(); } + + [[nodiscard]] Element &element_at(const ElementIdentifier id) { + check_element_id(id); + return m_elements[id - 1]; + } + + [[nodiscard]] const Element &element_at(const ElementIdentifier id) const { + check_element_id(id); + return m_elements[id - 1]; + } + + void append_child(const ElementIdentifier parent_id, + const ElementIdentifier child_id) { + Element &parent = element_at(parent_id); + link_child(parent_id, child_id, parent.first_child_id, + parent.last_child_id); + } + +protected: + ~ElementRegistry() = default; + + std::tuple create_element_(ElementType type) { + if (m_elements.size() >= std::numeric_limits::max()) { + throw std::overflow_error( + "ElementRegistry::create_element: out of identifiers"); + } + + Element &element = m_elements.emplace_back(); + const ElementIdentifier element_id = m_elements.size(); + element.type = type; + return {element_id, element}; + } + + /// Links @p child_id as the last child of the chain @p first_id / @p last_id + /// - the element's own, or one of the secondary chains a payload holds. + void link_child(const ElementIdentifier parent_id, + const ElementIdentifier child_id, Id &first_id, Id &last_id) { + Element &child = element_at(child_id); + if (child.parent_id != null_element_id) { + throw std::invalid_argument( + "ElementRegistry::link_child: child already has a parent"); + } + + child.parent_id = static_cast(parent_id); + child.previous_sibling_id = last_id; + + if (first_id == null_element_id) { + first_id = static_cast(child_id); + } else { + element_at(last_id).next_sibling_id = static_cast(child_id); + } + last_id = static_cast(child_id); + } + + void check_element_id(const ElementIdentifier id) const { + if (id == null_element_id) { + throw std::out_of_range( + "ElementRegistry::check_element_id: null identifier"); + } + if (id - 1 >= m_elements.size()) { + throw std::out_of_range( + "ElementRegistry::check_element_id: identifier out of range"); + } + } + + std::deque m_elements; +}; + +} // namespace odr::internal diff --git a/src/odr/internal/csv/csv_document.cpp b/src/odr/internal/csv/csv_document.cpp index 1cc21e042..038eeca18 100644 --- a/src/odr/internal/csv/csv_document.cpp +++ b/src/odr/internal/csv/csv_document.cpp @@ -1,14 +1,13 @@ #include #include -#include #include #include #include #include +#include #include -#include #include #include @@ -54,10 +53,11 @@ std::uint32_t column_of(const ElementIdentifier element_id) { return static_cast(element_id & column_mask); } -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::SheetAdapter, - public abstract::SheetCellAdapter, - public abstract::TextAdapter { +using AdapterBase = + internal::ElementAdapter; + +class ElementAdapter final : public AdapterBase { public: explicit ElementAdapter(const CsvDocument &document) : m_document{&document} {} @@ -119,41 +119,6 @@ class ElementAdapter final : public abstract::ElementAdapter, return null_element_id; } - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return false; - } - [[nodiscard]] DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const SheetAdapter * - sheet_adapter(const ElementIdentifier element_id) const override { - return kind_of(element_id) == Kind::sheet ? this : nullptr; - } - [[nodiscard]] const SheetCellAdapter * - sheet_cell_adapter(const ElementIdentifier element_id) const override { - return kind_of(element_id) == Kind::cell ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return kind_of(element_id) == Kind::text ? this : nullptr; - } - // SheetAdapter [[nodiscard]] std::string sheet_name( diff --git a/src/odr/internal/iwork/iwork_document.cpp b/src/odr/internal/iwork/iwork_document.cpp index 502226801..c17bf45dd 100644 --- a/src/odr/internal/iwork/iwork_document.cpp +++ b/src/odr/internal/iwork/iwork_document.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -8,8 +7,8 @@ #include #include +#include #include -#include #include #include @@ -55,119 +54,17 @@ const ElementRegistry &Document::element_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::TextRootAdapter, - public abstract::SlideAdapter, - public abstract::SheetAdapter, - public abstract::SheetCellAdapter, - public abstract::TableAdapter, - public abstract::TableColumnAdapter, - public abstract::TableRowAdapter, - public abstract::TableCellAdapter, - public abstract::FrameAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::TextAdapter { -public: - explicit ElementAdapter(ElementRegistry ®istry) : m_registry(®istry) {} - - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::TextRootAdapter, abstract::SlideAdapter, + abstract::SheetAdapter, abstract::SheetCellAdapter, abstract::TableAdapter, + abstract::TableColumnAdapter, abstract::TableRowAdapter, + abstract::TableCellAdapter, abstract::FrameAdapter, + abstract::LineBreakAdapter, abstract::ParagraphAdapter, + abstract::TextAdapter>; - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return false; - } - [[nodiscard]] DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const TextRootAdapter * - text_root_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::root ? this : nullptr; - } - [[nodiscard]] const SlideAdapter * - slide_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::slide ? this : nullptr; - } - [[nodiscard]] const SheetAdapter * - sheet_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet ? this : nullptr; - } - [[nodiscard]] const SheetCellAdapter * - sheet_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet_cell ? this : nullptr; - } - [[nodiscard]] const TableAdapter * - table_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table ? this : nullptr; - } - [[nodiscard]] const TableColumnAdapter * - table_column_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_column ? this - : nullptr; - } - [[nodiscard]] const TableRowAdapter * - table_row_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_row ? this : nullptr; - } - [[nodiscard]] const TableCellAdapter * - table_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_cell ? this : nullptr; - } - [[nodiscard]] const FrameAdapter * - frame_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::frame ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } +class ElementAdapter final : public AdapterBase { +public: + explicit ElementAdapter(ElementRegistry ®istry) : AdapterBase(registry) {} // The page geometry sits in the document archive and the styles in // `Index/DocumentStylesheet.iwa`; neither is read yet. @@ -416,8 +313,6 @@ class ElementAdapter final : public abstract::ElementAdapter, } return points(*value); } - - ElementRegistry *m_registry{nullptr}; }; std::unique_ptr diff --git a/src/odr/internal/iwork/iwork_element_registry.cpp b/src/odr/internal/iwork/iwork_element_registry.cpp index d188b533c..a18e1e139 100644 --- a/src/odr/internal/iwork/iwork_element_registry.cpp +++ b/src/odr/internal/iwork/iwork_element_registry.cpp @@ -1,280 +1,85 @@ #include -#include -#include - namespace odr::internal::iwork { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_texts.clear(); - m_frames.clear(); - m_slides.clear(); - m_tables.clear(); - m_sheets.clear(); - m_cells.clear(); -} - ElementIdentifier ElementRegistry::Sheet::cell(const std::uint32_t column, const std::uint32_t row) const { const auto it = cells.find({row, column}); return it == cells.end() ? null_element_id : it->second; } -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type) { - Element &element = m_elements.emplace_back(); - ElementIdentifier element_id = m_elements.size(); - element.type = type; - return {element_id, element}; + return create_element_(type); } std::tuple ElementRegistry::create_text_element() { - const auto &[element_id, element] = create_element(ElementType::text); - auto [it, success] = m_texts.emplace(element_id, Text{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::text); + Text &text = m_texts.emplace(element_id, Text{}); + return {element_id, element, text}; } std::tuple ElementRegistry::create_frame_element() { - const auto &[element_id, element] = create_element(ElementType::frame); - auto [it, success] = m_frames.emplace(element_id, Frame{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::frame); + Frame &frame = m_frames.emplace(element_id, Frame{}); + return {element_id, element, frame}; } std::tuple ElementRegistry::create_slide_element() { - const auto &[element_id, element] = create_element(ElementType::slide); - auto [it, success] = m_slides.emplace(element_id, Slide{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::slide); + Slide &slide = m_slides.emplace(element_id, Slide{}); + return {element_id, element, slide}; } std::tuple ElementRegistry::create_table_element() { - const auto &[element_id, element] = create_element(ElementType::table); - auto [it, success] = m_tables.emplace(element_id, Table{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::table); + Table &table = m_tables.emplace(element_id, Table{}); + return {element_id, element, table}; } std::tuple ElementRegistry::create_sheet_element() { - const auto &[element_id, element] = create_element(ElementType::sheet); - auto [it, success] = m_sheets.emplace(element_id, Sheet{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::sheet); + Sheet &sheet = m_sheets.emplace(element_id, Sheet{}); + return {element_id, element, sheet}; } std::tuple ElementRegistry::create_cell_element(const ElementType type) { - const auto &[element_id, element] = create_element(type); - auto [it, success] = m_cells.emplace(element_id, Cell{}); - return {element_id, element, it->second}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -ElementRegistry::Frame & -ElementRegistry::frame_element_at(const ElementIdentifier id) { - check_frame_id(id); - return m_frames.at(id); -} - -ElementRegistry::Slide & -ElementRegistry::slide_element_at(const ElementIdentifier id) { - check_slide_id(id); - return m_slides.at(id); -} - -ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) { - check_table_id(id); - return m_tables.at(id); -} - -ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) { - check_sheet_id(id); - return m_sheets.at(id); -} - -ElementRegistry::Cell & -ElementRegistry::cell_element_at(const ElementIdentifier id) { - check_cell_id(id); - return m_cells.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Frame & -ElementRegistry::frame_element_at(const ElementIdentifier id) const { - check_frame_id(id); - return m_frames.at(id); -} - -const ElementRegistry::Slide & -ElementRegistry::slide_element_at(const ElementIdentifier id) const { - check_slide_id(id); - return m_slides.at(id); -} - -const ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) const { - check_table_id(id); - return m_tables.at(id); -} - -const ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) const { - check_sheet_id(id); - return m_sheets.at(id); -} - -const ElementRegistry::Cell & -ElementRegistry::cell_element_at(const ElementIdentifier id) const { - check_cell_id(id); - return m_cells.at(id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "ElementRegistry::append_child: child already has a parent"); - } - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + const auto &[element_id, element] = create_element_(type); + Cell &cell = m_cells.emplace(element_id, Cell{}); + return {element_id, element, cell}; } void ElementRegistry::append_table_column(const ElementIdentifier table_id, const ElementIdentifier column_id) { - check_table_id(table_id); - check_element_id(column_id); - if (element_at(column_id).parent_id != null_element_id) { - throw std::invalid_argument( - "ElementRegistry::append_table_column: column already has a parent"); - } - Table &table = table_element_at(table_id); - const ElementIdentifier previous_id = table.last_column_id; - - element_at(column_id).parent_id = table_id; - element_at(column_id).previous_sibling_id = previous_id; - if (previous_id == null_element_id) { - table.first_column_id = column_id; - } else { - element_at(previous_id).next_sibling_id = column_id; - } - table.last_column_id = column_id; + link_child(table_id, column_id, table.first_column_id, table.last_column_id); } void ElementRegistry::append_sheet_cell(const ElementIdentifier sheet_id, const ElementIdentifier cell_id) { - check_sheet_id(sheet_id); - check_cell_id(cell_id); - if (element_at(cell_id).parent_id != null_element_id) { - throw std::invalid_argument( - "ElementRegistry::append_sheet_cell: cell already has a parent"); - } - + Sheet &sheet = sheet_element_at(sheet_id); const Cell &cell = cell_element_at(cell_id); - element_at(cell_id).parent_id = sheet_id; - sheet_element_at(sheet_id).cells.emplace(std::pair(cell.row, cell.column), - cell_id); -} -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range("ElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "ElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_frame_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_frames.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_slide_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_slides.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_table_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_tables.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_sheet_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_sheets.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); + Element &cell_element = element_at(cell_id); + if (cell_element.parent_id != null_element_id) { + throw std::invalid_argument( + "ElementRegistry::append_sheet_cell: cell already has a parent"); } -} -void ElementRegistry::check_cell_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_cells.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } + cell_element.parent_id = sheet_id; + sheet.cells.emplace(std::pair(cell.row, cell.column), cell_id); } } // namespace odr::internal::iwork diff --git a/src/odr/internal/iwork/iwork_element_registry.hpp b/src/odr/internal/iwork/iwork_element_registry.hpp index ab9a9c399..aed11335e 100644 --- a/src/odr/internal/iwork/iwork_element_registry.hpp +++ b/src/odr/internal/iwork/iwork_element_registry.hpp @@ -3,19 +3,19 @@ #include #include -#include +#include + #include #include #include #include #include -#include #include -#include namespace odr::internal::iwork { -class ElementRegistry final { +class ElementRegistry final + : public internal::ElementRegistry> { public: struct Size final { float width{}; @@ -31,15 +31,6 @@ class ElementRegistry final { std::optional height{}; }; - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - }; - struct Text final { std::string text; }; @@ -90,10 +81,6 @@ class ElementRegistry final { std::uint32_t row) const; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type); std::tuple create_text_element(); std::tuple create_frame_element(); @@ -103,23 +90,47 @@ class ElementRegistry final { std::tuple create_cell_element(ElementType type); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - [[nodiscard]] Frame &frame_element_at(ElementIdentifier id); - [[nodiscard]] Slide &slide_element_at(ElementIdentifier id); - [[nodiscard]] Table &table_element_at(ElementIdentifier id); - [[nodiscard]] Sheet &sheet_element_at(ElementIdentifier id); - [[nodiscard]] Cell &cell_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Frame &frame_element_at(ElementIdentifier id) const; - [[nodiscard]] const Slide &slide_element_at(ElementIdentifier id) const; - [[nodiscard]] const Table &table_element_at(ElementIdentifier id) const; - [[nodiscard]] const Sheet &sheet_element_at(ElementIdentifier id) const; - [[nodiscard]] const Cell &cell_element_at(ElementIdentifier id) const; - - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] Frame &frame_element_at(const ElementIdentifier id) { + return m_frames.at(id); + } + [[nodiscard]] Slide &slide_element_at(const ElementIdentifier id) { + return m_slides.at(id); + } + [[nodiscard]] Table &table_element_at(const ElementIdentifier id) { + return m_tables.at(id); + } + [[nodiscard]] Sheet &sheet_element_at(const ElementIdentifier id) { + return m_sheets.at(id); + } + [[nodiscard]] Cell &cell_element_at(const ElementIdentifier id) { + return m_cells.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Frame & + frame_element_at(const ElementIdentifier id) const { + return m_frames.at(id); + } + [[nodiscard]] const Slide & + slide_element_at(const ElementIdentifier id) const { + return m_slides.at(id); + } + [[nodiscard]] const Table & + table_element_at(const ElementIdentifier id) const { + return m_tables.at(id); + } + [[nodiscard]] const Sheet & + sheet_element_at(const ElementIdentifier id) const { + return m_sheets.at(id); + } + [[nodiscard]] const Cell &cell_element_at(const ElementIdentifier id) const { + return m_cells.at(id); + } /// Links @p column_id into @p table_id's column chain. Columns are not /// children: a table's child chain is its rows. @@ -130,21 +141,12 @@ class ElementRegistry final { void append_sheet_cell(ElementIdentifier sheet_id, ElementIdentifier cell_id); private: - std::vector m_elements; - std::unordered_map m_texts; - std::unordered_map m_frames; - std::unordered_map m_slides; - std::unordered_map m_tables; - std::unordered_map m_sheets; - std::unordered_map m_cells; - - void check_element_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; - void check_frame_id(ElementIdentifier id) const; - void check_slide_id(ElementIdentifier id) const; - void check_table_id(ElementIdentifier id) const; - void check_sheet_id(ElementIdentifier id) const; - void check_cell_id(ElementIdentifier id) const; + SideTable m_texts; + SideTable m_frames; + SideTable m_slides; + SideTable m_tables; + SideTable m_sheets; + SideTable m_cells; }; } // namespace odr::internal::iwork diff --git a/src/odr/internal/markdown/markdown_document.cpp b/src/odr/internal/markdown/markdown_document.cpp index d4daa6a93..cb94ed09c 100644 --- a/src/odr/internal/markdown/markdown_document.cpp +++ b/src/odr/internal/markdown/markdown_document.cpp @@ -1,12 +1,11 @@ #include -#include #include #include #include +#include #include -#include #include @@ -36,124 +35,19 @@ const StyleRegistry &Document::style_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::TextRootAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter, - public abstract::LinkAdapter, - public abstract::ListAdapter, - public abstract::ListItemAdapter, - public abstract::TableAdapter, - public abstract::TableColumnAdapter, - public abstract::TableRowAdapter, - public abstract::TableCellAdapter { +using AdapterBase = internal::RegistryElementAdapter< + const ElementRegistry, abstract::TextRootAdapter, + abstract::LineBreakAdapter, abstract::ParagraphAdapter, + abstract::SpanAdapter, abstract::TextAdapter, abstract::LinkAdapter, + abstract::ListAdapter, abstract::ListItemAdapter, abstract::TableAdapter, + abstract::TableColumnAdapter, abstract::TableRowAdapter, + abstract::TableCellAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const ElementRegistry ®istry, const StyleRegistry &style_registry) - : m_registry(®istry), m_style_registry(&style_registry) {} - - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool - element_is_unique(const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool - element_is_self_locatable(const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool - element_is_editable(const ElementIdentifier element_id) const override { - (void)element_id; - return false; - } - [[nodiscard]] DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const TextRootAdapter * - text_root_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::root ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] const LinkAdapter * - link_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::link ? this : nullptr; - } - [[nodiscard]] const ListAdapter * - list_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::list ? this : nullptr; - } - [[nodiscard]] const ListItemAdapter * - list_item_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::list_item ? this : nullptr; - } - [[nodiscard]] const TableAdapter * - table_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table ? this : nullptr; - } - [[nodiscard]] const TableColumnAdapter * - table_column_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_column ? this - : nullptr; - } - [[nodiscard]] const TableRowAdapter * - table_row_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_row ? this : nullptr; - } - [[nodiscard]] const TableCellAdapter * - table_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_cell ? this : nullptr; - } + : AdapterBase(registry), m_style_registry(&style_registry) {} /// Markdown is flow content: it has no page, and the viewport is the width. [[nodiscard]] PageLayout @@ -286,7 +180,6 @@ class ElementAdapter final : public abstract::ElementAdapter, } private: - const ElementRegistry *m_registry{nullptr}; const StyleRegistry *m_style_registry{nullptr}; [[nodiscard]] TextStyle diff --git a/src/odr/internal/markdown/markdown_element_registry.cpp b/src/odr/internal/markdown/markdown_element_registry.cpp index f5dc63496..c0fe14957 100644 --- a/src/odr/internal/markdown/markdown_element_registry.cpp +++ b/src/odr/internal/markdown/markdown_element_registry.cpp @@ -1,208 +1,88 @@ #include -#include - namespace odr::internal::markdown { -namespace { - -/// Looks a payload up, turning a missing entry into an out-of-range error -/// rather than the map's default-constructed one. -template auto &payload_at(Map &map, const ElementIdentifier id) { - const auto it = map.find(id); - if (it == map.end()) { - throw std::out_of_range( - "markdown::ElementRegistry: element has no payload of that kind"); - } - return it->second; -} - -} // namespace - -std::size_t ElementRegistry::size() const noexcept { return m_elements.size(); } - std::tuple ElementRegistry::create_element(const ElementType type) { - Element &element = m_elements.emplace_back(); - const ElementIdentifier element_id = m_elements.size(); - element.type = type; - return {element_id, element}; + return create_element_(type); } std::tuple ElementRegistry::create_text_element() { - const auto &[element_id, element] = create_element(ElementType::text); - const auto [it, inserted] = m_texts.emplace(element_id, Text{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::text); + Text &text = m_texts.emplace(element_id, Text{}); + return {element_id, element, text}; } std::tuple ElementRegistry::create_link_element() { - const auto &[element_id, element] = create_element(ElementType::link); - const auto [it, inserted] = m_links.emplace(element_id, Link{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::link); + Link &link = m_links.emplace(element_id, Link{}); + return {element_id, element, link}; } std::tuple ElementRegistry::create_list_element() { - const auto &[element_id, element] = create_element(ElementType::list); - const auto [it, inserted] = m_lists.emplace(element_id, List{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::list); + List &list = m_lists.emplace(element_id, List{}); + return {element_id, element, list}; } std::tuple ElementRegistry::create_list_item_element() { - const auto &[element_id, element] = create_element(ElementType::list_item); - const auto [it, inserted] = m_list_items.emplace(element_id, ListItem{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::list_item); + ListItem &list_item = m_list_items.emplace(element_id, ListItem{}); + return {element_id, element, list_item}; } std::tuple ElementRegistry::create_table_element() { - const auto &[element_id, element] = create_element(ElementType::table); - const auto [it, inserted] = m_tables.emplace(element_id, Table{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::table); + Table &table = m_tables.emplace(element_id, Table{}); + return {element_id, element, table}; } std::tuple ElementRegistry::create_table_cell_element() { - const auto &[element_id, element] = create_element(ElementType::table_cell); - const auto [it, inserted] = m_table_cells.emplace(element_id, TableCell{}); - return {element_id, element, it->second}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - return payload_at(m_texts, id); -} - -ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) { - return payload_at(m_tables, id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - return payload_at(m_texts, id); -} - -const ElementRegistry::Link & -ElementRegistry::link_element_at(const ElementIdentifier id) const { - return payload_at(m_links, id); -} - -const ElementRegistry::List & -ElementRegistry::list_element_at(const ElementIdentifier id) const { - return payload_at(m_lists, id); -} - -const ElementRegistry::ListItem & -ElementRegistry::list_item_element_at(const ElementIdentifier id) const { - return payload_at(m_list_items, id); -} - -const ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) const { - return payload_at(m_tables, id); -} - -const ElementRegistry::TableCell & -ElementRegistry::table_cell_element_at(const ElementIdentifier id) const { - return payload_at(m_table_cells, id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "markdown::ElementRegistry::append_child: child already has a parent"); - } - - Element &parent = element_at(parent_id); - link_child(parent_id, child_id, parent.first_child_id, parent.last_child_id); + const auto &[element_id, element] = create_element_(ElementType::table_cell); + TableCell &table_cell = m_table_cells.emplace(element_id, TableCell{}); + return {element_id, element, table_cell}; } void ElementRegistry::append_column(const ElementIdentifier table_id, const ElementIdentifier column_id) { - check_element_id(column_id); - if (element_at(column_id).parent_id != null_element_id) { - throw std::invalid_argument( - "markdown::ElementRegistry::append_column: child already has a parent"); - } - Table &table = table_element_at(table_id); link_child(table_id, column_id, table.first_column_id, table.last_column_id); } -void ElementRegistry::link_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id, - ElementIdentifier &first_id, - ElementIdentifier &last_id) { - Element &child = element_at(child_id); - child.parent_id = parent_id; - child.previous_sibling_id = last_id; - - if (first_id == null_element_id) { - first_id = child_id; - } else { - element_at(last_id).next_sibling_id = child_id; - } - last_id = child_id; -} - void ElementRegistry::set_element_text_style_index(const ElementIdentifier id, const std::uint32_t index) { check_element_id(id); - m_text_style_indices[id] = index; + m_text_style_indices.emplace(id, index); } std::uint32_t ElementRegistry::element_text_style_index(const ElementIdentifier id) const { - const auto it = m_text_style_indices.find(id); - return it != m_text_style_indices.end() ? it->second : 0; + const std::uint32_t *index = m_text_style_indices.find(id); + return index != nullptr ? *index : 0; } void ElementRegistry::set_element_paragraph_style_index( const ElementIdentifier id, const std::uint32_t index) { check_element_id(id); - m_paragraph_style_indices[id] = index; + m_paragraph_style_indices.emplace(id, index); } std::uint32_t ElementRegistry::element_paragraph_style_index( const ElementIdentifier id) const { - const auto it = m_paragraph_style_indices.find(id); - return it != m_paragraph_style_indices.end() ? it->second : 0; -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range( - "markdown::ElementRegistry::check_element_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "markdown::ElementRegistry::check_element_id: identifier out of range"); - } + const std::uint32_t *index = m_paragraph_style_indices.find(id); + return index != nullptr ? *index : 0; } } // namespace odr::internal::markdown diff --git a/src/odr/internal/markdown/markdown_element_registry.hpp b/src/odr/internal/markdown/markdown_element_registry.hpp index 05ec095b6..3a97f1bc4 100644 --- a/src/odr/internal/markdown/markdown_element_registry.hpp +++ b/src/odr/internal/markdown/markdown_element_registry.hpp @@ -5,26 +5,18 @@ #include #include +#include + #include #include #include #include -#include -#include namespace odr::internal::markdown { -class ElementRegistry final { +class ElementRegistry final + : public internal::ElementRegistry> { public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - }; - struct Text final { std::string text; }; @@ -55,8 +47,6 @@ class ElementRegistry final { std::optional horizontal_align; }; - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type); std::tuple create_text_element(); std::tuple create_link_element(); @@ -67,21 +57,35 @@ class ElementRegistry final { std::tuple create_table_cell_element(); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - [[nodiscard]] Table &table_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Link &link_element_at(ElementIdentifier id) const; - [[nodiscard]] const List &list_element_at(ElementIdentifier id) const; + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] Table &table_element_at(const ElementIdentifier id) { + return m_tables.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Link &link_element_at(const ElementIdentifier id) const { + return m_links.at(id); + } + [[nodiscard]] const List &list_element_at(const ElementIdentifier id) const { + return m_lists.at(id); + } [[nodiscard]] const ListItem & - list_item_element_at(ElementIdentifier id) const; - [[nodiscard]] const Table &table_element_at(ElementIdentifier id) const; + list_item_element_at(const ElementIdentifier id) const { + return m_list_items.at(id); + } + [[nodiscard]] const Table & + table_element_at(const ElementIdentifier id) const { + return m_tables.at(id); + } [[nodiscard]] const TableCell & - table_cell_element_at(ElementIdentifier id) const; + table_cell_element_at(const ElementIdentifier id) const { + return m_table_cells.at(id); + } - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); void append_column(ElementIdentifier table_id, ElementIdentifier column_id); /// Character style of an element, as an index into the document's @@ -97,22 +101,14 @@ class ElementRegistry final { element_paragraph_style_index(ElementIdentifier id) const; private: - std::vector m_elements; - std::unordered_map m_texts; - std::unordered_map m_links; - std::unordered_map m_lists; - std::unordered_map m_list_items; - std::unordered_map m_tables; - std::unordered_map m_table_cells; - std::unordered_map m_text_style_indices; - std::unordered_map - m_paragraph_style_indices; - - void check_element_id(ElementIdentifier id) const; - - /// Links @p child_id onto the chain @p first_id / @p last_id delimit. - void link_child(ElementIdentifier parent_id, ElementIdentifier child_id, - ElementIdentifier &first_id, ElementIdentifier &last_id); + SideTable m_texts; + SideTable m_links; + SideTable m_lists; + SideTable m_list_items; + SideTable
m_tables; + SideTable m_table_cells; + SideTable m_text_style_indices; + SideTable m_paragraph_style_indices; }; } // namespace odr::internal::markdown diff --git a/src/odr/internal/odf/AGENTS.md b/src/odr/internal/odf/AGENTS.md index 2078b8e6f..de0bb5678 100644 --- a/src/odr/internal/odf/AGENTS.md +++ b/src/odr/internal/odf/AGENTS.md @@ -26,11 +26,13 @@ goes back to the node. **This is why ODF alone can edit and save**: a text edit is a local DOM splice, and `save` re-serialises the mutated tree. The other engines throw away the source, so their models are read-only. -Everything else follows the shared registry/adapter pattern: a flat element -store, id = index + 1, `null_element_id == 0`, parent/child/ -sibling ids, per-subtype payload tables (`m_texts`, `m_tables`, `m_sheets`, -`m_sheet_cells`). One mega `ElementAdapter` multiply-inherits every abstract -per-type adapter and dispatches by returning `this`/`nullptr` on `element_type`. +Everything else comes from the shared machinery in `internal/common/`: +`internal::ElementRegistry` is the flat store (id = +index + 1, `null_element_id == 0`, parent/child/sibling ids), and this module +adds only the `pugi::xml_node` on the element and its payload tables +(`m_texts`, `m_tables`, `m_sheets`, `m_sheet_cells`). One mega `ElementAdapter` +derives from `internal::RegistryElementAdapter`, naming every abstract per-type +adapter in its pack; the `*_adapter(id)` dispatch is the base's. ## Design decisions @@ -64,18 +66,21 @@ vectors. The cells of every row live in one array per sheet, each row recording where its own run starts, so a sheet is two allocations rather than one per row. `register_cell` therefore has to follow its row's `register_row`. -The elements themselves are a `std::deque`: `create_element` hands back a -reference the parser holds on to, and a vector both invalidates it and peaks -holding two copies. `parse_sheet` counts the row and cell nodes first, so the +The elements themselves are a `std::deque` — that is now the shared +`internal::ElementRegistry`'s doing, for the reason odf needed it: +`create_element_` hands back a reference the parser holds on to, and a vector +both invalidates it and peaks holding two copies. +`parse_sheet` counts the row and cell nodes first, so the arrays are allocated once at the size they end at — a repeat collapses onto one entry, so the count is an upper bound, and one bounded by the dom. -**Ids are stored narrow, payloads in sorted arrays.** `StoredId` is 32 bits and -every boundary widens back to the public `ElementIdentifier`, which stays 64 — -`csv` packs coordinates into it. The payload tables are written in id order as -elements are created, so a binary search replaces a hash map; `m_list_types` and -`m_list_markers` stay hash maps, written when a list is resolved rather than -parsed. +**Ids are stored narrow, payloads in sorted arrays.** `StoredId` is 32 bits — +the `Id` this module gives `internal::ElementRegistry` — and every boundary +widens back to the public `ElementIdentifier`, which stays 64: `csv` packs +coordinates into it. The payload tables are written in id order as elements are +created, so `SortedSideTable`'s binary search replaces a hash map; +`m_list_types` and `m_list_markers` are plain `SideTable`s, written when a list +is resolved rather than parsed. **Styles resolve to a flattened `ResolvedStyle`, eagerly.** `StyleRegistry` first builds name→node indices from *both* files (automatic and named styles land diff --git a/src/odr/internal/odf/odf_document.cpp b/src/odr/internal/odf/odf_document.cpp index a1336ddd7..e02367fbb 100644 --- a/src/odr/internal/odf/odf_document.cpp +++ b/src/odr/internal/odf/odf_document.cpp @@ -1,10 +1,10 @@ #include #include -#include #include #include +#include #include #include #include @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -171,69 +170,23 @@ std::optional connector_box(const pugi::xml_node node) { return read_path(node); } -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::TextRootAdapter, - public abstract::SlideAdapter, - public abstract::PageAdapter, - public abstract::SheetAdapter, - public abstract::SheetCellAdapter, - public abstract::MasterPageAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter, - public abstract::LinkAdapter, - public abstract::BookmarkAdapter, - public abstract::ListAdapter, - public abstract::ListItemAdapter, - public abstract::TableAdapter, - public abstract::TableColumnAdapter, - public abstract::TableRowAdapter, - public abstract::TableCellAdapter, - public abstract::FrameAdapter, - public abstract::RectAdapter, - public abstract::LineAdapter, - public abstract::CircleAdapter, - public abstract::CustomShapeAdapter, - public abstract::ImageAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::TextRootAdapter, abstract::SlideAdapter, + abstract::PageAdapter, abstract::SheetAdapter, abstract::SheetCellAdapter, + abstract::MasterPageAdapter, abstract::LineBreakAdapter, + abstract::ParagraphAdapter, abstract::SpanAdapter, abstract::TextAdapter, + abstract::LinkAdapter, abstract::BookmarkAdapter, abstract::ListAdapter, + abstract::ListItemAdapter, abstract::TableAdapter, + abstract::TableColumnAdapter, abstract::TableRowAdapter, + abstract::TableCellAdapter, abstract::FrameAdapter, abstract::RectAdapter, + abstract::LineAdapter, abstract::CircleAdapter, + abstract::CustomShapeAdapter, abstract::ImageAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry) - : m_document(&document), m_registry(®istry) {} + : AdapterBase(registry), m_document(&document) {} - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } [[nodiscard]] bool element_is_editable(const ElementIdentifier element_id) const override { const ElementRegistry::Element &element = @@ -246,117 +199,6 @@ class ElementAdapter final : public abstract::ElementAdapter, } return true; } - [[nodiscard]] - DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const TextRootAdapter * - text_root_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::root ? this : nullptr; - } - [[nodiscard]] const SlideAdapter * - slide_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::slide ? this : nullptr; - } - [[nodiscard]] const PageAdapter * - page_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::page ? this : nullptr; - } - [[nodiscard]] const SheetAdapter * - sheet_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet ? this : nullptr; - } - [[nodiscard]] const SheetCellAdapter * - sheet_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet_cell ? this : nullptr; - } - [[nodiscard]] const MasterPageAdapter * - master_page_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::master_page ? this - : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] const LinkAdapter * - link_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::link ? this : nullptr; - } - [[nodiscard]] const BookmarkAdapter * - bookmark_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::bookmark ? this : nullptr; - } - [[nodiscard]] const ListAdapter * - list_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::list ? this : nullptr; - } - - [[nodiscard]] const ListItemAdapter * - list_item_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::list_item ? this : nullptr; - } - [[nodiscard]] const TableAdapter * - table_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table ? this : nullptr; - } - [[nodiscard]] const TableColumnAdapter * - table_column_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_column ? this - : nullptr; - } - [[nodiscard]] const TableRowAdapter * - table_row_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_row ? this : nullptr; - } - [[nodiscard]] const TableCellAdapter * - table_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_cell ? this : nullptr; - } - [[nodiscard]] const FrameAdapter * - frame_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::frame ? this : nullptr; - } - [[nodiscard]] const RectAdapter * - rect_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::rect ? this : nullptr; - } - [[nodiscard]] const LineAdapter * - line_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line ? this : nullptr; - } - [[nodiscard]] const CircleAdapter * - circle_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::circle ? this : nullptr; - } - [[nodiscard]] const CustomShapeAdapter * - custom_shape_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::custom_shape ? this - : nullptr; - } - [[nodiscard]] const ImageAdapter * - image_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::image ? this : nullptr; - } [[nodiscard]] PageLayout text_root_page_layout(const ElementIdentifier element_id) const override { @@ -1052,7 +894,6 @@ class ElementAdapter final : public abstract::ElementAdapter, private: const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; mutable std::mutex m_charts_mutex; mutable std::unordered_map> m_charts; diff --git a/src/odr/internal/odf/odf_element_registry.cpp b/src/odr/internal/odf/odf_element_registry.cpp index a630ae0fc..772254686 100644 --- a/src/odr/internal/odf/odf_element_registry.cpp +++ b/src/odr/internal/odf/odf_element_registry.cpp @@ -1,36 +1,14 @@ #include #include -#include #include namespace odr::internal::odf { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_texts.clear(); - m_tables.clear(); - m_sheets.clear(); - m_sheet_cells.clear(); - m_list_types.clear(); - m_list_markers.clear(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type, const pugi::xml_node node) { - if (m_elements.size() >= std::numeric_limits::max()) { - throw std::overflow_error( - "ElementRegistry::create_element: out of identifiers"); - } - - Element &element = m_elements.emplace_back(); - const ElementIdentifier element_id = m_elements.size(); - element.type = type; + const auto &[element_id, element] = create_element_(type); element.node = node; return {element_id, element}; } @@ -73,146 +51,32 @@ ElementRegistry::create_sheet_cell_element(const pugi::xml_node node, return {element_id, element, sheet_cell}; } -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_element_id(id); - return m_texts.at(id); -} - -ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) { - check_element_id(id); - return m_tables.at(id); -} - -ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) { - check_element_id(id); - return m_sheets.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_tables.at(id); -} - -const ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_sheets.at(id); -} - -const ElementRegistry::SheetCell & -ElementRegistry::sheet_cell_element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_sheet_cells.at(id); -} - -const ElementRegistry::SheetCell * -ElementRegistry::sheet_cell_element(const ElementIdentifier id) const { - return m_sheet_cells.find(id); -} - -void ElementRegistry::link_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id, - StoredId &first_id, StoredId &last_id) { - Element &child = element_at(child_id); - child.parent_id = static_cast(parent_id); - child.previous_sibling_id = last_id; - - if (first_id == null_element_id) { - first_id = static_cast(child_id); - } else { - element_at(last_id).next_sibling_id = static_cast(child_id); - } - last_id = static_cast(child_id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "DocumentElementRegistry::append_child: child already has a parent"); - } - - Element &parent = element_at(parent_id); - link_child(parent_id, child_id, parent.first_child_id, parent.last_child_id); -} - void ElementRegistry::append_column(const ElementIdentifier table_id, const ElementIdentifier column_id) { Table &table = table_element_at(table_id); - check_element_id(column_id); - if (element_at(column_id).parent_id != null_element_id) { - throw std::invalid_argument( - "DocumentElementRegistry::append_column: child already has a parent"); - } - link_child(table_id, column_id, table.first_column_id, table.last_column_id); } void ElementRegistry::append_shape(const ElementIdentifier sheet_id, const ElementIdentifier shape_id) { Sheet &sheet = sheet_element_at(sheet_id); - check_element_id(shape_id); - if (element_at(shape_id).parent_id != null_element_id) { - throw std::invalid_argument( - "DocumentElementRegistry::append_shape: child already has a parent"); - } - link_child(sheet_id, shape_id, sheet.first_shape_id, sheet.last_shape_id); } void ElementRegistry::append_sheet_cell(const ElementIdentifier sheet_id, const ElementIdentifier cell_id) { - check_sheet_id(sheet_id); - check_element_id(cell_id); - if (element_at(cell_id).parent_id != null_element_id) { - throw std::invalid_argument("DocumentElementRegistry::append_sheet_cell: " - "child already has a parent"); - } - - element_at(cell_id).parent_id = static_cast(sheet_id); -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { + if (m_sheets.find(sheet_id) == nullptr) { throw std::out_of_range( - "DocumentElementRegistry::check_id: null identifier"); + "ElementRegistry::append_sheet_cell: not a sheet identifier"); } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier out of range"); - } -} -void ElementRegistry::check_sheet_id(const ElementIdentifier id) const { - check_element_id(id); - if (m_sheets.find(id) == nullptr) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); + Element &cell = element_at(cell_id); + if (cell.parent_id != null_element_id) { + throw std::invalid_argument( + "ElementRegistry::append_sheet_cell: child already has a parent"); } + + cell.parent_id = static_cast(sheet_id); } void ElementRegistry::Sheet::register_column(const std::uint32_t column, @@ -331,26 +195,26 @@ ElementRegistry::Sheet::cell_node(const std::uint32_t column, void ElementRegistry::set_list_type(const ElementIdentifier id, const ListType type) { check_element_id(id); - m_list_types[id] = type; + m_list_types.emplace(id, type); } void ElementRegistry::set_list_marker(const ElementIdentifier id, ListMarker marker) { check_element_id(id); - m_list_markers[id] = std::move(marker); + m_list_markers.emplace(id, std::move(marker)); } [[nodiscard]] ListType ElementRegistry::list_type(const ElementIdentifier id) const { - const auto it = m_list_types.find(id); - return it != std::end(m_list_types) ? it->second : ListType::unordered; + const ListType *type = m_list_types.find(id); + return type != nullptr ? *type : ListType::unordered; } [[nodiscard]] const ListMarker & ElementRegistry::list_marker(const ElementIdentifier id) const { static const ListMarker none; - const auto it = m_list_markers.find(id); - return it != std::end(m_list_markers) ? it->second : none; + const ListMarker *marker = m_list_markers.find(id); + return marker != nullptr ? *marker : none; } } // namespace odr::internal::odf diff --git a/src/odr/internal/odf/odf_element_registry.hpp b/src/odr/internal/odf/odf_element_registry.hpp index 99badd90f..bc77d2258 100644 --- a/src/odr/internal/odf/odf_element_registry.hpp +++ b/src/odr/internal/odf/odf_element_registry.hpp @@ -3,39 +3,31 @@ #include #include +#include #include #include #include -#include #include -#include #include -#include -#include -#include +#include #include #include namespace odr::internal::odf { -class ElementRegistry final { -public: - /// An element is five ids, so the width is most of what one costs. Widened - /// back at every boundary; an id past @ref check_element_id fits. - using StoredId = std::uint32_t; - - struct Element final { - StoredId parent_id{null_element_id}; - StoredId first_child_id{null_element_id}; - StoredId last_child_id{null_element_id}; - StoredId previous_sibling_id{null_element_id}; - StoredId next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - pugi::xml_node node; - }; +/// An element is five ids, so the width is most of what one costs. Widened +/// back at every boundary; an id past `check_element_id` fits. +using StoredId = std::uint32_t; + +struct RegistryElement final : ElementNode { + pugi::xml_node node; +}; +class ElementRegistry final + : public internal::ElementRegistry { +public: struct Table final { StoredId first_column_id{null_element_id}; StoredId last_column_id{null_element_id}; @@ -105,68 +97,6 @@ class ElementRegistry final { bool is_repeated{false}; }; - /// Payloads keyed by element id and written in id order, so a lookup is a - /// binary search. A deque: `create_*_element` hands out a reference. - template class SideTable final { - public: - T &emplace(const ElementIdentifier id, T value) { - if (!m_entries.empty() && m_entries.back().first >= id) { - throw std::invalid_argument( - "ElementRegistry::SideTable::emplace: identifier out of order"); - } - return m_entries.emplace_back(static_cast(id), std::move(value)) - .second; - } - - [[nodiscard]] T *find(const ElementIdentifier id) { - const auto it = - std::ranges::lower_bound(m_entries, id, {}, &Entry::first); - return it != std::end(m_entries) && it->first == id ? &it->second - : nullptr; - } - - [[nodiscard]] const T *find(const ElementIdentifier id) const { - const auto it = - std::ranges::lower_bound(m_entries, id, {}, &Entry::first); - return it != std::end(m_entries) && it->first == id ? &it->second - : nullptr; - } - - [[nodiscard]] T &at(const ElementIdentifier id) { - T *entry = find(id); - if (entry == nullptr) { - throw std::out_of_range( - "ElementRegistry::SideTable::at: identifier not found"); - } - return *entry; - } - - [[nodiscard]] const T &at(const ElementIdentifier id) const { - const T *entry = find(id); - if (entry == nullptr) { - throw std::out_of_range( - "ElementRegistry::SideTable::at: identifier not found"); - } - return *entry; - } - - void clear() noexcept { m_entries.clear(); } - - [[nodiscard]] auto begin() const noexcept { return m_entries.begin(); } - [[nodiscard]] auto end() const noexcept { return m_entries.end(); } - [[nodiscard]] auto begin() noexcept { return m_entries.begin(); } - [[nodiscard]] auto end() noexcept { return m_entries.end(); } - - private: - using Entry = std::pair; - - std::deque m_entries; - }; - - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type, pugi::xml_node node); std::tuple @@ -179,19 +109,36 @@ class ElementRegistry final { create_sheet_cell_element(pugi::xml_node node, const TablePosition &position, bool is_repeated); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - [[nodiscard]] Table &table_element_at(ElementIdentifier id); - [[nodiscard]] Sheet &sheet_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Table &table_element_at(ElementIdentifier id) const; - [[nodiscard]] const Sheet &sheet_element_at(ElementIdentifier id) const; + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] Table &table_element_at(const ElementIdentifier id) { + return m_tables.at(id); + } + [[nodiscard]] Sheet &sheet_element_at(const ElementIdentifier id) { + return m_sheets.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Table & + table_element_at(const ElementIdentifier id) const { + return m_tables.at(id); + } + [[nodiscard]] const Sheet & + sheet_element_at(const ElementIdentifier id) const { + return m_sheets.at(id); + } [[nodiscard]] const SheetCell & - sheet_cell_element_at(ElementIdentifier id) const; + sheet_cell_element_at(const ElementIdentifier id) const { + return m_sheet_cells.at(id); + } - [[nodiscard]] const SheetCell *sheet_cell_element(ElementIdentifier id) const; + [[nodiscard]] const SheetCell * + sheet_cell_element(const ElementIdentifier id) const { + return m_sheet_cells.find(id); + } void set_list_type(ElementIdentifier id, ListType type); void set_list_marker(ElementIdentifier id, ListMarker marker); @@ -199,30 +146,18 @@ class ElementRegistry final { [[nodiscard]] ListType list_type(ElementIdentifier id) const; [[nodiscard]] const ListMarker &list_marker(ElementIdentifier id) const; - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); void append_column(ElementIdentifier table_id, ElementIdentifier column_id); void append_shape(ElementIdentifier sheet_id, ElementIdentifier shape_id); void append_sheet_cell(ElementIdentifier sheet_id, ElementIdentifier cell_id); private: - /// A deque, not a vector: `create_element` hands back a reference the parser - /// holds on to, and a vector both invalidates it and peaks holding two - /// copies. - std::deque m_elements; - SideTable m_texts; - SideTable
m_tables; - SideTable m_sheets; - SideTable m_sheet_cells; + SortedSideTable m_texts; + SortedSideTable m_tables; + SortedSideTable m_sheets; + SortedSideTable m_sheet_cells; // out of id order: written when a list is resolved, not when it is parsed - std::unordered_map m_list_types; - std::unordered_map m_list_markers; - - /// Links `child_id` as the last child of the chain `first_id`/`last_id`. - void link_child(ElementIdentifier parent_id, ElementIdentifier child_id, - StoredId &first_id, StoredId &last_id); - - void check_element_id(ElementIdentifier id) const; - void check_sheet_id(ElementIdentifier id) const; + SideTable m_list_types; + SideTable m_list_markers; }; } // namespace odr::internal::odf diff --git a/src/odr/internal/oldms/presentation/ppt_document.cpp b/src/odr/internal/oldms/presentation/ppt_document.cpp index 42d8667f4..07d6c5a80 100644 --- a/src/odr/internal/oldms/presentation/ppt_document.cpp +++ b/src/odr/internal/oldms/presentation/ppt_document.cpp @@ -1,14 +1,13 @@ #include -#include #include #include #include #include +#include #include #include -#include #include #include @@ -43,97 +42,18 @@ const StyleRegistry &Document::style_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::SlideAdapter, - public abstract::FrameAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter, - public abstract::ImageAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::SlideAdapter, abstract::FrameAdapter, + abstract::LineBreakAdapter, abstract::ParagraphAdapter, + abstract::SpanAdapter, abstract::TextAdapter, abstract::ImageAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry, const StyleRegistry &style_registry) - : m_document(&document), m_registry(®istry), + : AdapterBase(registry), m_document(&document), m_style_registry(&style_registry) {} - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return false; - } - [[nodiscard]] DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const SlideAdapter * - slide_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::slide ? this : nullptr; - } - [[nodiscard]] const FrameAdapter * - frame_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::frame ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] const ImageAdapter * - image_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::image ? this : nullptr; - } - [[nodiscard]] PageLayout slide_page_layout( [[maybe_unused]] const ElementIdentifier element_id) const override { // The DocumentAtom's slide size, with the default 4:3 slide as fallback @@ -283,7 +203,6 @@ class ElementAdapter final : public abstract::ElementAdapter, [[maybe_unused]] const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; const StyleRegistry *m_style_registry{nullptr}; }; diff --git a/src/odr/internal/oldms/presentation/ppt_element_registry.cpp b/src/odr/internal/oldms/presentation/ppt_element_registry.cpp index eea1434b3..9e1d1b958 100644 --- a/src/odr/internal/oldms/presentation/ppt_element_registry.cpp +++ b/src/odr/internal/oldms/presentation/ppt_element_registry.cpp @@ -1,123 +1,34 @@ #include -#include - namespace odr::internal::oldms::presentation { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_texts.clear(); - m_frames.clear(); - m_images.clear(); - m_style_indices.clear(); - m_slide_size.reset(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type) { - Element &element = m_elements.emplace_back(); - ElementIdentifier element_id = m_elements.size(); - element.type = type; - return {element_id, element}; + return create_element_(type); } std::tuple ElementRegistry::create_text_element() { - const auto &[element_id, element] = create_element(ElementType::text); - auto [it, success] = m_texts.emplace(element_id, Text{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::text); + Text &text = m_texts.emplace(element_id, Text{}); + return {element_id, element, text}; } std::tuple ElementRegistry::create_frame_element() { - const auto &[element_id, element] = create_element(ElementType::frame); - auto [it, success] = m_frames.emplace(element_id, Frame{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::frame); + Frame &frame = m_frames.emplace(element_id, Frame{}); + return {element_id, element, frame}; } std::tuple ElementRegistry::create_image_element() { - const auto &[element_id, element] = create_element(ElementType::image); - auto [it, success] = m_images.emplace(element_id, Image{}); - return {element_id, element, it->second}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -ElementRegistry::Frame & -ElementRegistry::frame_element_at(const ElementIdentifier id) { - check_frame_id(id); - return m_frames.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Frame & -ElementRegistry::frame_element_at(const ElementIdentifier id) const { - check_frame_id(id); - return m_frames.at(id); -} - -ElementRegistry::Image & -ElementRegistry::image_element_at(const ElementIdentifier id) { - check_image_id(id); - return m_images.at(id); -} - -const ElementRegistry::Image & -ElementRegistry::image_element_at(const ElementIdentifier id) const { - check_image_id(id); - return m_images.at(id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "ElementRegistry::append_child: child already has a parent"); - } - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + const auto &[element_id, element] = create_element_(ElementType::image); + Image &image = m_images.emplace(element_id, Image{}); + return {element_id, element, image}; } void ElementRegistry::set_slide_size(const std::int32_t width, @@ -133,45 +44,13 @@ ElementRegistry::slide_size() const { void ElementRegistry::set_element_style_index(const ElementIdentifier id, const std::uint32_t index) { check_element_id(id); - m_style_indices[id] = index; + m_style_indices.emplace(id, index); } std::uint32_t ElementRegistry::element_style_index(const ElementIdentifier id) const { - const auto it = m_style_indices.find(id); - return it != m_style_indices.end() ? it->second : 0; -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range("ElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "ElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_frame_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_frames.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_image_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_images.contains(id)) { - throw std::out_of_range( - "ElementRegistry::check_id: image identifier not found"); - } + const std::uint32_t *index = m_style_indices.find(id); + return index != nullptr ? *index : 0; } } // namespace odr::internal::oldms::presentation diff --git a/src/odr/internal/oldms/presentation/ppt_element_registry.hpp b/src/odr/internal/oldms/presentation/ppt_element_registry.hpp index 590b4c1f4..db4000267 100644 --- a/src/odr/internal/oldms/presentation/ppt_element_registry.hpp +++ b/src/odr/internal/oldms/presentation/ppt_element_registry.hpp @@ -3,29 +3,20 @@ #include #include +#include #include #include #include #include #include -#include #include -#include namespace odr::internal::oldms::presentation { -class ElementRegistry final { +class ElementRegistry final + : public internal::ElementRegistry> { public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - }; - struct Text final { std::string text; }; @@ -40,26 +31,32 @@ class ElementRegistry final { std::string href; //< pseudo-path naming the BLIP (no real container path) }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type); std::tuple create_text_element(); std::tuple create_frame_element(); std::tuple create_image_element(); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - [[nodiscard]] Frame &frame_element_at(ElementIdentifier id); - [[nodiscard]] Image &image_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Frame &frame_element_at(ElementIdentifier id) const; - [[nodiscard]] const Image &image_element_at(ElementIdentifier id) const; - - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] Frame &frame_element_at(const ElementIdentifier id) { + return m_frames.at(id); + } + [[nodiscard]] Image &image_element_at(const ElementIdentifier id) { + return m_images.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Frame & + frame_element_at(const ElementIdentifier id) const { + return m_frames.at(id); + } + [[nodiscard]] const Image & + image_element_at(const ElementIdentifier id) const { + return m_images.at(id); + } /// Character style of a span or paragraph element, as an index into the /// document's `StyleRegistry` (0 is the default style). @@ -72,17 +69,11 @@ class ElementRegistry final { slide_size() const; private: - std::vector m_elements; - std::unordered_map m_texts; - std::unordered_map m_frames; - std::unordered_map m_images; - std::unordered_map m_style_indices; + SideTable m_texts; + SideTable m_frames; + SideTable m_images; + SideTable m_style_indices; std::optional> m_slide_size; - - void check_element_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; - void check_frame_id(ElementIdentifier id) const; - void check_image_id(ElementIdentifier id) const; }; } // namespace odr::internal::oldms::presentation diff --git a/src/odr/internal/oldms/spreadsheet/xls_document.cpp b/src/odr/internal/oldms/spreadsheet/xls_document.cpp index a3df0d20d..8cee4648c 100644 --- a/src/odr/internal/oldms/spreadsheet/xls_document.cpp +++ b/src/odr/internal/oldms/spreadsheet/xls_document.cpp @@ -1,12 +1,11 @@ #include -#include #include #include #include +#include #include -#include #include @@ -39,86 +38,17 @@ const StyleRegistry &Document::style_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::SheetAdapter, - public abstract::SheetCellAdapter, - public abstract::ParagraphAdapter, - public abstract::TextAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::SheetAdapter, abstract::SheetCellAdapter, + abstract::ParagraphAdapter, abstract::TextAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry, const StyleRegistry &style_registry) - : m_document(&document), m_registry(®istry), + : AdapterBase(registry), m_document(&document), m_style_registry(&style_registry) {} - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - (void)element_id; - return false; - } - [[nodiscard]] - DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const SheetAdapter * - sheet_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet ? this : nullptr; - } - [[nodiscard]] const SheetCellAdapter * - sheet_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet_cell ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] std::string sheet_name(const ElementIdentifier element_id) const override { return m_registry->sheet_element_at(element_id).name; @@ -234,7 +164,6 @@ class ElementAdapter final : public abstract::ElementAdapter, // TODO remove maybe_unused [[maybe_unused]] const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; const StyleRegistry *m_style_registry{nullptr}; /// The font style of the sheet_cell ancestor (paragraph and text elements diff --git a/src/odr/internal/oldms/spreadsheet/xls_element_registry.cpp b/src/odr/internal/oldms/spreadsheet/xls_element_registry.cpp index 6c4a965d0..d5c66e6a4 100644 --- a/src/odr/internal/oldms/spreadsheet/xls_element_registry.cpp +++ b/src/odr/internal/oldms/spreadsheet/xls_element_registry.cpp @@ -1,7 +1,6 @@ #include #include -#include namespace odr::internal::oldms::spreadsheet { @@ -14,118 +13,34 @@ ElementIdentifier ElementRegistry::Sheet::cell(const std::uint32_t column, return null_element_id; } -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_texts.clear(); - m_sheets.clear(); - m_sheet_cells.clear(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type) { - Element &element = m_elements.emplace_back(); - ElementIdentifier element_id = m_elements.size(); - element.type = type; - return {element_id, element}; + return create_element_(type); } std::tuple ElementRegistry::create_text_element() { - const auto &[element_id, element] = create_element(ElementType::text); - auto [it, success] = m_texts.emplace(element_id, Text{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::text); + Text &text = m_texts.emplace(element_id, Text{}); + return {element_id, element, text}; } std::tuple ElementRegistry::create_sheet_element() { - const auto &[element_id, element] = create_element(ElementType::sheet); - auto [it, success] = m_sheets.emplace(element_id, Sheet{}); - return {element_id, element, it->second}; + const auto &[element_id, element] = create_element_(ElementType::sheet); + Sheet &sheet = m_sheets.emplace(element_id, Sheet{}); + return {element_id, element, sheet}; } std::tuple ElementRegistry::create_sheet_cell_element(const TablePosition &position) { - const auto &[element_id, element] = create_element(ElementType::sheet_cell); - auto [it, success] = m_sheet_cells.emplace(element_id, SheetCell{position}); - return {element_id, element, it->second}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) { - check_sheet_id(id); - return m_sheets.at(id); -} - -ElementRegistry::SheetCell & -ElementRegistry::sheet_cell_element_at(const ElementIdentifier id) { - check_sheet_cell_id(id); - return m_sheet_cells.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) const { - check_sheet_id(id); - return m_sheets.at(id); -} - -const ElementRegistry::SheetCell & -ElementRegistry::sheet_cell_element_at(const ElementIdentifier id) const { - check_sheet_cell_id(id); - return m_sheet_cells.at(id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "ElementRegistry::append_child: child already has a parent"); - } - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + const auto &[element_id, element] = create_element_(ElementType::sheet_cell); + SheetCell &sheet_cell = + m_sheet_cells.emplace(element_id, SheetCell{.position = position}); + return {element_id, element, sheet_cell}; } void ElementRegistry::append_sheet_cell(const ElementIdentifier sheet_id, @@ -140,38 +55,4 @@ void ElementRegistry::append_sheet_cell(const ElementIdentifier sheet_id, std::max(sheet.content.columns, cell.position.column + 1); } -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range("ElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "ElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range( - "ElementRegistry::check_id: text identifier not found"); - } -} - -void ElementRegistry::check_sheet_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_sheets.contains(id)) { - throw std::out_of_range( - "ElementRegistry::check_id: sheet identifier not found"); - } -} - -void ElementRegistry::check_sheet_cell_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_sheet_cells.contains(id)) { - throw std::out_of_range( - "ElementRegistry::check_id: sheet cell identifier not found"); - } -} - } // namespace odr::internal::oldms::spreadsheet diff --git a/src/odr/internal/oldms/spreadsheet/xls_element_registry.hpp b/src/odr/internal/oldms/spreadsheet/xls_element_registry.hpp index 0f5db7a74..b4d477e4f 100644 --- a/src/odr/internal/oldms/spreadsheet/xls_element_registry.hpp +++ b/src/odr/internal/oldms/spreadsheet/xls_element_registry.hpp @@ -5,24 +5,17 @@ #include #include +#include + #include #include #include -#include namespace odr::internal::oldms::spreadsheet { -class ElementRegistry final { +class ElementRegistry final + : public internal::ElementRegistry> { public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - }; - struct Text final { std::string text; }; @@ -48,43 +41,43 @@ class ElementRegistry final { std::uint16_t ixfe{0}; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type); std::tuple create_text_element(); std::tuple create_sheet_element(); std::tuple create_sheet_cell_element(const TablePosition &position); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - [[nodiscard]] Sheet &sheet_element_at(ElementIdentifier id); - [[nodiscard]] SheetCell &sheet_cell_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Sheet &sheet_element_at(ElementIdentifier id) const; + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] Sheet &sheet_element_at(const ElementIdentifier id) { + return m_sheets.at(id); + } + [[nodiscard]] SheetCell &sheet_cell_element_at(const ElementIdentifier id) { + return m_sheet_cells.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Sheet & + sheet_element_at(const ElementIdentifier id) const { + return m_sheets.at(id); + } [[nodiscard]] const SheetCell & - sheet_cell_element_at(ElementIdentifier id) const; + sheet_cell_element_at(const ElementIdentifier id) const { + return m_sheet_cells.at(id); + } - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); /// Registers a cell with its sheet: sets the cell's parent and adds it to /// the sheet's position lookup. Cells are not part of the sibling chain; /// they are addressed via `Sheet::cell` (like the ooxml/spreadsheet module). void append_sheet_cell(ElementIdentifier sheet_id, ElementIdentifier cell_id); private: - std::vector m_elements; - std::unordered_map m_texts; - std::unordered_map m_sheets; - std::unordered_map m_sheet_cells; - - void check_element_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; - void check_sheet_id(ElementIdentifier id) const; - void check_sheet_cell_id(ElementIdentifier id) const; + SideTable m_texts; + SideTable m_sheets; + SideTable m_sheet_cells; }; } // namespace odr::internal::oldms::spreadsheet diff --git a/src/odr/internal/oldms/text/doc_document.cpp b/src/odr/internal/oldms/text/doc_document.cpp index bb57bcf06..25ee70758 100644 --- a/src/odr/internal/oldms/text/doc_document.cpp +++ b/src/odr/internal/oldms/text/doc_document.cpp @@ -1,13 +1,12 @@ #include -#include #include #include #include +#include #include #include -#include namespace odr::internal::oldms::text { @@ -38,91 +37,17 @@ const StyleRegistry &Document::style_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::TextRootAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::TextRootAdapter, abstract::LineBreakAdapter, + abstract::ParagraphAdapter, abstract::SpanAdapter, abstract::TextAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry, const StyleRegistry &style_registry) - : m_document(&document), m_registry(®istry), + : AdapterBase(registry), m_document(&document), m_style_registry(&style_registry) {} - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - (void)element_id; - return false; - } - [[nodiscard]] - DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const TextRootAdapter * - text_root_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::root ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] PageLayout text_root_page_layout( [[maybe_unused]] const ElementIdentifier element_id) const override { (void)element_id; @@ -176,7 +101,6 @@ class ElementAdapter final : public abstract::ElementAdapter, // TODO remove maybe_unused [[maybe_unused]] const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; const StyleRegistry *m_style_registry{nullptr}; /// The character style stored for a paragraph or span element. diff --git a/src/odr/internal/oldms/text/doc_element_registry.cpp b/src/odr/internal/oldms/text/doc_element_registry.cpp index fa1e46279..0572d3e1e 100644 --- a/src/odr/internal/oldms/text/doc_element_registry.cpp +++ b/src/odr/internal/oldms/text/doc_element_registry.cpp @@ -1,111 +1,30 @@ #include -#include - namespace odr::internal::oldms::text { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_texts.clear(); - m_style_indices.clear(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type) { - Element &element = m_elements.emplace_back(); - ElementIdentifier element_id = m_elements.size(); - element.type = type; - return {element_id, element}; + return create_element_(type); } std::tuple ElementRegistry::create_text_element() { - const auto &[element_id, element] = create_element(ElementType::text); - auto [it, success] = m_texts.emplace(element_id, Text{}); - return {element_id, element, it->second}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "DocumentElementRegistry::append_child: child already has a parent"); - } - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + const auto &[element_id, element] = create_element_(ElementType::text); + Text &text = m_texts.emplace(element_id, Text{}); + return {element_id, element, text}; } void ElementRegistry::set_element_style_index(const ElementIdentifier id, const std::uint32_t index) { check_element_id(id); - m_style_indices[id] = index; + m_style_indices.emplace(id, index); } std::uint32_t ElementRegistry::element_style_index(const ElementIdentifier id) const { - const auto it = m_style_indices.find(id); - return it != m_style_indices.end() ? it->second : 0; -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } + const std::uint32_t *index = m_style_indices.find(id); + return index != nullptr ? *index : 0; } } // namespace odr::internal::oldms::text diff --git a/src/odr/internal/oldms/text/doc_element_registry.hpp b/src/odr/internal/oldms/text/doc_element_registry.hpp index ad28267b4..d1e34ca20 100644 --- a/src/odr/internal/oldms/text/doc_element_registry.hpp +++ b/src/odr/internal/oldms/text/doc_element_registry.hpp @@ -3,42 +3,30 @@ #include #include +#include + #include #include -#include -#include +#include namespace odr::internal::oldms::text { -class ElementRegistry final { +class ElementRegistry final + : public internal::ElementRegistry> { public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - }; - struct Text final { std::string text; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type); std::tuple create_text_element(); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } /// Character style of a span or paragraph element, as an index into the /// document's `StyleRegistry` (0 is the default style). @@ -46,12 +34,8 @@ class ElementRegistry final { [[nodiscard]] std::uint32_t element_style_index(ElementIdentifier id) const; private: - std::vector m_elements; - std::unordered_map m_texts; - std::unordered_map m_style_indices; - - void check_element_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; + SideTable m_texts; + SideTable m_style_indices; }; } // namespace odr::internal::oldms::text diff --git a/src/odr/internal/ooxml/AGENTS.md b/src/odr/internal/ooxml/AGENTS.md index c0d5e615e..fc4c26b1f 100644 --- a/src/odr/internal/ooxml/AGENTS.md +++ b/src/odr/internal/ooxml/AGENTS.md @@ -19,11 +19,12 @@ detection**; each is a self-contained module with its own `AGENTS.md`: ## Shared element model (same as ODF) Like [`odf/`](../odf/), every format keeps its parsed XML parts **resident** and -the `ElementRegistry` is a thin index over them: each `Element` holds a live -`pugi::xml_node` plus its tree ids (flat vector, id = index + 1, -`null_element_id == 0`, parent/child/sibling ids, per-subtype side maps). One -mega `ElementAdapter` per format multiply-inherits the abstract per-type adapters -and dispatches by returning `this`/`nullptr` on `element_type`. Parsing is a +the `ElementRegistry` is a thin index over them: its `RegistryElement` adds a +live `pugi::xml_node` to the shared `ElementNode`, and the store, the tree links +and the `SideTable` payloads come from `internal::ElementRegistry` (see the top +level [`AGENTS.md`](../../../../AGENTS.md)). One mega `ElementAdapter` per format +derives from `internal::RegistryElementAdapter`, naming the abstract per-type +adapters it answers for in the template pack; the hooks are the base's. Parsing is a static `unordered_map` dispatch table; unknown tags are skipped (children still visited). Text runs are coalesced into one `text` Element over a `[first, last]` node span. **Editing, where present, splices these live nodes;** diff --git a/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp b/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp index 2eae7c387..44c905ad9 100644 --- a/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp +++ b/src/odr/internal/ooxml/presentation/ooxml_presentation_document.cpp @@ -1,16 +1,15 @@ #include -#include #include #include #include +#include #include #include #include #include #include -#include #include #include @@ -139,127 +138,17 @@ const ElementRegistry &Document::element_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::SlideAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter, - public abstract::LinkAdapter, - public abstract::BookmarkAdapter, - public abstract::TableAdapter, - public abstract::TableColumnAdapter, - public abstract::TableRowAdapter, - public abstract::TableCellAdapter, - public abstract::FrameAdapter, - public abstract::ImageAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::SlideAdapter, abstract::LineBreakAdapter, + abstract::ParagraphAdapter, abstract::SpanAdapter, abstract::TextAdapter, + abstract::LinkAdapter, abstract::BookmarkAdapter, abstract::TableAdapter, + abstract::TableColumnAdapter, abstract::TableRowAdapter, + abstract::TableCellAdapter, abstract::FrameAdapter, abstract::ImageAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry) - : m_document(&document), m_registry(®istry) {} - - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - // read-only; text_set_content below stays dormant until save is wired up - return false; - } - [[nodiscard]] - DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const SlideAdapter * - slide_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::slide ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] const LinkAdapter * - link_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::link ? this : nullptr; - } - [[nodiscard]] const BookmarkAdapter * - bookmark_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::bookmark ? this : nullptr; - } - [[nodiscard]] const TableAdapter * - table_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table ? this : nullptr; - } - [[nodiscard]] const TableColumnAdapter * - table_column_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_column ? this - : nullptr; - } - [[nodiscard]] const TableRowAdapter * - table_row_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_row ? this : nullptr; - } - [[nodiscard]] const TableCellAdapter * - table_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_cell ? this : nullptr; - } - [[nodiscard]] const FrameAdapter * - frame_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::frame ? this : nullptr; - } - [[nodiscard]] const ImageAdapter * - image_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::image ? this : nullptr; - } + : AdapterBase(registry), m_document(&document) {} [[nodiscard]] PageLayout slide_page_layout(const ElementIdentifier element_id) const override { @@ -528,7 +417,6 @@ class ElementAdapter final : public abstract::ElementAdapter, private: const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; [[nodiscard]] pugi::xml_node get_node(const ElementIdentifier element_id) const { diff --git a/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.cpp b/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.cpp index d0ac87244..cc26d350c 100644 --- a/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.cpp +++ b/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.cpp @@ -1,25 +1,11 @@ #include -#include - namespace odr::internal::ooxml::presentation { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_tables.clear(); - m_texts.clear(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type, const pugi::xml_node node) { - Element &element = m_elements.emplace_back(); - const ElementIdentifier element_id = m_elements.size(); - element.type = type; + const auto &[element_id, element] = create_element_(type); element.node = node; return {element_id, element}; } @@ -28,8 +14,8 @@ std::tuple ElementRegistry::create_table_element(const pugi::xml_node node) { const auto &[element_id, element] = create_element(ElementType::table, node); - auto [it, success] = m_tables.emplace(element_id, Table{}); - return {element_id, element, it->second}; + Table &table = m_tables.emplace(element_id, Table{}); + return {element_id, element, table}; } std::tuplesecond}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) { - check_table_id(id); - return m_tables.at(id); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) const { - check_table_id(id); - return m_tables.at(id); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_table_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_tables.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + Text &text = m_texts.emplace(element_id, Text{last_node}); + return {element_id, element, text}; } void ElementRegistry::append_column(const ElementIdentifier table_id, const ElementIdentifier column_id) { - check_table_id(table_id); - check_element_id(column_id); - - const ElementIdentifier previous_sibling_id = - table_element_at(table_id).last_column_id; - - element_at(column_id).parent_id = table_id; - element_at(column_id).previous_sibling_id = previous_sibling_id; - - if (table_element_at(table_id).first_column_id == null_element_id) { - table_element_at(table_id).first_column_id = column_id; - } else { - element_at(previous_sibling_id).next_sibling_id = column_id; - } - table_element_at(table_id).last_column_id = column_id; + Table &table = table_element_at(table_id); + link_child(table_id, column_id, table.first_column_id, table.last_column_id); } } // namespace odr::internal::ooxml::presentation diff --git a/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.hpp b/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.hpp index 1530caa36..43f0aea7e 100644 --- a/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.hpp +++ b/src/odr/internal/ooxml/presentation/ooxml_presentation_element_registry.hpp @@ -3,26 +3,21 @@ #include #include -#include -#include -#include +#include + +#include #include namespace odr::internal::ooxml::presentation { -class ElementRegistry final { -public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - pugi::xml_node node; - }; +struct RegistryElement final : ElementNode { + pugi::xml_node node; +}; +class ElementRegistry final + : public internal::ElementRegistry { +public: struct Table final { ElementIdentifier first_column_id{null_element_id}; ElementIdentifier last_column_id{null_element_id}; @@ -32,10 +27,6 @@ class ElementRegistry final { pugi::xml_node last; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type, pugi::xml_node node); std::tuple @@ -43,25 +34,26 @@ class ElementRegistry final { std::tuple create_text_element(pugi::xml_node first_node, pugi::xml_node last_node); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Table &table_element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); + [[nodiscard]] Table &table_element_at(const ElementIdentifier id) { + return m_tables.at(id); + } + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + + [[nodiscard]] const Table & + table_element_at(const ElementIdentifier id) const { + return m_tables.at(id); + } + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Table &table_element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); void append_column(ElementIdentifier table_id, ElementIdentifier column_id); private: - std::vector m_elements; - std::unordered_map m_tables; - std::unordered_map m_texts; - - void check_element_id(ElementIdentifier id) const; - void check_table_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; + SideTable
m_tables; + SideTable m_texts; }; } // namespace odr::internal::ooxml::presentation diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp index d1fe65e0c..c8134e3fe 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_document.cpp @@ -1,12 +1,11 @@ #include -#include #include #include #include +#include #include -#include #include #include @@ -80,105 +79,16 @@ Document::parse_xml_(const AbsPath &path) { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::SheetAdapter, - public abstract::SheetCellAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter, - public abstract::LinkAdapter, - public abstract::FrameAdapter, - public abstract::ImageAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::SheetAdapter, abstract::SheetCellAdapter, + abstract::LineBreakAdapter, abstract::ParagraphAdapter, + abstract::SpanAdapter, abstract::TextAdapter, abstract::LinkAdapter, + abstract::FrameAdapter, abstract::ImageAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry) - : m_document(&document), m_registry(®istry) {} - - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_editable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return false; - } - [[nodiscard]] - DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const SheetAdapter * - sheet_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet ? this : nullptr; - } - [[nodiscard]] const SheetCellAdapter * - sheet_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::sheet_cell ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] const LinkAdapter * - link_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::link ? this : nullptr; - } - [[nodiscard]] const FrameAdapter * - frame_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::frame ? this : nullptr; - } - [[nodiscard]] const ImageAdapter * - image_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::image ? this : nullptr; - } + : AdapterBase(registry), m_document(&document) {} [[nodiscard]] std::string sheet_name(const ElementIdentifier element_id) const override { @@ -423,7 +333,6 @@ class ElementAdapter final : public abstract::ElementAdapter, private: const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; [[nodiscard]] pugi::xml_node get_node(const ElementIdentifier element_id) const { diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.cpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.cpp index eb4b2a3f6..d639ed2ef 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.cpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.cpp @@ -6,24 +6,10 @@ namespace odr::internal::ooxml::spreadsheet { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_element_relations.clear(); - m_texts.clear(); - m_sheets.clear(); - m_sheet_cells.clear(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type, const pugi::xml_node node) { - Element &element = m_elements.emplace_back(); - const ElementIdentifier element_id = m_elements.size(); - element.type = type; + const auto &[element_id, element] = create_element_(type); element.node = node; return {element_id, element}; } @@ -34,16 +20,16 @@ ElementRegistry::create_text_element(const pugi::xml_node first_node, const pugi::xml_node last_node) { const auto &[element_id, element] = create_element(ElementType::text, first_node); - auto [it, success] = m_texts.emplace(element_id, Text{last_node}); - return {element_id, element, it->second}; + Text &text = m_texts.emplace(element_id, Text{last_node}); + return {element_id, element, text}; } std::tuple ElementRegistry::create_sheet_element(const pugi::xml_node node) { const auto &[element_id, element] = create_element(ElementType::sheet, node); - auto [it, success] = m_sheets.emplace(element_id, Sheet{}); - return {element_id, element, it->second}; + Sheet &sheet = m_sheets.emplace(element_id, Sheet{}); + return {element_id, element, sheet}; } std::tuplesecond}; + return {element_id, element, sheet_cell}; } ElementRegistry::ElementRelations & @@ -62,145 +48,26 @@ ElementRegistry::attach_element_relations(const ElementIdentifier id, const Relations &relations, const AbsPath &origin) { check_element_id(id); - if (m_element_relations.contains(id)) { - throw std::runtime_error("DocumentElementRegistry::attach_element_" - "relations: relations already attached"); - } - ElementRelations &result = m_element_relations[id]; - result.relations = &relations; - result.origin = origin; - return result; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) { - check_sheet_id(id); - return m_sheets.at(id); -} - -ElementRegistry::SheetCell & -ElementRegistry::sheet_cell_element_at(const ElementIdentifier id) { - check_sheet_cell_id(id); - return m_sheet_cells.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Sheet & -ElementRegistry::sheet_element_at(const ElementIdentifier id) const { - check_sheet_id(id); - return m_sheets.at(id); -} - -const ElementRegistry::SheetCell & -ElementRegistry::sheet_cell_element_at(const ElementIdentifier id) const { - check_sheet_cell_id(id); - return m_sheet_cells.at(id); -} - -const ElementRegistry::ElementRelations * -ElementRegistry::element_relations(const ElementIdentifier id) const { - if (const auto it = m_element_relations.find(id); - it != m_element_relations.end()) { - return &it->second; + if (m_element_relations.find(id) != nullptr) { + throw std::runtime_error("ElementRegistry::attach_element_relations: " + "relations already attached"); } - return nullptr; -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_sheet_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_sheets.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_sheet_cell_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_sheet_cells.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + return m_element_relations.emplace( + id, ElementRelations{.relations = &relations, .origin = origin}); } void ElementRegistry::append_shape(const ElementIdentifier sheet_id, const ElementIdentifier shape_id) { - check_sheet_id(sheet_id); - check_element_id(shape_id); - - const ElementIdentifier previous_sibling_id = - sheet_element_at(sheet_id).last_shape_id; - - element_at(shape_id).parent_id = sheet_id; - element_at(shape_id).previous_sibling_id = previous_sibling_id; - - if (sheet_element_at(sheet_id).first_shape_id == null_element_id) { - sheet_element_at(sheet_id).first_shape_id = shape_id; - } else { - element_at(previous_sibling_id).next_sibling_id = shape_id; - } - sheet_element_at(sheet_id).last_shape_id = shape_id; + Sheet &sheet = sheet_element_at(sheet_id); + link_child(sheet_id, shape_id, sheet.first_shape_id, sheet.last_shape_id); } void ElementRegistry::append_sheet_cell(const ElementIdentifier sheet_id, const ElementIdentifier cell_id) { - check_sheet_id(sheet_id); - check_element_id(cell_id); - + if (m_sheets.find(sheet_id) == nullptr) { + throw std::out_of_range( + "ElementRegistry::append_sheet_cell: not a sheet identifier"); + } element_at(cell_id).parent_id = sheet_id; } diff --git a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.hpp b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.hpp index 4ef63f107..1d04c4860 100644 --- a/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.hpp +++ b/src/odr/internal/ooxml/spreadsheet/ooxml_spreadsheet_element_registry.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -11,25 +12,20 @@ #include #include +#include #include -#include #include namespace odr::internal::ooxml::spreadsheet { -class ElementRegistry final { -public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - pugi::xml_node node; - }; +struct RegistryElement final : ElementNode { + pugi::xml_node node; +}; +class ElementRegistry final + : public internal::ElementRegistry { +public: struct ElementRelations final { const Relations *relations{nullptr}; AbsPath origin; @@ -88,10 +84,6 @@ class ElementRegistry final { bool is_covered{false}; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type, pugi::xml_node node); std::tuple @@ -105,34 +97,38 @@ class ElementRegistry final { const Relations &relations, const AbsPath &origin); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Sheet &sheet_element_at(ElementIdentifier id); - [[nodiscard]] SheetCell &sheet_cell_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Sheet &sheet_element_at(ElementIdentifier id) const; + [[nodiscard]] Sheet &sheet_element_at(const ElementIdentifier id) { + return m_sheets.at(id); + } + [[nodiscard]] SheetCell &sheet_cell_element_at(const ElementIdentifier id) { + return m_sheet_cells.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Sheet & + sheet_element_at(const ElementIdentifier id) const { + return m_sheets.at(id); + } [[nodiscard]] const SheetCell & - sheet_cell_element_at(ElementIdentifier id) const; + sheet_cell_element_at(const ElementIdentifier id) const { + return m_sheet_cells.at(id); + } [[nodiscard]] const ElementRelations * - element_relations(ElementIdentifier id) const; + element_relations(const ElementIdentifier id) const { + return m_element_relations.find(id); + } - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); void append_shape(ElementIdentifier sheet_id, ElementIdentifier shape_id); void append_sheet_cell(ElementIdentifier sheet_id, ElementIdentifier cell_id); private: - std::vector m_elements; - std::unordered_map m_element_relations; - std::unordered_map m_texts; - std::unordered_map m_sheets; - std::unordered_map m_sheet_cells; - - void check_element_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; - void check_sheet_id(ElementIdentifier id) const; - void check_sheet_cell_id(ElementIdentifier id) const; + SideTable m_element_relations; + SideTable m_texts; + SideTable m_sheets; + SideTable m_sheet_cells; }; } // namespace odr::internal::ooxml::spreadsheet diff --git a/src/odr/internal/ooxml/text/ooxml_text_document.cpp b/src/odr/internal/ooxml/text/ooxml_text_document.cpp index 6b44f00ca..32f902dc6 100644 --- a/src/odr/internal/ooxml/text/ooxml_text_document.cpp +++ b/src/odr/internal/ooxml/text/ooxml_text_document.cpp @@ -1,14 +1,13 @@ #include -#include #include #include #include +#include #include #include #include -#include #include #include @@ -155,137 +154,23 @@ void Document::save(std::ostream & /*out*/, const char * /*password*/) const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::TextRootAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::SpanAdapter, - public abstract::TextAdapter, - public abstract::LinkAdapter, - public abstract::BookmarkAdapter, - public abstract::ListAdapter, - public abstract::ListItemAdapter, - public abstract::TableAdapter, - public abstract::TableColumnAdapter, - public abstract::TableRowAdapter, - public abstract::TableCellAdapter, - public abstract::FrameAdapter, - public abstract::ImageAdapter { +using AdapterBase = internal::RegistryElementAdapter< + ElementRegistry, abstract::TextRootAdapter, abstract::LineBreakAdapter, + abstract::ParagraphAdapter, abstract::SpanAdapter, abstract::TextAdapter, + abstract::LinkAdapter, abstract::BookmarkAdapter, abstract::ListAdapter, + abstract::ListItemAdapter, abstract::TableAdapter, + abstract::TableColumnAdapter, abstract::TableRowAdapter, + abstract::TableCellAdapter, abstract::FrameAdapter, abstract::ImageAdapter>; + +class ElementAdapter final : public AdapterBase { public: ElementAdapter(const Document &document, ElementRegistry ®istry) - : m_document(&document), m_registry(®istry) {} - - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } + : AdapterBase(registry), m_document(&document) {} - [[nodiscard]] bool element_is_unique( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } - [[nodiscard]] bool element_is_self_locatable( - [[maybe_unused]] const ElementIdentifier element_id) const override { - return true; - } [[nodiscard]] bool element_is_editable( [[maybe_unused]] const ElementIdentifier element_id) const override { return true; } - [[nodiscard]] - DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const TextRootAdapter * - text_root_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::root ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const SpanAdapter * - span_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::span ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } - [[nodiscard]] const LinkAdapter * - link_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::link ? this : nullptr; - } - [[nodiscard]] const BookmarkAdapter * - bookmark_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::bookmark ? this : nullptr; - } - [[nodiscard]] const ListAdapter * - list_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::list ? this : nullptr; - } - - [[nodiscard]] const ListItemAdapter * - list_item_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::list_item ? this : nullptr; - } - [[nodiscard]] const TableAdapter * - table_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table ? this : nullptr; - } - [[nodiscard]] const TableColumnAdapter * - table_column_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_column ? this - : nullptr; - } - [[nodiscard]] const TableRowAdapter * - table_row_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_row ? this : nullptr; - } - [[nodiscard]] const TableCellAdapter * - table_cell_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::table_cell ? this : nullptr; - } - [[nodiscard]] const FrameAdapter * - frame_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::frame ? this : nullptr; - } - [[nodiscard]] const ImageAdapter * - image_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::image ? this : nullptr; - } [[nodiscard]] PageLayout text_root_page_layout( [[maybe_unused]] const ElementIdentifier element_id) const override { @@ -611,7 +496,6 @@ class ElementAdapter final : public abstract::ElementAdapter, private: const Document *m_document{nullptr}; - ElementRegistry *m_registry{nullptr}; [[nodiscard]] pugi::xml_node get_node(const ElementIdentifier element_id) const { diff --git a/src/odr/internal/ooxml/text/ooxml_text_element_registry.cpp b/src/odr/internal/ooxml/text/ooxml_text_element_registry.cpp index 1b8870bc1..960f6325e 100644 --- a/src/odr/internal/ooxml/text/ooxml_text_element_registry.cpp +++ b/src/odr/internal/ooxml/text/ooxml_text_element_registry.cpp @@ -1,27 +1,11 @@ #include -#include - namespace odr::internal::ooxml::text { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_tables.clear(); - m_texts.clear(); - m_list_types.clear(); - m_list_markers.clear(); -} - -[[nodiscard]] std::size_t ElementRegistry::size() const noexcept { - return m_elements.size(); -} - std::tuple ElementRegistry::create_element(const ElementType type, const pugi::xml_node node) { - Element &element = m_elements.emplace_back(); - const ElementIdentifier element_id = m_elements.size(); - element.type = type; + const auto &[element_id, element] = create_element_(type); element.node = node; return {element_id, element}; } @@ -30,8 +14,8 @@ std::tuple ElementRegistry::create_table_element(const pugi::xml_node node) { const auto &[element_id, element] = create_element(ElementType::table, node); - auto [it, success] = m_tables.emplace(element_id, Table{}); - return {element_id, element, it->second}; + Table &table = m_tables.emplace(element_id, Table{}); + return {element_id, element, table}; } std::tuplesecond}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) { - return m_tables.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Table & -ElementRegistry::table_element_at(const ElementIdentifier id) const { - return m_tables.at(id); -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_table_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_tables.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range( - "DocumentElementRegistry::check_id: identifier not found"); - } -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; + Text &text = m_texts.emplace(element_id, Text{last_node}); + return {element_id, element, text}; } void ElementRegistry::append_column(const ElementIdentifier table_id, const ElementIdentifier column_id) { - check_table_id(table_id); - check_element_id(column_id); - - const ElementIdentifier previous_sibling_id = - table_element_at(table_id).last_column_id; - - element_at(column_id).parent_id = table_id; - element_at(column_id).previous_sibling_id = previous_sibling_id; - - if (table_element_at(table_id).first_column_id == null_element_id) { - table_element_at(table_id).first_column_id = column_id; - } else { - element_at(previous_sibling_id).next_sibling_id = column_id; - } - table_element_at(table_id).last_column_id = column_id; + Table &table = table_element_at(table_id); + link_child(table_id, column_id, table.first_column_id, table.last_column_id); } void ElementRegistry::set_list_type(const ElementIdentifier id, const ListType type) { check_element_id(id); - m_list_types[id] = type; + m_list_types.emplace(id, type); } void ElementRegistry::set_list_marker(const ElementIdentifier id, ListMarker marker) { check_element_id(id); - m_list_markers[id] = std::move(marker); + m_list_markers.emplace(id, std::move(marker)); } -[[nodiscard]] ListType -ElementRegistry::list_type(const ElementIdentifier id) const { - const auto it = m_list_types.find(id); - return it != std::end(m_list_types) ? it->second : ListType::unordered; +ListType ElementRegistry::list_type(const ElementIdentifier id) const { + const ListType *type = m_list_types.find(id); + return type != nullptr ? *type : ListType::unordered; } -[[nodiscard]] const ListMarker & +const ListMarker & ElementRegistry::list_marker(const ElementIdentifier id) const { static const ListMarker none; - const auto it = m_list_markers.find(id); - return it != std::end(m_list_markers) ? it->second : none; + const ListMarker *marker = m_list_markers.find(id); + return marker != nullptr ? *marker : none; } } // namespace odr::internal::ooxml::text diff --git a/src/odr/internal/ooxml/text/ooxml_text_element_registry.hpp b/src/odr/internal/ooxml/text/ooxml_text_element_registry.hpp index e3c9eb9e9..3743699b2 100644 --- a/src/odr/internal/ooxml/text/ooxml_text_element_registry.hpp +++ b/src/odr/internal/ooxml/text/ooxml_text_element_registry.hpp @@ -3,28 +3,22 @@ #include #include +#include #include -#include -#include -#include +#include #include namespace odr::internal::ooxml::text { -class ElementRegistry final { -public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - pugi::xml_node node; - }; +struct RegistryElement final : ElementNode { + pugi::xml_node node; +}; +class ElementRegistry final + : public internal::ElementRegistry { +public: struct Table final { ElementIdentifier first_column_id{null_element_id}; ElementIdentifier last_column_id{null_element_id}; @@ -34,10 +28,6 @@ class ElementRegistry final { pugi::xml_node last; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type, pugi::xml_node node); std::tuple @@ -45,15 +35,21 @@ class ElementRegistry final { std::tuple create_text_element(pugi::xml_node first_node, pugi::xml_node last_node); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - [[nodiscard]] Table &table_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - [[nodiscard]] const Table &table_element_at(ElementIdentifier id) const; + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] Table &table_element_at(const ElementIdentifier id) { + return m_tables.at(id); + } + + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } + [[nodiscard]] const Table & + table_element_at(const ElementIdentifier id) const { + return m_tables.at(id); + } - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); void append_column(ElementIdentifier table_id, ElementIdentifier column_id); void set_list_type(ElementIdentifier id, ListType type); @@ -63,15 +59,10 @@ class ElementRegistry final { [[nodiscard]] const ListMarker &list_marker(ElementIdentifier id) const; private: - std::vector m_elements; - std::unordered_map m_tables; - std::unordered_map m_texts; - std::unordered_map m_list_types; - std::unordered_map m_list_markers; - - void check_element_id(ElementIdentifier id) const; - void check_table_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; + SideTable
m_tables; + SideTable m_texts; + SideTable m_list_types; + SideTable m_list_markers; }; } // namespace odr::internal::ooxml::text diff --git a/src/odr/internal/rtf/AGENTS.md b/src/odr/internal/rtf/AGENTS.md index df6c3fcb8..797c6de28 100644 --- a/src/odr/internal/rtf/AGENTS.md +++ b/src/odr/internal/rtf/AGENTS.md @@ -22,7 +22,7 @@ bytes ─▶ Tokenizer ─▶ TreeBuilder ─▶ ElementRegistry ─▶ Document | `rtf_tokenizer.*` | Bytes → tokens. Knows nothing about groups or destinations. | | `rtf_state.*` | The group stack: `{` saves, `}` restores. | | `rtf_parser.*` | `parse_tree` — tokens → `root → (paragraph \| page break) → (text \| line break)`. | -| `rtf_element_registry.*` | The flat registry, copied from `oldms/text` minus its style index. | +| `rtf_element_registry.*` | `internal::ElementRegistry` plus one `Text` payload — the smallest registry there is, and the one to read first. | | `rtf_document.*` | `internal::Document` + the element adapter. | | `rtf_file.*` | `abstract::DocumentFile`; validates the magic, hands out the document. | diff --git a/src/odr/internal/rtf/rtf_document.cpp b/src/odr/internal/rtf/rtf_document.cpp index 260d715b2..941e57a50 100644 --- a/src/odr/internal/rtf/rtf_document.cpp +++ b/src/odr/internal/rtf/rtf_document.cpp @@ -1,13 +1,12 @@ #include -#include #include #include #include #include +#include #include -#include #include #include @@ -35,82 +34,15 @@ const ElementRegistry &Document::element_registry() const { namespace { -class ElementAdapter final : public abstract::ElementAdapter, - public abstract::TextRootAdapter, - public abstract::LineBreakAdapter, - public abstract::ParagraphAdapter, - public abstract::TextAdapter { +using AdapterBase = internal::RegistryElementAdapter< + const ElementRegistry, abstract::TextRootAdapter, + abstract::LineBreakAdapter, abstract::ParagraphAdapter, + abstract::TextAdapter>; + +class ElementAdapter final : public AdapterBase { public: explicit ElementAdapter(const ElementRegistry ®istry) - : m_registry(®istry) {} - - [[nodiscard]] ElementType - element_type(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).type; - } - - [[nodiscard]] ElementIdentifier - element_parent(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).parent_id; - } - [[nodiscard]] ElementIdentifier - element_first_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).first_child_id; - } - [[nodiscard]] ElementIdentifier - element_last_child(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).last_child_id; - } - [[nodiscard]] ElementIdentifier - element_previous_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).previous_sibling_id; - } - [[nodiscard]] ElementIdentifier - element_next_sibling(const ElementIdentifier element_id) const override { - return m_registry->element_at(element_id).next_sibling_id; - } - - [[nodiscard]] bool - element_is_unique(const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool - element_is_self_locatable(const ElementIdentifier element_id) const override { - (void)element_id; - return true; - } - [[nodiscard]] bool - element_is_editable(const ElementIdentifier element_id) const override { - (void)element_id; - return false; - } - [[nodiscard]] DocumentPath - element_document_path(const ElementIdentifier element_id) const override { - return util::document::extract_path(*this, element_id, null_element_id); - } - [[nodiscard]] ElementIdentifier - element_navigate_path(const ElementIdentifier element_id, - const DocumentPath &path) const override { - return util::document::navigate_path(*this, element_id, path); - } - - [[nodiscard]] const TextRootAdapter * - text_root_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::root ? this : nullptr; - } - [[nodiscard]] const LineBreakAdapter * - line_break_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::line_break ? this : nullptr; - } - [[nodiscard]] const ParagraphAdapter * - paragraph_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::paragraph ? this : nullptr; - } - [[nodiscard]] const TextAdapter * - text_adapter(const ElementIdentifier element_id) const override { - return element_type(element_id) == ElementType::text ? this : nullptr; - } + : AdapterBase(registry) {} [[nodiscard]] PageLayout text_root_page_layout(const ElementIdentifier element_id) const override { @@ -156,9 +88,6 @@ class ElementAdapter final : public abstract::ElementAdapter, (void)element_id; return {}; } - -private: - const ElementRegistry *m_registry{nullptr}; }; std::unique_ptr diff --git a/src/odr/internal/rtf/rtf_element_registry.cpp b/src/odr/internal/rtf/rtf_element_registry.cpp index 31df8dbbf..fc706b88b 100644 --- a/src/odr/internal/rtf/rtf_element_registry.cpp +++ b/src/odr/internal/rtf/rtf_element_registry.cpp @@ -1,94 +1,18 @@ #include -#include - namespace odr::internal::rtf { -void ElementRegistry::clear() noexcept { - m_elements.clear(); - m_texts.clear(); -} - -std::size_t ElementRegistry::size() const noexcept { return m_elements.size(); } - std::tuple ElementRegistry::create_element(const ElementType type) { - Element &element = m_elements.emplace_back(); - ElementIdentifier element_id = m_elements.size(); - element.type = type; - return {element_id, element}; + return create_element_(type); } std::tuple ElementRegistry::create_text_element() { - const auto &[element_id, element] = create_element(ElementType::text); - auto [it, success] = m_texts.emplace(element_id, Text{}); - return {element_id, element, it->second}; -} - -ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) { - check_element_id(id); - return m_elements.at(id - 1); -} - -ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) { - check_text_id(id); - return m_texts.at(id); -} - -const ElementRegistry::Element & -ElementRegistry::element_at(const ElementIdentifier id) const { - check_element_id(id); - return m_elements.at(id - 1); -} - -const ElementRegistry::Text & -ElementRegistry::text_element_at(const ElementIdentifier id) const { - check_text_id(id); - return m_texts.at(id); -} - -void ElementRegistry::append_child(const ElementIdentifier parent_id, - const ElementIdentifier child_id) { - check_element_id(parent_id); - check_element_id(child_id); - if (element_at(child_id).parent_id != null_element_id) { - throw std::invalid_argument( - "ElementRegistry::append_child: child already has a parent"); - } - - const ElementIdentifier previous_sibling_id = - element_at(parent_id).last_child_id; - - element_at(child_id).parent_id = parent_id; - element_at(child_id).previous_sibling_id = previous_sibling_id; - - if (element_at(parent_id).first_child_id == null_element_id) { - element_at(parent_id).first_child_id = child_id; - } else { - element_at(previous_sibling_id).next_sibling_id = child_id; - } - element_at(parent_id).last_child_id = child_id; -} - -void ElementRegistry::check_element_id(const ElementIdentifier id) const { - if (id == null_element_id) { - throw std::out_of_range("ElementRegistry::check_id: null identifier"); - } - if (id - 1 >= m_elements.size()) { - throw std::out_of_range( - "ElementRegistry::check_id: identifier out of range"); - } -} - -void ElementRegistry::check_text_id(const ElementIdentifier id) const { - check_element_id(id); - if (!m_texts.contains(id)) { - throw std::out_of_range("ElementRegistry::check_id: identifier not found"); - } + const auto &[element_id, element] = create_element_(ElementType::text); + Text &text = m_texts.emplace(element_id, Text{}); + return {element_id, element, text}; } } // namespace odr::internal::rtf diff --git a/src/odr/internal/rtf/rtf_element_registry.hpp b/src/odr/internal/rtf/rtf_element_registry.hpp index cf1f8b013..c59c12226 100644 --- a/src/odr/internal/rtf/rtf_element_registry.hpp +++ b/src/odr/internal/rtf/rtf_element_registry.hpp @@ -3,50 +3,32 @@ #include #include -#include +#include + #include #include -#include -#include namespace odr::internal::rtf { -class ElementRegistry final { +class ElementRegistry final + : public internal::ElementRegistry> { public: - struct Element final { - ElementIdentifier parent_id{null_element_id}; - ElementIdentifier first_child_id{null_element_id}; - ElementIdentifier last_child_id{null_element_id}; - ElementIdentifier previous_sibling_id{null_element_id}; - ElementIdentifier next_sibling_id{null_element_id}; - ElementType type{ElementType::none}; - }; - struct Text final { std::string text; }; - void clear() noexcept; - - [[nodiscard]] std::size_t size() const noexcept; - std::tuple create_element(ElementType type); std::tuple create_text_element(); - [[nodiscard]] Element &element_at(ElementIdentifier id); - [[nodiscard]] Text &text_element_at(ElementIdentifier id); - - [[nodiscard]] const Element &element_at(ElementIdentifier id) const; - [[nodiscard]] const Text &text_element_at(ElementIdentifier id) const; - - void append_child(ElementIdentifier parent_id, ElementIdentifier child_id); + [[nodiscard]] Text &text_element_at(const ElementIdentifier id) { + return m_texts.at(id); + } + [[nodiscard]] const Text &text_element_at(const ElementIdentifier id) const { + return m_texts.at(id); + } private: - std::vector m_elements; - std::unordered_map m_texts; - - void check_element_id(ElementIdentifier id) const; - void check_text_id(ElementIdentifier id) const; + SideTable m_texts; }; } // namespace odr::internal::rtf