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/qstringlist.h>
9#include <QtCore/qdebug.h>
10#include <QtCore/qurl.h>
11
12// ### Remove me
13#include <private/qqmlengine_p.h>
14
15QT_BEGIN_NAMESPACE
16
17QQmlListAccessor::QQmlListAccessor()
18: m_type(Invalid)
19{
20}
21
22QQmlListAccessor::~QQmlListAccessor()
23{
24}
25
26QVariant QQmlListAccessor::list() const
27{
28 return d;
29}
30
31void QQmlListAccessor::setList(const QVariant &v)
32{
33 d = v;
34
35 // An incoming JS array as model is treated as a variant list, so we need to
36 // convert it first with toVariant().
37 QMetaType variantsType = d.metaType();
38 if (variantsType == QMetaType::fromType<QJSValue>()) {
39 d = d.value<QJSValue>().toVariant();
40 variantsType = d.metaType();
41 }
42
43 if (!d.isValid()) {
44 m_type = Invalid;
45 } else if (variantsType == QMetaType::fromType<QStringList>()) {
46 m_type = StringList;
47 } else if (variantsType == QMetaType::fromType<QList<QUrl>>()) {
48 m_type = UrlList;
49 } else if (variantsType == QMetaType::fromType<QVariantList>()) {
50 m_type = VariantList;
51 } else if (variantsType == QMetaType::fromType<QList<QObject *>>()) {
52 m_type = ObjectList;
53 } else if (variantsType.flags() & QMetaType::IsQmlList) {
54 d = QVariant::fromValue(value: QQmlListReference(d));
55 m_type = ListProperty;
56 } else if (variantsType == QMetaType::fromType<QQmlListReference>()) {
57 m_type = ListProperty;
58 } else if (variantsType.flags() & QMetaType::PointerToQObject) {
59 m_type = Instance;
60 } else if (int i = 0; [&](){bool ok = false; i = v.toInt(ok: &ok); return ok;}()) {
61 // Here we have to check for an upper limit, because down the line code might (well, will)
62 // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX:
63 // QVector<QPointer<QQuickItem>> something;
64 // something.resize(count());
65 // (See e.g. QQuickRepeater::regenerate())
66 // This will allocate data along the lines of:
67 // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize
68 // So, doing an approximate round-down-to-nice-number, we get:
69 const int upperLimit = 100 * 1000 * 1000;
70
71 if (i < 0) {
72 qWarning(msg: "Model size of %d is less than 0", i);
73 m_type = Invalid;
74 } else if (i > upperLimit) {
75 qWarning(msg: "Model size of %d is bigger than the upper limit %d", i, upperLimit);
76 m_type = Invalid;
77 } else {
78 m_type = Integer;
79 d = i;
80 }
81 } else {
82 const QQmlType type = QQmlMetaType::qmlListType(metaType: v.metaType());
83 if (type.isSequentialContainer()) {
84 m_metaSequence = type.listMetaSequence();
85 m_type = Sequence;
86 } else {
87 m_type = Instance;
88 }
89 }
90}
91
92qsizetype QQmlListAccessor::count() const
93{
94 switch(m_type) {
95 case StringList:
96 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
97 return reinterpret_cast<const QStringList *>(d.constData())->size();
98 case UrlList:
99 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
100 return reinterpret_cast<const QList<QUrl> *>(d.constData())->size();
101 case VariantList:
102 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
103 return reinterpret_cast<const QVariantList *>(d.constData())->size();
104 case ObjectList:
105 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
106 return reinterpret_cast<const QList<QObject *> *>(d.constData())->size();
107 case ListProperty:
108 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
109 return reinterpret_cast<const QQmlListReference *>(d.constData())->count();
110 case Sequence:
111 Q_ASSERT(m_metaSequence != QMetaSequence());
112 return m_metaSequence.size(container: d.constData());
113 case Instance:
114 return 1;
115 case Integer:
116 return *reinterpret_cast<const int *>(d.constData());
117 case Invalid:
118 return 0;
119 }
120 Q_UNREACHABLE_RETURN(0);
121}
122
123QVariant QQmlListAccessor::at(qsizetype idx) const
124{
125 Q_ASSERT(idx >= 0 && idx < count());
126 switch(m_type) {
127 case StringList:
128 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
129 return QVariant::fromValue(value: reinterpret_cast<const QStringList *>(d.constData())->at(i: idx));
130 case UrlList:
131 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
132 return QVariant::fromValue(value: reinterpret_cast<const QList<QUrl> *>(d.constData())->at(i: idx));
133 case VariantList:
134 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
135 return reinterpret_cast<const QVariantList *>(d.constData())->at(i: idx);
136 case ObjectList:
137 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
138 return QVariant::fromValue(value: reinterpret_cast<const QList<QObject *> *>(d.constData())->at(i: idx));
139 case ListProperty:
140 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
141 return QVariant::fromValue(value: reinterpret_cast<const QQmlListReference *>(d.constData())->at(idx));
142 case Sequence: {
143 Q_ASSERT(m_metaSequence != QMetaSequence());
144 QVariant result;
145 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
146 if (valueMetaType == QMetaType::fromType<QVariant>()) {
147 m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: &result);
148 } else {
149 result = QVariant(valueMetaType);
150 m_metaSequence.valueAtIndex(container: d.constData(), index: idx, result: result.data());
151 }
152 return result;
153 }
154 case Instance:
155 return d;
156 case Integer:
157 return QVariant(idx);
158 case Invalid:
159 return QVariant();
160 }
161 Q_UNREACHABLE_RETURN(QVariant());
162}
163
164void QQmlListAccessor::set(qsizetype idx, const QVariant &value)
165{
166 Q_ASSERT(idx >= 0 && idx < count());
167 switch (m_type) {
168 case StringList:
169 Q_ASSERT(d.metaType() == QMetaType::fromType<QStringList>());
170 (*static_cast<QStringList *>(d.data()))[idx] = value.toString();
171 break;
172 case UrlList:
173 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QUrl>>());
174 (*static_cast<QList<QUrl> *>(d.data()))[idx] = value.value<QUrl>();
175 break;
176 case VariantList:
177 Q_ASSERT(d.metaType() == QMetaType::fromType<QVariantList>());
178 (*static_cast<QVariantList *>(d.data()))[idx] = value;
179 break;
180 case ObjectList:
181 Q_ASSERT(d.metaType() == QMetaType::fromType<QList<QObject *>>());
182 (*static_cast<QList<QObject *> *>(d.data()))[idx] = value.value<QObject *>();
183 break;
184 case ListProperty:
185 Q_ASSERT(d.metaType() == QMetaType::fromType<QQmlListReference>());
186 static_cast<QQmlListReference *>(d.data())->replace(idx, value.value<QObject *>());
187 break;
188 case Sequence: {
189 Q_ASSERT(m_metaSequence != QMetaSequence());
190 const QMetaType valueMetaType = m_metaSequence.valueMetaType();
191 if (valueMetaType == QMetaType::fromType<QVariant>()) {
192 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: &value);
193 } else if (valueMetaType == value.metaType()) {
194 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: value.constData());
195 } else {
196 QVariant converted = value;
197 converted.convert(type: valueMetaType);
198 m_metaSequence.setValueAtIndex(container: d.data(), index: idx, value: converted.constData());
199 }
200 break;
201 }
202 case Instance:
203 d = value;
204 break;
205 case Integer:
206 break;;
207 case Invalid:
208 break;
209 }
210}
211
212bool QQmlListAccessor::isValid() const
213{
214 return m_type != Invalid;
215}
216
217QT_END_NAMESPACE
218

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