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 }