1 | // Copyright (C) 2022 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include <QtProtobuf/qprotobufmessage.h> |
5 | |
6 | #include <QtProtobuf/private/qprotobufmessage_p.h> |
7 | #include <QtProtobuf/private/qprotobufpropertyorderingbuilder_p.h> |
8 | #include <QtProtobuf/qabstractprotobufserializer.h> |
9 | #include <QtProtobuf/qprotobufpropertyordering.h> |
10 | |
11 | #include <QtCore/qassert.h> |
12 | #include <QtCore/qmetaobject.h> |
13 | |
14 | #include <QtCore/private/qmetaobjectbuilder_p.h> |
15 | |
16 | #include <string> |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | static std::string nullTerminate(QLatin1StringView l1) noexcept |
21 | { |
22 | return l1.isNull() ? std::string{} : std::string{l1.data(), size_t(l1.size())}; |
23 | } |
24 | |
25 | /*! |
26 | \class QProtobufMessage |
27 | \inmodule QtProtobuf |
28 | |
29 | \brief Base class for all protobuf messages. |
30 | |
31 | Provides access to the properties of a message, using setProperty() |
32 | and property(), without depending on what the message is. |
33 | */ |
34 | |
35 | /*! |
36 | \internal |
37 | Used from generated classes to construct the QProtobufMessage base class. |
38 | Internally the \a metaObject is used to query QMetaProperty |
39 | */ |
40 | QProtobufMessage::QProtobufMessage(const QMetaObject *metaObject, |
41 | const QtProtobufPrivate::QProtobufPropertyOrdering *ordering) |
42 | : d_ptr(new QProtobufMessagePrivate(metaObject, ordering)) |
43 | { |
44 | } |
45 | |
46 | /*! |
47 | \internal |
48 | Allows constructing QProtobufMessage using the private message implementation from the |
49 | derived class. |
50 | */ |
51 | QProtobufMessage::QProtobufMessage(QProtobufMessagePrivate &dd) : d_ptr(&dd) |
52 | { |
53 | } |
54 | |
55 | QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QProtobufMessagePrivate) |
56 | |
57 | QProtobufMessagePrivate::QProtobufMessagePrivate(const QMetaObject *metaObject, |
58 | const QtProtobufPrivate::QProtobufPropertyOrdering |
59 | *ordering) |
60 | : metaObject(metaObject), ordering(ordering) |
61 | { |
62 | } |
63 | |
64 | QProtobufMessagePrivate::~QProtobufMessagePrivate() |
65 | = default; |
66 | |
67 | /*! |
68 | \internal |
69 | */ |
70 | int QProtobufMessagePrivate::propertyIndex(QAnyStringView propertyName) const |
71 | { |
72 | return propertyName.visit(v: [this](auto propertyName) { |
73 | if constexpr (std::is_same_v<QStringView, decltype(propertyName)>) { |
74 | return metaObject->indexOfProperty(name: propertyName.toLatin1().constData()); |
75 | } else if constexpr (std::is_same_v<QUtf8StringView, decltype(propertyName)>) { |
76 | return metaObject->indexOfProperty(name: propertyName.toString().toLatin1().constData()); |
77 | } else if constexpr (std::is_same_v<QLatin1StringView, decltype(propertyName)>) { |
78 | return metaObject->indexOfProperty(name: nullTerminate(propertyName).data()); |
79 | } |
80 | return -1; |
81 | }); |
82 | } |
83 | |
84 | void QProtobufMessagePrivate::storeUnknownEntry(QProtobufMessage *message, |
85 | QByteArrayView entry, int fieldNumber) |
86 | { |
87 | Q_ASSERT(message); |
88 | message->d_ptr.detach(); |
89 | message->d_ptr->unknownEntries[fieldNumber].append(t: entry.toByteArray()); |
90 | } |
91 | |
92 | std::optional<QMetaProperty> QProtobufMessagePrivate::metaProperty(QAnyStringView name) const |
93 | { |
94 | const int index = propertyIndex(propertyName: name); |
95 | const QMetaProperty property = metaObject->property(index); |
96 | if (property.isValid()) |
97 | return property; |
98 | return std::nullopt; |
99 | } |
100 | |
101 | std::optional<QMetaProperty> |
102 | QProtobufMessagePrivate::metaProperty(QtProtobufPrivate::QProtobufFieldInfo info) const |
103 | { |
104 | const int propertyIndex = info.propertyIndex() + metaObject->propertyOffset(); |
105 | const QMetaProperty metaProperty = metaObject->property(index: propertyIndex); |
106 | if (metaProperty.isValid()) |
107 | return metaProperty; |
108 | return std::nullopt; |
109 | } |
110 | |
111 | /*! |
112 | Set the property \a propertyName to the value stored in \a value. |
113 | |
114 | If the \a propertyName isn't a known fields, then the value is ignored, and |
115 | the function returns \c false. |
116 | |
117 | Returns \c false if it fails to store the \a value on the property. |
118 | Otherwise \c{true}. |
119 | */ |
120 | bool QProtobufMessage::setProperty(QAnyStringView propertyName, const QVariant &value) |
121 | { |
122 | Q_D(QProtobufMessage); |
123 | |
124 | if (auto mp = d->metaProperty(name: propertyName)) |
125 | return mp->writeOnGadget(gadget: this, value); |
126 | |
127 | return false; |
128 | } |
129 | |
130 | /*! |
131 | \overload |
132 | \since 6.6 |
133 | */ |
134 | bool QProtobufMessage::setProperty(QAnyStringView propertyName, QVariant &&value) |
135 | { |
136 | Q_D(QProtobufMessage); |
137 | |
138 | if (auto mp = d->metaProperty(name: propertyName)) |
139 | return mp->writeOnGadget(gadget: this, value: std::move(value)); |
140 | |
141 | return false; |
142 | } |
143 | |
144 | /*! |
145 | Get the value of the property \a propertyName. |
146 | |
147 | If the \a propertyName isn't known then the returned QVariant is invalid. |
148 | */ |
149 | QVariant QProtobufMessage::property(QAnyStringView propertyName) const |
150 | { |
151 | Q_D(const QProtobufMessage); |
152 | |
153 | if (const auto mp = d->metaProperty(name: propertyName)) |
154 | return mp->readOnGadget(gadget: this); |
155 | return {}; |
156 | } |
157 | |
158 | /*! |
159 | \internal |
160 | */ |
161 | QProtobufMessage::QProtobufMessage(const QProtobufMessage &other) |
162 | = default; |
163 | |
164 | /*! |
165 | \internal |
166 | */ |
167 | QProtobufMessage &QProtobufMessage::operator=(const QProtobufMessage &other) |
168 | = default; |
169 | |
170 | |
171 | /*! |
172 | \internal |
173 | */ |
174 | QProtobufMessage::~QProtobufMessage() |
175 | = default; |
176 | |
177 | /*! |
178 | \since 6.8 |
179 | Returns the pointer to the property ordering of the derived protobuf message. |
180 | */ |
181 | const QtProtobufPrivate::QProtobufPropertyOrdering *QProtobufMessage::propertyOrdering() const |
182 | { |
183 | Q_D(const QProtobufMessage); |
184 | return d->ordering; |
185 | } |
186 | |
187 | /*! |
188 | \internal |
189 | */ |
190 | bool comparesEqual(const QProtobufMessage &lhs, const QProtobufMessage &rhs) noexcept |
191 | { |
192 | if (lhs.d_ptr == rhs.d_ptr) |
193 | return true; |
194 | return lhs.d_func()->unknownEntries == rhs.d_func()->unknownEntries; |
195 | } |
196 | |
197 | namespace QtProtobufPrivate { |
198 | /*! |
199 | \internal |
200 | */ |
201 | extern QProtobufMessagePointer constructMessageByName(const QString &messageType); |
202 | } |
203 | |
204 | /*! |
205 | Constructs QProtobufMessage using \a messageType. |
206 | Returns a pointer to the constructed QProtobufMessage. |
207 | |
208 | This function attempts to create a message with a type that matches \a messageType. |
209 | If \a messageType is unknown, the function returns \nullptr. If the message |
210 | is not found in the registry, the function returns \nullptr. |
211 | The function caller is given ownership of the constructed message. |
212 | */ |
213 | QProtobufMessagePointer QProtobufMessage::constructByName(const QString &messageType) |
214 | { |
215 | return QtProtobufPrivate::constructMessageByName(messageType); |
216 | } |
217 | |
218 | /*! |
219 | \typedef QProtobufMessagePointer |
220 | \relates QProtobufMessage |
221 | \inmodule QtProtobuf |
222 | |
223 | Synonym for std::unique_ptr<QProtobufMessage, QProtobufMessageDeleter>. |
224 | Use this to manage the lifetime of dynamically allocated QProtobufMessages, |
225 | such as those created by calling QProtobufMessage::constructByName. |
226 | */ |
227 | |
228 | /*! |
229 | \class QProtobufMessageDeleter |
230 | \inmodule QtProtobuf |
231 | \brief Calls the destructor of the child class of a QProtobufMessage. |
232 | |
233 | This class calls the destructor of a protobuf message using the meta-type |
234 | system. It is intended to be used with smart pointers, such as std::unique_ptr. |
235 | |
236 | \sa QProtobufMessagePointer |
237 | */ |
238 | |
239 | /*! |
240 | Destroys the message pointed to by \a ptr. |
241 | This is intended for use with smart pointers. |
242 | |
243 | \sa QProtobufMessagePointer |
244 | */ |
245 | void QProtobufMessageDeleter::operator()(QProtobufMessage *ptr) const noexcept |
246 | { |
247 | if (!ptr) |
248 | return; |
249 | const QMetaObject *mobj = ptr->d_ptr->metaObject; |
250 | QMetaType type = mobj->metaType(); |
251 | type.destroy(data: ptr); |
252 | } |
253 | |
254 | /*! |
255 | Returns the field numbers that were not known to QtProtobuf during |
256 | deserialization. |
257 | \since 6.7 |
258 | */ |
259 | QList<qint32> QProtobufMessage::unknownFieldNumbers() const |
260 | { |
261 | Q_D(const QProtobufMessage); |
262 | return d->unknownEntries.keys(); |
263 | } |
264 | |
265 | /*! |
266 | Returns the unknown \a field values sorted as they were received from the |
267 | wire. |
268 | \since 6.7 |
269 | */ |
270 | QList<QByteArray> QProtobufMessage::unknownFieldData(qint32 field) const |
271 | { |
272 | Q_D(const QProtobufMessage); |
273 | return d->unknownEntries.value(key: field); |
274 | } |
275 | |
276 | /*! |
277 | \since 6.8 |
278 | Serializes this protobuf message into a QByteArray using \a serializer. |
279 | |
280 | \sa deserialize() |
281 | */ |
282 | QByteArray QProtobufMessage::serialize(QAbstractProtobufSerializer *serializer) const |
283 | { |
284 | return serializer->serialize(message: this); |
285 | } |
286 | |
287 | /*! |
288 | \since 6.8 |
289 | Deserializes this protobuf message from a QByteArray \a data using |
290 | \a serializer. |
291 | Returns \c true if deserialization was successful, otherwise \c false. |
292 | |
293 | \sa serialize() |
294 | */ |
295 | bool QProtobufMessage::deserialize(QAbstractProtobufSerializer *serializer, QByteArrayView data) |
296 | { |
297 | return serializer->deserialize(message: this, data); |
298 | } |
299 | |
300 | static bool isProtobufMessage(QMetaType type) |
301 | { |
302 | if (const auto *mo = type.metaObject(); mo && mo->inherits(metaObject: &QProtobufMessage::staticMetaObject)) |
303 | return true; |
304 | return false; |
305 | } |
306 | |
307 | using StaticMetaCallFn = void (*)(QObject *, QMetaObject::Call, int, void **); |
308 | QMetaObject *buildMetaObject(QMetaType key, QMetaType value, StaticMetaCallFn metaCall) |
309 | { |
310 | using namespace Qt::StringLiterals; |
311 | QMetaObjectBuilder builder; |
312 | builder.addProperty(name: "key", type: key.name(), metaType: key); |
313 | if (isProtobufMessage(type: key)) { |
314 | auto propBuilder = builder.addProperty(name: "has_key", type: "bool", metaType: QMetaType(QMetaType::Bool)); |
315 | propBuilder.setWritable(false); |
316 | } |
317 | builder.addProperty(name: "value", type: value.name(), metaType: value); |
318 | if (isProtobufMessage(type: value)) { |
319 | auto propBuilder = builder.addProperty(name: "has_value", type: "bool", metaType: QMetaType(QMetaType::Bool)); |
320 | propBuilder.setWritable(false); |
321 | } |
322 | builder.setClassName("QProtobufMapEntry<"_ba+ key.name() + ", "+ value.name() + '>'); |
323 | builder.setSuperClass(&QProtobufMapEntryBase::staticMetaObject); |
324 | builder.setStaticMetacallFunction(metaCall); |
325 | builder.setFlags(MetaObjectFlag::PropertyAccessInStaticMetaCall); |
326 | return builder.toMetaObject(); |
327 | } |
328 | |
329 | QtProtobufPrivate::FieldFlags getFlagForType(QMetaType type) |
330 | { |
331 | using FieldFlag = QtProtobufPrivate::FieldFlag; |
332 | QtProtobufPrivate::FieldFlags flag = {}; |
333 | |
334 | if (isProtobufMessage(type)) |
335 | flag |= FieldFlag::Message | FieldFlag::ExplicitPresence; |
336 | if (type.flags() & QMetaType::IsEnumeration) |
337 | flag |= FieldFlag::Enum; |
338 | if (QByteArrayView(type.name()).startsWith(other: "QList<")) // Surely there's a better way |
339 | flag |= FieldFlag::Repeated; |
340 | if (QByteArrayView(type.name()).startsWith(other: "QHash<")) // Surely there's a better way |
341 | flag |= FieldFlag::Map; |
342 | |
343 | flag |= FieldFlag::Optional; // Hardcoded for MapEntry uses... |
344 | |
345 | // Need to get this info:: |
346 | // NonPacked = 0x1, |
347 | // Oneof = 0x02, |
348 | // Optional = 0x04, |
349 | // Repeated = 0x40, |
350 | // Map = 0x80, |
351 | |
352 | return flag; |
353 | } |
354 | |
355 | QtProtobufPrivate::QProtobufPropertyOrdering::Data *buildMapEntryOrdering(QMetaType key, |
356 | QMetaType value) |
357 | { |
358 | using namespace QtProtobufPrivate; |
359 | QProtobufPropertyOrderingBuilder builder("MapEntry"); |
360 | |
361 | const auto keyFlags = getFlagForType(type: key); |
362 | builder.addV0Field(jsonName: "key", fieldNumber: 1, propertyIndex: 0, flags: keyFlags); |
363 | |
364 | const uint valueIndex = keyFlags.testFlag(flag: FieldFlag::ExplicitPresence) ? 2 : 1; |
365 | builder.addV0Field(jsonName: "value", fieldNumber: 2, propertyIndex: valueIndex, flags: getFlagForType(type: value)); |
366 | |
367 | return builder.build(); |
368 | } |
369 | |
370 | class QProtobufMapEntryBasePrivate : public QProtobufMessagePrivate |
371 | { |
372 | Q_DISABLE_COPY_MOVE(QProtobufMapEntryBasePrivate) |
373 | public: |
374 | QProtobufMapEntryBasePrivate(QMetaType key, QMetaType value, StaticMetaCallFn metaCall); |
375 | ~QProtobufMapEntryBasePrivate() override; |
376 | |
377 | QtProtobufPrivate::QProtobufPropertyOrdering::Data *data = nullptr; |
378 | QtProtobufPrivate::QProtobufPropertyOrdering mutableOrdering{}; |
379 | QMetaObject *mutableMetaObject = nullptr; |
380 | }; |
381 | |
382 | QProtobufMapEntryBase::QProtobufMapEntryBase(QMetaType key, QMetaType value, |
383 | StaticMetaCallFn metaCall) |
384 | : QProtobufMessage(*new QProtobufMapEntryBasePrivate(key, value, metaCall)) |
385 | { |
386 | } |
387 | |
388 | QProtobufMapEntryBasePrivate::QProtobufMapEntryBasePrivate(QMetaType key, QMetaType value, |
389 | StaticMetaCallFn metaCall) |
390 | : QProtobufMessagePrivate(), |
391 | data(buildMapEntryOrdering(key, value)), |
392 | mutableMetaObject(buildMetaObject(key, value, metaCall)) |
393 | { |
394 | mutableOrdering.data = data; |
395 | QProtobufMessagePrivate::ordering = &mutableOrdering; |
396 | QProtobufMessagePrivate::metaObject = mutableMetaObject; |
397 | } |
398 | |
399 | QProtobufMapEntryBase::~QProtobufMapEntryBase() |
400 | = default; |
401 | |
402 | QProtobufMapEntryBasePrivate::~QProtobufMapEntryBasePrivate() |
403 | { |
404 | data->~Data(); |
405 | free(ptr: data); |
406 | free(ptr: mutableMetaObject); |
407 | } |
408 | |
409 | QT_END_NAMESPACE |
410 | |
411 | #include "moc_qprotobufmessage.cpp" |
412 |
Definitions
- nullTerminate
- QProtobufMessage
- QProtobufMessage
- QProtobufMessagePrivate
- ~QProtobufMessagePrivate
- propertyIndex
- storeUnknownEntry
- metaProperty
- metaProperty
- setProperty
- setProperty
- property
- QProtobufMessage
- operator=
- ~QProtobufMessage
- propertyOrdering
- comparesEqual
- constructByName
- operator()
- unknownFieldNumbers
- unknownFieldData
- serialize
- deserialize
- isProtobufMessage
- buildMetaObject
- getFlagForType
- buildMapEntryOrdering
- QProtobufMapEntryBasePrivate
- QProtobufMapEntryBasePrivate
- QProtobufMapEntryBase
- QProtobufMapEntryBasePrivate
- ~QProtobufMapEntryBase
Start learning QML with our Intro Training
Find out more