From f6413400a00362cf307d0fbb85daf96265091686 Mon Sep 17 00:00:00 2001 From: Daniel Pfeifer Date: Sat, 23 May 2015 23:04:50 +0200 Subject: Add cmXMLWriter class to consolidate XML generation Explicitly track XML generation state (indentation, element closure, etc.) so that clients can avoid manually/implicitly maintaining it. --- Source/CMakeLists.txt | 2 + Source/cmXMLWriter.cxx | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ Source/cmXMLWriter.h | 120 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 Source/cmXMLWriter.cxx create mode 100644 Source/cmXMLWriter.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 9624401..a7adb51 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -347,6 +347,8 @@ set(SRCS cmXMLParser.h cmXMLSafe.cxx cmXMLSafe.h + cmXMLWriter.cxx + cmXMLWriter.h cmake.cxx cmake.h diff --git a/Source/cmXMLWriter.cxx b/Source/cmXMLWriter.cxx new file mode 100644 index 0000000..f9b3b49 --- /dev/null +++ b/Source/cmXMLWriter.cxx @@ -0,0 +1,134 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2015 Daniel Pfeifer + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmXMLWriter.h" +#include "cmXMLSafe.h" + +#include +#include + +cmXMLWriter::cmXMLWriter(std::ostream& output, std::size_t level) +: Output(output) +, Level(level) +, ElementOpen(false) +, BreakAttrib(false) +, IsContent(false) +{ +} + +cmXMLWriter::~cmXMLWriter() +{ + assert(this->Elements.empty()); +} + +void cmXMLWriter::StartDocument(const char* encoding) +{ + this->Output << ""; +} + +void cmXMLWriter::EndDocument() +{ + assert(this->Elements.empty()); + this->Output << '\n'; +} + +void cmXMLWriter::StartElement(std::string const& name) +{ + this->CloseStartElement(); + this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->Output << '<' << name; + this->Elements.push(name); + this->ElementOpen = true; + this->BreakAttrib = false; +} + +void cmXMLWriter::EndElement() +{ + assert(!this->Elements.empty()); + if (this->ElementOpen) + { + this->Output << "/>"; + } + else + { + this->ConditionalLineBreak(!this->IsContent, this->Elements.size() - 1); + this->IsContent = false; + this->Output << "Elements.top() << '>'; + } + this->Elements.pop(); + this->ElementOpen = false; +} + +void cmXMLWriter::BreakAttributes() +{ + this->BreakAttrib = true; +} + +void cmXMLWriter::Comment(const char* comment) +{ + this->CloseStartElement(); + this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->Output << ""; +} + +void cmXMLWriter::CData(std::string const& data) +{ + this->PreContent(); + this->Output << ""; +} + +void cmXMLWriter::ProcessingInstruction(const char* target, const char* data) +{ + this->CloseStartElement(); + this->ConditionalLineBreak(!this->IsContent, this->Elements.size()); + this->Output << ""; +} + +void cmXMLWriter::FragmentFile(const char* fname) +{ + this->CloseStartElement(); + std::ifstream fin(fname, std::ios::in | std::ios::binary); + this->Output << fin.rdbuf(); +} + +void cmXMLWriter::ConditionalLineBreak(bool condition, std::size_t indent) +{ + if (condition) + { + this->Output << '\n' << std::string(indent + this->Level, '\t'); + } +} + +void cmXMLWriter::PreAttribute() +{ + assert(this->ElementOpen); + this->ConditionalLineBreak(this->BreakAttrib, this->Elements.size()); + if (!this->BreakAttrib) + { + this->Output << ' '; + } +} + +void cmXMLWriter::PreContent() +{ + this->CloseStartElement(); + this->IsContent = true; +} + +void cmXMLWriter::CloseStartElement() +{ + if (this->ElementOpen) + { + this->ConditionalLineBreak(this->BreakAttrib, this->Elements.size()); + this->Output << '>'; + this->ElementOpen = false; + } +} diff --git a/Source/cmXMLWriter.h b/Source/cmXMLWriter.h new file mode 100644 index 0000000..c38c0de --- /dev/null +++ b/Source/cmXMLWriter.h @@ -0,0 +1,120 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2015 Daniel Pfeifer + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmXMLWiter_h +#define cmXMLWiter_h + +#include "cmStandardIncludes.h" +#include "cmXMLSafe.h" + +#include +#include +#include +#include + +class cmXMLWriter +{ +public: + cmXMLWriter(std::ostream& output, std::size_t level = 0); + ~cmXMLWriter(); + + void StartDocument(const char* encoding = "UTF-8"); + void EndDocument(); + + void StartElement(std::string const& name); + void EndElement(); + + void BreakAttributes(); + + template + void Attribute(const char* name, T const& value) + { + this->PreAttribute(); + this->Output << name << "=\"" << SafeAttribute(value) << '"'; + } + + template + void Element(std::string const& name, T const& value) + { + this->StartElement(name); + this->Content(value); + this->EndElement(); + } + + template + void Content(T const& content) + { + this->PreContent(); + this->Output << SafeContent(content); + } + + void Comment(const char* comment); + + void CData(std::string const& data); + + void ProcessingInstruction(const char* target, const char* data); + + void FragmentFile(const char* fname); + +private: + cmXMLWriter(const cmXMLWriter&); + cmXMLWriter& operator=(const cmXMLWriter&); + + void ConditionalLineBreak(bool condition, std::size_t indent); + + void PreAttribute(); + void PreContent(); + + void CloseStartElement(); + +private: + static cmXMLSafe SafeAttribute(const char* value) + { + return cmXMLSafe(value); + } + + static cmXMLSafe SafeAttribute(std::string const& value) + { + return cmXMLSafe(value); + } + + template + static T SafeAttribute(T value) + { + return value; + } + + static cmXMLSafe SafeContent(const char* value) + { + return cmXMLSafe(value).Quotes(false); + } + + static cmXMLSafe SafeContent(std::string const& value) + { + return cmXMLSafe(value).Quotes(false); + } + + template + static T SafeContent(T value) + { + return value; + } + +private: + std::ostream& Output; + std::stack > Elements; + std::size_t Level; + bool ElementOpen; + bool BreakAttrib; + bool IsContent; +}; + +#endif -- cgit v0.12