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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | static 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 | */ |
35 | QProtobufMessage::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 | */ |
45 | const QMetaObject *QProtobufMessage::metaObject() const |
46 | { |
47 | return d_ptr->metaObject; |
48 | } |
49 | |
50 | /*! |
51 | \internal |
52 | */ |
53 | int 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 | |
67 | void QProtobufMessagePrivate::storeUnknownEntry(QByteArrayView entry) |
68 | { |
69 | ++unknownEntries[entry.toByteArray()]; |
70 | } |
71 | |
72 | std::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 | |
81 | std::optional<QMetaProperty> |
82 | QProtobufMessagePrivate::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 | */ |
100 | bool 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 | */ |
114 | bool 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 | */ |
129 | QVariant 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 | */ |
141 | QProtobufMessage::QProtobufMessage(const QProtobufMessage &other) |
142 | : d_ptr(new QProtobufMessagePrivate(*other.d_ptr)) |
143 | { |
144 | } |
145 | |
146 | /*! |
147 | \internal |
148 | */ |
149 | QProtobufMessage &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 | */ |
159 | QProtobufMessage::~QProtobufMessage() |
160 | { |
161 | delete d_ptr; |
162 | } |
163 | |
164 | /*! |
165 | \internal |
166 | */ |
167 | bool 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 | |
174 | namespace QtProtobufPrivate { |
175 | /*! |
176 | \internal |
177 | */ |
178 | extern 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 | */ |
190 | QProtobufMessagePointer 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 | */ |
223 | void 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 | |
232 | QVariant |
233 | QProtobufMessage::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 | |
254 | bool 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 | |
264 | bool 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 | |
274 | QT_END_NAMESPACE |
275 | |
276 | #include "moc_qprotobufmessage.cpp" |
277 | |