1 | // Copyright (C) 2023 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 | #ifndef QJSLIST_H |
5 | #define QJSLIST_H |
6 | |
7 | #include <QtQml/qtqmlglobal.h> |
8 | #include <QtQml/qqmllist.h> |
9 | #include <QtQml/qjsengine.h> |
10 | #include <QtCore/qobject.h> |
11 | #include <QtCore/qstring.h> |
12 | |
13 | #include <algorithm> |
14 | |
15 | // |
16 | // W A R N I N G |
17 | // ------------- |
18 | // |
19 | // This file is not part of the Qt API. It exists purely as an |
20 | // implementation detail. This header file may change from version to |
21 | // version. It will be kept compatible with the intended usage by |
22 | // code generated using qmlcachegen. |
23 | // |
24 | // We mean it. |
25 | // |
26 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | struct QJSListIndexClamp |
30 | { |
31 | static qsizetype clamp(int start, qsizetype max, qsizetype min = 0) |
32 | { |
33 | Q_ASSERT(min >= 0); |
34 | Q_ASSERT(min <= max); |
35 | return std::clamp(val: start < 0 ? max + qsizetype(start) : qsizetype(start), lo: min, hi: max); |
36 | } |
37 | }; |
38 | |
39 | template<typename List, typename Value = typename List::value_type> |
40 | struct QJSList : private QJSListIndexClamp |
41 | { |
42 | Q_DISABLE_COPY_MOVE(QJSList) |
43 | |
44 | QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} |
45 | |
46 | bool includes(const Value &value) const |
47 | { |
48 | return std::find(m_list->cbegin(), m_list->cend(), value) != m_list->cend(); |
49 | } |
50 | |
51 | bool includes(const Value &value, int start) const |
52 | { |
53 | return std::find(m_list->cbegin() + clamp(start, max: m_list->size()), m_list->cend(), value) |
54 | != m_list->cend(); |
55 | } |
56 | |
57 | QString join(const QString &separator = QStringLiteral("," )) const |
58 | { |
59 | QString result; |
60 | bool atBegin = true; |
61 | std::for_each(m_list->cbegin(), m_list->cend(), [&](const Value &value) { |
62 | if (atBegin) |
63 | atBegin = false; |
64 | else |
65 | result += separator; |
66 | result += m_engine->coerceValue<Value, QString>(value); |
67 | }); |
68 | return result; |
69 | } |
70 | |
71 | List slice() const |
72 | { |
73 | return *m_list; |
74 | } |
75 | List slice(int start) const |
76 | { |
77 | List result; |
78 | std::copy(m_list->cbegin() + clamp(start, max: m_list->size()), m_list->cend(), |
79 | std::back_inserter(result)); |
80 | return result; |
81 | } |
82 | List slice(int start, int end) const |
83 | { |
84 | const qsizetype size = m_list->size(); |
85 | const qsizetype clampedStart = clamp(start, max: size); |
86 | const qsizetype clampedEnd = clamp(start: end, max: size, min: clampedStart); |
87 | |
88 | List result; |
89 | std::copy(m_list->cbegin() + clampedStart, m_list->cbegin() + clampedEnd, |
90 | std::back_inserter(result)); |
91 | return result; |
92 | } |
93 | |
94 | int indexOf(const Value &value) const |
95 | { |
96 | const auto begin = m_list->cbegin(); |
97 | const auto end = m_list->cend(); |
98 | const auto it = std::find(begin, end, value); |
99 | if (it == end) |
100 | return -1; |
101 | const qsizetype result = it - begin; |
102 | Q_ASSERT(result >= 0); |
103 | return result > std::numeric_limits<int>::max() ? -1 : int(result); |
104 | } |
105 | int indexOf(const Value &value, int start) const |
106 | { |
107 | const auto begin = m_list->cbegin(); |
108 | const auto end = m_list->cend(); |
109 | const auto it = std::find(begin + clamp(start, max: m_list->size()), end, value); |
110 | if (it == end) |
111 | return -1; |
112 | const qsizetype result = it - begin; |
113 | Q_ASSERT(result >= 0); |
114 | return result > std::numeric_limits<int>::max() ? -1 : int(result); |
115 | } |
116 | |
117 | int lastIndexOf(const Value &value) const |
118 | { |
119 | const auto begin = std::make_reverse_iterator(m_list->cend()); |
120 | const auto end = std::make_reverse_iterator(m_list->cbegin()); |
121 | const auto it = std::find(begin, end, value); |
122 | const qsizetype result = (end - it) - 1; |
123 | return result > std::numeric_limits<int>::max() ? -1 : int(result); |
124 | } |
125 | int lastIndexOf(const Value &value, int start) const |
126 | { |
127 | const qsizetype size = m_list->size(); |
128 | if (size == 0) |
129 | return -1; |
130 | |
131 | // Construct a one-past-end iterator as input. |
132 | const qsizetype clampedStart = std::min(a: clamp(start, max: size), b: size - 1); |
133 | const auto begin = std::make_reverse_iterator(m_list->cbegin() + clampedStart + 1); |
134 | |
135 | const auto end = std::make_reverse_iterator(m_list->cbegin()); |
136 | const auto it = std::find(begin, end, value); |
137 | const qsizetype result = (end - it) - 1; |
138 | return result > std::numeric_limits<int>::max() ? -1 : int(result); |
139 | } |
140 | |
141 | QString toString() const { return join(); } |
142 | |
143 | private: |
144 | List *m_list = nullptr; |
145 | QJSEngine *m_engine = nullptr; |
146 | }; |
147 | |
148 | template<> |
149 | struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClamp |
150 | { |
151 | Q_DISABLE_COPY_MOVE(QJSList) |
152 | |
153 | QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} |
154 | |
155 | bool includes(const QObject *value) const |
156 | { |
157 | if (!m_list->count || !m_list->at) |
158 | return false; |
159 | |
160 | const qsizetype size = m_list->count(m_list); |
161 | for (qsizetype i = 0; i < size; ++i) { |
162 | if (m_list->at(m_list, i) == value) |
163 | return true; |
164 | } |
165 | |
166 | return false; |
167 | } |
168 | bool includes(const QObject *value, int start) const |
169 | { |
170 | if (!m_list->count || !m_list->at) |
171 | return false; |
172 | |
173 | const qsizetype size = m_list->count(m_list); |
174 | for (qsizetype i = clamp(start, max: size); i < size; ++i) { |
175 | if (m_list->at(m_list, i) == value) |
176 | return true; |
177 | } |
178 | |
179 | return false; |
180 | } |
181 | |
182 | QString join(const QString &separator = QStringLiteral("," )) const |
183 | { |
184 | if (!m_list->count || !m_list->at) |
185 | return QString(); |
186 | |
187 | QString result; |
188 | for (qsizetype i = 0, end = m_list->count(m_list); i < end; ++i) { |
189 | if (i != 0) |
190 | result += separator; |
191 | result += m_engine->coerceValue<QObject *, QString>(from: m_list->at(m_list, i)); |
192 | } |
193 | |
194 | return result; |
195 | } |
196 | |
197 | QObjectList slice() const |
198 | { |
199 | return m_list->toList<QObjectList>(); |
200 | } |
201 | QObjectList slice(int start) const |
202 | { |
203 | if (!m_list->count || !m_list->at) |
204 | return QObjectList(); |
205 | |
206 | const qsizetype size = m_list->count(m_list); |
207 | const qsizetype clampedStart = clamp(start, max: size); |
208 | QObjectList result; |
209 | result.reserve(asize: size - clampedStart); |
210 | for (qsizetype i = clampedStart; i < size; ++i) |
211 | result.append(t: m_list->at(m_list, i)); |
212 | return result; |
213 | } |
214 | QObjectList slice(int start, int end) const |
215 | { |
216 | if (!m_list->count || !m_list->at) |
217 | return QObjectList(); |
218 | |
219 | const qsizetype size = m_list->count(m_list); |
220 | const qsizetype clampedStart = clamp(start, max: size); |
221 | const qsizetype clampedEnd = clamp(start: end, max: size, min: clampedStart); |
222 | QObjectList result; |
223 | result.reserve(asize: clampedEnd - clampedStart); |
224 | for (qsizetype i = clampedStart; i < clampedEnd; ++i) |
225 | result.append(t: m_list->at(m_list, i)); |
226 | return result; |
227 | } |
228 | |
229 | int indexOf(const QObject *value) const |
230 | { |
231 | if (!m_list->count || !m_list->at) |
232 | return -1; |
233 | |
234 | const qsizetype end |
235 | = std::min(a: m_list->count(m_list), b: qsizetype(std::numeric_limits<int>::max())); |
236 | for (qsizetype i = 0; i < end; ++i) { |
237 | if (m_list->at(m_list, i) == value) |
238 | return int(i); |
239 | } |
240 | return -1; |
241 | } |
242 | int indexOf(const QObject *value, int start) const |
243 | { |
244 | if (!m_list->count || !m_list->at) |
245 | return -1; |
246 | |
247 | const qsizetype size = m_list->count(m_list); |
248 | for (qsizetype i = clamp(start, max: size), |
249 | end = std::min(a: size, b: qsizetype(std::numeric_limits<int>::max())); |
250 | i < end; ++i) { |
251 | if (m_list->at(m_list, i) == value) |
252 | return int(i); |
253 | } |
254 | return -1; |
255 | } |
256 | |
257 | int lastIndexOf(const QObject *value) const |
258 | { |
259 | if (!m_list->count || !m_list->at) |
260 | return -1; |
261 | |
262 | for (qsizetype i = m_list->count(m_list) - 1; i >= 0; --i) { |
263 | if (m_list->at(m_list, i) == value) |
264 | return i > std::numeric_limits<int>::max() ? -1 : int(i); |
265 | } |
266 | return -1; |
267 | } |
268 | int lastIndexOf(const QObject *value, int start) const |
269 | { |
270 | if (!m_list->count || !m_list->at) |
271 | return -1; |
272 | |
273 | const qsizetype size = m_list->count(m_list); |
274 | if (size == 0) |
275 | return -1; |
276 | |
277 | qsizetype clampedStart = std::min(a: clamp(start, max: size), b: size - 1); |
278 | for (qsizetype i = clampedStart; i >= 0; --i) { |
279 | if (m_list->at(m_list, i) == value) |
280 | return i > std::numeric_limits<int>::max() ? -1 : int(i); |
281 | } |
282 | return -1; |
283 | } |
284 | |
285 | QString toString() const { return join(); } |
286 | |
287 | private: |
288 | QQmlListProperty<QObject> *m_list = nullptr; |
289 | QJSEngine *m_engine = nullptr; |
290 | }; |
291 | |
292 | QT_END_NAMESPACE |
293 | |
294 | #endif // QJSLIST_H |
295 | |