7#include "cppgenerator.h"
10#include "typehelper.h"
16CppGenerator::CppGenerator()
20CppGenerator::~CppGenerator()
24bool CppGenerator::generate(
Node const *node)
26 Q_ASSERT(node->
type() == Node::Document);
28 mHeaderFile.
setFileName(QStringLiteral(
"protocol_gen.h"));
30 std::cerr << qPrintable(mHeaderFile.
errorString()) << std::endl;
35 mImplFile.
setFileName(QStringLiteral(
"protocol_gen.cpp"));
37 std::cerr << qPrintable(mImplFile.
errorString()) << std::endl;
42 return generateDocument(
static_cast<DocumentNode
const *
>(node));
45void CppGenerator::writeHeaderHeader(DocumentNode
const *node)
47 mHeader <<
"// This is an auto-generated file.\n"
48 "// Any changes to this file will be overwritten\n"
50 "// clazy:excludeall=function-args-by-value\n"
52 "namespace Akonadi {\n"
53 "namespace Protocol {\n"
55 "AKONADIPRIVATE_EXPORT int version();\n"
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";
66 "} // namespace Protocol\n"
67 "} // namespace Akonadi\n\n";
70void CppGenerator::writeHeaderFooter(DocumentNode
const * )
75void CppGenerator::writeImplHeader(DocumentNode
const *node)
77 mImpl <<
"// This is an auto-generated file.\n"
78 "// Any changes to this file will be overwritten\n"
80 "// clazy:excludeall=function-args-by-value\n"
82 "namespace Akonadi {\n"
83 "namespace Protocol {\n"
94void CppGenerator::writeImplFooter(DocumentNode
const * )
96 mImpl <<
"} // namespace Protocol\n"
97 "} // namespace Akonadi\n";
100bool CppGenerator::generateDocument(DocumentNode
const *node)
102 writeHeaderHeader(node);
103 writeImplHeader(node);
105 writeImplSerializer(node);
107 for (
const auto *classNode : node->children()) {
108 if (!generateClass(
static_cast<ClassNode
const *
>(classNode))) {
113 writeHeaderFooter(node);
114 writeImplFooter(node);
119void CppGenerator::writeImplSerializer(DocumentNode
const *node)
121 mImpl <<
"void serialize(DataStream &stream, const CommandPtr &cmd)\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"
127 " case Command::Invalid | Command::_ResponseBit:\n"
128 " stream << cmdCast<Response>(cmd);\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()
139 }
else if (classNode->classType() == ClassNode::Command) {
140 mImpl <<
" case Command::" << classNode->name()
142 " stream << cmdCast<"
143 << classNode->className()
146 }
else if (classNode->classType() == ClassNode::Notification) {
147 mImpl <<
" case Command::" << classNode->name()
149 " stream << cmdCast<"
150 << classNode->className()
158 mImpl <<
"CommandPtr deserialize(QIODevice *device)\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"
167 " if (cmdType & Command::_ResponseBit) {\n"
168 " cmd = Factory::response(Command::Type(cmdType & ~Command::_ResponseBit));\n"
170 " cmd = Factory::command(cmdType);\n"
172 " switch (static_cast<int>(cmdType)) {\n"
173 " case Command::Invalid:\n"
174 " stream >> cmdCast<Command>(cmd);\n"
176 " case Command::Invalid | Command::_ResponseBit:\n"
177 " stream >> cmdCast<Response>(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()
188 }
else if (classNode->classType() == ClassNode::Command) {
189 mImpl <<
" case Command::" << classNode->name()
191 " stream >> cmdCast<"
192 << classNode->className()
195 }
else if (classNode->classType() == ClassNode::Notification) {
196 mImpl <<
" case Command::" << classNode->name()
198 " stream >> cmdCast<"
199 << classNode->className()
205 " return CommandPtr::create();\n"
209 mImpl <<
"QString debugString(const Command &cmd)\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"
216 " case Command::Invalid | Command::_ResponseBit:\n"
217 " QDebug(&out).noquote() << static_cast<const Response &>(cmd);\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()
228 }
else if (classNode->classType() == ClassNode::Command) {
229 mImpl <<
" case Command::" << classNode->name()
231 " QDebug(&out).noquote() << static_cast<const "
232 << classNode->className()
235 }
else if (classNode->classType() == ClassNode::Notification) {
236 mImpl <<
" case Command::" << classNode->name()
238 " QDebug(&out).noquote() << static_cast<const "
239 << classNode->className()
245 " return QString();\n"
250void CppGenerator::writeHeaderEnum(EnumNode
const *node)
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();
263 if (node->enumType() == EnumNode::TypeFlag) {
264 mHeader <<
" Q_DECLARE_FLAGS(" << node->name() <<
"s, " << node->name() <<
")\n\n";
268void CppGenerator::writeHeaderClass(ClassNode
const *node)
271 const QString parentClass = node->parentClassName();
272 const bool isTypeClass = node->classType() == ClassNode::Class;
274 mHeader <<
"namespace Akonadi {\n"
275 "namespace Protocol {\n\n"
276 "AKONADIPRIVATE_EXPORT DataStream &operator<<(DataStream &stream, const "
279 "AKONADIPRIVATE_EXPORT DataStream &operator>>(DataStream &stream, "
282 "AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, const "
287 << node->className() <<
"Ptr = QSharedPointer<" << node->className()
291 mHeader <<
"class AKONADIPRIVATE_EXPORT " << node->className() <<
"\n";
293 mHeader <<
"class AKONADIPRIVATE_EXPORT " << node->className() <<
" : public " << parentClass <<
"\n";
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);
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;
317 mHeader <<
"const " << arg.type <<
" &" << arg.name;
319 if (!arg.defaultValue.isEmpty()) {
320 mHeader <<
" = " << arg.defaultValue;
322 if (i < args.count() - 1) {
330 mHeader <<
" " << node->className() <<
"(const " << node->className()
331 <<
" &) = default;\n"
333 << node->className() <<
"(" << node->className()
334 <<
" &&) = default;\n"
340 << node->className() <<
" &operator=(const " << node->className()
341 <<
" &) = default;\n"
343 << node->className() <<
" &operator=(" << node->className()
344 <<
" &&) = default;\n"
345 " bool operator==(const "
347 <<
" &other) const;\n"
348 " inline bool operator!=(const "
349 << node->className() <<
" &other) const { return !operator==(other); }\n";
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()
359 << prop->type() <<
" &" << prop->name() <<
"() { return " << prop->mVariableName() <<
"; }\n";
361 mHeader <<
" inline " << prop->type() <<
" " << prop->name() <<
"() const { return " << prop->mVariableName() <<
"; }\n";
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()) {
368 if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
373 mHeader <<
" void " << prop->setterName() << varType << prop->name() <<
");\n";
376 if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
381 mHeader <<
" inline void " << prop->setterName() << varType << prop->name() <<
") { " << prop->mVariableName() <<
" = " << prop->name()
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";
392 mHeader <<
" void toJson(QJsonObject &stream) const;\n";
395 mHeader <<
"protected:\n";
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) {
408 mHeader <<
" = false";
415 " friend AKONADIPRIVATE_EXPORT Akonadi::Protocol::DataStream &operator<<(Akonadi::Protocol::DataStream &stream, const Akonadi::Protocol::"
418 " friend AKONADIPRIVATE_EXPORT Akonadi::Protocol::DataStream &operator>>(Akonadi::Protocol::DataStream &stream, Akonadi::Protocol::"
421 " friend AKONADIPRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Akonadi::Protocol::"
425 "} // namespace Protocol\n"
426 "} // namespace Akonadi\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";
434void CppGenerator::writeImplSerializer(PropertyNode
const *node,
const char *streamingOperator)
436 const auto deps = node->dependencies();
437 if (deps.isEmpty()) {
438 mImpl <<
" stream " << streamingOperator <<
" obj." << node->mVariableName() <<
";\n";
441 auto it = deps.cend();
447 if (it == deps.cbegin()) {
455 << streamingOperator <<
" obj." << node->mVariableName()
461void CppGenerator::writeImplClass(ClassNode
const *node)
463 const QString parentClass = node->parentClassName();
464 const auto &children = node->children();
468 for (
const auto *child : children) {
469 if (child->type() == Node::Ctor) {
470 const auto *
const ctor =
static_cast<CtorNode
const *
>(child);
471 const auto args = ctor->arguments();
472 mImpl << node->className() <<
"::" << node->className() <<
"(";
473 for (
int i = 0; i < args.count(); ++i) {
474 const auto &arg = args[i];
475 if (TypeHelper::isNumericType(arg.type) || TypeHelper::isBoolType(arg.type)) {
476 mImpl << arg.type <<
" " << arg.name;
478 mImpl <<
"const " << arg.type <<
" &" << arg.name;
480 if (i < args.count() - 1) {
485 char startChar =
',';
487 const QString type = node->name() + ((node->classType() == ClassNode::Notification) ? QStringLiteral(
"Notification") :
QString());
488 mImpl <<
" : " << parentClass <<
"(Command::" <<
type <<
")\n";
492 for (
const auto *prop : properties) {
493 auto arg = std::find_if(args.cbegin(), args.cend(), [prop](
const CtorNode::Argument &arg) {
494 return arg.name == prop->name();
496 if (arg != args.cend()) {
497 mImpl <<
" " << startChar <<
" " << prop->mVariableName() <<
"(" << arg->name <<
")\n";
508 mImpl <<
"bool " << node->className() <<
"::operator==(const " << node->className()
509 <<
" &other) const\n"
511 mImpl <<
" return true // simplifies generation\n";
513 mImpl <<
" && " << parentClass <<
"::operator==(other)\n";
515 for (
const auto *prop : properties) {
516 if (prop->isPointer()) {
517 mImpl <<
" && *" << prop->mVariableName() <<
" == *other." << prop->mVariableName() <<
"\n";
518 }
else if (TypeHelper::isContainer(prop->type())) {
519 mImpl <<
" && containerComparator(" << prop->mVariableName() <<
", other." << prop->mVariableName() <<
")\n";
521 mImpl <<
" && " << prop->mVariableName() <<
" == other." << prop->mVariableName() <<
"\n";
529 for (
const auto *prop : properties) {
530 if (prop->readOnly()) {
534 if (
auto *
const setter = prop->setter()) {
535 mImpl <<
"void " << node->className() <<
"::" << setter->name <<
"(const " << setter->type
538 if (!setter->append.isEmpty()) {
539 mImpl <<
" m" << setter->append[0].toUpper() <<
QStringView(setter->append).
mid(1) <<
" << val;\n";
541 if (!setter->remove.isEmpty()) {
543 mImpl <<
" auto it = std::find(" << mVar <<
".begin(), " << mVar
553 writeImplPropertyDependencies(prop);
555 }
else if (!prop->dependencies().isEmpty()) {
556 if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
557 mImpl <<
"void " << node->className() <<
"::" << prop->setterName() <<
"(" << prop->type()
561 << prop->mVariableName() <<
" = val;\n";
564 mImpl <<
"void " << node->className() <<
"::" << prop->setterName() <<
"(const " << prop->type()
568 << prop->mVariableName() <<
" = val;\n";
570 writeImplPropertyDependencies(prop);
577 CppHelper::sortMembersForSerialization(serializeProperties);
579 mImpl <<
"DataStream &operator<<(DataStream &stream, const " << node->className()
583 mImpl <<
" stream << static_cast<const " << parentClass <<
" &>(obj);\n";
585 for (
const auto *prop : std::as_const(serializeProperties)) {
586 writeImplSerializer(prop,
"<<");
588 mImpl <<
" return stream;\n"
593 mImpl <<
"DataStream &operator>>(DataStream &stream, " << node->className()
597 mImpl <<
" stream >> static_cast<" << parentClass <<
" &>(obj);\n";
599 for (
const auto *prop : std::as_const(serializeProperties)) {
600 writeImplSerializer(prop,
">>");
602 mImpl <<
" return stream;\n"
607 mImpl <<
"QDebug operator<<(QDebug dbg, const " << node->className()
611 mImpl <<
" dbg.noquote() << static_cast<const " << parentClass <<
" &>(obj)\n";
613 mImpl <<
" dbg.noquote()\n";
616 for (
const auto *prop : std::as_const(serializeProperties)) {
617 if (prop->isPointer()) {
618 mImpl <<
" << \"" << prop->name() <<
":\" << *obj." << prop->mVariableName() <<
" << \"\\n\"\n";
619 }
else if (TypeHelper::isContainer(prop->type())) {
620 mImpl <<
" << \"" << prop->name()
622 " for (const auto &type : std::as_const(obj."
623 << prop->mVariableName()
625 " dbg.noquote() << \" \" << ";
626 if (TypeHelper::isPointerType(TypeHelper::containerType(prop->type()))) {
631 mImpl <<
" << \"\\n\";\n"
633 " dbg.noquote() << \"]\\n\"\n";
635 mImpl <<
" << \"" << prop->name() <<
":\" << obj." << prop->mVariableName() <<
" << \"\\n\"\n";
644 mImpl <<
"void " << node->className()
645 <<
"::toJson(QJsonObject &json) const\n"
648 mImpl <<
" static_cast<const " << parentClass <<
" *>(this)->toJson(json);\n";
649 }
else if (serializeProperties.isEmpty()) {
650 mImpl <<
" Q_UNUSED(json)\n";
652 for (
const auto *prop : std::as_const(serializeProperties)) {
653 if (prop->isPointer()) {
655 " QJsonObject jsonObject;\n"
657 << prop->mVariableName()
658 <<
"->toJson(jsonObject);\n"
659 " json[QStringLiteral(\""
661 <<
"\")] = jsonObject;\n"
663 }
else if (TypeHelper::isContainer(prop->type())) {
664 const auto &containerType = TypeHelper::containerType(prop->type());
666 " QJsonArray jsonArray;\n"
667 " for (const auto &type : std::as_const("
668 << prop->mVariableName() <<
")) {\n";
669 if (TypeHelper::isPointerType(containerType)) {
670 mImpl <<
" QJsonObject jsonObject;\n"
671 " type->toJson(jsonObject); /* "
674 " jsonArray.append(jsonObject);\n";
675 }
else if (TypeHelper::isNumericType(containerType) || TypeHelper::isBoolType(containerType)) {
676 mImpl <<
" jsonArray.append(type); /* " << containerType <<
" */\n";
678 mImpl <<
" jsonArray.append(QString::fromUtf8(type)); /* " << containerType <<
"*/\n";
679 }
else if (TypeHelper::isBuiltInType(containerType)) {
680 if (TypeHelper::containerType(prop->type()) ==
QLatin1StringView(
"Akonadi::Protocol::ChangeNotification::Relation")) {
681 mImpl <<
" QJsonObject jsonObject;\n"
682 " type.toJson(jsonObject); /* "
685 " jsonArray.append(jsonObject);\n";
687 mImpl <<
" jsonArray.append(type); /* " << containerType <<
" */\n";
690 mImpl <<
" QJsonObject jsonObject;\n"
691 " type.toJson(jsonObject); /* "
694 " jsonArray.append(jsonObject);\n";
697 <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = jsonArray;\n"
700 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = static_cast<int>(" << prop->mVariableName() <<
");/* " << prop->type() <<
" */\n";
701 }
else if (TypeHelper::isNumericType(prop->type()) || TypeHelper::isBoolType(prop->type())) {
702 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = " << prop->mVariableName() <<
";/* " << prop->type() <<
" */\n";
703 }
else if (TypeHelper::isBuiltInType(prop->type())) {
705 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = QJsonArray::fromStringList(" << prop->mVariableName() <<
");/* "
706 << prop->type() <<
" */\n";
708 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = " << prop->mVariableName() <<
".toString()/* " << prop->type() <<
" */;\n";
710 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = QString::fromUtf8(" << prop->mVariableName() <<
")/* " << prop->type()
714 " QJsonObject jsonObject;\n"
716 << prop->mVariableName() <<
".toJson(jsonObject); /* " << prop->type()
718 " json[QStringLiteral(\""
719 << prop->name() <<
"\")] = "
723 mImpl <<
" switch (" << prop->mVariableName()
725 " case Tristate::True:\n"
726 " json[QStringLiteral(\""
728 <<
"\")] = QStringLiteral(\"True\");\n"
730 " case Tristate::False:\n"
731 " json[QStringLiteral(\""
733 <<
"\")] = QStringLiteral(\"False\");\n"
735 " case Tristate::Undefined:\n"
736 " json[QStringLiteral(\""
738 <<
"\")] = QStringLiteral(\"Undefined\");\n"
743 " QJsonObject jsonObject;\n"
745 << prop->mVariableName()
746 <<
".constBegin();\n"
747 " const auto &end = "
748 << prop->mVariableName()
750 " while (i != end) {\n"
751 " jsonObject[QString::fromUtf8(i.key())] = QString::fromUtf8(i.value());\n"
754 " json[QStringLiteral(\""
756 <<
"\")] = jsonObject;\n"
758 }
else if (prop->isEnum()) {
759 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = static_cast<int>(" << prop->mVariableName() <<
");/* " << prop->type()
762 mImpl <<
" json[QStringLiteral(\"" << prop->name() <<
"\")] = " << prop->mVariableName() <<
";/* " << prop->type() <<
"*/\n";
766 " QJsonObject jsonObject;\n"
768 << prop->mVariableName() <<
".toJson(jsonObject); /* " << prop->type()
770 " json[QStringLiteral(\""
772 <<
"\")] = jsonObject;\n"
780void CppGenerator::writeImplPropertyDependencies(
const PropertyNode *node)
782 const auto deps = node->dependencies();
786 for (
auto it = deps.cbegin(), end = deps.cend(); it != end; ++it) {
787 if (key != it.key()) {
789 const auto children = node->parent()->children();
790 for (
const auto *child : children) {
791 if (child->type() == Node::Property && child != node) {
792 const auto *prop =
static_cast<const PropertyNode *
>(child);
793 if (prop->name() == key) {
794 enumType = prop->type();
812bool CppGenerator::generateClass(ClassNode
const *node)
814 writeHeaderClass(node);
816 mImpl <<
"\n\n/************************* " << node->className() <<
" *************************/\n\n";
817 writeImplClass(node);
virtual QString type() const
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
QString errorString() const const
bool isEmpty() const const
bool isEmpty() const const
QString toUpper() const const
QString join(QChar separator) const const
QStringView mid(qsizetype start, qsizetype length) const const
void setDevice(QIODevice *device)