enumconverter.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #include "enumconverter_p.h"
  2. #include "exception.h"
  3. #include "cborserializer.h"
  4. #include <cmath>
  5. using namespace QtJsonSerializer;
  6. using namespace QtJsonSerializer::TypeConverters;
  7. namespace {
  8. // Q_NORETURN inline void throwSer(QByteArray &&what, bool ser)
  9. // {
  10. // if (ser)
  11. // throw SerializationException{std::move(what)};
  12. // else
  13. // throw DeserializationException{std::move(what)};
  14. // }
  15. Q_NORETURN inline void throwSer(const QByteArray &what, bool ser)
  16. {
  17. if (ser)
  18. throw SerializationException{std::move(what)};
  19. else
  20. throw DeserializationException{std::move(what)};
  21. }
  22. }
  23. EnumConverter::EnumConverter()
  24. {
  25. setPriority(TypeConverter::Low);
  26. }
  27. bool EnumConverter::canConvert(int metaTypeId) const
  28. {
  29. return QMetaType(metaTypeId).flags().testFlag(QMetaType::IsEnumeration) ||
  30. testForEnum(metaTypeId); // NOTE check once in a while if still needed
  31. }
  32. QList<QCborTag> EnumConverter::allowedCborTags(int metaTypeId) const
  33. {
  34. const auto metaEnum = getEnum(metaTypeId, false);
  35. if (metaEnum.isFlag())
  36. return {static_cast<QCborTag>(CborSerializer::Flags)};
  37. else
  38. return {static_cast<QCborTag>(CborSerializer::Enum)};
  39. }
  40. QList<QCborValue::Type> EnumConverter::allowedCborTypes(int metaTypeId, QCborTag tag) const
  41. {
  42. Q_UNUSED(metaTypeId)
  43. Q_UNUSED(tag)
  44. return {QCborValue::Integer, QCborValue::String};
  45. }
  46. QCborValue EnumConverter::serialize(int propertyType, const QVariant &value) const
  47. {
  48. const auto metaEnum = getEnum(propertyType, true);
  49. const auto tag = static_cast<QCborTag>(metaEnum.isFlag() ? CborSerializer::Flags : CborSerializer::Enum);
  50. if (helper()->getProperty("enumAsString").toBool()) {
  51. if (metaEnum.isFlag())
  52. return {tag, QString::fromUtf8(metaEnum.valueToKeys(value.toInt()))};
  53. else
  54. return {tag, QString::fromUtf8(metaEnum.valueToKey(value.toInt()))};
  55. } else
  56. return {tag, value.toInt()};
  57. }
  58. QVariant EnumConverter::deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const
  59. {
  60. Q_UNUSED(parent)
  61. const auto metaEnum = getEnum(propertyType, false);
  62. auto cValue = value.isTag() ? value.taggedValue() : value;
  63. if (cValue.isString()) {
  64. auto result = -1;
  65. auto ok = false;
  66. if (metaEnum.isFlag())
  67. result = metaEnum.keysToValue(qUtf8Printable(cValue.toString()), &ok);
  68. else
  69. result = metaEnum.keyToValue(qUtf8Printable(cValue.toString()), &ok);
  70. if (ok)
  71. return result;
  72. else if(metaEnum.isFlag() && cValue.toString().isEmpty())
  73. return 0;
  74. else {
  75. throw DeserializationException{QByteArray{"Invalid value for enum type \""} +
  76. metaEnum.name() +
  77. "\": " +
  78. cValue.toString().toUtf8()};
  79. }
  80. } else {
  81. const auto intValue = cValue.toInteger();
  82. if (!metaEnum.isFlag() && metaEnum.valueToKey(intValue) == nullptr) {
  83. throw DeserializationException{"Invalid integer value. Not a valid enum/flags element: " +
  84. QByteArray::number(intValue)};
  85. }
  86. return static_cast<int>(intValue);
  87. }
  88. }
  89. QVariant EnumConverter::deserializeJson(int propertyType, const QCborValue &value, QObject *parent) const
  90. {
  91. if (value.isDouble()) {
  92. double intpart;
  93. if (std::modf(value.toDouble(), &intpart) != 0.0) {
  94. throw DeserializationException{"Invalid value (double) for enum type found: " +
  95. QByteArray::number(value.toDouble())};
  96. }
  97. }
  98. return deserializeCbor(propertyType, value, parent);
  99. }
  100. bool EnumConverter::testForEnum(int metaTypeId) const
  101. {
  102. try {
  103. getEnum(metaTypeId, true);
  104. return true;
  105. } catch (Exception &) {
  106. return false;
  107. }
  108. }
  109. QMetaEnum EnumConverter::getEnum(int metaTypeId, bool ser) const
  110. {
  111. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
  112. const auto mo = QMetaType::metaObjectForType(metaTypeId);
  113. #else
  114. const auto mo = QMetaType(metaTypeId).metaObject();
  115. #endif
  116. if (!mo)
  117. throwSer(QByteArray{"Unable to get metaobject for type "} + QMetaTypeName(metaTypeId), ser);
  118. const auto enumName = QString::fromUtf8(QMetaTypeName(metaTypeId))
  119. .split(QStringLiteral("::"))
  120. .last()
  121. .toUtf8();
  122. auto mIndex = mo->indexOfEnumerator(enumName.data());
  123. if (mIndex < 0) {
  124. throwSer(QByteArray{"Unable to get QMetaEnum for type "} +
  125. QMetaTypeName(metaTypeId) +
  126. QByteArray{" using the owning meta object "} +
  127. mo->className(),
  128. ser);
  129. }
  130. return mo->enumerator(mIndex);
  131. }