# Program Listing for File document.hpp

↰ Return to documentation for file (`zeem/document.hpp`)

    /*-
     * SPDX-License-Identifier: BSD-2-Clause
     *
     * Copyright (c) 2024 Maarten L. Hekkelman
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer
     * 2. Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
     * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */

    #pragma once

    #include "zeem/node.hpp"
    #include "zeem/parser.hpp"
    #include "zeem/text.hpp"
    #include "zeem/version.hpp"

    #include <cstddef>
    #include <functional>
    #include <iosfwd>
    #include <memory>
    #include <string>
    #include <string_view>
    #include <type_traits>
    #include <utility>
    #include <vector>

    namespace zeem
    {

    struct doc_type
    {
        std::string m_root;
        std::string m_pubid; // pubid is empty for SYSTEM DOCTYPE
        std::string m_dtd;
    };

    class document final : public element_container
    {
      public:
        [[nodiscard]] node_type type() const override { return node_type::document; }

        document();

        document(const document &doc);

        document(document &&other) noexcept
            : document()
        {
            swap(*this, other);
        }

        document &operator=(document doc) noexcept
        {
            swap(*this, doc);
            return *this;
        }

        explicit document(std::string_view s);

        explicit document(std::istream &is);

        document(std::istream &is, std::string base_dir);

        ~document() override = default;

        friend void swap(document &a, document &b) noexcept;

        [[nodiscard]] bool is_validating() const { return m_validating; }
        void set_validating(bool validate) { m_validating = validate; }

        [[nodiscard]] bool is_validating_ns() const { return m_validating_ns; }
        void set_validating_ns(bool validate) { m_validating_ns = validate; }

        [[nodiscard]] bool preserves_cdata() const { return m_preserve_cdata; }

        void set_preserve_cdata(bool p) { m_preserve_cdata = p; }

        [[nodiscard]] bool collapses_empty_tags() const { return m_fmt.collapse_tags; }

        void set_collapse_empty_tags(bool c) { m_fmt.collapse_tags = c; }

        [[nodiscard]] bool write_html() const { return m_fmt.html; }

        void set_write_html(bool f) { m_fmt.html = f; }

        [[nodiscard]] bool suppresses_comments() const { return m_fmt.suppress_comments; }

        void set_suppress_comments(bool s) { m_fmt.suppress_comments = s; }

        [[nodiscard]] bool escapes_white_space() const { return m_fmt.escape_white_space; }

        void set_escape_white_space(bool e) { m_fmt.escape_white_space = e; }

        [[nodiscard]] bool escapes_double_quote() const { return m_fmt.escape_double_quote; }

        void set_escape_double_quote(bool e) { m_fmt.escape_double_quote = e; }

        [[nodiscard]] bool wraps_prolog() const { return m_wrap_prolog; }

        void set_wrap_prolog(bool w) { m_wrap_prolog = w; }

        [[nodiscard]] doc_type get_doctype() const { return m_doctype; }

        void set_doctype(std::string root, std::string pubid, std::string dtd)
        {
            set_doctype({ std::move(root), std::move(pubid), std::move(dtd) });
        }

        void set_doctype(const doc_type &doctype)
        {
            m_doctype = doctype;
            m_write_doctype = true;
        }

        [[nodiscard]] bool writes_xml_decl() const { return m_write_xml_decl; }

        void set_write_xml_decl(bool w) { m_write_xml_decl = w; }

        [[nodiscard]] bool writes_doctype() const { return m_write_doctype; }

        void set_write_doctype(bool f) { m_write_doctype = f; }

        [[nodiscard]] bool is_html5() const;

        friend std::ostream &operator<<(std::ostream &os, const document &doc);

        friend std::istream &operator>>(std::istream &is, document &doc);

        bool operator==(const document &doc) const;

        void set_base_dir(std::string path);

        template <typename Callback>
        void set_entity_loader(Callback &&cb)
        {
            m_external_entity_ref_loader = std::forward<Callback>(cb);
        }

        [[nodiscard]] encoding_type get_encoding() const;
        void set_encoding(encoding_type enc);

        [[nodiscard]] version_type get_version() const;
        void set_version(version_type v);

        element_container *root() override { return this; }
        [[nodiscard]] const element_container *root() const override { return this; }

        element *child()
        {
            return empty() ? nullptr : &front();
        }

        [[nodiscard]] const element *child() const
        {
            return empty() ? nullptr : &front();
        }

        template <typename... Args>
            requires std::is_constructible_v<element, Args...>
        auto emplace(Args &&...args)
        {
            return emplace_back(std::forward<Args>(args)...);
        }

        [[nodiscard]] std::string str() const override;

        void write(std::ostream &os, format_info fmt) const override;

      protected:
        node *insert_impl(const node *p, node *n) override;

      private:
        void XmlDeclHandler(encoding_type encoding, bool standalone, version_type version);
        void StartElementHandler(const std::string &name, const std::string &uri, const parser::attr_list_type &atts);
        void EndElementHandler(const std::string &name, const std::string &uri);
        void CharacterDataHandler(std::string data);
        void ProcessingInstructionHandler(std::string target, std::string data);
        void CommentHandler(std::string comment);
        void StartCdataSectionHandler();
        void EndCdataSectionHandler();
        void StartNamespaceDeclHandler(std::string prefix, std::string uri);
        void EndNamespaceDeclHandler(std::string_view prefix);
        void DoctypeDeclHandler(std::string root, std::string publicId, std::string uri);
        void NotationDeclHandler(std::string name, std::string sysid, std::string pubid);

        std::unique_ptr<std::istream> external_entity_ref(std::string_view base, std::string_view pubid, std::string_view sysid);
        void parse(std::istream &data);

        std::function<std::unique_ptr<std::istream>(std::string_view base, std::string_view pubid, std::string_view sysid)>
            m_external_entity_ref_loader;

        std::string m_dtd_dir;

        // some content information
        doc_type m_doctype;
        bool m_validating;
        bool m_validating_ns = false;
        bool m_preserve_cdata;
        bool m_has_xml_decl;
        encoding_type m_encoding;
        version_type m_version;
        bool m_standalone;
        bool m_wrap_prolog = true;
        bool m_write_doctype = false;
        bool m_write_xml_decl = false;

        format_info m_fmt;

        struct notation
        {
            std::string m_name;
            std::string m_sysid;
            std::string m_pubid;
        };

        element_container *m_cur = nullptr; // construction
        cdata *m_cdata = nullptr;           // only defined in a CDATA section
        std::vector<std::pair<std::string, std::string>> m_namespaces;
        std::vector<notation> m_notations;
        size_t m_root_size_at_first_notation = 0; // for processing instructions that occur before a notation

    };

    namespace literals
    {
        document operator""_xml(const char *text, size_t length);
    } // namespace literals

    } // namespace zeem