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 "qprotobufmessage_p.h"
5#include "qprotobufmessage.h"
6#include "qprotobufoneof.h"
7
8#include <QtCore/qassert.h>
9#include <QtCore/qmetaobject.h>
10
11#include <string>
12
13QT_BEGIN_NAMESPACE
14
15static std::string nul_terminate(QLatin1StringView l1) noexcept
16{
17 return l1.isNull() ? std::string{} : std::string{l1.data(), size_t(l1.size())};
18}
19
20/*!
21 \class QProtobufMessage
22 \inmodule QtProtobuf
23
24 \brief Base class for all protobuf messages.
25
26 Provides access to the properties of a message, using setProperty()
27 and property(), without depending on what the message is.
28*/
29
30/*!
31 \internal
32 Used from generated classes to construct the QProtobufMessage base class.
33 Internally the \a metaObject is used to query QMetaProperty
34*/
35QProtobufMessage::QProtobufMessage(const QMetaObject *metaObject)
36 : d_ptr(new QProtobufMessagePrivate)
37{
38 d_ptr->metaObject = metaObject;
39}
40
41/*!
42 \internal
43 The QMetaObject which was passed to the QProtobufMessage constructor.
44*/
45const QMetaObject *QProtobufMessage::metaObject() const
46{
47 return d_ptr->metaObject;
48}
49
50/*!
51 \internal
52*/
53int QProtobufMessagePrivate::getPropertyIndex(QAnyStringView propertyName) const
54{
55 return propertyName.visit(v: [this](auto propertyName) {
56 if constexpr (std::is_same_v<QStringView, decltype(propertyName)>) {
57 return metaObject->indexOfProperty(name: propertyName.toLatin1().constData());
58 } else if constexpr (std::is_same_v<QUtf8StringView, decltype(propertyName)>) {
59 return metaObject->indexOfProperty(name: propertyName.toString().toLatin1().constData());
60 } else if constexpr (std::is_same_v<QLatin1StringView, decltype(propertyName)>) {
61 return metaObject->indexOfProperty(name: nul_terminate(propertyName).data());
62 }
63 return -1;
64 });
65}
66
67void QProtobufMessagePrivate::storeUnknownEntry(QByteArrayView entry)
68{
69 ++unknownEntries[entry.toByteArray()];
70}
71
72std::optional<QMetaProperty> QProtobufMessagePrivate::metaProperty(QAnyStringView name) const
73{
74 const int index = getPropertyIndex(propertyName: name);
75 const QMetaProperty property = metaObject->property(index);
76 if (property.isValid())
77 return property;
78 return std::nullopt;
79}
80
81std::optional<QMetaProperty>
82QProtobufMessagePrivate::metaProperty(QtProtobufPrivate::QProtobufPropertyOrderingInfo info) const
83{
84 const int propertyIndex = info.getPropertyIndex() + metaObject->propertyOffset();
85 const QMetaProperty metaProperty = metaObject->property(index: propertyIndex);
86 if (metaProperty.isValid())
87 return metaProperty;
88 return std::nullopt;
89}
90
91/*!
92 Set the property \a propertyName to the value stored in \a value.
93
94 If the \a propertyName isn't a part of the known fields then the value will
95 not be written and the function returns \c false.
96
97 Returns \c false if it failed to store the \a value on the property.
98 Otherwise \c{true}.
99*/
100bool QProtobufMessage::setProperty(QAnyStringView propertyName, const QVariant &value)
101{
102 Q_D(QProtobufMessage);
103
104 if (auto mp = d->metaProperty(name: propertyName))
105 return mp->writeOnGadget(gadget: this, value);
106
107 return false;
108}
109
110/*!
111 \overload
112 \since 6.6
113*/
114bool QProtobufMessage::setProperty(QAnyStringView propertyName, QVariant &&value)
115{
116 Q_D(QProtobufMessage);
117
118 if (auto mp = d->metaProperty(name: propertyName))
119 return mp->writeOnGadget(gadget: this, value: std::move(value));
120
121 return false;
122}
123
124/*!
125 Get the value of the property \a propertyName.
126
127 If the \a propertyName isn't known then the returned QVariant is invalid.
128*/
129QVariant QProtobufMessage::property(QAnyStringView propertyName) const
130{
131 Q_D(const QProtobufMessage);
132
133 if (const auto mp = d->metaProperty(name: propertyName))
134 return mp->readOnGadget(gadget: this);
135 return {};
136}
137
138/*!
139 \internal
140*/
141QProtobufMessage::QProtobufMessage(const QProtobufMessage &other)
142 : d_ptr(new QProtobufMessagePrivate(*other.d_ptr))
143{
144}
145
146/*!
147 \internal
148*/
149QProtobufMessage &QProtobufMessage::operator=(const QProtobufMessage &other)
150{
151 if (this != &other)
152 *d_ptr = *other.d_ptr;
153 return *this;
154}
155
156/*!
157 \internal
158*/
159QProtobufMessage::~QProtobufMessage()
160{
161 delete d_ptr;
162}
163
164/*!
165 \internal
166*/
167bool QProtobufMessage::isEqual(const QProtobufMessage &lhs, const QProtobufMessage &rhs) noexcept
168{
169 if (lhs.d_ptr == rhs.d_ptr)
170 return true;
171 return lhs.d_func()->unknownEntries == rhs.d_func()->unknownEntries;
172}
173
174namespace QtProtobufPrivate {
175/*!
176 \internal
177*/
178extern QProtobufMessagePointer constructMessageByName(const QString &messageType);
179}
180
181/*!
182 Constructs QProtobufMessage using \a messageType.
183 Returns a pointer to the constructed QProtobufMessage.
184
185 This function attempts to create a message whose type matches \a messageType. If \a messageType
186 is unknown, the function returns \nullptr. If the message is not found in the registry, the
187 function returns \nullptr.
188 Ownership of the constructed message is given to the function caller.
189*/
190QProtobufMessagePointer QProtobufMessage::constructByName(const QString &messageType)
191{
192 return QtProtobufPrivate::constructMessageByName(messageType);
193}
194
195/*!
196 \typedef QProtobufMessagePointer
197 \relates QProtobufMessage
198 \inmodule QtProtobuf
199
200 Synonym for std::unique_ptr<QProtobufMessage, QProtobufMessageDeleter>.
201 Use this to manage the lifetime of dynamically allocated QProtobufMessages,
202 such as those created by calling QProtobufMessage::constructByName.
203*/
204
205/*!
206 \class QProtobufMessageDeleter
207 \inmodule QtProtobuf
208 \brief Calls the destructor of the child class of a QProtobufMessage.
209
210 This class calls the destructor of a protobuf message using the meta-type
211 system. This class is intended to be used with smart pointers, such as
212 std::unique_ptr.
213
214 \sa QProtobufMessagePointer
215*/
216
217/*!
218 Destroys the message pointed to by \a ptr.
219 This is intended for use with smart pointers.
220
221 \sa QProtobufMessagePointer
222*/
223void QProtobufMessageDeleter::operator()(QProtobufMessage *ptr) noexcept
224{
225 if (!ptr)
226 return;
227 const QMetaObject *mobj = ptr->metaObject();
228 QMetaType type = mobj->metaType();
229 type.destroy(data: ptr);
230}
231
232QVariant
233QProtobufMessage::property(const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo) const
234{
235 int propertyIndex = fieldInfo.getPropertyIndex() + metaObject()->propertyOffset();
236 QMetaProperty metaProperty = metaObject()->property(index: propertyIndex);
237
238 if (!metaProperty.isValid())
239 return {};
240
241 if (fieldInfo.getFieldFlags() & QtProtobufPrivate::Oneof) {
242 int hasPropertyIndex = propertyIndex + 1;
243 QMetaProperty hasProperty = metaObject()->property(index: hasPropertyIndex);
244 Q_ASSERT_X(hasProperty.isValid() && hasProperty.metaType().id() == QMetaType::Bool,
245 "QProtobufMessage", "The 'oneof' field doesn't have the follow 'has' property.");
246 if (!hasProperty.readOnGadget(gadget: this).toBool())
247 return QVariant(metaProperty.metaType());
248 }
249
250 QVariant propertyValue = metaProperty.readOnGadget(gadget: this);
251 return propertyValue;
252}
253
254bool QProtobufMessage::setProperty(
255 const QtProtobufPrivate::QProtobufPropertyOrderingInfo &fieldInfo, const QVariant &value)
256{
257 Q_D(QProtobufMessage);
258 const auto mp = d->metaProperty(info: fieldInfo);
259 if (!mp)
260 return false;
261 return mp->writeOnGadget(gadget: this, value);
262}
263
264bool QProtobufMessage::setProperty(const QtProtobufPrivate::QProtobufPropertyOrderingInfo &info,
265 QVariant &&value)
266{
267 Q_D(QProtobufMessage);
268 const auto mp = d->metaProperty(info);
269 if (!mp)
270 return false;
271 return mp->writeOnGadget(gadget: this, value);
272}
273
274QT_END_NAMESPACE
275
276#include "moc_qprotobufmessage.cpp"
277

source code of qtgrpc/src/protobuf/qprotobufmessage.cpp