From 07ea740d11f0d6c358719da1a455d7bc527509fa Mon Sep 17 00:00:00 2001 From: Crzyrndm Date: Sat, 23 Jun 2018 19:08:39 +1200 Subject: [PATCH] Add the ext_list class for parsing extLst xml elements to/from -- no tests as yet --- include/xlnt/packaging/ext_list.hpp | 72 +++++++++++ .../detail/implementations/workbook_impl.hpp | 2 + .../detail/implementations/worksheet_impl.hpp | 4 + source/packaging/ext_list.cpp | 121 ++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 include/xlnt/packaging/ext_list.hpp create mode 100644 source/packaging/ext_list.cpp diff --git a/include/xlnt/packaging/ext_list.hpp b/include/xlnt/packaging/ext_list.hpp new file mode 100644 index 00000000..3e7e710e --- /dev/null +++ b/include/xlnt/packaging/ext_list.hpp @@ -0,0 +1,72 @@ +// Copyright (c) 2014-2018 Thomas Fussell +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE +// +// @license: http://www.opensource.org/licenses/mit-license.php +// @author: see AUTHORS file + +#pragma once + +#include +#include + +#include +#include + +namespace xml { +class parser; +class serializer; +} + +namespace xlnt { + +/// +/// A list of xml extensions that may or may not be understood by the parser +/// preservation is required for round-tripping even if extension is not understood +/// [serialised: extLst] +/// +class XLNT_API ext_list +{ +public: + struct ext + { + public: + ext(xml::parser &parser); + ext(const uri& ID, const std::string& serialised); + void serialise(xml::serializer &serialiser); + + uri extension_ID_; + std::string serialised_value_; + }; + ext_list() = default; // default ctor required by xlnt::optional + explicit ext_list(xml::parser &parser); + void serialize(xml::serializer &serialiser, std::string nmsp); + + void add_extension(const uri &ID, const std::string &element); + + bool has_extension(const uri &extension_uri) const; + + const ext &extension(const uri &extension_uri) const; + + const std::vector &extensions() const; + +private: + std::vector extensions_; +}; +} // namespace xlnt \ No newline at end of file diff --git a/source/detail/implementations/workbook_impl.hpp b/source/detail/implementations/workbook_impl.hpp index 613728ba..3ecdd6a8 100644 --- a/source/detail/implementations/workbook_impl.hpp +++ b/source/detail/implementations/workbook_impl.hpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -123,6 +124,7 @@ struct workbook_impl optional calculation_properties_; optional abs_path_; optional arch_id_flags_; + optional extensions_; }; } // namespace detail diff --git a/source/detail/implementations/worksheet_impl.hpp b/source/detail/implementations/worksheet_impl.hpp index 7bbe86cc..01f0c7b0 100644 --- a/source/detail/implementations/worksheet_impl.hpp +++ b/source/detail/implementations/worksheet_impl.hpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -81,6 +82,7 @@ struct worksheet_impl views_ = other.views_; column_breaks_ = other.column_breaks_; row_breaks_ = other.row_breaks_; + extension_list_ = other.extension_list_; for (auto &row : cell_map_) { @@ -123,6 +125,8 @@ struct worksheet_impl std::vector row_breaks_; std::unordered_map comments_; + + optional extension_list_; }; } // namespace detail diff --git a/source/packaging/ext_list.cpp b/source/packaging/ext_list.cpp new file mode 100644 index 00000000..13c9b28b --- /dev/null +++ b/source/packaging/ext_list.cpp @@ -0,0 +1,121 @@ +#include +#include + +#include + +namespace { + // send elements straight from parser to serialiser without modification + // runs until the end of the current open element + void roundtrip(xml::parser& p, xml::serializer &s) + { + int nest_level = 0; + while (nest_level > 0 || (p.peek() != xml::parser::event_type::end_element && p.peek() != xml::parser::event_type::eof)) + { + switch (p.next()) + { + case xml::parser::start_element: + { + ++nest_level; + s.start_element(p.qname()); + for (auto &ele : p.attribute_map()) + { + s.attribute(ele.first, ele.second.value); + ele.second.handled = true; + } + break; + } + case xml::parser::end_element: + { + --nest_level; + s.end_element(); + break; + } + case xml::parser::start_namespace_decl: + { + s.namespace_decl(p.namespace_(), p.prefix()); + break; + } + case xml::parser::end_namespace_decl: + { // nothing required here + break; + } + case xml::parser::characters: + { + s.characters(p.value()); + break; + } + } + } + } +} + +namespace xlnt { + +ext_list::ext::ext(xml::parser &parser) +{ + // because libstudxml is a streaming parser, this is parsed once to acquire the complete string form, and then a second time for the URI + std::ostringstream serialisation_stream; + xml::serializer s(serialisation_stream, "", 0); + roundtrip(parser, s); + serialised_value_ = serialisation_stream.str(); + const std::string uri_specifier = R"(uri=")"; + int offset = serialised_value_.find(uri_specifier) + uri_specifier.size(); + int len = serialised_value_.find('"', offset) - offset; + extension_ID_ = uri(serialised_value_.substr(offset, len)); +} + +ext_list::ext::ext(const uri &ID, const std::string &serialised) + : extension_ID_(ID), serialised_value_(serialised) +{} + +void ext_list::ext::serialise(xml::serializer &serialiser) +{ + std::istringstream ser(serialised_value_); + xml::parser p(ser, "", xml::parser::receive_default); + int nest_level = 0; + roundtrip(p, serialiser); +} + +ext_list::ext_list(xml::parser &parser) +{ + // begin with the start element already parsed + while (parser.peek() == xml::parser::start_element) + { + extensions_.push_back(ext(parser)); + } + // end without parsing the end element +} + +void ext_list::serialize(xml::serializer &serialiser, std::string namesp) +{ + serialiser.start_element("extLst", namesp); + for (auto &ext : extensions_) + { + ext.serialise(serialiser); + } + serialiser.end_element(); +} + +void ext_list::add_extension(const uri &ID, const std::string &element) +{ + extensions_.push_back(ext{ID, element}); +} + +bool ext_list::has_extension(const uri &extension_uri) const +{ + return extensions_.end() != std::find_if(extensions_.begin(), extensions_.end(), + [&extension_uri](const ext &ext) { return extension_uri == ext.extension_ID_; }); +} + +const ext_list::ext &ext_list::extension(const uri &extension_uri) const +{ + return *std::find_if(extensions_.begin(), extensions_.end(), + [&extension_uri](const ext &ext) { return extension_uri == ext.extension_ID_; }); +} + +const std::vector &ext_list::extensions() const +{ + return extensions_; +} + +} \ No newline at end of file