#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_STORAGE_XML_XMLBLOBWRITER_H #define NUCLEX_STORAGE_XML_XMLBLOBWRITER_H #include "Nuclex/Storage/Config.h" #include "Nuclex/Storage/Xml/XmlWriter.h" #include #include namespace Nuclex { namespace Storage { // ------------------------------------------------------------------------------------------- // class Blob; // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Storage namespace Nuclex { namespace Storage { namespace Xml { // ------------------------------------------------------------------------------------------- // /// Writes data in the XML format class XmlBlobWriter : public XmlWriter { /// Initializes a new XML writer writing into a blob /// Blob the XML writer will write into public: NUCLEX_STORAGE_API XmlBlobWriter(const std::shared_ptr &blob); /// Destroys the XML reader public: NUCLEX_STORAGE_API ~XmlBlobWriter(); /// Retrieves the currently selected binary data format /// The format in which binary data will be read public: NUCLEX_STORAGE_API XmlBinaryFormat GetBinaryFormat() const { return this->binaryFormat; } /// Selects the binary data format to use for reading binary data /// Format in which binary data will be read public: NUCLEX_STORAGE_API void SetBinaryFormat(XmlBinaryFormat newBinaryFormat) { this->binaryFormat = newBinaryFormat; } /// /// Writes the XML declaration, containing the version of the XML standard and /// the encoding used /// public: NUCLEX_STORAGE_API void WriteDeclaration(const std::string &encoding = "utf-8"); /// Opens an XML element /// Name of the XML element that will be opened /// /// All values written after this will end up inside the XML element. Consider using /// the ElementScope helper to ensure there's no mismatch between Open/Close calls and /// to simplify your code. /// public: NUCLEX_STORAGE_API void BeginElement(const std::string &elementName); /// Closes the current XML element public: NUCLEX_STORAGE_API void EndElement(); /// Begins an XML comment /// /// All values written after this will end up as individual lines inside the XML /// comment. Consider using the CommentScope helper to ensure there's no mismatch /// between Open/Close calls and to simplify your code. /// public: NUCLEX_STORAGE_API void BeginComment(); /// Ends the current XML comment public: NUCLEX_STORAGE_API void EndComment(); /// Opens an XML attribute in the current element /// Name of the attribute that will be opened /// /// All values written after this will end up inside the XML attribute. It is unusual /// to store more than one value in an XML attribute and separate attributes should /// be given preference. Consider using the AttributeScope helper to ensure there's /// no mismatch between Open/Close calls and to simplify your code. /// public: NUCLEX_STORAGE_API void BeginAttribute(const std::string &attributeName); /// Closes the current XML attribute public: NUCLEX_STORAGE_API void EndAttribute(); /// Writes a boolean into the stream /// Boolean that will be written public: NUCLEX_STORAGE_API void Write(bool value); /// Writes an unsigned 8 bit integer into the stream /// 8 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::uint8_t value); /// Writes a signed 8 bit integer into the stream /// 8 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::int8_t value); /// Writes an unsigned 16 bit integer into the stream /// 16 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::uint16_t value); /// Writes a signed 16 bit integer into the stream /// 16 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::int16_t value); /// Writes an unsigned 32 bit integer into the stream /// 32 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::uint32_t value); /// Writes a signed 32 bit integer into the stream /// 32 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::int32_t value); /// Writes an unsigned 64 bit integer into the stream /// 64 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::uint64_t value); /// Writes a signed 64 bit integer into the stream /// 64 bit integer that will be written public: NUCLEX_STORAGE_API void Write(std::int64_t value); /// Writes a floating point value into the stream /// Floating point value that will be written public: NUCLEX_STORAGE_API void Write(float value); /// Writes a double precision floating point value into the stream /// Double precision floating point value that will be written public: NUCLEX_STORAGE_API void Write(double value); /// Writes a string into the stream /// String that will be written public: NUCLEX_STORAGE_API void Write(const std::string &value); /// Writes a unicode string into the stream /// Unicode string that will be written public: NUCLEX_STORAGE_API void Write(const std::wstring &value); /// Writes a chunk of bytes into the stream /// Buffer the bytes that will be written /// Number of bytes to write public: NUCLEX_STORAGE_API void Write(const void *buffer, std::size_t byteCount); // Unhide the overloaded Read() methods in the base class // See http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9 using XmlWriter::Write; /// Applies a line break to the element the writer is currently in private: void lineBreakOuterElement(); /// Closes the element the writer is currently in private: void closeElement(); /// Appends a comment to the current element /// Comment that will be appended private: void writeComment(const std::string &comment); /// Appends content to the current element /// Content that will be appended private: void writeData(const std::string &text); /// Stores private implementation details private: class Impl; #pragma region enum DeferredToken /// Which kind of token the XML blob writer is still waiting to write public: enum class DeferredToken { /// Nothing is waiting to be written None, /// An element opening is waiting to be written /// /// /// In this case, the element name has been pushed onto our /// stack, but the element opening itself wasn't written at all yet because we don't /// know yet whether it will be an empty element, a single-line data element or /// a multi-line element with children. /// /// /// Buffer contents: whitespace up to the element start. We already flushed /// the outer element's line because only back then did we know whether this is /// its first child (increase indentation) or the nth (keep indentation). /// /// ElementOpening, /// An element's single-line data is waiting to be written /// /// /// We've written the element opening, but are waiting on the data in /// the field. Currently the data seems like it would /// allow of a single-line element, but more data or children might still be /// added to the element. /// /// /// Buffer contents: The element opening tag without . /// If the element is closed, the content and closing tag should be appended, /// otherwise a line break and indentation increase should follow. /// /// ElementOpeningWithContent, /// We're in an element after its first child has been written /// /// /// After something has been written that didn't allow for a single-line element /// (a comment, content that's too long for one line or a child element), we're /// in this state that allows other children to be appended or the element to be /// closed regularly with the closing tag on its own line. /// /// /// Buffer contents: the last line of whatever content the element is carrying. /// If more content follows, an indentation-keeping flush should be used, /// otherwise indentation should be decreased and the closing tag written. /// /// ElementChildren, /// A comment opening is waiting to be written /// /// /// A comment was started, but it isn't clear yet whether this will be an empty /// comment, a single-line comment or a multi-line comment. /// /// /// Buffer contents: whitespace up to the comment start. We already flushed /// the owning element's line because only back then did we know whether this is /// its first child (increase indentation) or the nth (keep indentation). /// /// CommentOpening, /// A comment's single-line text is waiting to be written /// /// /// We've written the comment opening, but are waiting on the text in /// the field. Currently the text seems like it would /// allow of a single-line comment, but more text might be added to the comment. /// /// /// Buffer contents: The comment opening tag without . /// If the comment is closed, the text and comment closing should be appended, /// otherwise a line break and indentation increase should follow. /// /// CommentOpeningWithText, /// We're in a comment after its first line has been written /// /// /// The comment was turned into a multi-line comment and we've written everything /// up to the final line, because we don't know what if the comment will end or /// if more text will be written into it. /// /// /// Buffer contents: the last line of the text the comment is carrying. /// If more text follows, an indentation-keeping flush should be used, /// otherwise indentation should be decreased and the comment closing written. /// /// CommentText }; #pragma endregion // enum DeferredToken /// Blob the XML writer is writing to private: std::shared_ptr blob; /// Implementation details private: std::unique_ptr impl; /// Format in which binary data will be written private: XmlBinaryFormat binaryFormat; /// Names of currently opened XML elements private: std::stack elementNames; /// Token that still needs to be written private: DeferredToken deferredToken; /// Whether the XML writer is currently writing an attribute private: bool isInAttribute; /// Whether the XML writer is currently writing a comment private: bool isInComment; /// Comment or element content being written private: std::string content; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Storage::Xml #endif // NUCLEX_STORAGE_XML_XMLBLOBWRITER_H