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 
35 using namespace Cutelee;
36 
37 Q_GLOBAL_STATIC(CustomTypeRegistry, customTypes)
38 
39 void Cutelee::MetaType::internalLock() { return customTypes()->mutex.lock(); }
40 
41 void Cutelee::MetaType::internalUnlock()
42 {
43  return customTypes()->mutex.unlock();
44 }
45 
46 void 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 
54 static 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 
115 static 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 
131 static 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 
163 QVariant 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  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 
307 bool Cutelee::MetaType::lookupAlreadyRegistered(int id)
308 {
309  return customTypes()->lookupAlreadyRegistered(id);
310 }
QVariant readOnGadget(const void *gadget) const const
QStringList keys() const const
const QMetaObject * metaObjectForType(int type)
int size() const const
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:7
QJsonObject::const_iterator constEnd() const const
bool isEnumType() const const
void reserve(int alloc)
int size() const const
T value() const const
const char * name() const const
QJsonObject::const_iterator constBegin() const const
QString fromUtf8(const char *str, int size)
QJsonValue at(int i) const const
int size() const const
const char * constData() const const
QVariant read(const QObject *object) const const
QVariant toVariant() const const
int keyToValue(const char *key, bool *ok) const const
QMetaEnum enumerator() const const
QVariant fromValue(const T &value)
const char * name() const const
void push_back(const QJsonValue &value)
QByteArray toLatin1() const const
QJsonValue value(const QString &key) const const
QVariant value(const QVariant &key) const const
const QMetaObject * metaObject() const const
QByteArray toUtf8() const const