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