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
78 QMetaProperty mp;
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();
185 case QJsonValue::Double:
186 return val.toDouble();
187 case QJsonValue::String:
188 return val.toString();
189 case QJsonValue::Array:
190 return doJsonArrayLookUp(val.toArray(), property);
191 case QJsonValue::Object:
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
221 auto iter = object.value<QAssociativeIterable>();
222
223 auto mappedValue = iter.value(property);
224 if (mappedValue.isValid())
225 return mappedValue;
226
227 if (property == QStringLiteral("size")
228 || property == QStringLiteral("count")) {
229 return iter.size();
230 }
231
232 if (property == QStringLiteral("items")) {
233 auto it = iter.begin();
234 const auto end = iter.end();
235 QVariantList list;
236 for (; it != end; ++it) {
237 list.push_back(QVariantList{it.key(), it.value()});
238 }
239 return list;
240 }
241
242 if (property == QStringLiteral("keys")) {
243 auto it = iter.begin();
244 const auto end = iter.end();
245 QVariantList list;
246 for (; it != end; ++it) {
247 list.push_back(it.key());
248 }
249 return list;
250 }
251
252 if (property == QStringLiteral("values")) {
253 auto it = iter.begin();
254 const auto end = iter.end();
255 QVariantList list;
256 for (; it != end; ++it) {
257 list.push_back(it.value());
258 }
259 return list;
260 }
261
262 return QVariant();
263 }
264#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
265 auto mo = QMetaType::metaObjectForType(object.userType());
266#else
267 auto mo = QMetaType(object.userType()).metaObject();
268#endif
269 if (mo) {
270 QMetaType mt(object.userType());
271 if (mt.flags().testFlag(QMetaType::IsGadget)) {
272 const auto idx = mo->indexOfProperty(property.toUtf8().constData());
273 if (idx >= 0) {
274 const auto mp = mo->property(idx);
275 if (mp.isEnumType()) {
276 MetaEnumVariable mev(mp.enumerator(), mp.readOnGadget(object.constData()).value<int>());
277 return QVariant::fromValue(mev);
278 }
279 return mp.readOnGadget(object.constData());
280 }
281
282 QMetaEnum me;
283 for (auto i = 0; i < mo->enumeratorCount(); ++i) {
284 me = mo->enumerator(i);
285
286 if (QLatin1String(me.name()) == property) {
287 MetaEnumVariable mev(me);
288 return QVariant::fromValue(mev);
289 }
290
291 const auto value = me.keyToValue(property.toLatin1().constData());
292
293 if (value < 0) {
294 continue;
295 }
296
297 MetaEnumVariable mev(me, value);
298 return QVariant::fromValue(mev);
299 }
300 }
301 }
302
303 return customTypes()->lookup(object, property);
304}
305
306bool Cutelee::MetaType::lookupAlreadyRegistered(int id)
307{
308 return customTypes()->lookupAlreadyRegistered(id);
309}
The Cutelee namespace holds all public Cutelee API.
Definition Mainpage.dox:8