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 
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 
306 bool Cutelee::MetaType::lookupAlreadyRegistered(int id)
307 {
308  return customTypes()->lookupAlreadyRegistered(id);
309 }
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:8