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 = m_metaSequence.valueMetaType().flags().testFlag(flag: QMetaType::PointerToQObject)
114 ? ObjectSequence
115 : ValueSequence;
116 return;
117 }
118
119 QSequentialIterable iterable;
120 if (QMetaType::convert(
121 fromType: variantsType, from: d.constData(),
122 toType: QMetaType::fromType<QSequentialIterable>(), to: &iterable)) {
123 const QMetaSequence sequence = iterable.metaContainer();
124
125 if (sequence.hasSize() && sequence.canGetValueAtIndex()) {
126 // If the resulting iterable is useful for anything, use it.
127 m_metaSequence = sequence;
128 m_type = sequence.valueMetaType().flags().testFlag(flag: QMetaType::PointerToQObject)
129 ? ObjectSequence
130 : ValueSequence;
131 return;
132 }
133
134 if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) {
135 // As a last resort, try to read the contents of the container via an iterator
136 // and build a QVariantList from them.
137 QVariantList variantList;
138 for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
139 variantList.push_back(t: *it);
140 d = std::move(variantList);
141 m_type = VariantList;
142 return;
143 }
144 }
145
146 m_type = Instance;
147 return;
148}
149
150qsizetype QQmlListAccessor::count() const
151{
152 switch(m_type) {
153 case StringList:
154 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
155 return reinterpret_cast<const QStringList *>(d.constData())->size();
156 case UrlList:
157 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
158 return reinterpret_cast<const QList<QUrl> *>(d.constData())->size();
159 case VariantList:
160 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
161 return reinterpret_cast<const QVariantList *>(d.constData())->size();
162 case ObjectList:
163 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
164 return reinterpret_cast<const QList<QObject *> *>(d.constData())->size();
165 case ListProperty:
166 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
167 return reinterpret_cast<const QQmlListReference *>(d.constData())->count();
168 case ValueSequence:
169 case ObjectSequence:
170 Q_ASSERT(m_metaSequence != QMetaSequence());
171 return m_metaSequence.size(container: d.constData());
172 case Instance:
173 return 1;
174 case Integer:
175 return *reinterpret_cast<const int *>(d.constData());
176 case Invalid:
177 return 0;
178 }
179 Q_UNREACHABLE_RETURN(0);
180}
181
182QVariant QQmlListAccessor::at(qsizetype idx) const
183{
184 Q_ASSERT(idx >= 0 && idx < count());
185 switch(m_type) {
186 case StringList:
187 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
188 return QVariant::fromValue(value: reinterpret_cast<const QStringList *>(d.constData())->at(i: idx));
189 case UrlList:
190 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
191 return QVariant::fromValue(value: reinterpret_cast<const QList<QUrl> *>(d.constData())->at(i: idx));
192 case VariantList:
193 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
194 return reinterpret_cast<const QVariantList *>(d.constData())->at(i: idx);
195 case ObjectList:
196 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
197 return QVariant::fromValue(value: reinterpret_cast<const QList<QObject *> *>(d.constData())->at(i: idx));
198 case ListProperty:
199 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
200 return QVariant::fromValue(value: reinterpret_cast<const QQmlListReference *>(d.constData())->at(idx));
201 case ValueSequence:
202 case ObjectSequence: {
203 Q_ASSERT(m_metaSequence != QMetaSequence());
204 QVariant result;
205 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
206 if (valueMetaType == QMetaType::fromType<QVariant>()) {
207 m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: &result);
208 } else {
209 result = QVariant(valueMetaType);
210 m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: result.data());
211 }
212 return result;
213 }
214 case Instance:
215 return d;
216 case Integer:
217 return QVariant(idx);
218 case Invalid:
219 return QVariant();
220 }
221 Q_UNREACHABLE_RETURN(QVariant());
222}
223
224void QQmlListAccessor::set(qsizetype idx, const QVariant &value)
225{
226 Q_ASSERT(idx >= 0 && idx < count());
227 switch (m_type) {
228 case StringList:
229 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
230 (*static_cast<QStringList *>(d.data()))[idx] = value.toString();
231 break;
232 case UrlList:
233 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
234 (*static_cast<QList<QUrl> *>(d.data()))[idx] = value.value<QUrl>();
235 break;
236 case VariantList:
237 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
238 (*static_cast<QVariantList *>(d.data()))[idx] = value;
239 break;
240 case ObjectList:
241 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
242 (*static_cast<QList<QObject *> *>(d.data()))[idx] = value.value<QObject *>();
243 break;
244 case ListProperty:
245 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
246 static_cast<QQmlListReference *>(d.data())->replace(idx, value.value<QObject *>());
247 break;
248 case ValueSequence:
249 case ObjectSequence: {
250 Q_ASSERT(m_metaSequence != QMetaSequence());
251 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
252 if (valueMetaType == QMetaType::fromType<QVariant>()) {
253 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: &value);
254 } else if (valueMetaType == value.metaType()) {
255 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: value.constData());
256 } else {
257 QVariant converted = value;
258 converted.convert(type: valueMetaType);
259 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: converted.constData());
260 }
261 break;
262 }
263 case Instance:
264 d = value;
265 break;
266 case Integer:
267 break;;
268 case Invalid:
269 break;
270 }
271}
272
273bool QQmlListAccessor::isValid() const
274{
275 return m_type != Invalid;
276}
277
278QT_END_NAMESPACE
279

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