File indexing completed on 2024-11-10 04:40:46
0001 /* 0002 SPDX-FileCopyrightText: 2017 Daniel Vrátil <dvratil@kde.og> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "cppgenerator.h" 0008 #include "cpphelper.h" 0009 #include "nodetree.h" 0010 #include "typehelper.h" 0011 0012 #include <QDebug> 0013 0014 #include <iostream> 0015 0016 CppGenerator::CppGenerator() 0017 { 0018 } 0019 0020 CppGenerator::~CppGenerator() 0021 { 0022 } 0023 0024 bool CppGenerator::generate(Node const *node) 0025 { 0026 Q_ASSERT(node->type() == Node::Document); 0027 0028 mHeaderFile.setFileName(QStringLiteral("protocol_gen.h")); 0029 if (!mHeaderFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 0030 std::cerr << qPrintable(mHeaderFile.errorString()) << std::endl; 0031 return false; 0032 } 0033 mHeader.setDevice(&mHeaderFile); 0034 0035 mImplFile.setFileName(QStringLiteral("protocol_gen.cpp")); 0036 if (!mImplFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 0037 std::cerr << qPrintable(mImplFile.errorString()) << std::endl; 0038 return false; 0039 } 0040 mImpl.setDevice(&mImplFile); 0041 0042 return generateDocument(static_cast<DocumentNode const *>(node)); 0043 } 0044 0045 void CppGenerator::writeHeaderHeader(DocumentNode const *node) 0046 { 0047 mHeader << "// This is an auto-generated file.\n" 0048 "// Any changes to this file will be overwritten\n" 0049 "\n" 0050 "// clazy:excludeall=function-args-by-value\n" 0051 "\n" 0052 "namespace Akonadi {\n" 0053 "namespace Protocol {\n" 0054 "\n" 0055 "AKONADIPRIVATE_EXPORT int version();\n" 0056 "\n"; 0057 0058 // Forward declarations 0059 for (const auto *child : std::as_const(node->children())) { 0060 if (child->type() == Node::Class) { 0061 mHeader << "class " << static_cast<const ClassNode *>(child)->className() << ";\n"; 0062 } 0063 } 0064 0065 mHeader << "\n" 0066 "} // namespace Protocol\n" 0067 "} // namespace Akonadi\n\n"; 0068 } 0069 0070 void CppGenerator::writeHeaderFooter(DocumentNode const * /*node*/) 0071 { 0072 // Nothing to do 0073 } 0074 0075 void CppGenerator::writeImplHeader(DocumentNode const *node) 0076 { 0077 mImpl << "// This is an auto-generated file.\n" 0078 "// Any changes to this file will be overwritten\n" 0079 "\n" 0080 "// clazy:excludeall=function-args-by-value\n" 0081 "\n" 0082 "namespace Akonadi {\n" 0083 "namespace Protocol {\n" 0084 "\n" 0085 "int version()\n" 0086 "{\n" 0087 " return " 0088 << node->version() 0089 << ";\n" 0090 "}\n" 0091 "\n"; 0092 } 0093 0094 void CppGenerator::writeImplFooter(DocumentNode const * /*unused*/) 0095 { 0096 mImpl << "} // namespace Protocol\n" 0097 "} // namespace Akonadi\n"; 0098 } 0099 0100 bool CppGenerator::generateDocument(DocumentNode const *node) 0101 { 0102 writeHeaderHeader(node); 0103 writeImplHeader(node); 0104 0105 writeImplSerializer(node); 0106 0107 for (const auto *classNode : node->children()) { 0108 if (!generateClass(static_cast<ClassNode const *>(classNode))) { 0109 return false; 0110 } 0111 } 0112 0113 writeHeaderFooter(node); 0114 writeImplFooter(node); 0115 0116 return true; 0117 } 0118 0119 void CppGenerator::writeImplSerializer(DocumentNode const *node) 0120 { 0121 mImpl << "void serialize(DataStream &stream, const CommandPtr &cmd)\n" 0122 "{\n" 0123 " switch (static_cast<int>(cmd->type() | (cmd->isResponse() ? Command::_ResponseBit : 0))) {\n" 0124 " case Command::Invalid:\n" 0125 " stream << cmdCast<Command>(cmd);\n" 0126 " break;\n" 0127 " case Command::Invalid | Command::_ResponseBit:\n" 0128 " stream << cmdCast<Response>(cmd);\n" 0129 " break;\n"; 0130 for (const auto *child : std::as_const(node->children())) { 0131 const auto *classNode = static_cast<ClassNode const *>(child); 0132 if (classNode->classType() == ClassNode::Response) { 0133 mImpl << " case Command::" << classNode->name() 0134 << " | Command::_ResponseBit:\n" 0135 " stream << cmdCast<" 0136 << classNode->className() 0137 << ">(cmd);\n" 0138 " break;\n"; 0139 } else if (classNode->classType() == ClassNode::Command) { 0140 mImpl << " case Command::" << classNode->name() 0141 << ":\n" 0142 " stream << cmdCast<" 0143 << classNode->className() 0144 << ">(cmd);\n" 0145 " break;\n"; 0146 } else if (classNode->classType() == ClassNode::Notification) { 0147 mImpl << " case Command::" << classNode->name() 0148 << "Notification:\n" 0149 " stream << cmdCast<" 0150 << classNode->className() 0151 << ">(cmd);\n" 0152 " break;\n"; 0153 } 0154 } 0155 mImpl << " }\n" 0156 "}\n\n"; 0157 0158 mImpl << "CommandPtr deserialize(QIODevice *device)\n" 0159 "{\n" 0160 " DataStream stream(device);\n" 0161 " stream.waitForData(sizeof(Command::Type));\n" 0162 " Command::Type cmdType;\n" 0163 " if (Q_UNLIKELY(device->peek((char *) &cmdType, sizeof(Command::Type)) != sizeof(Command::Type))) {\n" 0164 " throw ProtocolException(\"Failed to peek command type\");\n" 0165 " }\n" 0166 " CommandPtr cmd;\n" 0167 " if (cmdType & Command::_ResponseBit) {\n" 0168 " cmd = Factory::response(Command::Type(cmdType & ~Command::_ResponseBit));\n" 0169 " } else {\n" 0170 " cmd = Factory::command(cmdType);\n" 0171 " }\n\n" 0172 " switch (static_cast<int>(cmdType)) {\n" 0173 " case Command::Invalid:\n" 0174 " stream >> cmdCast<Command>(cmd);\n" 0175 " return cmd;\n" 0176 " case Command::Invalid | Command::_ResponseBit:\n" 0177 " stream >> cmdCast<Response>(cmd);\n" 0178 " return cmd;\n"; 0179 for (const auto *child : std::as_const(node->children())) { 0180 const auto *classNode = static_cast<ClassNode const *>(child); 0181 if (classNode->classType() == ClassNode::Response) { 0182 mImpl << " case Command::" << classNode->name() 0183 << " | Command::_ResponseBit:\n" 0184 " stream >> cmdCast<" 0185 << classNode->className() 0186 << ">(cmd);\n" 0187 " return cmd;\n"; 0188 } else if (classNode->classType() == ClassNode::Command) { 0189 mImpl << " case Command::" << classNode->name() 0190 << ":\n" 0191 " stream >> cmdCast<" 0192 << classNode->className() 0193 << ">(cmd);\n" 0194 " return cmd;\n"; 0195 } else if (classNode->classType() == ClassNode::Notification) { 0196 mImpl << " case Command::" << classNode->name() 0197 << "Notification:\n" 0198 " stream >> cmdCast<" 0199 << classNode->className() 0200 << ">(cmd);\n" 0201 " return cmd;\n"; 0202 } 0203 } 0204 mImpl << " }\n" 0205 " return CommandPtr::create();\n" 0206 "}\n" 0207 "\n"; 0208 0209 mImpl << "QString debugString(const Command &cmd)\n" 0210 "{\n" 0211 " QString out;\n" 0212 " switch (static_cast<int>(cmd.type() | (cmd.isResponse() ? Command::_ResponseBit : 0))) {\n" 0213 " case Command::Invalid:\n" 0214 " QDebug(&out).noquote() << static_cast<const Command &>(cmd);\n" 0215 " return out;\n" 0216 " case Command::Invalid | Command::_ResponseBit:\n" 0217 " QDebug(&out).noquote() << static_cast<const Response &>(cmd);\n" 0218 " return out;\n"; 0219 for (const auto *child : std::as_const(node->children())) { 0220 const auto *classNode = static_cast<ClassNode const *>(child); 0221 if (classNode->classType() == ClassNode::Response) { 0222 mImpl << " case Command::" << classNode->name() 0223 << " | Command::_ResponseBit:\n" 0224 " QDebug(&out).noquote() << static_cast<const " 0225 << classNode->className() 0226 << " &>(cmd);\n" 0227 " return out;\n"; 0228 } else if (classNode->classType() == ClassNode::Command) { 0229 mImpl << " case Command::" << classNode->name() 0230 << ":\n" 0231 " QDebug(&out).noquote() << static_cast<const " 0232 << classNode->className() 0233 << " &>(cmd);\n" 0234 " return out;\n"; 0235 } else if (classNode->classType() == ClassNode::Notification) { 0236 mImpl << " case Command::" << classNode->name() 0237 << "Notification:\n" 0238 " QDebug(&out).noquote() << static_cast<const " 0239 << classNode->className() 0240 << " &>(cmd);\n" 0241 " return out;\n"; 0242 } 0243 } 0244 mImpl << " }\n" 0245 " return QString();\n" 0246 "}\n" 0247 "\n"; 0248 } 0249 0250 void CppGenerator::writeHeaderEnum(EnumNode const *node) 0251 { 0252 mHeader << " enum " << node->name() << " {\n"; 0253 for (const auto *enumChild : node->children()) { 0254 Q_ASSERT(enumChild->type() == Node::EnumValue); 0255 const auto *const valueNode = static_cast<EnumValueNode const *>(enumChild); 0256 mHeader << " " << valueNode->name(); 0257 if (!valueNode->value().isEmpty()) { 0258 mHeader << " = " << valueNode->value(); 0259 } 0260 mHeader << ",\n"; 0261 } 0262 mHeader << " };\n"; 0263 if (node->enumType() == EnumNode::TypeFlag) { 0264 mHeader << " Q_DECLARE_FLAGS(" << node->name() << "s, " << node->name() << ")\n\n"; 0265 } 0266 } 0267 0268 void CppGenerator::writeHeaderClass(ClassNode const *node) 0269 { 0270 // Begin class 0271 const QString parentClass = node->parentClassName(); 0272 const bool isTypeClass = node->classType() == ClassNode::Class; 0273 0274 mHeader << "namespace Akonadi {\n" 0275 "namespace Protocol {\n\n" 0276 "AKONADIPRIVATE_EXPORT DataStream &operator<<(DataStream &stream, const " 0277 << node->className() 0278 << " &obj);\n" 0279 "AKONADIPRIVATE_EXPORT DataStream &operator>>(DataStream &stream, " 0280 << node->className() 0281 << " &obj);\n" 0282 "AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, const " 0283 << node->className() 0284 << " &obj);\n" 0285 "\n" 0286 "using " 0287 << node->className() << "Ptr = QSharedPointer<" << node->className() 0288 << ">;\n" 0289 "\n"; 0290 if (isTypeClass) { 0291 mHeader << "class AKONADIPRIVATE_EXPORT " << node->className() << "\n"; 0292 } else { 0293 mHeader << "class AKONADIPRIVATE_EXPORT " << node->className() << " : public " << parentClass << "\n"; 0294 } 0295 mHeader << "{\n\n" 0296 "public:\n"; 0297 0298 // Enums 0299 for (const auto *child : node->children()) { 0300 if (child->type() == Node::Enum) { 0301 const auto *const enumNode = static_cast<EnumNode const *>(child); 0302 writeHeaderEnum(enumNode); 0303 } 0304 } 0305 0306 // Ctors, dtor 0307 for (const auto *child : std::as_const(node->children())) { 0308 if (child->type() == Node::Ctor) { 0309 const auto *const ctor = static_cast<const CtorNode *>(child); 0310 const auto args = ctor->arguments(); 0311 mHeader << " explicit " << node->className() << "("; 0312 for (int i = 0; i < args.count(); ++i) { 0313 const auto &arg = args[i]; 0314 if (TypeHelper::isNumericType(arg.type) || TypeHelper::isBoolType(arg.type)) { 0315 mHeader << arg.type << " " << arg.name; 0316 } else { 0317 mHeader << "const " << arg.type << " &" << arg.name; 0318 } 0319 if (!arg.defaultValue.isEmpty()) { 0320 mHeader << " = " << arg.defaultValue; 0321 } 0322 if (i < args.count() - 1) { 0323 mHeader << ", "; 0324 } 0325 } 0326 mHeader << ");\n"; 0327 } 0328 } 0329 0330 mHeader << " " << node->className() << "(const " << node->className() 0331 << " &) = default;\n" 0332 " " 0333 << node->className() << "(" << node->className() 0334 << " &&) = default;\n" 0335 " ~" 0336 << node->className() 0337 << "() = default;\n" 0338 "\n" 0339 " " 0340 << node->className() << " &operator=(const " << node->className() 0341 << " &) = default;\n" 0342 " " 0343 << node->className() << " &operator=(" << node->className() 0344 << " &&) = default;\n" 0345 " bool operator==(const " 0346 << node->className() 0347 << " &other) const;\n" 0348 " inline bool operator!=(const " 0349 << node->className() << " &other) const { return !operator==(other); }\n"; 0350 0351 // Properties 0352 for (const auto *child : node->children()) { 0353 if (child->type() == Node::Property) { 0354 const auto *const prop = static_cast<PropertyNode const *>(child); 0355 if (prop->asReference()) { 0356 mHeader << " inline const " << prop->type() << " &" << prop->name() << "() const { return " << prop->mVariableName() 0357 << "; }\n" 0358 " inline " 0359 << prop->type() << " &" << prop->name() << "() { return " << prop->mVariableName() << "; }\n"; 0360 } else { 0361 mHeader << " inline " << prop->type() << " " << prop->name() << "() const { return " << prop->mVariableName() << "; }\n"; 0362 } 0363 if (!prop->readOnly()) { 0364 if (auto *setter = prop->setter()) { 0365 mHeader << " void " << setter->name << "(const " << setter->type << " &" << prop->name() << ");\n"; 0366 } else if (!prop->dependencies().isEmpty()) { 0367 QString varType; 0368 if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) { 0369 varType = QLatin1Char('(') + prop->type() + QLatin1Char(' '); 0370 } else { 0371 varType = QLatin1StringView("(const ") + prop->type() + QLatin1StringView(" &"); 0372 } 0373 mHeader << " void " << prop->setterName() << varType << prop->name() << ");\n"; 0374 } else { 0375 QString varType; 0376 if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) { 0377 varType = QLatin1Char('(') + prop->type() + QLatin1Char(' '); 0378 } else { 0379 varType = QLatin1StringView("(const ") + prop->type() + QLatin1StringView(" &"); 0380 } 0381 mHeader << " inline void " << prop->setterName() << varType << prop->name() << ") { " << prop->mVariableName() << " = " << prop->name() 0382 << "; }\n"; 0383 if (!TypeHelper::isNumericType(prop->type()) && !TypeHelper::isBoolType(prop->type())) { 0384 mHeader << " inline void " << prop->setterName() << "(" << prop->type() << " &&" << prop->name() << ") { " << prop->mVariableName() 0385 << " = std::move(" << prop->name() << "); }\n"; 0386 } 0387 } 0388 } 0389 mHeader << "\n"; 0390 } 0391 } 0392 mHeader << " void toJson(QJsonObject &stream) const;\n"; 0393 0394 // End of class 0395 mHeader << "protected:\n"; 0396 const auto properties = node->properties(); 0397 for (const auto *prop : properties) { 0398 mHeader << " " << prop->type() << " " << prop->mVariableName(); 0399 const auto defaultValue = prop->defaultValue(); 0400 const bool isDefaultValue = !defaultValue.isEmpty(); 0401 const bool isNumeric = TypeHelper::isNumericType(prop->type()); 0402 const bool isBool = TypeHelper::isBoolType(prop->type()); 0403 if (isDefaultValue) { 0404 mHeader << " = " << defaultValue; 0405 } else if (isNumeric) { 0406 mHeader << " = 0"; 0407 } else if (isBool) { 0408 mHeader << " = false"; 0409 } 0410 mHeader << ";\n"; 0411 } 0412 0413 mHeader << "\n" 0414 "private:\n" 0415 " friend AKONADIPRIVATE_EXPORT Akonadi::Protocol::DataStream &operator<<(Akonadi::Protocol::DataStream &stream, const Akonadi::Protocol::" 0416 << node->className() 0417 << " &obj);\n" 0418 " friend AKONADIPRIVATE_EXPORT Akonadi::Protocol::DataStream &operator>>(Akonadi::Protocol::DataStream &stream, Akonadi::Protocol::" 0419 << node->className() 0420 << " &obj);\n" 0421 " friend AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Akonadi::Protocol::" 0422 << node->className() 0423 << " &obj);\n" 0424 "};\n\n" 0425 "} // namespace Protocol\n" 0426 "} // namespace Akonadi\n" 0427 "\n"; 0428 mHeader << "Q_DECLARE_METATYPE(Akonadi::Protocol::" << node->className() << ")\n\n"; 0429 if (node->classType() != ClassNode::Class) { 0430 mHeader << "Q_DECLARE_METATYPE(Akonadi::Protocol::" << node->className() << "Ptr)\n\n"; 0431 } 0432 } 0433 0434 void CppGenerator::writeImplSerializer(PropertyNode const *node, const char *streamingOperator) 0435 { 0436 const auto deps = node->dependencies(); 0437 if (deps.isEmpty()) { 0438 mImpl << " stream " << streamingOperator << " obj." << node->mVariableName() << ";\n"; 0439 } else { 0440 mImpl << " if ("; 0441 auto it = deps.cend(); 0442 while (1 + 1 == 2) { 0443 --it; 0444 const QString mVar = it.key(); 0445 mImpl << "(obj." 0446 << "m" << mVar[0].toUpper() << QStringView(mVar).mid(1) << " & " << it.value() << ")"; 0447 if (it == deps.cbegin()) { 0448 break; 0449 } else { 0450 mImpl << " && "; 0451 } 0452 } 0453 mImpl << ") {\n" 0454 " stream " 0455 << streamingOperator << " obj." << node->mVariableName() 0456 << ";\n" 0457 " }\n"; 0458 } 0459 } 0460 0461 void CppGenerator::writeImplClass(ClassNode const *node) 0462 { 0463 const QString parentClass = node->parentClassName(); 0464 const auto &children = node->children(); 0465 const auto properties = node->properties(); 0466 0467 // Ctors 0468 for (const auto *child : children) { 0469 if (child->type() == Node::Ctor) { 0470 const auto *const ctor = static_cast<CtorNode const *>(child); 0471 const auto args = ctor->arguments(); 0472 mImpl << node->className() << "::" << node->className() << "("; 0473 for (int i = 0; i < args.count(); ++i) { 0474 const auto &arg = args[i]; 0475 if (TypeHelper::isNumericType(arg.type) || TypeHelper::isBoolType(arg.type)) { 0476 mImpl << arg.type << " " << arg.name; 0477 } else { 0478 mImpl << "const " << arg.type << " &" << arg.name; 0479 } 0480 if (i < args.count() - 1) { 0481 mImpl << ", "; 0482 } 0483 } 0484 mImpl << ")\n"; 0485 char startChar = ','; 0486 if (!parentClass.isEmpty()) { 0487 const QString type = node->name() + ((node->classType() == ClassNode::Notification) ? QStringLiteral("Notification") : QString()); 0488 mImpl << " : " << parentClass << "(Command::" << type << ")\n"; 0489 } else { 0490 startChar = ':'; 0491 } 0492 for (const auto *prop : properties) { 0493 auto arg = std::find_if(args.cbegin(), args.cend(), [prop](const CtorNode::Argument &arg) { 0494 return arg.name == prop->name(); 0495 }); 0496 if (arg != args.cend()) { 0497 mImpl << " " << startChar << " " << prop->mVariableName() << "(" << arg->name << ")\n"; 0498 startChar = ','; 0499 } 0500 } 0501 mImpl << "{\n" 0502 "}\n" 0503 "\n"; 0504 } 0505 } 0506 0507 // Comparison operator 0508 mImpl << "bool " << node->className() << "::operator==(const " << node->className() 0509 << " &other) const\n" 0510 "{\n"; 0511 mImpl << " return true // simplifies generation\n"; 0512 if (!parentClass.isEmpty()) { 0513 mImpl << " && " << parentClass << "::operator==(other)\n"; 0514 } 0515 for (const auto *prop : properties) { 0516 if (prop->isPointer()) { 0517 mImpl << " && *" << prop->mVariableName() << " == *other." << prop->mVariableName() << "\n"; 0518 } else if (TypeHelper::isContainer(prop->type())) { 0519 mImpl << " && containerComparator(" << prop->mVariableName() << ", other." << prop->mVariableName() << ")\n"; 0520 } else { 0521 mImpl << " && " << prop->mVariableName() << " == other." << prop->mVariableName() << "\n"; 0522 } 0523 } 0524 mImpl << " ;\n" 0525 "}\n" 0526 "\n"; 0527 0528 // non-trivial setters 0529 for (const auto *prop : properties) { 0530 if (prop->readOnly()) { 0531 continue; 0532 } 0533 0534 if (auto *const setter = prop->setter()) { 0535 mImpl << "void " << node->className() << "::" << setter->name << "(const " << setter->type 0536 << " &val)\n" 0537 "{\n"; 0538 if (!setter->append.isEmpty()) { 0539 mImpl << " m" << setter->append[0].toUpper() << QStringView(setter->append).mid(1) << " << val;\n"; 0540 } 0541 if (!setter->remove.isEmpty()) { 0542 const QString mVar = QLatin1StringView("m") + setter->remove[0].toUpper() + QStringView(setter->remove).mid(1); 0543 mImpl << " auto it = std::find(" << mVar << ".begin(), " << mVar 0544 << ".end(), val);\n" 0545 " if (it != " 0546 << mVar 0547 << ".end()) {\n" 0548 " " 0549 << mVar 0550 << ".erase(it);\n" 0551 " }\n"; 0552 } 0553 writeImplPropertyDependencies(prop); 0554 mImpl << "}\n\n"; 0555 } else if (!prop->dependencies().isEmpty()) { 0556 if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) { 0557 mImpl << "void " << node->className() << "::" << prop->setterName() << "(" << prop->type() 0558 << " val)\n" 0559 "{\n" 0560 " " 0561 << prop->mVariableName() << " = val;\n"; 0562 0563 } else { 0564 mImpl << "void " << node->className() << "::" << prop->setterName() << "(const " << prop->type() 0565 << " &val)\n" 0566 "{\n" 0567 " " 0568 << prop->mVariableName() << " = val;\n"; 0569 } 0570 writeImplPropertyDependencies(prop); 0571 mImpl << "}\n\n"; 0572 } 0573 } 0574 0575 // serialize 0576 auto serializeProperties = properties; 0577 CppHelper::sortMembersForSerialization(serializeProperties); 0578 0579 mImpl << "DataStream &operator<<(DataStream &stream, const " << node->className() 0580 << " &obj)\n" 0581 "{\n"; 0582 if (!parentClass.isEmpty()) { 0583 mImpl << " stream << static_cast<const " << parentClass << " &>(obj);\n"; 0584 } 0585 for (const auto *prop : std::as_const(serializeProperties)) { 0586 writeImplSerializer(prop, "<<"); 0587 } 0588 mImpl << " return stream;\n" 0589 "}\n" 0590 "\n"; 0591 0592 // deserialize 0593 mImpl << "DataStream &operator>>(DataStream &stream, " << node->className() 0594 << " &obj)\n" 0595 "{\n"; 0596 if (!parentClass.isEmpty()) { 0597 mImpl << " stream >> static_cast<" << parentClass << " &>(obj);\n"; 0598 } 0599 for (const auto *prop : std::as_const(serializeProperties)) { 0600 writeImplSerializer(prop, ">>"); 0601 } 0602 mImpl << " return stream;\n" 0603 "}\n" 0604 "\n"; 0605 0606 // debug 0607 mImpl << "QDebug operator<<(QDebug dbg, const " << node->className() 0608 << " &obj)\n" 0609 "{\n"; 0610 if (!parentClass.isEmpty()) { 0611 mImpl << " dbg.noquote() << static_cast<const " << parentClass << " &>(obj)\n"; 0612 } else { 0613 mImpl << " dbg.noquote()\n"; 0614 } 0615 0616 for (const auto *prop : std::as_const(serializeProperties)) { 0617 if (prop->isPointer()) { 0618 mImpl << " << \"" << prop->name() << ":\" << *obj." << prop->mVariableName() << " << \"\\n\"\n"; 0619 } else if (TypeHelper::isContainer(prop->type())) { 0620 mImpl << " << \"" << prop->name() 0621 << ": [\\n\";\n" 0622 " for (const auto &type : std::as_const(obj." 0623 << prop->mVariableName() 0624 << ")) {\n" 0625 " dbg.noquote() << \" \" << "; 0626 if (TypeHelper::isPointerType(TypeHelper::containerType(prop->type()))) { 0627 mImpl << "*type"; 0628 } else { 0629 mImpl << "type"; 0630 } 0631 mImpl << " << \"\\n\";\n" 0632 " }\n" 0633 " dbg.noquote() << \"]\\n\"\n"; 0634 } else { 0635 mImpl << " << \"" << prop->name() << ":\" << obj." << prop->mVariableName() << " << \"\\n\"\n"; 0636 } 0637 } 0638 mImpl << " ;\n" 0639 " return dbg;\n" 0640 "}\n" 0641 "\n"; 0642 0643 // toJson 0644 mImpl << "void " << node->className() 0645 << "::toJson(QJsonObject &json) const\n" 0646 "{\n"; 0647 if (!parentClass.isEmpty()) { 0648 mImpl << " static_cast<const " << parentClass << " *>(this)->toJson(json);\n"; 0649 } else if (serializeProperties.isEmpty()) { 0650 mImpl << " Q_UNUSED(json)\n"; 0651 } 0652 for (const auto *prop : std::as_const(serializeProperties)) { 0653 if (prop->isPointer()) { 0654 mImpl << " {\n" 0655 " QJsonObject jsonObject;\n" 0656 " " 0657 << prop->mVariableName() 0658 << "->toJson(jsonObject);\n" 0659 " json[QStringLiteral(\"" 0660 << prop->name() 0661 << "\")] = jsonObject;\n" 0662 " }\n"; 0663 } else if (TypeHelper::isContainer(prop->type())) { 0664 const auto &containerType = TypeHelper::containerType(prop->type()); 0665 mImpl << " {\n" 0666 " QJsonArray jsonArray;\n" 0667 " for (const auto &type : std::as_const(" 0668 << prop->mVariableName() << ")) {\n"; 0669 if (TypeHelper::isPointerType(containerType)) { 0670 mImpl << " QJsonObject jsonObject;\n" 0671 " type->toJson(jsonObject); /* " 0672 << containerType 0673 << " */\n" 0674 " jsonArray.append(jsonObject);\n"; 0675 } else if (TypeHelper::isNumericType(containerType) || TypeHelper::isBoolType(containerType)) { 0676 mImpl << " jsonArray.append(type); /* " << containerType << " */\n"; 0677 } else if (containerType == QLatin1StringView("QByteArray")) { 0678 mImpl << " jsonArray.append(QString::fromUtf8(type)); /* " << containerType << "*/\n"; 0679 } else if (TypeHelper::isBuiltInType(containerType)) { 0680 if (TypeHelper::containerType(prop->type()) == QLatin1StringView("Akonadi::Protocol::ChangeNotification::Relation")) { 0681 mImpl << " QJsonObject jsonObject;\n" 0682 " type.toJson(jsonObject); /* " 0683 << containerType 0684 << " */\n" 0685 " jsonArray.append(jsonObject);\n"; 0686 } else { 0687 mImpl << " jsonArray.append(type); /* " << containerType << " */\n"; 0688 } 0689 } else { 0690 mImpl << " QJsonObject jsonObject;\n" 0691 " type.toJson(jsonObject); /* " 0692 << containerType 0693 << " */\n" 0694 " jsonArray.append(jsonObject);\n"; 0695 } 0696 mImpl << " }\n" 0697 << " json[QStringLiteral(\"" << prop->name() << "\")] = jsonArray;\n" 0698 << " }\n"; 0699 } else if (prop->type() == QLatin1StringView("uint")) { 0700 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = static_cast<int>(" << prop->mVariableName() << ");/* " << prop->type() << " */\n"; 0701 } else if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) { 0702 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = " << prop->mVariableName() << ";/* " << prop->type() << " */\n"; 0703 } else if (TypeHelper::isBuiltInType(prop->type())) { 0704 if (prop->type() == QLatin1StringView("QStringList")) { 0705 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = QJsonArray::fromStringList(" << prop->mVariableName() << ");/* " 0706 << prop->type() << " */\n"; 0707 } else if (prop->type() == QLatin1StringView("QDateTime")) { 0708 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = " << prop->mVariableName() << ".toString()/* " << prop->type() << " */;\n"; 0709 } else if (prop->type() == QLatin1StringView("QByteArray")) { 0710 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = QString::fromUtf8(" << prop->mVariableName() << ")/* " << prop->type() 0711 << " */;\n"; 0712 } else if (prop->type() == QLatin1StringView("Scope")) { 0713 mImpl << " {\n" 0714 " QJsonObject jsonObject;\n" 0715 " " 0716 << prop->mVariableName() << ".toJson(jsonObject); /* " << prop->type() 0717 << " */\n" 0718 " json[QStringLiteral(\"" 0719 << prop->name() << "\")] = " 0720 << "jsonObject;\n" 0721 " }\n"; 0722 } else if (prop->type() == QLatin1StringView("Tristate")) { 0723 mImpl << " switch (" << prop->mVariableName() 0724 << ") {\n;" 0725 " case Tristate::True:\n" 0726 " json[QStringLiteral(\"" 0727 << prop->name() 0728 << "\")] = QStringLiteral(\"True\");\n" 0729 " break;\n" 0730 " case Tristate::False:\n" 0731 " json[QStringLiteral(\"" 0732 << prop->name() 0733 << "\")] = QStringLiteral(\"False\");\n" 0734 " break;\n" 0735 " case Tristate::Undefined:\n" 0736 " json[QStringLiteral(\"" 0737 << prop->name() 0738 << "\")] = QStringLiteral(\"Undefined\");\n" 0739 " break;\n" 0740 " }\n"; 0741 } else if (prop->type() == QLatin1StringView("Akonadi::Protocol::Attributes")) { 0742 mImpl << " {\n" 0743 " QJsonObject jsonObject;\n" 0744 " auto i = " 0745 << prop->mVariableName() 0746 << ".constBegin();\n" 0747 " const auto &end = " 0748 << prop->mVariableName() 0749 << ".constEnd();\n" 0750 " while (i != end) {\n" 0751 " jsonObject[QString::fromUtf8(i.key())] = QString::fromUtf8(i.value());\n" 0752 " ++i;\n" 0753 " }\n" 0754 " json[QStringLiteral(\"" 0755 << prop->name() 0756 << "\")] = jsonObject;\n" 0757 " }\n"; 0758 } else if (prop->isEnum()) { 0759 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = static_cast<int>(" << prop->mVariableName() << ");/* " << prop->type() 0760 << "*/\n"; 0761 } else { 0762 mImpl << " json[QStringLiteral(\"" << prop->name() << "\")] = " << prop->mVariableName() << ";/* " << prop->type() << "*/\n"; 0763 } 0764 } else { 0765 mImpl << " {\n" 0766 " QJsonObject jsonObject;\n" 0767 " " 0768 << prop->mVariableName() << ".toJson(jsonObject); /* " << prop->type() 0769 << " */\n" 0770 " json[QStringLiteral(\"" 0771 << prop->name() 0772 << "\")] = jsonObject;\n" 0773 " }\n"; 0774 } 0775 } 0776 mImpl << "}\n" 0777 "\n"; 0778 } 0779 0780 void CppGenerator::writeImplPropertyDependencies(const PropertyNode *node) 0781 { 0782 const auto deps = node->dependencies(); 0783 QString key; 0784 QStringList values; 0785 QString enumType; 0786 for (auto it = deps.cbegin(), end = deps.cend(); it != end; ++it) { 0787 if (key != it.key()) { 0788 key = it.key(); 0789 const auto children = node->parent()->children(); 0790 for (const auto *child : children) { 0791 if (child->type() == Node::Property && child != node) { 0792 const auto *prop = static_cast<const PropertyNode *>(child); 0793 if (prop->name() == key) { 0794 enumType = prop->type(); 0795 break; 0796 } 0797 } 0798 } 0799 if (!values.isEmpty()) { 0800 mImpl << " m" << key[0].toUpper() << QStringView(key).mid(1) << " |= " << enumType << "(" << values.join(QLatin1StringView(" | ")) << ");\n"; 0801 values.clear(); 0802 } 0803 } 0804 values << *it; 0805 } 0806 0807 if (!values.isEmpty()) { 0808 mImpl << " m" << key[0].toUpper() << QStringView(key).mid(1) << " |= " << enumType << "(" << values.join(QLatin1StringView(" | ")) << ");\n"; 0809 } 0810 } 0811 0812 bool CppGenerator::generateClass(ClassNode const *node) 0813 { 0814 writeHeaderClass(node); 0815 0816 mImpl << "\n\n/************************* " << node->className() << " *************************/\n\n"; 0817 writeImplClass(node); 0818 0819 return true; 0820 }