Cutelee 6.1.0
metatype.cpp
1/*
2 This file is part of the Cutelee template system.
3
4 Copyright (c) 2010 Michael Jansen <kde@michael-jansen.biz>
5 Copyright (c) 2010 Stephen Kelly <steveire@gmail.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either version
10 2.1 of the Licence, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library. If not, see <http://www.gnu.org/licenses/>.
19
20*/
21
22#include "metatype.h"
23
24#include "customtyperegistry_p.h"
25#include "metaenumvariable_p.h"
26
27#include <QtCore/QDebug>
28#include <QJsonValue>
29#include <QAssociativeIterable>
30#include <QJsonArray>
31#include <QJsonObject>
32#include <QJsonDocument>
33#include <QSequentialIterable>
34
35using namespace Cutelee;
36
37Q_GLOBAL_STATIC(CustomTypeRegistry, customTypes)
38
39void Cutelee::MetaType::internalLock() { return customTypes()->mutex.lock(); }
40
41void Cutelee::MetaType::internalUnlock()
42{
43 return customTypes()->mutex.unlock();
44}
45
46void Cutelee::MetaType::registerLookUpOperator(int id, LookupFunction f)
47{
48 Q_ASSERT(id > 0);
49 Q_ASSERT(f);
50
51 customTypes()->registerLookupOperator(id, f);
52}
53
54static QVariant doQobjectLookUp(const QObject *const object,
55 const QString &property)
56{
57 if (!object)
58 return QVariant();
59 if (property == QStringLiteral("children")) {
60 const auto childList = object->children();
61 if (childList.isEmpty())
62 return QVariant();
63 QVariantList children;
64
65 auto it = childList.constBegin();
66 const auto end = childList.constEnd();
67 for (; it != end; ++it)
68 children.append(QVariant::fromValue(*it));
69 return children;
70 }
71
72 if (property == QStringLiteral("objectName")) {
73 return object->objectName();
74 }
75 // Can't be const because of invokeMethod.
76 auto metaObj = object->metaObject();
77
79 for (auto i = 0; i < metaObj->propertyCount(); ++i) {
80 // TODO only read-only properties should be allowed here.
81 // This might also handle the variant messing I hit before.
82 mp = metaObj->property(i);
83
84 if (QString::fromUtf8(mp.name()) != property)
85 continue;
86
87 if (mp.isEnumType()) {
88 MetaEnumVariable mev(mp.enumerator(), mp.read(object).value<int>());
89 return QVariant::fromValue(mev);
90 }
91
92 return mp.read(object);
93 }
94 QMetaEnum me;
95 for (auto i = 0; i < metaObj->enumeratorCount(); ++i) {
96 me = metaObj->enumerator(i);
97
98 if (QLatin1String(me.name()) == property) {
99 MetaEnumVariable mev(me);
100 return QVariant::fromValue(mev);
101 }
102
103 const auto value = me.keyToValue(property.toLatin1().constData());
104
105 if (value < 0)
106 continue;
107
108 const MetaEnumVariable mev(me, value);
109
110 return QVariant::fromValue(mev);
111 }
112 return object->property(property.toUtf8().constData());
113}
114
115static QVariant doJsonArrayLookUp(const QJsonArray &list,
116 const QString &property)
117{
118 if (property == QLatin1String("count") || property == QLatin1String("size")) {
119 return list.size();
120 }
121
122 bool ok = false;
123 const int listIndex = property.toInt(&ok);
124 if (!ok || listIndex >= list.size()) {
125 return QVariant();
126 }
127
128 return list.at(listIndex).toVariant();
129}
130
131static QVariant doJsonObjectLookUp(const QJsonObject &obj,
132 const QString &property)
133{
134 if (property == QLatin1String("count") || property == QLatin1String("size")) {
135 return obj.size();
136 }
137
138 if (property == QLatin1String("items")) {
139 QVariantList list;
140 list.reserve(obj.size());
141 for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
142 list.push_back(QVariantList{it.key(), it.value().toVariant()});
143 }
144 return list;
145 }
146
147 if (property == QLatin1String("keys")) {
148 return obj.keys();
149 }
150
151 if (property == QLatin1String("values")) {
152 QVariantList list;
153 list.reserve(obj.size());
154 for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
155 list.push_back(it.value().toVariant());
156 }
157 return list;
158 }
159
160 return obj.value(property).toVariant();
161}
162
163QVariant Cutelee::MetaType::lookup(const QVariant &object,
164 const QString &property)
165{
166 if (object.canConvert<QObject *>()) {
167 return doQobjectLookUp(object.value<QObject *>(), property);
168 }
169 if (object.userType() == QMetaType::QJsonDocument) {
170 const auto doc = object.toJsonDocument();
171 if (doc.isObject()) {
172 return doJsonObjectLookUp(doc.object(), property);
173 }
174 if (doc.isArray()) {
175 return doJsonArrayLookUp(doc.array(), property);
176 }
177 return QVariant();
178 }
179 if (object.userType() == QMetaType::QJsonValue) {
180 const auto val = object.toJsonValue();
181
182 switch (val.type()) {
183 case QJsonValue::Bool:
184 return val.toBool();
186 return val.toDouble();
188 return val.toString();
190 return doJsonArrayLookUp(val.toArray(), property);
192 return doJsonObjectLookUp(val.toObject(), property);
193 default:
194 return QVariant();
195 }
196 }
197 if (object.userType() == QMetaType::QJsonArray) {
198 return doJsonArrayLookUp(object.toJsonArray(), property);
199 }
200 if (object.userType() == QMetaType::QJsonObject) {
201 return doJsonObjectLookUp(object.toJsonObject(), property);
202 }
203 if (object.canConvert<QVariantList>()) {
204 auto iter = object.value<QSequentialIterable>();
205 if (property == QStringLiteral("size")
206 || property == QStringLiteral("count")) {
207 return iter.size();
208 }
209
210 auto ok = false;
211 const auto listIndex = property.toInt(&ok);
212
213 if (!ok || listIndex >= iter.size()) {
214 return QVariant();
215 }
216
217 return iter.at(listIndex);
218 }
219 if (object.canConvert<QVariantHash>() ||
220 object.canConvert<QVariantMap>()) {
221
222 auto iter = object.value<QAssociativeIterable>();
223
224 auto mappedValue = iter.value(property);
225 if (mappedValue.isValid())
226 return mappedValue;
227
228 if (property == QStringLiteral("size")
229 || property == QStringLiteral("count")) {
230 return iter.size();
231 }
232
233 if (property == QStringLiteral("items")) {
234 auto it = iter.begin();
235 const auto end = iter.end();
236 QVariantList list;
237 for (; it != end; ++it) {
238 list.push_back(QVariantList{it.key(), it.value()});
239 }
240 return list;
241 }
242
243 if (property == QStringLiteral("keys")) {
244 auto it = iter.begin();
245 const auto end = iter.end();
246 QVariantList list;
247 for (; it != end; ++it) {
248 list.push_back(it.key());
249 }
250 return list;
251 }
252
253 if (property == QStringLiteral("values")) {
254 auto it = iter.begin();
255 const auto end = iter.end();
256 QVariantList list;
257 for (; it != end; ++it) {
258 list.push_back(it.value());
259 }
260 return list;
261 }
262
263 return QVariant();
264 }
265#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
266 auto mo = QMetaType::metaObjectForType(object.userType());
267#else
268 auto mo = QMetaType(object.userType()).metaObject();
269#endif
270 if (mo) {
271 QMetaType mt(object.userType());
272 if (mt.flags().testFlag(QMetaType::IsGadget)) {
273 const auto idx = mo->indexOfProperty(property.toUtf8().constData());
274 if (idx >= 0) {
275 const auto mp = mo->property(idx);
276 if (mp.isEnumType()) {
277 MetaEnumVariable mev(mp.enumerator(), mp.readOnGadget(object.constData()).value<int>());
278 return QVariant::fromValue(mev);
279 }
280 return mp.readOnGadget(object.constData());
281 }
282
283 QMetaEnum me;
284 for (auto i = 0; i < mo->enumeratorCount(); ++i) {
285 me = mo->enumerator(i);
286
287 if (QLatin1String(me.name()) == property) {
288 MetaEnumVariable mev(me);
289 return QVariant::fromValue(mev);
290 }
291
292 const auto value = me.keyToValue(property.toLatin1().constData());
293
294 if (value < 0) {
295 continue;
296 }
297
298 MetaEnumVariable mev(me, value);
299 return QVariant::fromValue(mev);
300 }
301 }
302 }
303
304 return customTypes()->lookup(object, property);
305}
306
307bool Cutelee::MetaType::lookupAlreadyRegistered(int id)
308{
309 return customTypes()->lookupAlreadyRegistered(id);
310}
The Cutelee namespace holds all public Cutelee API.
Definition Mainpage.dox:8
QVariant value(const QVariant &key) const const
const char * constData() const const
QJsonValue at(int i) const const
int size() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
QStringList keys() const const
int size() const const
QJsonValue value(const QString &key) const const
QVariant toVariant() const const
int keyToValue(const char *key, bool *ok) const const
const char * name() const const
QMetaEnum enumerator() const const
bool isEnumType() const const
const char * name() const const
QVariant read(const QObject *object) const const
QVariant readOnGadget(const void *gadget) const const
const QMetaObject * metaObject() const const
const QMetaObject * metaObjectForType(int type)
QVariant at(int idx) const const
QString fromUtf8(const char *str, int size)
QByteArray toLatin1() const const
QByteArray toUtf8() const const
QVariant fromValue(const T &value)
int toInt(bool *ok) const const
T value() const const