openMSX
XMLOutputStream.hh
Go to the documentation of this file.
1#ifndef XMLOUTPUTSTREAM_HH
2#define XMLOUTPUTSTREAM_HH
3
4#include "XMLEscape.hh"
5
6#include "ranges.hh"
7
8#include <algorithm>
9#include <array>
10#include <concepts>
11#include <string>
12#include <string_view>
13#include <vector>
14
74template<typename Operations>
76{
77public:
78 explicit XMLOutputStream(Operations& ops_)
79 : ops(ops_) {}
80
81 void begin(std::string_view tag);
82 void attribute(std::string_view name, std::string_view value);
83 void data(std::string_view value);
84 void end(std::string_view tag);
85
86 void with_tag(std::string_view tag, std::invocable auto next);
87
88private:
89 void writeSpaces(unsigned n);
90 void writeChar(char c);
91 void writeString(std::string_view s);
92 void writeEscapedString(std::string_view s);
93
94private:
95 Operations& ops;
96 unsigned level = 0;
97 enum State {
98 INDENT, // the next (begin or end) tag needs to be indented
99 CLOSE, // the begin tag still needs to be closed
100 DATA, // we got data, but no end-tag yet
101 };
102 State state = INDENT;
103#ifdef DEBUG
104 std::vector<std::string> stack;
105#endif
106};
107
108
109template<typename Writer>
110void XMLOutputStream<Writer>::begin(std::string_view tag)
111{
112#ifdef DEBUG
113 stack.emplace_back(tag);
114#endif
115 ops.check(state != DATA);
116 if (state == CLOSE) {
117 writeString(">\n");
118 }
119 writeSpaces(2 * level);
120 writeChar('<');
121 writeString(tag);
122 ++level;
123 state = CLOSE;
124}
125
126template<typename Writer>
127void XMLOutputStream<Writer>::attribute(std::string_view name, std::string_view value)
128{
129 ops.check(level > 0);
130 ops.check(state == CLOSE);
131 writeChar(' ');
132 writeString(name);
133 writeString("=\"");
134 writeEscapedString(value);
135 writeChar('"');
136}
137
138template<typename Writer>
139void XMLOutputStream<Writer>::data(std::string_view value)
140{
141 ops.check(level > 0);
142 ops.check(state == CLOSE);
143
144 if (value.empty()) return;
145
146 writeChar('>');
147 writeEscapedString(value);
148 state = DATA;
149}
150
151template<typename Writer>
152void XMLOutputStream<Writer>::end(std::string_view tag)
153{
154#ifdef DEBUG
155 ops.check(stack.size() == level);
156 ops.check(!stack.empty());
157 ops.check(stack.back() == tag);
158 stack.pop_back();
159#endif
160 ops.check(level > 0);
161 --level;
162 if (state == CLOSE) {
163 writeString("/>\n");
164 } else {
165 if (state == INDENT) {
166 writeSpaces(2 * level);
167 }
168 writeString("</");
169 writeString(tag);
170 writeString(">\n");
171 }
172 state = INDENT;
173}
174
175template<typename Writer>
176void XMLOutputStream<Writer>::with_tag(std::string_view tag, std::invocable auto next)
177{
178 begin(tag);
179 next();
180 end(tag);
181}
182
183template<typename Writer>
185{
186 static constexpr std::array<char, 64> spaces = {
187 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
188 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
189 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
190 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
191 };
192 while (n) {
193 auto t = std::min(64u, n);
194 ops.write(subspan(spaces, 0, t));
195 n -= t;
196 }
197}
198
199template<typename Writer>
201{
202 ops.write1(c);
203}
204
205template<typename Writer>
206void XMLOutputStream<Writer>::writeString(std::string_view s)
207{
208 ops.write(s);
209}
210
211template<typename Writer>
212void XMLOutputStream<Writer>::writeEscapedString(std::string_view s)
213{
214 XMLEscape(s, [&](std::string_view chunk) { writeString(chunk); });
215}
216
217#endif
TclObject t
void XMLEscape(std::string_view s, Output output)
Definition XMLEscape.hh:22
'XMLOutputStream' is a helper to write an XML file in a streaming way.
void attribute(std::string_view name, std::string_view value)
void begin(std::string_view tag)
XMLOutputStream(Operations &ops_)
void end(std::string_view tag)
void with_tag(std::string_view tag, std::invocable auto next)
void data(std::string_view value)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:481
auto spaces(size_t n)
Definition strCat.hh:801
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)