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 
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  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 
303 bool Cutelee::MetaType::lookupAlreadyRegistered(int id)
304 {
305  return customTypes()->lookupAlreadyRegistered(id);
306 }
QVariant readOnGadget(const void *gadget) const const
QStringList keys() const const
qsizetype size() const const
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:7
const_iterator constEnd() const const
bool isEnumType() const const
void reserve(qsizetype size)
QVariant fromValue(T &&value)
qsizetype size() const const
T value() const const
const char * name() const const
QString fromUtf8(QByteArrayView str)
qsizetype size() const const
const_iterator constBegin() const const
QJsonValue at(qsizetype i) 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
QJsonValue value(QLatin1StringView key) const const
const char * name() const const
void push_back(const QJsonValue &value)
QByteArray toLatin1() const const
const QMetaObject * metaObject() const const
QByteArray toUtf8() const const