Cutelee 6.2.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 if (property == QStringLiteral("size")
225 || property == QStringLiteral("count")) {
226 return iter.size();
227 }
228
229 if (property == QStringLiteral("items")) {
230 auto it = iter.begin();
231 const auto end = iter.end();
232 QVariantList list;
233 for (; it != end; ++it) {
234 list.push_back(QVariantList{it.key(), it.value()});
235 }
236 return list;
237 }
238
239 if (property == QStringLiteral("keys")) {
240 auto it = iter.begin();
241 const auto end = iter.end();
242 QVariantList list;
243 for (; it != end; ++it) {
244 list.push_back(it.key());
245 }
246 return list;
247 }
248
249 if (property == QStringLiteral("values")) {
250 auto it = iter.begin();
251 const auto end = iter.end();
252 QVariantList list;
253 for (; it != end; ++it) {
254 list.push_back(it.value());
255 }
256 return list;
257 }
258
259 auto mappedValue = iter.value(property);
260 if (mappedValue.isValid())
261 return mappedValue;
262
263 return QVariant();
264 }
265 auto mo = QMetaType(object.userType()).metaObject();
266 if (mo) {
267 QMetaType mt(object.userType());
268 if (mt.flags().testFlag(QMetaType::IsGadget)) {
269 const auto idx = mo->indexOfProperty(property.toUtf8().constData());
270 if (idx >= 0) {
271 const auto mp = mo->property(idx);
272 if (mp.isEnumType()) {
273 MetaEnumVariable mev(mp.enumerator(), mp.readOnGadget(object.constData()).value<int>());
274 return QVariant::fromValue(mev);
275 }
276 return mp.readOnGadget(object.constData());
277 }
278
279 QMetaEnum me;
280 for (auto i = 0; i < mo->enumeratorCount(); ++i) {
281 me = mo->enumerator(i);
282
283 if (QLatin1String(me.name()) == property) {
284 MetaEnumVariable mev(me);
285 return QVariant::fromValue(mev);
286 }
287
288 const auto value = me.keyToValue(property.toLatin1().constData());
289
290 if (value < 0) {
291 continue;
292 }
293
294 MetaEnumVariable mev(me, value);
295 return QVariant::fromValue(mev);
296 }
297 }
298 }
299
300 return customTypes()->lookup(object, property);
301}
302
303bool Cutelee::MetaType::lookupAlreadyRegistered(int id)
304{
305 return customTypes()->lookupAlreadyRegistered(id);
306}
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(qsizetype i) const const
qsizetype size() const const
QJsonObject::const_iterator constBegin() const const
QJsonObject::const_iterator constEnd() const const
QStringList keys() const const
qsizetype size() const const
QJsonValue value(QLatin1StringView 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
QVariant at(qsizetype idx) const const
QString fromUtf8(QByteArrayView str)
QByteArray toLatin1() const const
QByteArray toUtf8() const const
QVariant fromValue(T &&value)
int toInt(bool *ok) const const
T value() const &const