1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmllistaccessor_p.h"
5
6#include <private/qqmlmetatype_p.h>
7
8#include <QtCore/qdebug.h>
9#include <QtCore/qsequentialiterable.h>
10#include <QtCore/qstringlist.h>
11#include <QtCore/qurl.h>
12
13QT_BEGIN_NAMESPACE
14
15QQmlListAccessor::QQmlListAccessor()
16: m_type(Invalid)
17{
18}
19
20QQmlListAccessor::~QQmlListAccessor()
21{
22}
23
24QVariant QQmlListAccessor::list() const
25{
26 return d;
27}
28
29void QQmlListAccessor::setList(const QVariant &v)
30{
31 d = v;
32
33 // An incoming JS array as model is treated as a variant list, so we need to
34 // convert it first with toVariant().
35 QMetaType variantsType = d.metaType();
36 if (variantsType == QMetaType::fromType<QJSValue>()) {
37 d = d.value<QJSValue>().toVariant();
38 variantsType = d.metaType();
39 }
40
41 if (!d.isValid()) {
42 m_type = Invalid;
43 return;
44 }
45
46 if (variantsType == QMetaType::fromType<QStringList>()) {
47 m_type = StringList;
48 return;
49 }
50
51 if (variantsType == QMetaType::fromType<QList<QUrl>>()) {
52 m_type = UrlList;
53 return;
54 }
55
56 if (variantsType == QMetaType::fromType<QVariantList>()) {
57 m_type = VariantList;
58 return;
59 }
60
61 if (variantsType == QMetaType::fromType<QList<QObject *>>()) {
62 m_type = ObjectList;
63 return;
64 }
65
66 if (variantsType.flags() & QMetaType::IsQmlList) {
67 d = QVariant::fromValue(value: QQmlListReference(d));
68 m_type = ListProperty;
69 return;
70 }
71
72 if (variantsType == QMetaType::fromType<QQmlListReference>()) {
73 m_type = ListProperty;
74 return;
75 }
76
77 if (variantsType.flags() & QMetaType::PointerToQObject) {
78 m_type = Instance;
79 return;
80 }
81
82 if (int i = 0; [&](){bool ok = false; i = v.toInt(ok: &ok); return ok;}()) {
83 // Here we have to check for an upper limit, because down the line code might (well, will)
84 // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX:
85 // QVector<QPointer<QQuickItem>> something;
86 // something.resize(count());
87 // (See e.g. QQuickRepeater::regenerate())
88 // This will allocate data along the lines of:
89 // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize
90 // So, doing an approximate round-down-to-nice-number, we get:
91 const int upperLimit = 100 * 1000 * 1000;
92
93 if (i < 0) {
94 qWarning(msg: "Model size of %d is less than 0", i);
95 m_type = Invalid;
96 return;
97 }
98
99 if (i > upperLimit) {
100 qWarning(msg: "Model size of %d is bigger than the upper limit %d", i, upperLimit);
101 m_type = Invalid;
102 return;
103 }
104
105 m_type = Integer;
106 d = i;
107 return;
108 }
109
110 const QQmlType type = QQmlMetaType::qmlListType(metaType: variantsType);
111 if (type.isSequentialContainer()) {
112 m_metaSequence = type.listMetaSequence();
113 m_type = Sequence;
114 return;
115 }
116
117 QSequentialIterable iterable;
118 if (QMetaType::convert(
119 fromType: variantsType, from: d.constData(),
120 toType: QMetaType::fromType<QSequentialIterable>(), to: &iterable)) {
121 const QMetaSequence sequence = iterable.metaContainer();
122
123 if (sequence.hasSize() && sequence.canGetValueAtIndex()) {
124 // If the resulting iterable is useful for anything, use it.
125 m_metaSequence = sequence;
126 m_type = Sequence;
127 return;
128 }
129
130 if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
131 // As a last resort, try to read the contents of the container via an iterator
132 // and build a QVariantList from them.
133 QVariantList variantList;
134 for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
135 variantList.push_back(t: *it);
136 d = std::move(variantList);
137 m_type = VariantList;
138 return;
139 }
140 }
141
142 m_type = Instance;
143 return;
144}
145
146qsizetype QQmlListAccessor::count() const
147{
148 switch(m_type) {
149 case StringList:
150 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
151 return reinterpret_cast<const QStringList *>(d.constData())->size();
152 case UrlList:
153 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
154 return reinterpret_cast<const QList<QUrl> *>(d.constData())->size();
155 case VariantList:
156 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
157 return reinterpret_cast<const QVariantList *>(d.constData())->size();
158 case ObjectList:
159 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
160 return reinterpret_cast<const QList<QObject *> *>(d.constData())->size();
161 case ListProperty:
162 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
163 return reinterpret_cast<const QQmlListReference *>(d.constData())->count();
164 case Sequence:
165 Q_ASSERT(m_metaSequence != QMetaSequence());
166 return m_metaSequence.size(container: d.constData());
167 case Instance:
168 return 1;
169 case Integer:
170 return *reinterpret_cast<const int *>(d.constData());
171 case Invalid:
172 return 0;
173 }
174 Q_UNREACHABLE_RETURN(0);
175}
176
177QVariant QQmlListAccessor::at(qsizetype idx) const
178{
179 Q_ASSERT(idx >= 0 && idx < count());
180 switch(m_type) {
181 case StringList:
182 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
183 return QVariant::fromValue(value: reinterpret_cast<const QStringList *>(d.constData())->at(i: idx));
184 case UrlList:
185 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
186 return QVariant::fromValue(value: reinterpret_cast<const QList<QUrl> *>(d.constData())->at(i: idx));
187 case VariantList:
188 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
189 return reinterpret_cast<const QVariantList *>(d.constData())->at(i: idx);
190 case ObjectList:
191 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
192 return QVariant::fromValue(value: reinterpret_cast<const QList<QObject *> *>(d.constData())->at(i: idx));
193 case ListProperty:
194 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
195 return QVariant::fromValue(value: reinterpret_cast<const QQmlListReference *>(d.constData())->at(idx));
196 case Sequence: {
197 Q_ASSERT(m_metaSequence != QMetaSequence());
198 QVariant result;
199 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
200 if (valueMetaType == QMetaType::fromType<QVariant>()) {
201 m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: &result);
202 } else {
203 result = QVariant(valueMetaType);
204 m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: result.data());
205 }
206 return result;
207 }
208 case Instance:
209 return d;
210 case Integer:
211 return QVariant(idx);
212 case Invalid:
213 return QVariant();
214 }
215 Q_UNREACHABLE_RETURN(QVariant());
216}
217
218void QQmlListAccessor::set(qsizetype idx, const QVariant &value)
219{
220 Q_ASSERT(idx >= 0 && idx < count());
221 switch (m_type) {
222 case StringList:
223 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
224 (*static_cast<QStringList *>(d.data()))[idx] = value.toString();
225 break;
226 case UrlList:
227 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
228 (*static_cast<QList<QUrl> *>(d.data()))[idx] = value.value<QUrl>();
229 break;
230 case VariantList:
231 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
232 (*static_cast<QVariantList *>(d.data()))[idx] = value;
233 break;
234 case ObjectList:
235 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
236 (*static_cast<QList<QObject *> *>(d.data()))[idx] = value.value<QObject *>();
237 break;
238 case ListProperty:
239 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
240 static_cast<QQmlListReference *>(d.data())->replace(idx, value.value<QObject *>());
241 break;
242 case Sequence: {
243 Q_ASSERT(m_metaSequence != QMetaSequence());
244 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
245 if (valueMetaType == QMetaType::fromType<QVariant>()) {
246 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: &value);
247 } else if (valueMetaType == value.metaType()) {
248 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: value.constData());
249 } else {
250 QVariant converted = value;
251 converted.convert(type: valueMetaType);
252 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: converted.constData());
253 }
254 break;
255 }
256 case Instance:
257 d = value;
258 break;
259 case Integer:
260 break;;
261 case Invalid:
262 break;
263 }
264}
265
266bool QQmlListAccessor::isValid() const
267{
268 return m_type != Invalid;
269}
270
271QT_END_NAMESPACE
272

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/qmlmodels/qqmllistaccessor.cpp