Akonadi

cppgenerator.cpp
1 /*
2  SPDX-FileCopyrightText: 2017 Daniel Vr├ítil <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "cppgenerator.h"
8 #include "cpphelper.h"
9 #include "nodetree.h"
10 #include "typehelper.h"
11 
12 #include <QDebug>
13 
14 #include <iostream>
15 
16 CppGenerator::CppGenerator()
17 {
18 }
19 
20 CppGenerator::~CppGenerator()
21 {
22 }
23 
24 bool CppGenerator::generate(Node const *node)
25 {
26  Q_ASSERT(node->type() == Node::Document);
27 
28  mHeaderFile.setFileName(QStringLiteral("protocol_gen.h"));
29  if (!mHeaderFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
30  std::cerr << qPrintable(mHeaderFile.errorString()) << std::endl;
31  return false;
32  }
33  mHeader.setDevice(&mHeaderFile);
34 
35  mImplFile.setFileName(QStringLiteral("protocol_gen.cpp"));
36  if (!mImplFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
37  std::cerr << qPrintable(mImplFile.errorString()) << std::endl;
38  return false;
39  }
40  mImpl.setDevice(&mImplFile);
41 
42  return generateDocument(static_cast<DocumentNode const *>(node));
43 }
44 
45 void CppGenerator::writeHeaderHeader(DocumentNode const *node)
46 {
47  mHeader << "// This is an auto-generated file.\n"
48  "// Any changes to this file will be overwritten\n"
49  "\n"
50  "// clazy:excludeall=function-args-by-value\n"
51  "\n"
52  "namespace Akonadi {\n"
53  "namespace Protocol {\n"
54  "\n"
55  "AKONADIPRIVATE_EXPORT int version();\n"
56  "\n";
57 
58  // Forward declarations
59  for (const auto *child : std::as_const(node->children())) {
60  if (child->type() == Node::Class) {
61  mHeader << "class " << static_cast<const ClassNode *>(child)->className() << ";\n";
62  }
63  }
64 
65  mHeader << "\n"
66  "} // namespace Protocol\n"
67  "} // namespace Akonadi\n\n";
68 }
69 
70 void CppGenerator::writeHeaderFooter(DocumentNode const * /*node*/)
71 {
72  // Nothing to do
73 }
74 
75 void CppGenerator::writeImplHeader(DocumentNode const *node)
76 {
77  mImpl << "// This is an auto-generated file.\n"
78  "// Any changes to this file will be overwritten\n"
79  "\n"
80  "// clazy:excludeall=function-args-by-value\n"
81  "\n"
82  "namespace Akonadi {\n"
83  "namespace Protocol {\n"
84  "\n"
85  "int version()\n"
86  "{\n"
87  " return "
88  << node->version()
89  << ";\n"
90  "}\n"
91  "\n";
92 }
93 
94 void CppGenerator::writeImplFooter(DocumentNode const * /*unused*/)
95 {
96  mImpl << "} // namespace Protocol\n"
97  "} // namespace Akonadi\n";
98 }
99 
100 bool CppGenerator::generateDocument(DocumentNode const *node)
101 {
102  writeHeaderHeader(node);
103  writeImplHeader(node);
104 
105  writeImplSerializer(node);
106 
107  for (const auto *classNode : node->children()) {
108  if (!generateClass(static_cast<ClassNode const *>(classNode))) {
109  return false;
110  }
111  }
112 
113  writeHeaderFooter(node);
114  writeImplFooter(node);
115 
116  return true;
117 }
118 
119 void CppGenerator::writeImplSerializer(DocumentNode const *node)
120 {
121  mImpl << "void serialize(DataStream &stream, const CommandPtr &cmd)\n"
122  "{\n"
123  " switch (static_cast<int>(cmd->type() | (cmd->isResponse() ? Command::_ResponseBit : 0))) {\n"
124  " case Command::Invalid:\n"
125  " stream << cmdCast<Command>(cmd);\n"
126  " break;\n"
127  " case Command::Invalid | Command::_ResponseBit:\n"
128  " stream << cmdCast<Response>(cmd);\n"
129  " break;\n";
130  for (const auto *child : std::as_const(node->children())) {
131  const auto *classNode = static_cast<ClassNode const *>(child);
132  if (classNode->classType() == ClassNode::Response) {
133  mImpl << " case Command::" << classNode->name()
134  << " | Command::_ResponseBit:\n"
135  " stream << cmdCast<"
136  << classNode->className()
137  << ">(cmd);\n"
138  " break;\n";
139  } else if (classNode->classType() == ClassNode::Command) {
140  mImpl << " case Command::" << classNode->name()
141  << ":\n"
142  " stream << cmdCast<"
143  << classNode->className()
144  << ">(cmd);\n"
145  " break;\n";
146  } else if (classNode->classType() == ClassNode::Notification) {
147  mImpl << " case Command::" << classNode->name()
148  << "Notification:\n"
149  " stream << cmdCast<"
150  << classNode->className()
151  << ">(cmd);\n"
152  " break;\n";
153  }
154  }
155  mImpl << " }\n"
156  "}\n\n";
157 
158  mImpl << "CommandPtr deserialize(QIODevice *device)\n"
159  "{\n"
160  " DataStream stream(device);\n"
161  " stream.waitForData(sizeof(Command::Type));\n"
162  " Command::Type cmdType;\n"
163  " if (Q_UNLIKELY(device->peek((char *) &cmdType, sizeof(Command::Type)) != sizeof(Command::Type))) {\n"
164  " throw ProtocolException(\"Failed to peek command type\");\n"
165  " }\n"
166  " CommandPtr cmd;\n"
167  " if (cmdType & Command::_ResponseBit) {\n"
168  " cmd = Factory::response(Command::Type(cmdType & ~Command::_ResponseBit));\n"
169  " } else {\n"
170  " cmd = Factory::command(cmdType);\n"
171  " }\n\n"
172  " switch (static_cast<int>(cmdType)) {\n"
173  " case Command::Invalid:\n"
174  " stream >> cmdCast<Command>(cmd);\n"
175  " return cmd;\n"
176  " case Command::Invalid | Command::_ResponseBit:\n"
177  " stream >> cmdCast<Response>(cmd);\n"
178  " return cmd;\n";
179  for (const auto *child : std::as_const(node->children())) {
180  const auto *classNode = static_cast<ClassNode const *>(child);
181  if (classNode->classType() == ClassNode::Response) {
182  mImpl << " case Command::" << classNode->name()
183  << " | Command::_ResponseBit:\n"
184  " stream >> cmdCast<"
185  << classNode->className()
186  << ">(cmd);\n"
187  " return cmd;\n";
188  } else if (classNode->classType() == ClassNode::Command) {
189  mImpl << " case Command::" << classNode->name()
190  << ":\n"
191  " stream >> cmdCast<"
192  << classNode->className()
193  << ">(cmd);\n"
194  " return cmd;\n";
195  } else if (classNode->classType() == ClassNode::Notification) {
196  mImpl << " case Command::" << classNode->name()
197  << "Notification:\n"
198  " stream >> cmdCast<"
199  << classNode->className()
200  << ">(cmd);\n"
201  " return cmd;\n";
202  }
203  }
204  mImpl << " }\n"
205  " return CommandPtr::create();\n"
206  "}\n"
207  "\n";
208 
209  mImpl << "QString debugString(const Command &cmd)\n"
210  "{\n"
211  " QString out;\n"
212  " switch (static_cast<int>(cmd.type() | (cmd.isResponse() ? Command::_ResponseBit : 0))) {\n"
213  " case Command::Invalid:\n"
214  " QDebug(&out).noquote() << static_cast<const Command &>(cmd);\n"
215  " return out;\n"
216  " case Command::Invalid | Command::_ResponseBit:\n"
217  " QDebug(&out).noquote() << static_cast<const Response &>(cmd);\n"
218  " return out;\n";
219  for (const auto *child : std::as_const(node->children())) {
220  const auto *classNode = static_cast<ClassNode const *>(child);
221  if (classNode->classType() == ClassNode::Response) {
222  mImpl << " case Command::" << classNode->name()
223  << " | Command::_ResponseBit:\n"
224  " QDebug(&out).noquote() << static_cast<const "
225  << classNode->className()
226  << " &>(cmd);\n"
227  " return out;\n";
228  } else if (classNode->classType() == ClassNode::Command) {
229  mImpl << " case Command::" << classNode->name()
230  << ":\n"
231  " QDebug(&out).noquote() << static_cast<const "
232  << classNode->className()
233  << " &>(cmd);\n"
234  " return out;\n";
235  } else if (classNode->classType() == ClassNode::Notification) {
236  mImpl << " case Command::" << classNode->name()
237  << "Notification:\n"
238  " QDebug(&out).noquote() << static_cast<const "
239  << classNode->className()
240  << " &>(cmd);\n"
241  " return out;\n";
242  }
243  }
244  mImpl << " }\n"
245  " return QString();\n"
246  "}\n"
247  "\n";
248 }
249 
250 void CppGenerator::writeHeaderEnum(EnumNode const *node)
251 {
252  mHeader << " enum " << node->name() << " {\n";
253  for (const auto *enumChild : node->children()) {
254  Q_ASSERT(enumChild->type() == Node::EnumValue);
255  const auto *const valueNode = static_cast<EnumValueNode const *>(enumChild);
256  mHeader << " " << valueNode->name();
257  if (!valueNode->value().isEmpty()) {
258  mHeader << " = " << valueNode->value();
259  }
260  mHeader << ",\n";
261  }
262  mHeader << " };\n";
263  if (node->enumType() == EnumNode::TypeFlag) {
264  mHeader << " Q_DECLARE_FLAGS(" << node->name() << "s, " << node->name() << ")\n\n";
265  }
266 }
267 
268 void CppGenerator::writeHeaderClass(ClassNode const *node)
269 {
270  // Begin class
271  const QString parentClass = node->parentClassName();
272  const bool isTypeClass = node->classType() == ClassNode::Class;
273 
274  mHeader << "namespace Akonadi {\n"
275  "namespace Protocol {\n\n"
276  "AKONADIPRIVATE_EXPORT DataStream &operator<<(DataStream &stream, const "
277  << node->className()
278  << " &obj);\n"
279  "AKONADIPRIVATE_EXPORT DataStream &operator>>(DataStream &stream, "
280  << node->className()
281  << " &obj);\n"
282  "AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, const "
283  << node->className()
284  << " &obj);\n"
285  "\n"
286  "using "
287  << node->className() << "Ptr = QSharedPointer<" << node->className()
288  << ">;\n"
289  "\n";
290  if (isTypeClass) {
291  mHeader << "class AKONADIPRIVATE_EXPORT " << node->className() << "\n";
292  } else {
293  mHeader << "class AKONADIPRIVATE_EXPORT " << node->className() << " : public " << parentClass << "\n";
294  }
295  mHeader << "{\n\n"
296  "public:\n";
297 
298  // Enums
299  for (const auto *child : node->children()) {
300  if (child->type() == Node::Enum) {
301  const auto *const enumNode = static_cast<EnumNode const *>(child);
302  writeHeaderEnum(enumNode);
303  }
304  }
305 
306  // Ctors, dtor
307  for (const auto *child : std::as_const(node->children())) {
308  if (child->type() == Node::Ctor) {
309  const auto *const ctor = static_cast<const CtorNode *>(child);
310  const auto args = ctor->arguments();
311  mHeader << " explicit " << node->className() << "(";
312  for (int i = 0; i < args.count(); ++i) {
313  const auto &arg = args[i];
314  if (TypeHelper::isNumericType(arg.type) || TypeHelper::isBoolType(arg.type)) {
315  mHeader << arg.type << " " << arg.name;
316  } else {
317  mHeader << "const " << arg.type << " &" << arg.name;
318  }
319  if (!arg.defaultValue.isEmpty()) {
320  mHeader << " = " << arg.defaultValue;
321  }
322  if (i < args.count() - 1) {
323  mHeader << ", ";
324  }
325  }
326  mHeader << ");\n";
327  }
328  }
329 
330  mHeader << " " << node->className() << "(const " << node->className()
331  << " &) = default;\n"
332  " "
333  << node->className() << "(" << node->className()
334  << " &&) = default;\n"
335  " ~"
336  << node->className()
337  << "() = default;\n"
338  "\n"
339  " "
340  << node->className() << " &operator=(const " << node->className()
341  << " &) = default;\n"
342  " "
343  << node->className() << " &operator=(" << node->className()
344  << " &&) = default;\n"
345  " bool operator==(const "
346  << node->className()
347  << " &other) const;\n"
348  " inline bool operator!=(const "
349  << node->className() << " &other) const { return !operator==(other); }\n";
350 
351  // Properties
352  for (const auto *child : node->children()) {
353  if (child->type() == Node::Property) {
354  const auto *const prop = static_cast<PropertyNode const *>(child);
355  if (prop->asReference()) {
356  mHeader << " inline const " << prop->type() << " &" << prop->name() << "() const { return " << prop->mVariableName()
357  << "; }\n"
358  " inline "
359  << prop->type() << " &" << prop->name() << "() { return " << prop->mVariableName() << "; }\n";
360  } else {
361  mHeader << " inline " << prop->type() << " " << prop->name() << "() const { return " << prop->mVariableName() << "; }\n";
362  }
363  if (!prop->readOnly()) {
364  if (auto *setter = prop->setter()) {
365  mHeader << " void " << setter->name << "(const " << setter->type << " &" << prop->name() << ");\n";
366  } else if (!prop->dependencies().isEmpty()) {
367  QString varType;
368  if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
369  varType = QLatin1Char('(') + prop->type() + QLatin1Char(' ');
370  } else {
371  varType = QLatin1String("(const ") + prop->type() + QLatin1String(" &");
372  }
373  mHeader << " void " << prop->setterName() << varType << prop->name() << ");\n";
374  } else {
375  QString varType;
376  if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
377  varType = QLatin1Char('(') + prop->type() + QLatin1Char(' ');
378  } else {
379  varType = QLatin1String("(const ") + prop->type() + QLatin1String(" &");
380  }
381  mHeader << " inline void " << prop->setterName() << varType << prop->name() << ") { " << prop->mVariableName() << " = " << prop->name()
382  << "; }\n";
383  if (!TypeHelper::isNumericType(prop->type()) && !TypeHelper::isBoolType(prop->type())) {
384  mHeader << " inline void " << prop->setterName() << "(" << prop->type() << " &&" << prop->name() << ") { " << prop->mVariableName()
385  << " = std::move(" << prop->name() << "); }\n";
386  }
387  }
388  }
389  mHeader << "\n";
390  }
391  }
392  mHeader << " void toJson(QJsonObject &stream) const;\n";
393 
394  // End of class
395  mHeader << "protected:\n";
396  const auto properties = node->properties();
397  for (const auto *prop : properties) {
398  mHeader << " " << prop->type() << " " << prop->mVariableName();
399  const auto defaultValue = prop->defaultValue();
400  const bool isDefaultValue = !defaultValue.isEmpty();
401  const bool isNumeric = TypeHelper::isNumericType(prop->type());
402  const bool isBool = TypeHelper::isBoolType(prop->type());
403  if (isDefaultValue) {
404  mHeader << " = " << defaultValue;
405  } else if (isNumeric) {
406  mHeader << " = 0";
407  } else if (isBool) {
408  mHeader << " = false";
409  }
410  mHeader << ";\n";
411  }
412 
413  mHeader << "\n"
414  "private:\n"
415  " friend AKONADIPRIVATE_EXPORT Akonadi::Protocol::DataStream &operator<<(Akonadi::Protocol::DataStream &stream, const Akonadi::Protocol::"
416  << node->className()
417  << " &obj);\n"
418  " friend AKONADIPRIVATE_EXPORT Akonadi::Protocol::DataStream &operator>>(Akonadi::Protocol::DataStream &stream, Akonadi::Protocol::"
419  << node->className()
420  << " &obj);\n"
421  " friend AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Akonadi::Protocol::"
422  << node->className()
423  << " &obj);\n"
424  "};\n\n"
425  "} // namespace Protocol\n"
426  "} // namespace Akonadi\n"
427  "\n";
428  mHeader << "Q_DECLARE_METATYPE(Akonadi::Protocol::" << node->className() << ")\n\n";
429  if (node->classType() != ClassNode::Class) {
430  mHeader << "Q_DECLARE_METATYPE(Akonadi::Protocol::" << node->className() << "Ptr)\n\n";
431  }
432 }
433 
434 void CppGenerator::writeImplSerializer(PropertyNode const *node, const char *streamingOperator)
435 {
436  const auto deps = node->dependencies();
437  if (deps.isEmpty()) {
438  mImpl << " stream " << streamingOperator << " obj." << node->mVariableName() << ";\n";
439  } else {
440  mImpl << " if (";
441  auto it = deps.cend();
442  while (1 + 1 == 2) {
443  --it;
444  const QString mVar = it.key();
445  mImpl << "(obj."
446  << "m" << mVar[0].toUpper()
447 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
448  << mVar.midRef(1)
449 #else
450  << QStringView(mVar).mid(1)
451 #endif
452  << " & " << it.value() << ")";
453  if (it == deps.cbegin()) {
454  break;
455  } else {
456  mImpl << " && ";
457  }
458  }
459  mImpl << ") {\n"
460  " stream "
461  << streamingOperator << " obj." << node->mVariableName()
462  << ";\n"
463  " }\n";
464  }
465 }
466 
467 void CppGenerator::writeImplClass(ClassNode const *node)
468 {
469  const QString parentClass = node->parentClassName();
470  const auto &children = node->children();
471  const auto properties = node->properties();
472 
473  // Ctors
474  for (const auto *child : children) {
475  if (child->type() == Node::Ctor) {
476  const auto *const ctor = static_cast<CtorNode const *>(child);
477  const auto args = ctor->arguments();
478  mImpl << node->className() << "::" << node->className() << "(";
479  for (int i = 0; i < args.count(); ++i) {
480  const auto &arg = args[i];
481  if (TypeHelper::isNumericType(arg.type) || TypeHelper::isBoolType(arg.type)) {
482  mImpl << arg.type << " " << arg.name;
483  } else {
484  mImpl << "const " << arg.type << " &" << arg.name;
485  }
486  if (i < args.count() - 1) {
487  mImpl << ", ";
488  }
489  }
490  mImpl << ")\n";
491  char startChar = ',';
492  if (!parentClass.isEmpty()) {
493  const QString type = node->name() + ((node->classType() == ClassNode::Notification) ? QStringLiteral("Notification") : QString());
494  mImpl << " : " << parentClass << "(Command::" << type << ")\n";
495  } else {
496  startChar = ':';
497  }
498  for (const auto *prop : properties) {
499  auto arg = std::find_if(args.cbegin(), args.cend(), [prop](const CtorNode::Argument &arg) {
500  return arg.name == prop->name();
501  });
502  if (arg != args.cend()) {
503  mImpl << " " << startChar << " " << prop->mVariableName() << "(" << arg->name << ")\n";
504  startChar = ',';
505  }
506  }
507  mImpl << "{\n"
508  "}\n"
509  "\n";
510  }
511  }
512 
513  // Comparison operator
514  mImpl << "bool " << node->className() << "::operator==(const " << node->className()
515  << " &other) const\n"
516  "{\n";
517  mImpl << " return true // simplifies generation\n";
518  if (!parentClass.isEmpty()) {
519  mImpl << " && " << parentClass << "::operator==(other)\n";
520  }
521  for (const auto *prop : properties) {
522  if (prop->isPointer()) {
523  mImpl << " && *" << prop->mVariableName() << " == *other." << prop->mVariableName() << "\n";
524  } else if (TypeHelper::isContainer(prop->type())) {
525  mImpl << " && containerComparator(" << prop->mVariableName() << ", other." << prop->mVariableName() << ")\n";
526  } else {
527  mImpl << " && " << prop->mVariableName() << " == other." << prop->mVariableName() << "\n";
528  }
529  }
530  mImpl << " ;\n"
531  "}\n"
532  "\n";
533 
534  // non-trivial setters
535  for (const auto *prop : properties) {
536  if (prop->readOnly()) {
537  continue;
538  }
539 
540  if (auto *const setter = prop->setter()) {
541  mImpl << "void " << node->className() << "::" << setter->name << "(const " << setter->type
542  << " &val)\n"
543  "{\n";
544  if (!setter->append.isEmpty()) {
545  mImpl << " m" << setter->append[0].toUpper()
546 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
547  << setter->append.midRef(1)
548 #else
549  << QStringView(setter->append).mid(1)
550 #endif
551  << " << val;\n";
552  }
553  if (!setter->remove.isEmpty()) {
554  const QString mVar = QLatin1String("m") + setter->remove[0].toUpper()
555 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
556  + setter->remove.midRef(1);
557 #else
558  + QStringView(setter->remove).mid(1);
559 #endif
560  mImpl << " auto it = std::find(" << mVar << ".begin(), " << mVar
561  << ".end(), val);\n"
562  " if (it != "
563  << mVar
564  << ".end()) {\n"
565  " "
566  << mVar
567  << ".erase(it);\n"
568  " }\n";
569  }
570  writeImplPropertyDependencies(prop);
571  mImpl << "}\n\n";
572  } else if (!prop->dependencies().isEmpty()) {
573  if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
574  mImpl << "void " << node->className() << "::" << prop->setterName() << "(" << prop->type()
575  << " val)\n"
576  "{\n"
577  " "
578  << prop->mVariableName() << " = val;\n";
579 
580  } else {
581  mImpl << "void " << node->className() << "::" << prop->setterName() << "(const " << prop->type()
582  << " &val)\n"
583  "{\n"
584  " "
585  << prop->mVariableName() << " = val;\n";
586  }
587  writeImplPropertyDependencies(prop);
588  mImpl << "}\n\n";
589  }
590  }
591 
592  // serialize
593  auto serializeProperties = properties;
594  CppHelper::sortMembersForSerialization(serializeProperties);
595 
596  mImpl << "DataStream &operator<<(DataStream &stream, const " << node->className()
597  << " &obj)\n"
598  "{\n";
599  if (!parentClass.isEmpty()) {
600  mImpl << " stream << static_cast<const " << parentClass << " &>(obj);\n";
601  }
602  for (const auto *prop : std::as_const(serializeProperties)) {
603  writeImplSerializer(prop, "<<");
604  }
605  mImpl << " return stream;\n"
606  "}\n"
607  "\n";
608 
609  // deserialize
610  mImpl << "DataStream &operator>>(DataStream &stream, " << node->className()
611  << " &obj)\n"
612  "{\n";
613  if (!parentClass.isEmpty()) {
614  mImpl << " stream >> static_cast<" << parentClass << " &>(obj);\n";
615  }
616  for (const auto *prop : std::as_const(serializeProperties)) {
617  writeImplSerializer(prop, ">>");
618  }
619  mImpl << " return stream;\n"
620  "}\n"
621  "\n";
622 
623  // debug
624  mImpl << "QDebug operator<<(QDebug dbg, const " << node->className()
625  << " &obj)\n"
626  "{\n";
627  if (!parentClass.isEmpty()) {
628  mImpl << " dbg.noquote() << static_cast<const " << parentClass << " &>(obj)\n";
629  } else {
630  mImpl << " dbg.noquote()\n";
631  }
632 
633  for (const auto *prop : std::as_const(serializeProperties)) {
634  if (prop->isPointer()) {
635  mImpl << " << \"" << prop->name() << ":\" << *obj." << prop->mVariableName() << " << \"\\n\"\n";
636  } else if (TypeHelper::isContainer(prop->type())) {
637  mImpl << " << \"" << prop->name()
638  << ": [\\n\";\n"
639  " for (const auto &type : std::as_const(obj."
640  << prop->mVariableName()
641  << ")) {\n"
642  " dbg.noquote() << \" \" << ";
643  if (TypeHelper::isPointerType(TypeHelper::containerType(prop->type()))) {
644  mImpl << "*type";
645  } else {
646  mImpl << "type";
647  }
648  mImpl << " << \"\\n\";\n"
649  " }\n"
650  " dbg.noquote() << \"]\\n\"\n";
651  } else {
652  mImpl << " << \"" << prop->name() << ":\" << obj." << prop->mVariableName() << " << \"\\n\"\n";
653  }
654  }
655  mImpl << " ;\n"
656  " return dbg;\n"
657  "}\n"
658  "\n";
659 
660  // toJson
661  mImpl << "void " << node->className()
662  << "::toJson(QJsonObject &json) const\n"
663  "{\n";
664  if (!parentClass.isEmpty()) {
665  mImpl << " static_cast<const " << parentClass << " *>(this)->toJson(json);\n";
666  } else if (serializeProperties.isEmpty()) {
667  mImpl << " Q_UNUSED(json)\n";
668  }
669  for (const auto *prop : std::as_const(serializeProperties)) {
670  if (prop->isPointer()) {
671  mImpl << " {\n"
672  " QJsonObject jsonObject;\n"
673  " "
674  << prop->mVariableName()
675  << "->toJson(jsonObject);\n"
676  " json[QStringLiteral(\""
677  << prop->name()
678  << "\")] = jsonObject;\n"
679  " }\n";
680  } else if (TypeHelper::isContainer(prop->type())) {
681  const auto &containerType = TypeHelper::containerType(prop->type());
682  mImpl << " {\n"
683  " QJsonArray jsonArray;\n"
684  " for (const auto &type : std::as_const("
685  << prop->mVariableName() << ")) {\n";
686  if (TypeHelper::isPointerType(containerType)) {
687  mImpl << " QJsonObject jsonObject;\n"
688  " type->toJson(jsonObject); /* "
689  << containerType
690  << " */\n"
691  " jsonArray.append(jsonObject);\n";
692  } else if (TypeHelper::isNumericType(containerType) || TypeHelper::isBoolType(containerType)) {
693  mImpl << " jsonArray.append(type); /* " << containerType << " */\n";
694  } else if (containerType == QLatin1String("QByteArray")) {
695  mImpl << " jsonArray.append(QString::fromUtf8(type)); /* " << containerType << "*/\n";
696  } else if (TypeHelper::isBuiltInType(containerType)) {
697  if (TypeHelper::containerType(prop->type()) == QLatin1String("Akonadi::Protocol::ChangeNotification::Relation")) {
698  mImpl << " QJsonObject jsonObject;\n"
699  " type.toJson(jsonObject); /* "
700  << containerType
701  << " */\n"
702  " jsonArray.append(jsonObject);\n";
703  } else {
704  mImpl << " jsonArray.append(type); /* " << containerType << " */\n";
705  }
706  } else {
707  mImpl << " QJsonObject jsonObject;\n"
708  " type.toJson(jsonObject); /* "
709  << containerType
710  << " */\n"
711  " jsonArray.append(jsonObject);\n";
712  }
713  mImpl << " }\n"
714  << " json[QStringLiteral(\"" << prop->name() << "\")] = jsonArray;\n"
715  << " }\n";
716  } else if (prop->type() == QLatin1String("uint")) {
717  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = static_cast<int>(" << prop->mVariableName() << ");/* " << prop->type() << " */\n";
718  } else if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
719  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = " << prop->mVariableName() << ";/* " << prop->type() << " */\n";
720  } else if (TypeHelper::isBuiltInType(prop->type())) {
721  if (prop->type() == QLatin1String("QStringList")) {
722  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = QJsonArray::fromStringList(" << prop->mVariableName() << ");/* "
723  << prop->type() << " */\n";
724  } else if (prop->type() == QLatin1String("QDateTime")) {
725  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = " << prop->mVariableName() << ".toString()/* " << prop->type() << " */;\n";
726  } else if (prop->type() == QLatin1String("QByteArray")) {
727  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = QString::fromUtf8(" << prop->mVariableName() << ")/* " << prop->type()
728  << " */;\n";
729  } else if (prop->type() == QLatin1String("Scope")) {
730  mImpl << " {\n"
731  " QJsonObject jsonObject;\n"
732  " "
733  << prop->mVariableName() << ".toJson(jsonObject); /* " << prop->type()
734  << " */\n"
735  " json[QStringLiteral(\""
736  << prop->name() << "\")] = "
737  << "jsonObject;\n"
738  " }\n";
739  } else if (prop->type() == QLatin1String("Tristate")) {
740  mImpl << " switch (" << prop->mVariableName()
741  << ") {\n;"
742  " case Tristate::True:\n"
743  " json[QStringLiteral(\""
744  << prop->name()
745  << "\")] = QStringLiteral(\"True\");\n"
746  " break;\n"
747  " case Tristate::False:\n"
748  " json[QStringLiteral(\""
749  << prop->name()
750  << "\")] = QStringLiteral(\"False\");\n"
751  " break;\n"
752  " case Tristate::Undefined:\n"
753  " json[QStringLiteral(\""
754  << prop->name()
755  << "\")] = QStringLiteral(\"Undefined\");\n"
756  " break;\n"
757  " }\n";
758  } else if (prop->type() == QLatin1String("Akonadi::Protocol::Attributes")) {
759  mImpl << " {\n"
760  " QJsonObject jsonObject;\n"
761  " auto i = "
762  << prop->mVariableName()
763  << ".constBegin();\n"
764  " const auto &end = "
765  << prop->mVariableName()
766  << ".constEnd();\n"
767  " while (i != end) {\n"
768  " jsonObject[QString::fromUtf8(i.key())] = QString::fromUtf8(i.value());\n"
769  " ++i;\n"
770  " }\n"
771  " json[QStringLiteral(\""
772  << prop->name()
773  << "\")] = jsonObject;\n"
774  " }\n";
775  } else if (prop->type() == QLatin1String("ModifySubscriptionCommand::ModifiedParts")
776  || prop->type() == QLatin1String("ModifyTagCommand::ModifiedParts")
777  || prop->type() == QLatin1String("ModifyCollectionCommand::ModifiedParts")
778  || prop->type() == QLatin1String("ModifyItemsCommand::ModifiedParts")
779  || prop->type() == QLatin1String("CreateItemCommand::MergeModes")) {
780  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = static_cast<int>(" << prop->mVariableName() << ");/* " << prop->type()
781  << "*/\n";
782  } else {
783  mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = " << prop->mVariableName() << ";/* " << prop->type() << "*/\n";
784  }
785  } else {
786  mImpl << " {\n"
787  " QJsonObject jsonObject;\n"
788  " "
789  << prop->mVariableName() << ".toJson(jsonObject); /* " << prop->type()
790  << " */\n"
791  " json[QStringLiteral(\""
792  << prop->name()
793  << "\")] = jsonObject;\n"
794  " }\n";
795  }
796  }
797  mImpl << "}\n"
798  "\n";
799 }
800 
801 void CppGenerator::writeImplPropertyDependencies(const PropertyNode *node)
802 {
803  const auto deps = node->dependencies();
804  QString key;
806  QString enumType;
807  for (auto it = deps.cbegin(), end = deps.cend(); it != end; ++it) {
808  if (key != it.key()) {
809  key = it.key();
810  const auto children = node->parent()->children();
811  for (const auto *child : children) {
812  if (child->type() == Node::Property && child != node) {
813  const auto *prop = static_cast<const PropertyNode *>(child);
814  if (prop->name() == key) {
815  enumType = prop->type();
816  break;
817  }
818  }
819  }
820  if (!values.isEmpty()) {
821  mImpl << " m" << key[0].toUpper()
822  << QStringView(key).mid(1)
823  << " |= " << enumType << "(" << values.join(QLatin1String(" | ")) << ");\n";
824  values.clear();
825  }
826  }
827  values << *it;
828  }
829 
830  if (!values.isEmpty()) {
831  mImpl << " m" << key[0].toUpper()
832  << QStringView(key).mid(1)
833  << " |= " << enumType << "(" << values.join(QLatin1String(" | ")) << ");\n";
834  }
835 }
836 
837 bool CppGenerator::generateClass(ClassNode const *node)
838 {
839  writeHeaderClass(node);
840 
841  mImpl << "\n\n/************************* " << node->className() << " *************************/\n\n";
842  writeImplClass(node);
843 
844  return true;
845 }
virtual QString type() const
Definition: nodetree.cpp:25
QString toUpper() const const
QStringRef midRef(int position, int n) const const
QStringView mid(qsizetype start) const const
bool isEmpty() const const
KGuiItem properties()
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
Definition: nodetree.h:12
QVector< V > values(const QMultiHash< K, V > &c)
QString & append(QChar ch)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jun 30 2022 03:51:45 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.