MythTV master
mythhttpmetamethod.cpp
Go to the documentation of this file.
1// Qt
2#include <QChar> // Fix Qt6 GCC SFINAE warning
3#include <QDateTime>
4#include <QFileInfo>
5#include <QTimeZone>
6#include <algorithm>
7
8// MythTV
9#include "mythlogging.h"
11
12#define LOC QString("MetaMethod: ")
13
21MythHTTPMetaMethod::MythHTTPMetaMethod(int Index, QMetaMethod& Method, int RequestTypes,
22 const QString& ReturnName, bool Slot)
23 : m_index(Index),
24 m_requestTypes(RequestTypes),
25 m_method(Method)
26{
27 // Static list of unsupported return types (cannot be serialised (QHash) or of no use (pointers))
28 static const std::vector<int> s_invalidTypes =
29 {
30 QMetaType::UnknownType, QMetaType::VoidStar, QMetaType::QObjectStar,
31 QMetaType::QVariantHash, QMetaType::QRect, QMetaType::QRectF,
32 QMetaType::QSize, QMetaType::QSizeF, QMetaType::QLine,
33 QMetaType::QLineF, QMetaType::QPoint, QMetaType::QPointF
34 };
35
36 // Static list of unsupported parameters (all invalid types plus extras)
37 static const std::vector<int> s_invalidParams =
38 {
39 QMetaType::UnknownType, QMetaType::VoidStar, QMetaType::QObjectStar,
40 QMetaType::QVariantHash, QMetaType::QRect, QMetaType::QRectF,
41 QMetaType::QSize, QMetaType::QSizeF, QMetaType::QLine,
42 QMetaType::QLineF, QMetaType::QPoint, QMetaType::QPointF,
43 QMetaType::QVariantMap, QMetaType::QStringList, QMetaType::QVariantList
44 };
45
46 int returntype = Method.returnType();
47
48 // Discard methods with an unsupported return type
49#ifdef __cpp_lib_ranges_contains
50 if (std::ranges::contains(s_invalidTypes, returntype))
51#else
52 if (std::ranges::any_of(s_invalidTypes,
53 [&returntype](int Type) { return Type == returntype; }))
54#endif
55 {
56 LOG(VB_HTTP, LOG_ERR, LOC + QString("Method '%1' has unsupported return type '%2'").arg(Method.name().constData(), Method.typeName()));
57 return;
58 }
59
60 // Warn about complicated methods not supported by QMetaMethod - these will
61 // fail if all arguments are required and used
62 if (Method.parameterCount() > (Q_METAMETHOD_INVOKE_MAX_ARGS - 1))
63 {
64 LOG(VB_HTTP, LOG_WARNING, LOC + QString("Method '%1' takes more than %2 parameters; will probably fail")
65 .arg(Method.name().constData()).arg(Q_METAMETHOD_INVOKE_MAX_ARGS - 1));
66 }
67
68 // Decide on the name of the returned type
69 if (Slot && ValidReturnType(returntype))
70 {
71 // Explicitly set via Q_CLASSINFO (name=XXX)
72 m_returnTypeName = ReturnName;
73 if (m_returnTypeName.isEmpty())
74 {
75 // If this is a user type, we assume its name should be used, otherwise
76 // prefer deduction from methodname
77 if (returntype <= QMetaType::User)
78 if (QString(Method.name()).startsWith(QStringLiteral("Get"), Qt::CaseInsensitive))
79 m_returnTypeName = Method.name().mid(3);
80 // Default to the type name
81 if (m_returnTypeName.isEmpty())
82 m_returnTypeName = Method.typeName();
83 // Duplicated from MythXMLSerialiser
84 if (m_returnTypeName.startsWith("Q"))
86 m_returnTypeName.remove("DTC::");
87 m_returnTypeName.remove(QChar('*'));
88 }
89 }
90
91 // Add method name and return type
92 m_types.emplace_back(returntype >= 0 ? returntype : 0);
93 m_names.emplace_back(Method.name());
94
95 // Add type/value for each method parameter
96 auto names = Method.parameterNames();
97 auto types = Method.parameterTypes();
98
99 // Add type/value for each method parameter
100 for (int i = 0; i < names.size(); ++i)
101 {
102#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
103 int type = QMetaType::type(types[i]);
104#else
105 int type = QMetaType::fromName(types[i]).id();
106#endif
107
108 // Discard methods that use unsupported parameter types.
109 // Note: slots only - these are supportable for signals
110#ifdef __cpp_lib_ranges_contains
111 if (Slot && std::ranges::contains(s_invalidParams, type))
112#else
113 if (Slot && std::ranges::any_of(s_invalidParams,
114 [&type](int Type) { return type == Type; }))
115#endif
116 {
117 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Method '%1' has unsupported parameter type '%2' (%3)")
118 .arg(Method.name().constData(), types[i].constData()).arg(type));
119 return;
120 }
121
122 m_names.emplace_back(names[i]);
123 m_types.emplace_back(type);
124 }
125
126 m_valid = true;
127}
128
129HTTPMethodPtr MythHTTPMetaMethod::Create(int Index, QMetaMethod &Method, int RequestTypes,
130 const QString &ReturnName, bool Slot)
131{
132 HTTPMethodPtr result =
133 std::shared_ptr<MythHTTPMetaMethod>(new MythHTTPMetaMethod(Index, Method, RequestTypes, ReturnName, Slot));
134 if (result->m_valid)
135 return result;
136 return nullptr;
137}
138
144void* MythHTTPMetaMethod::CreateParameter(void* Parameter, int Type, const QString& Value)
145{
146#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
147 QByteArray typeName = QMetaType::typeName(Type);
148#else
149 QByteArray typeName = QMetaType(Type).name();
150#endif
151
152 // Enum types
153#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
154 auto typeflags = QMetaType::typeFlags(Type);
155#else
156 auto typeflags = QMetaType(Type).flags();
157#endif
158 if ((typeflags & QMetaType::IsEnumeration) == QMetaType::IsEnumeration)
159 {
160 // QMetaEnum::keyToValue will return -1 for an unrecognised enumerant, so
161 // default to -1 for all error cases
162 int value = -1;
163 if (int index = typeName.lastIndexOf("::" ); index > -1)
164 {
165 QString enumname = typeName.mid(index + 2);
166#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
167 const auto * metaobject = QMetaType::metaObjectForType(Type);
168#else
169 const auto * metaobject = QMetaType(Type).metaObject();
170#endif
171 if (metaobject)
172 {
173 int enumindex = metaobject->indexOfEnumerator(enumname.toUtf8().constData());
174 if (enumindex >= 0)
175 {
176 QMetaEnum metaEnum = metaobject->enumerator(enumindex);
177 value = metaEnum.keyToValue(Value.toUtf8().constData());
178 }
179 }
180 }
181 *(static_cast<int*>(Parameter)) = value;
182 return Parameter;
183 }
184
185 // Handle parameters of type std::chrono::seconds
186 if (typeName == "std::chrono::seconds")
187 {
188 *(static_cast<std::chrono::seconds*>(Parameter)) = std::chrono::seconds(Value.toInt());
189 return Parameter;
190 }
191
192 switch (Type)
193 {
194 case QMetaType::QVariant : *(static_cast<QVariant *>(Parameter)) = QVariant(Value); break;
195 case QMetaType::Bool : *(static_cast<bool *>(Parameter)) = ToBool(Value ); break;
196 case QMetaType::Char : *(static_cast<char *>(Parameter)) = (Value.length() > 0) ? Value.at(0).toLatin1() : 0; break;
197 case QMetaType::UChar : *(static_cast<unsigned char*>(Parameter)) = (Value.length() > 0) ? static_cast<unsigned char>(Value.at(0).toLatin1()) : 0; break;
198 case QMetaType::QChar : *(static_cast<QChar *>(Parameter)) = (Value.length() > 0) ? Value.at(0) : QChar(0); break;
199 case QMetaType::Short : *(static_cast<short *>(Parameter)) = Value.toShort(); break;
200 case QMetaType::UShort : *(static_cast<ushort *>(Parameter)) = Value.toUShort(); break;
201 case QMetaType::Int : *(static_cast<int *>(Parameter)) = Value.toInt(); break;
202 case QMetaType::UInt : *(static_cast<uint *>(Parameter)) = Value.toUInt(); break;
203 case QMetaType::Long : *(static_cast<long *>(Parameter)) = Value.toLong(); break;
204 case QMetaType::ULong : *(static_cast<ulong *>(Parameter)) = Value.toULong(); break;
205 case QMetaType::LongLong : *(static_cast<qlonglong *>(Parameter)) = Value.toLongLong(); break;
206 case QMetaType::ULongLong : *(static_cast<qulonglong *>(Parameter)) = Value.toULongLong(); break;
207 case QMetaType::Double : *(static_cast<double *>(Parameter)) = Value.toDouble(); break;
208 case QMetaType::Float : *(static_cast<float *>(Parameter)) = Value.toFloat(); break;
209 case QMetaType::QString : *(static_cast<QString *>(Parameter)) = Value; break;
210 case QMetaType::QByteArray: *(static_cast<QByteArray *>(Parameter)) = Value.toUtf8(); break;
211 case QMetaType::QTime : *(static_cast<QTime *>(Parameter)) = QTime::fromString(Value, Qt::ISODate ); break;
212 case QMetaType::QDate : *(static_cast<QDate *>(Parameter)) = QDate::fromString(Value, Qt::ISODate ); break;
213 case QMetaType::QDateTime :
214 {
215 QDateTime dt = QDateTime::fromString(Value, Qt::ISODate);
216#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
217 dt.setTimeSpec(Qt::UTC);
218#else
219 dt.setTimeZone(QTimeZone(QTimeZone::UTC));
220#endif
221 *(static_cast<QDateTime*>(Parameter)) = dt;
222 break;
223 }
224 default:
225 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Unknown QMetaType:%1 %2").arg(Type).arg(QString(typeName)));
226 break;
227 }
228 return Parameter;
229}
230
231QVariant MythHTTPMetaMethod::CreateReturnValue(int Type, void* Value)
232{
233 if (!(ValidReturnType(Type)))
234 return {};
235
236 // This assumes any user type will be derived from QObject...
237 // (Exception for QFileInfo)
238 if (
239#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
240 Type == QMetaType::type("QFileInfo")
241#else
242 Type == QMetaType::fromName("QFileInfo").id()
243#endif
244 )
245 return QVariant::fromValue<QFileInfo>(*(static_cast<QFileInfo*>(Value)));
246
247 if (Type > QMetaType::User)
248 {
249 QObject* object = *(static_cast<QObject**>(Value));
250 return QVariant::fromValue<QObject*>(object);
251 }
252
253#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
254 return {Type, Value};
255#else
256 return QVariant(QMetaType(Type), Value);
257#endif
258}
std::vector< int > m_types
static void * CreateParameter(void *Parameter, int Type, const QString &Value)
Populate the QMetaType object referenced by Parameter with Value.
static QVariant CreateReturnValue(int Type, void *Value)
static bool ToBool(const QString &Value)
static HTTPMethodPtr Create(int Index, QMetaMethod &Method, int RequestTypes, const QString &ReturnName={}, bool Slot=true)
std::vector< QString > m_names
MythHTTPMetaMethod(int Index, QMetaMethod &Method, int RequestTypes, const QString &ReturnName, bool Slot)
static bool ValidReturnType(int Type)
unsigned int uint
Definition: compat.h:60
static const struct wl_interface * types[]
#define LOC
std::shared_ptr< MythHTTPMetaMethod > HTTPMethodPtr
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
@ ISODate
Default UTC.
Definition: mythdate.h:18
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39