#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_XMLWRITER_H #define NUCLEX_STORAGE_XML_XMLWRITER_H #include "Nuclex/Storage/Config.h" #include "Nuclex/Storage/Writer.h" #include "Nuclex/Storage/Xml/XmlBinaryFormat.h" #include #include #include namespace Nuclex { namespace Storage { namespace Xml { // ------------------------------------------------------------------------------------------- // /// Writes data using the XML format /// /// /// This interface extends the with features for controlling /// the generation of XML elements, attributes and comments. It is suitable both for /// populating an XML DOM tree and for generating an XML output stream without seeking. /// /// /// /// XmlBlobWriter blobWriter(std::make_shared<MemoryBlob>()); /// /// XmlWriter &writer = blobWriter; /// /// writer.WriteDeclaration(); /// { XmlWriter::ElementScope(writer, "Scene"); /// writer.SetAttributeValue("Culling", "Grid"); /// /// writer.WriteComment("Something to populate the scene with"); /// { XmlWriter::ElementScope(writer, "Entity"); /// writer.Write(123.456f); /// } /// } /// /// /// class XmlWriter : public Writer { #pragma region class AttributeScope /// Automatically enters an attribute for the lifetime of the scope public: class AttributeScope { /// Initializes a new attribute scope using the specified XML writer /// XML writer the attribute scope will use /// Name of the attribute that will be written public: NUCLEX_STORAGE_API AttributeScope(XmlWriter &writer, const std::string &attributeName) : writer(writer) { this->writer.BeginAttribute(attributeName); } /// Initializes a new attribute scope using the specified XML writer /// XML writer the attribute scope will use /// Name of the attribute that will be written public: NUCLEX_STORAGE_API AttributeScope( const std::shared_ptr &writer, const std::string &attributeName ) : writer(*writer.get()) { this->writer.BeginAttribute(attributeName); } /// Destroys the attribute scope and ends writing the attribute public: NUCLEX_STORAGE_API ~AttributeScope() { this->writer.EndAttribute(); } private: AttributeScope(const AttributeScope &); private: AttributeScope &operator =(const AttributeScope &); /// XML writer in which the scope will create an attribute private: XmlWriter &writer; }; #pragma endregion // class AttributeScope #pragma region class ElementScope /// Automatically enters an element for the lifetime of the scope public: class ElementScope { /// Initializes a new element scope using the specified XML writer /// XML writer the element scope will use /// Name of the element that will be written public: NUCLEX_STORAGE_API ElementScope(XmlWriter &writer, const std::string &elementName) : writer(writer) { this->writer.BeginElement(elementName); } /// Initializes a new element scope using the specified XML writer /// XML writer the element scope will use /// Name of the element that will be written public: NUCLEX_STORAGE_API ElementScope( const std::shared_ptr &writer, const std::string &elementName ) : writer(*writer.get()) { this->writer.BeginElement(elementName); } /// Destroys the element scope and ends writing the element public: NUCLEX_STORAGE_API ~ElementScope() { this->writer.EndElement(); } private: ElementScope(const ElementScope &); private: ElementScope &operator =(const ElementScope &); /// XML writer in which the scope will create an element private: XmlWriter &writer; }; #pragma endregion // class ElementScope #pragma region class CommentScope /// Automatically enters a comment for the lifetime of the scope public: class CommentScope { /// Initializes a new comment scope using the specified XML writer /// XML writer the comment scope will use public: NUCLEX_STORAGE_API CommentScope(XmlWriter &writer) : writer(writer) { this->writer.BeginComment(); } /// Initializes a new comment scope using the specified XML writer /// XML writer the comment scope will use public: NUCLEX_STORAGE_API CommentScope(const std::shared_ptr &writer) : writer(*writer.get()) { this->writer.BeginComment(); } /// Destroys the comment scope and ends writing the comment public: NUCLEX_STORAGE_API ~CommentScope() { this->writer.EndComment(); } private: CommentScope(const CommentScope &); private: CommentScope &operator =(const CommentScope &); /// XML writer in which the scope will create a comment private: XmlWriter &writer; }; #pragma endregion // class CommentScope /// Destroys the XML writer public: NUCLEX_STORAGE_API virtual ~XmlWriter() {} /// Retrieves the currently selected binary data format /// The format in which binary data will be written public: virtual XmlBinaryFormat GetBinaryFormat() const = 0; /// Selects the binary data format to use for writing binary data /// Format in which binary data will be written public: virtual void SetBinaryFormat(XmlBinaryFormat binaryFormat) = 0; /// /// Writes the XML declaration, containing the version of the XML standard and /// the encoding used /// public: virtual void WriteDeclaration(const std::string &encoding = "utf-8") = 0; /// 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: virtual void BeginElement(const std::string &elementName) = 0; /// Closes the current XML element public: virtual void EndElement() = 0; /// 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: virtual void BeginComment() = 0; /// Ends the current XML comment public: virtual void EndComment() = 0; /// Writes a comment into the XML plaintext /// Comment that will be written public: NUCLEX_STORAGE_API void WriteComment(const std::string &comment) { CommentScope scope(*this); Write(comment); } /// 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: virtual void BeginAttribute(const std::string &attributeName) = 0; /// Closes the current XML attribute public: virtual void EndAttribute() = 0; /// Assigns the value of an attribute in the current element /// Name of the attribue whose value will be assigned /// Value that will be assigned to the attribute public: template void SetAttributeValue( const std::string &attributeName, const TValue &value ) { AttributeScope scope(*this, attributeName); Write(value); } // Unhide the overloaded Read() methods in the base class // See http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9 using Writer::Write; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Storage::Xml #endif // NUCLEX_STORAGE_XML_XMLWRITER_H