Line data Source code
1 : /**
2 : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_DATA_SPDATAENCODESERENITY_H_
25 : #define STAPPLER_DATA_SPDATAENCODESERENITY_H_
26 :
27 : #include "SPDataValue.h"
28 : #include "SPFilesystem.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::data::serenity {
31 :
32 : bool shouldEncodePercent(char c);
33 :
34 : template <typename StringType>
35 0 : inline void encodeString(const Callback<void(StringView)> &stream, const StringType &str) {
36 0 : for (auto &i : str) {
37 0 : if (shouldEncodePercent(i)) {
38 0 : stream << '%' << base16::charToHex(i);
39 : } else {
40 0 : stream << i;
41 : }
42 : }
43 0 : }
44 :
45 : template <typename Interface>
46 : struct RawEncoder : public Interface::AllocBaseType {
47 : using InterfaceType = Interface;
48 : using ValueType = ValueTemplate<Interface>;
49 :
50 : enum Type {
51 : Dict,
52 : Array,
53 : Plain,
54 : };
55 :
56 0 : inline RawEncoder(const Callback<void(StringView)> *s) : stream(s) { }
57 :
58 0 : inline void write(nullptr_t) { (*stream) << "null"; }
59 0 : inline void write(bool value) { (*stream) << ((value)?"true":"false"); }
60 0 : inline void write(int64_t value) { (*stream) << value; }
61 0 : inline void write(double value) { (*stream) << value; }
62 :
63 0 : inline void write(const typename ValueType::StringType &str) {
64 0 : encodeString(*stream, str);
65 0 : }
66 :
67 0 : inline void write(const typename ValueType::BytesType &data) {
68 0 : (*stream) << '~';
69 0 : encodeString(*stream, data);
70 0 : }
71 :
72 0 : inline void onBeginArray(const typename ValueType::ArrayType &arr) {
73 0 : if (type == Type::Dict) {
74 0 : type = Type::Plain;
75 : } else {
76 0 : type = Type::Array;
77 0 : (*stream) << "~(";
78 : }
79 0 : preventKey = false;
80 0 : }
81 :
82 0 : inline void onEndArray(const typename ValueType::ArrayType &arr) {
83 0 : if (type != Type::Plain) {
84 0 : (*stream) << ')';
85 0 : preventKey = true;
86 : } else {
87 0 : preventKey = false;
88 : }
89 0 : }
90 :
91 0 : inline void onBeginDict(const typename ValueType::DictionaryType &dict) {
92 0 : (*stream) << '(';
93 0 : type = Type::Dict;
94 0 : preventKey = false;
95 0 : }
96 0 : inline void onEndDict(const typename ValueType::DictionaryType &dict) {
97 0 : (*stream) << ')';
98 0 : preventKey = true;
99 0 : }
100 0 : inline void onKey(const typename ValueType::StringType &str) { write(str); }
101 0 : inline void onNextValue() {
102 0 : if (!preventKey) {
103 0 : (*stream) << ((type == Type::Dict)?';':',');
104 : } else {
105 0 : preventKey = false;
106 : }
107 0 : }
108 :
109 0 : inline void onArrayValue(const ValueType &val) {
110 0 : auto tmpType = type;
111 0 : val.encode(*this);
112 0 : type = tmpType;
113 0 : }
114 :
115 0 : inline void onKeyValuePair(const typename ValueType::StringType &key, const ValueType &val) {
116 0 : auto tmpType = type;
117 0 : onKey(key);
118 0 : if (!val.isBool() || !val.asBool()) {
119 0 : if (!val.isDictionary()) {
120 0 : (*stream) << ':';
121 : }
122 0 : if (val.isArray() && val.size() < 2) {
123 0 : type = Type::Plain; // prevent plain array
124 : }
125 0 : val.encode(*this);
126 : } else {
127 0 : (*stream) << ":true";
128 : }
129 0 : type = tmpType;
130 0 : }
131 :
132 : bool preventKey = false;
133 : const Callback<void(StringView)> *stream;
134 : Type type = Type::Dict;
135 : };
136 :
137 : template <typename Interface>
138 : struct PrettyEncoder : public Interface::AllocBaseType {
139 : using InterfaceType = Interface;
140 : using ValueType = ValueTemplate<Interface>;
141 :
142 : enum Type {
143 : Dict,
144 : Array,
145 : Plain,
146 : };
147 :
148 0 : PrettyEncoder(const Callback<void(StringView)> *stream) : stream(stream) { }
149 :
150 0 : void write(nullptr_t) { (*stream) << "null"; offsetted = false; }
151 0 : void write(bool value) { (*stream) << ((value)?"true":"false"); offsetted = false; }
152 0 : void write(int64_t value) { (*stream) << value; offsetted = false; }
153 0 : void write(double value) { (*stream) << value; offsetted = false; }
154 :
155 0 : void write(const typename ValueType::StringType &str) {
156 0 : encodeString(*stream, str);
157 0 : offsetted = false;
158 0 : }
159 :
160 0 : void write(const typename ValueType::BytesType &data) {
161 0 : (*stream) << '~';
162 0 : encodeString(*stream, data);
163 0 : }
164 :
165 0 : bool isObjectArray(const typename ValueType::ArrayType &arr) {
166 0 : for (auto &it : arr) {
167 0 : if (!it.isDictionary()) {
168 0 : return false;
169 : }
170 : }
171 0 : return true;
172 : }
173 :
174 0 : void onBeginArray(const typename ValueType::ArrayType &arr) {
175 0 : if (type == Type::Dict) {
176 0 : type = Type::Plain;
177 : } else {
178 0 : type = Type::Array;
179 0 : (*stream) << "~(";
180 : }
181 :
182 0 : if (!isObjectArray(arr)) {
183 0 : ++ depth;
184 0 : bstack.push_back(false);
185 0 : offsetted = false;
186 : } else {
187 0 : bstack.push_back(true);
188 : }
189 0 : }
190 :
191 0 : void onEndArray(const typename ValueType::ArrayType &arr) {
192 0 : if (!bstack.empty()) {
193 0 : if (!bstack.back()) {
194 0 : -- depth;
195 0 : (*stream) << '\n';
196 0 : for (size_t i = 0; i < depth; i++) {
197 0 : (*stream) << '\t';
198 : }
199 : }
200 0 : bstack.pop_back();
201 : } else {
202 0 : -- depth;
203 0 : (*stream) << '\n';
204 0 : for (size_t i = 0; i < depth; i++) {
205 0 : (*stream) << '\t';
206 : }
207 : }
208 0 : if (type != Type::Plain) {
209 0 : (*stream) << ')';
210 : }
211 0 : popComplex = true;
212 0 : }
213 :
214 0 : void onBeginDict(const typename ValueType::DictionaryType &dict) {
215 0 : (*stream) << '(';
216 0 : type = Type::Dict;
217 0 : ++ depth;
218 0 : }
219 :
220 0 : void onEndDict(const typename ValueType::DictionaryType &dict) {
221 0 : -- depth;
222 0 : (*stream) << '\n';
223 0 : for (size_t i = 0; i < depth; i++) {
224 0 : (*stream) << '\t';
225 : }
226 0 : (*stream) << ')';
227 0 : popComplex = true;
228 0 : }
229 :
230 0 : void onKey(const typename ValueType::StringType &str) {
231 0 : (*stream) << '\n';
232 0 : for (size_t i = 0; i < depth; i++) {
233 0 : (*stream) << '\t';
234 : }
235 0 : write(str);
236 0 : offsetted = true;
237 0 : }
238 :
239 0 : void onNextValue() {
240 0 : (*stream) << ((type == Type::Dict)?';':',');
241 0 : }
242 :
243 0 : void onValue(const ValueType &val) {
244 0 : if (depth > 0) {
245 0 : if (popComplex && (val.isArray() || val.isDictionary())) {
246 0 : (*stream) << ' ';
247 : } else {
248 0 : if (!offsetted) {
249 0 : (*stream) << '\n';
250 0 : for (size_t i = 0; i < depth; i++) {
251 0 : (*stream) << '\t';
252 : }
253 0 : offsetted = true;
254 : }
255 : }
256 0 : popComplex = false;
257 : }
258 0 : }
259 :
260 0 : inline void onArrayValue(const ValueType &val) {
261 0 : auto tmpType = type;
262 0 : val.encode(*this);
263 0 : type = tmpType;
264 0 : }
265 :
266 0 : inline void onKeyValuePair(const typename ValueType::StringType &key, const ValueType &val) {
267 0 : auto tmpType = type;
268 0 : onKey(key);
269 0 : if (!val.isBool() || !val.asBool()) {
270 0 : (*stream) << ' ';
271 0 : if (!val.isDictionary()) {
272 0 : (*stream) << ": ";
273 : }
274 0 : if (val.isArray() && val.size() < 2) {
275 0 : type = Type::Plain; // prevent plain array
276 : }
277 0 : val.encode(*this);
278 : } else {
279 0 : (*stream) << ": true";
280 : }
281 0 : type = tmpType;
282 0 : }
283 :
284 : size_t depth = 0;
285 : bool popComplex = false;
286 : bool offsetted = false;
287 : const Callback<void(StringView)> *stream;
288 : typename Interface::template ArrayType<bool> bstack;
289 : Type type = Type::Dict;
290 : };
291 :
292 : template <typename Interface>
293 0 : inline void write(const Callback<void(StringView)> &stream, const ValueTemplate<Interface> &val, bool pretty) {
294 0 : if (pretty) {
295 0 : PrettyEncoder<Interface> encoder(&stream);
296 0 : val.encode(encoder);
297 0 : } else {
298 0 : RawEncoder<Interface> encoder(&stream);
299 0 : val.encode(encoder);
300 : }
301 0 : }
302 :
303 : template <typename Interface>
304 0 : inline auto write(const ValueTemplate<Interface> &val, bool pretty = false) -> typename Interface::StringType {
305 0 : typename Interface::StringType stream;
306 0 : write<Interface>([&] (StringView str) {
307 0 : stream.append(str.data(), str.size());
308 : }, val, pretty);
309 0 : return stream;
310 0 : }
311 :
312 : template <typename Interface>
313 0 : bool save(const ValueTemplate<Interface> &val, StringView ipath, bool pretty) {
314 0 : auto path = filesystem::native::posixToNative<Interface>(ipath);
315 0 : std::ofstream stream(path.data());
316 0 : if (stream.is_open()) {
317 0 : write([&] (StringView str) {
318 0 : stream.write(str.data(), str.size());
319 : }, val, pretty);
320 0 : stream.flush();
321 0 : stream.close();
322 0 : return true;
323 : }
324 0 : return false;
325 0 : }
326 :
327 : template <typename Interface>
328 : auto toString(const ValueTemplate<Interface> &data, bool pretty) -> typename Interface::StringType {
329 : return write(data, pretty);
330 : }
331 :
332 : }
333 :
334 : #endif /* STAPPLER_DATA_SPDATAENCODESERENITY_H_ */
|