1// Aseprite Code Generator
2// Copyright (C) 2019 Igara Studio S.A.
3// Copyright (C) 2014-2018 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#include "base/exception.h"
9#include "base/file_handle.h"
10#include "base/fs.h"
11#include "base/split_string.h"
12#include "base/string.h"
13#include "gen/common.h"
14#include "gen/pref_types.h"
15
16#include <iostream>
17#include <stdexcept>
18#include <vector>
19
20typedef std::vector<TiXmlElement*> XmlElements;
21
22static void print_pref_class_def(TiXmlElement* elem, const std::string& className, const char* section, int indentSpaces)
23{
24 std::string indent(indentSpaces, ' ');
25 std::cout
26 << "\n"
27 << indent << "class " << className << " : public Section {\n"
28 << indent << "public:\n"
29 << indent << " " << className << "(const std::string& name);\n";
30
31 if (elem->Attribute("canforce"))
32 std::cout << indent << " void forceSection();\n";
33 if (elem->Attribute("canclear"))
34 std::cout << indent << " void clearSection();\n";
35
36 std::cout
37 << indent << " void load();\n"
38 << indent << " void save();\n"
39 << indent << " Section* section(const char* id) override;\n"
40 << indent << " OptionBase* option(const char* id) override;\n";
41
42 TiXmlElement* child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
43 while (child) {
44 if (child->Value()) {
45 std::string name = child->Value();
46 const char* childId = child->Attribute("id");
47
48 if (name == "option") {
49 if (!child->Attribute("type")) throw std::runtime_error("missing 'type' attr in <option>");
50 if (!childId) throw std::runtime_error("missing 'id' attr in <option>");
51 std::string memberName = convert_xmlid_to_cppid(childId, false);
52 std::cout
53 << indent << " Option<" << child->Attribute("type") << "> " << memberName << ";\n";
54 }
55 else if (name == "section") {
56 if (!childId) throw std::runtime_error("missing 'id' attr in <section>");
57 std::string childClassName = convert_xmlid_to_cppid(childId, true);
58 std::string memberName = convert_xmlid_to_cppid(childId, false);
59 print_pref_class_def(child, childClassName, childId, indentSpaces+2);
60 std::cout
61 << indent << " " << childClassName << " " << memberName << ";\n";
62 }
63 }
64 child = child->NextSiblingElement();
65 }
66
67 std::cout
68 << indent << "};\n";
69}
70
71static void print_pref_class_impl(TiXmlElement* elem, const std::string& prefix, const std::string& className, const char* section)
72{
73 std::cout
74 << "\n"
75 << prefix << className << "::" << className << "(const std::string& name)\n";
76
77 if (section)
78 std::cout << " : Section(std::string(!name.empty() ? name + \".\": \"\") + \"" << section << "\")\n";
79 else
80 std::cout << " : Section(name)\n";
81
82 TiXmlElement* child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
83 while (child) {
84 if (child->Value()) {
85 std::string name = child->Value();
86 const char* childId = child->Attribute("id");
87
88 if (name == "option") {
89 if (!child->Attribute("type")) throw std::runtime_error("missing 'type' attr in <option>");
90 if (!childId) throw std::runtime_error("missing 'id' attr in <option>");
91 std::string memberName = convert_xmlid_to_cppid(childId, false);
92 std::cout << " , "
93 << memberName << "(this, \"" << childId << "\"";
94 if (child->Attribute("default"))
95 std::cout << ", " << child->Attribute("default");
96 std::cout << ")\n";
97 }
98 else if (name == "section") {
99 if (!childId) throw std::runtime_error("missing 'id' attr in <option>");
100 std::string memberName = convert_xmlid_to_cppid(childId, false);
101 std::cout << " , " << memberName << "(name)\n";
102 }
103 }
104 child = child->NextSiblingElement();
105 }
106
107 std::cout
108 << "{\n"
109 << "}\n";
110
111 // Section::forceSection()
112 if (elem->Attribute("canforce")) {
113 std::cout
114 << "\n"
115 << "void " << prefix << className << "::forceSection()\n"
116 << "{\n";
117
118 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): nullptr);
119 while (child) {
120 if (child->Value()) {
121 std::string name = child->Value();
122 const char* childId = child->Attribute("id");
123
124 if (name == "option") {
125 std::string memberName = convert_xmlid_to_cppid(childId, false);
126 std::cout << " " << memberName << ".forceDirtyFlag();\n";
127 }
128 }
129 child = child->NextSiblingElement();
130 }
131 std::cout
132 << "}\n";
133 }
134
135 // Section::clearSection()
136 if (elem->Attribute("canclear")) {
137 std::cout
138 << "\n"
139 << "void " << prefix << className << "::clearSection()\n"
140 << "{\n";
141
142 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): nullptr);
143 while (child) {
144 if (child->Value()) {
145 std::string name = child->Value();
146 const char* childId = child->Attribute("id");
147
148 if (name == "option") {
149 std::string memberName = convert_xmlid_to_cppid(childId, false);
150 std::cout << " " << memberName << ".clearValue();\n";
151 }
152 }
153 child = child->NextSiblingElement();
154 }
155 std::cout
156 << "}\n";
157 }
158
159 // Section::load()
160
161 std::cout
162 << "\n"
163 << "void " << prefix << className << "::load()\n"
164 << "{\n";
165
166 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
167 while (child) {
168 if (child->Value()) {
169 std::string name = child->Value();
170 const char* childId = child->Attribute("id");
171
172 if (name == "option") {
173 std::string memberName = convert_xmlid_to_cppid(childId, false);
174
175 const char* migrate = child->Attribute("migrate");
176 if (migrate) {
177 std::vector<std::string> parts;
178 base::split_string(migrate, parts, ".");
179
180 std::cout << " load_option_with_migration(" << memberName
181 << ", \"" << parts[0]
182 << "\", \"" << parts[1] << "\");\n";
183 }
184 else
185 std::cout << " load_option(" << memberName << ");\n";
186 }
187 else if (name == "section") {
188 std::string memberName = convert_xmlid_to_cppid(childId, false);
189 std::cout << " " << memberName << ".load();\n";
190 }
191 }
192 child = child->NextSiblingElement();
193 }
194
195 std::cout
196 << "}\n"
197 << "\n";
198
199 // Section::save()
200
201 std::cout
202 << "void " << prefix << className << "::save()\n"
203 << "{\n";
204
205 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
206 while (child) {
207 if (child->Value()) {
208 std::string name = child->Value();
209 if (name == "option") {
210 std::string memberName = convert_xmlid_to_cppid(child->Attribute("id"), false);
211 std::cout << " save_option(" << memberName << ");\n";
212 }
213 else if (name == "section") {
214 std::string memberName = convert_xmlid_to_cppid(child->Attribute("id"), false);
215 std::cout << " " << memberName << ".save();\n";
216 }
217 }
218 child = child->NextSiblingElement();
219 }
220
221 std::cout
222 << "}\n"
223 << "\n";
224
225 // Section::section(id)
226
227 std::cout
228 << "Section* " << prefix << className << "::section(const char* id)\n"
229 << "{\n";
230
231 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
232 while (child) {
233 if (child->Value()) {
234 std::string name = child->Value();
235 const char* childId = child->Attribute("id");
236 if (name == "section") {
237 std::string memberName = convert_xmlid_to_cppid(childId, false);
238 std::cout << " if (std::strcmp(id, " << memberName << ".name()) == 0) return &" << memberName << ";\n";
239 }
240 }
241 child = child->NextSiblingElement();
242 }
243
244 std::cout
245 << " return nullptr;\n"
246 << "}\n"
247 << "\n";
248
249 // Section::option(id)
250
251 std::cout
252 << "OptionBase* " << prefix << className << "::option(const char* id)\n"
253 << "{\n";
254
255 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
256 while (child) {
257 if (child->Value()) {
258 std::string name = child->Value();
259 const char* childId = child->Attribute("id");
260 if (name == "option") {
261 std::string memberName = convert_xmlid_to_cppid(childId, false);
262 std::cout << " if (std::strcmp(id, " << memberName << ".id()) == 0) return &" << memberName << ";\n";
263 }
264 }
265 child = child->NextSiblingElement();
266 }
267
268 std::cout
269 << " return nullptr;\n"
270 << "}\n";
271
272 // Sub-sections
273
274 child = (elem->FirstChild() ? elem->FirstChild()->ToElement(): NULL);
275 while (child) {
276 if (child->Value()) {
277 std::string name = child->Value();
278 if (name == "section") {
279 std::string childClassName = convert_xmlid_to_cppid(child->Attribute("id"), true);
280 print_pref_class_impl(child, className + "::", childClassName, child->Attribute("id"));
281 }
282 }
283 child = child->NextSiblingElement();
284 }
285}
286
287void gen_pref_header(TiXmlDocument* doc, const std::string& inputFn)
288{
289 std::cout
290 << "// Don't modify, generated file from " << inputFn << "\n"
291 << "\n";
292
293 std::cout
294 << "#ifndef GENERATED_PREF_TYPES_H_INCLUDED\n"
295 << "#define GENERATED_PREF_TYPES_H_INCLUDED\n"
296 << "#pragma once\n"
297 << "\n"
298 << "#include <string>\n"
299 << "\n"
300 << "namespace app {\n"
301 << "namespace gen {\n";
302
303 TiXmlHandle handle(doc);
304 TiXmlElement* elem = handle
305 .FirstChild("preferences")
306 .FirstChild("types")
307 .FirstChild("enum").ToElement();
308 while (elem) {
309 if (!elem->Attribute("id")) throw std::runtime_error("missing 'id' attr in <enum>");
310 std::cout
311 << "\n"
312 << " enum class " << elem->Attribute("id") << " {\n";
313
314 TiXmlElement* child = elem->FirstChildElement("value");
315 while (child) {
316 if (!child->Attribute("id")) throw std::runtime_error("missing 'id' attr in <value>");
317 if (!child->Attribute("value")) throw std::runtime_error("missing 'value' attr in <value>");
318
319 std::cout << " " << child->Attribute("id") << " = "
320 << child->Attribute("value") << ",\n";
321 child = child->NextSiblingElement("value");
322 }
323
324 std::cout
325 << " };\n";
326
327 elem = elem->NextSiblingElement("enum");
328 }
329
330 elem = handle
331 .FirstChild("preferences")
332 .FirstChild("global").ToElement();
333 if (elem)
334 print_pref_class_def(elem, "GlobalPref", NULL, 2);
335
336 elem = handle
337 .FirstChild("preferences")
338 .FirstChild("tool").ToElement();
339 if (elem)
340 print_pref_class_def(elem, "ToolPref", NULL, 2);
341
342 elem = handle
343 .FirstChild("preferences")
344 .FirstChild("document").ToElement();
345 if (elem)
346 print_pref_class_def(elem, "DocPref", NULL, 2);
347
348 std::cout
349 << "\n"
350 << "} // namespace gen\n"
351 << "} // namespace app\n"
352 << "\n"
353 << "#endif\n";
354}
355
356void gen_pref_impl(TiXmlDocument* doc, const std::string& inputFn)
357{
358 std::cout
359 << "// Don't modify, generated file from " << inputFn << "\n"
360 << "\n"
361 << "#ifdef HAVE_CONFIG_H\n"
362 << "#include \"config.h\"\n"
363 << "#endif\n"
364 << "\n"
365 << "#include \"app/pref/option_io.h\"\n"
366 << "#include \"app/pref/preferences.h\"\n"
367 << "\n"
368 << "#include <cstring>\n"
369 << "\n"
370 << "namespace app {\n"
371 << "namespace gen {\n";
372
373 TiXmlHandle handle(doc);
374 TiXmlElement* elem = handle
375 .FirstChild("preferences")
376 .FirstChild("global").ToElement();
377 if (elem)
378 print_pref_class_impl(elem, "", "GlobalPref", NULL);
379
380 elem = handle
381 .FirstChild("preferences")
382 .FirstChild("tool").ToElement();
383 if (elem)
384 print_pref_class_impl(elem, "", "ToolPref", NULL);
385
386 elem = handle
387 .FirstChild("preferences")
388 .FirstChild("document").ToElement();
389 if (elem)
390 print_pref_class_impl(elem, "", "DocPref", NULL);
391
392 std::cout
393 << "\n"
394 << "} // namespace gen\n"
395 << "} // namespace app\n";
396}
397