1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #pragma once
4 
5 #include "cmConfigure.h" // IWYU pragma: keep
6 
7 #include <chrono>
8 #include <cstddef> // IWYU pragma: keep
9 #include <ctime>
10 #include <ostream>
11 #include <stack>
12 #include <string>
13 #include <vector>
14 
15 #include "cmXMLSafe.h"
16 
17 class cmXMLWriter
18 {
19 public:
20   cmXMLWriter(std::ostream& output, std::size_t level = 0);
21   ~cmXMLWriter();
22 
23   cmXMLWriter(cmXMLWriter const&) = delete;
24   cmXMLWriter& operator=(cmXMLWriter const&) = delete;
25 
26   void StartDocument(const char* encoding = "UTF-8");
27   void EndDocument();
28 
29   void StartElement(std::string const& name);
30   void EndElement();
31 
32   void BreakAttributes();
33 
34   template <typename T>
Attribute(const char * name,T const & value)35   void Attribute(const char* name, T const& value)
36   {
37     this->PreAttribute();
38     this->Output << name << "=\"" << SafeAttribute(value) << '"';
39   }
40 
41   void Element(const char* name);
42 
43   template <typename T>
Element(std::string const & name,T const & value)44   void Element(std::string const& name, T const& value)
45   {
46     this->StartElement(name);
47     this->Content(value);
48     this->EndElement();
49   }
50 
51   template <typename T>
Content(T const & content)52   void Content(T const& content)
53   {
54     this->PreContent();
55     this->Output << SafeContent(content);
56   }
57 
58   void Comment(const char* comment);
59 
60   void CData(std::string const& data);
61 
62   void Doctype(const char* doctype);
63 
64   void ProcessingInstruction(const char* target, const char* data);
65 
66   void FragmentFile(const char* fname);
67 
68   void SetIndentationElement(std::string const& element);
69 
70 private:
71   void ConditionalLineBreak(bool condition);
72 
73   void PreAttribute();
74   void PreContent();
75 
76   void CloseStartElement();
77 
SafeAttribute(const char * value)78   static cmXMLSafe SafeAttribute(const char* value) { return { value }; }
79 
SafeAttribute(std::string const & value)80   static cmXMLSafe SafeAttribute(std::string const& value)
81   {
82     return { value };
83   }
84 
85   template <typename T>
SafeAttribute(T value)86   static T SafeAttribute(T value)
87   {
88     return value;
89   }
90 
SafeContent(const char * value)91   static cmXMLSafe SafeContent(const char* value)
92   {
93     return cmXMLSafe(value).Quotes(false);
94   }
95 
SafeContent(std::string const & value)96   static cmXMLSafe SafeContent(std::string const& value)
97   {
98     return cmXMLSafe(value).Quotes(false);
99   }
100 
101   /*
102    * Convert a std::chrono::system::time_point to the number of seconds since
103    * the UN*X epoch.
104    *
105    * It would be tempting to convert a time_point to number of seconds by
106    * using time_since_epoch(). Unfortunately the C++11 standard does not
107    * specify what the epoch of the system_clock must be.
108    * Therefore we must assume it is an arbitrary point in time. Instead of this
109    * method, it is recommended to convert it by means of the to_time_t method.
110    */
SafeContent(std::chrono::system_clock::time_point const & value)111   static std::time_t SafeContent(
112     std::chrono::system_clock::time_point const& value)
113   {
114     return std::chrono::system_clock::to_time_t(value);
115   }
116 
117   template <typename T>
SafeContent(T value)118   static T SafeContent(T value)
119   {
120     return value;
121   }
122 
123   std::ostream& Output;
124   std::stack<std::string, std::vector<std::string>> Elements;
125   std::string IndentationElement;
126   std::size_t Level;
127   std::size_t Indent;
128   bool ElementOpen;
129   bool BreakAttrib;
130   bool IsContent;
131 };
132 
133 class cmXMLElement; // IWYU pragma: keep
134 
135 class cmXMLDocument
136 {
137 public:
cmXMLDocument(cmXMLWriter & xml)138   cmXMLDocument(cmXMLWriter& xml)
139     : xmlwr(xml)
140   {
141     this->xmlwr.StartDocument();
142   }
~cmXMLDocument()143   ~cmXMLDocument() { this->xmlwr.EndDocument(); }
144   cmXMLDocument(const cmXMLDocument&) = delete;
145   cmXMLDocument& operator=(const cmXMLDocument&) = delete;
146 
147 private:
148   friend class cmXMLElement;
149   cmXMLWriter& xmlwr;
150 };
151 
152 class cmXMLElement
153 {
154 public:
cmXMLElement(cmXMLWriter & xml,const char * tag)155   cmXMLElement(cmXMLWriter& xml, const char* tag)
156     : xmlwr(xml)
157   {
158     this->xmlwr.StartElement(tag);
159   }
cmXMLElement(cmXMLElement & par,const char * tag)160   cmXMLElement(cmXMLElement& par, const char* tag)
161     : xmlwr(par.xmlwr)
162   {
163     this->xmlwr.StartElement(tag);
164   }
cmXMLElement(cmXMLDocument & doc,const char * tag)165   cmXMLElement(cmXMLDocument& doc, const char* tag)
166     : xmlwr(doc.xmlwr)
167   {
168     this->xmlwr.StartElement(tag);
169   }
~cmXMLElement()170   ~cmXMLElement() { this->xmlwr.EndElement(); }
171 
172   cmXMLElement(const cmXMLElement&) = delete;
173   cmXMLElement& operator=(const cmXMLElement&) = delete;
174 
175   template <typename T>
Attribute(const char * name,T const & value)176   cmXMLElement& Attribute(const char* name, T const& value)
177   {
178     this->xmlwr.Attribute(name, value);
179     return *this;
180   }
181   template <typename T>
Content(T const & content)182   void Content(T const& content)
183   {
184     this->xmlwr.Content(content);
185   }
186   template <typename T>
Element(std::string const & name,T const & value)187   void Element(std::string const& name, T const& value)
188   {
189     this->xmlwr.Element(name, value);
190   }
Comment(const char * comment)191   void Comment(const char* comment) { this->xmlwr.Comment(comment); }
192 
193 private:
194   cmXMLWriter& xmlwr;
195 };
196