1// Copyright (C) 2017 Ford Motor Company
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 QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
5#define QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qremoteobjectabstractitemmodeltypes_p.h"
19#include "qremoteobjectabstractitemmodelreplica.h"
20#include "qremoteobjectreplica.h"
21#include "qremoteobjectpendingcall.h"
22#include <list>
23#include <unordered_map>
24#include <unordered_set>
25
26QT_BEGIN_NAMESPACE
27
28namespace {
29 const int DefaultNodesCacheSize = 1000;
30}
31
32struct CacheEntry
33{
34 QHash<int, QVariant> data;
35 Qt::ItemFlags flags;
36
37 explicit CacheEntry()
38 : flags(Qt::NoItemFlags)
39 {}
40};
41
42using CachedRowEntry = QList<CacheEntry>;
43
44template <class Key, class Value>
45struct LRUCache
46{
47 typedef std::pair<Key, Value*> Pair;
48 std::list<Pair> cachedItems;
49 typedef typename std::list<Pair>::iterator CacheIterator;
50 std::unordered_map<Key, CacheIterator> cachedItemsMap;
51 size_t cacheSize;
52
53 explicit LRUCache()
54 {
55 bool ok;
56 cacheSize = qEnvironmentVariableIntValue(varName: "QTRO_NODES_CACHE_SIZE" , ok: &ok);
57 if (!ok)
58 cacheSize = DefaultNodesCacheSize;
59 }
60
61 ~LRUCache()
62 {
63 clear();
64 }
65
66 inline void cleanCache()
67 {
68 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
69
70 auto it = cachedItems.rbegin();
71 while (cachedItemsMap.size() > cacheSize) {
72 // Do not trash elements with children
73 // Workaround QTreeView bugs which caches the children indexes for very long time
74 while (it->second->hasChildren && it != cachedItems.rend())
75 ++it;
76
77 if (it == cachedItems.rend())
78 break;
79
80 cachedItemsMap.erase(it->first);
81 delete it->second;
82 cachedItems.erase((++it).base());
83 }
84 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
85 }
86
87 void setCacheSize(size_t rootCacheSize)
88 {
89 cacheSize = rootCacheSize;
90 cleanCache();
91 cachedItemsMap.reserve(rootCacheSize);
92 }
93
94 void changeKeys(Key key, Key delta) {
95 std::vector<std::pair<Key, CacheIterator>> changed;
96 auto it = cachedItemsMap.begin();
97 while (it != cachedItemsMap.end()) {
98 if (it->first >= key) {
99 changed.emplace_back(it->first + delta, it->second);
100 it->second->first += delta;
101 it = cachedItemsMap.erase(it);
102 } else {
103 ++it;
104 }
105 }
106 for (auto pair : changed)
107 cachedItemsMap[pair.first] = pair.second;
108 }
109
110 void insert(Key key, Value *value)
111 {
112 changeKeys(key, delta: 1);
113 ensure(key, value);
114 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
115 }
116
117 void ensure(Key key, Value *value)
118 {
119 cachedItems.emplace_front(key, value);
120 cachedItemsMap[key] = cachedItems.begin();
121 cleanCache();
122 }
123
124 void remove(Key key)
125 {
126 auto it = cachedItemsMap.find(key);
127 if (it != cachedItemsMap.end()) {
128 delete it->second->second;
129 cachedItems.erase(it->second);
130 cachedItemsMap.erase(it);
131 }
132 changeKeys(key, delta: -1);
133 Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
134 }
135
136 Value *get(Key key)
137 {
138 auto it = cachedItemsMap.find(key);
139 if (it == cachedItemsMap.end())
140 return nullptr;
141
142 // Move the accessed item to front
143 cachedItems.splice(cachedItems.begin(), cachedItems, it->second);
144 Q_ASSERT(it->second->first == key);
145 return it->second->second;
146 }
147
148 Key find(Value *val)
149 {
150 for (auto it = cachedItemsMap.begin(); it != cachedItemsMap.end(); ++it) {
151 if (it->second->second == val)
152 return it->first;
153 }
154 Q_ASSERT_X(false, __FUNCTION__, "Value not found");
155 return Key{};
156 }
157
158 bool exists(Value *val)
159 {
160 for (const auto &pair : cachedItems)
161 if (pair.second == val)
162 return true;
163
164 return false;
165 }
166
167 bool exists(Key key)
168 {
169 return cachedItemsMap.find(key) != cachedItemsMap.end();
170 }
171
172 size_t size()
173 {
174 return cachedItemsMap.size();
175 }
176
177 void clear()
178 {
179 for (const auto &pair : cachedItems)
180 delete pair.second;
181 cachedItems.clear();
182 cachedItemsMap.clear();
183 }
184};
185
186class QAbstractItemModelReplicaImplementation;
187struct CacheData
188{
189 QAbstractItemModelReplicaImplementation *replicaModel;
190 CacheData *parent;
191 CachedRowEntry cachedRowEntry;
192
193 bool hasChildren;
194 LRUCache<int, CacheData> children;
195 int columnCount;
196 int rowCount;
197
198 explicit CacheData(QAbstractItemModelReplicaImplementation *model, CacheData *parentItem = nullptr);
199
200 ~CacheData();
201
202 void ensureChildren(int start, int end)
203 {
204 for (int i = start; i <= end; ++i)
205 if (!children.exists(key: i))
206 children.ensure(key: i, value: new CacheData(replicaModel, this));
207 }
208
209 void insertChildren(int start, int end) {
210 Q_ASSERT_X(start >= 0 && start <= end, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2")).arg(start).arg(end)));
211 for (int i = start; i <= end; ++i) {
212 auto cacheData = new CacheData(replicaModel, this);
213 cacheData->columnCount = columnCount;
214 children.insert(key: i, value: cacheData);
215 ++rowCount;
216 }
217 if (rowCount)
218 hasChildren = true;
219 }
220 void removeChildren(int start, int end) {
221 Q_ASSERT_X(start >= 0 && start <= end && end < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2 < %3")).arg(start).arg(end).arg(rowCount)));
222 for (int i = end; i >= start; --i) {
223 children.remove(key: i);
224 --rowCount;
225 }
226 hasChildren = rowCount;
227 }
228 void clear() {
229 cachedRowEntry.clear();
230 children.clear();
231 hasChildren = false;
232 columnCount = 0;
233 rowCount = 0;
234 }
235};
236
237struct RequestedData
238{
239 QtPrivate::IndexList start;
240 QtPrivate::IndexList end;
241 QList<int> roles;
242};
243
244struct RequestedHeaderData
245{
246 int role;
247 int section;
248 Qt::Orientation orientation;
249};
250
251class SizeWatcher : public QRemoteObjectPendingCallWatcher
252{
253 Q_OBJECT
254public:
255 SizeWatcher(QtPrivate::IndexList _parentList, const QRemoteObjectPendingReply<QSize> &reply)
256 : QRemoteObjectPendingCallWatcher(reply),
257 parentList(_parentList) {}
258 QtPrivate::IndexList parentList;
259};
260
261class RowWatcher : public QRemoteObjectPendingCallWatcher
262{
263 Q_OBJECT
264public:
265 RowWatcher(QtPrivate::IndexList _start, QtPrivate::IndexList _end, QList<int> _roles, const QRemoteObjectPendingReply<QtPrivate::DataEntries> &reply)
266 : QRemoteObjectPendingCallWatcher(reply),
267 start(_start),
268 end(_end),
269 roles(_roles) {}
270 QtPrivate::IndexList start, end;
271 QList<int> roles;
272};
273
274class HeaderWatcher : public QRemoteObjectPendingCallWatcher
275{
276 Q_OBJECT
277public:
278 HeaderWatcher(QList<Qt::Orientation> _orientations, QList<int> _sections, QList<int> _roles, const QRemoteObjectPendingReply<QVariantList> &reply)
279 : QRemoteObjectPendingCallWatcher(reply),
280 orientations(_orientations),
281 sections(_sections),
282 roles(_roles) {}
283 QList<Qt::Orientation> orientations;
284 QList<int> sections, roles;
285};
286
287class QAbstractItemModelReplicaImplementation : public QRemoteObjectReplica
288{
289 Q_OBJECT
290 //TODO Use an input name for the model on the Replica side
291 Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "ServerModelAdapter")
292 Q_PROPERTY(QList<int> availableRoles READ availableRoles NOTIFY availableRolesChanged)
293 Q_PROPERTY(QIntHash roleNames READ roleNames)
294public:
295 QAbstractItemModelReplicaImplementation();
296 QAbstractItemModelReplicaImplementation(QRemoteObjectNode *node, const QString &name);
297 ~QAbstractItemModelReplicaImplementation() override;
298 void initialize() override;
299 static void registerMetatypes();
300
301 inline const QList<int> &availableRoles() const
302 {
303 if (m_availableRoles.isEmpty())
304 m_availableRoles = propAsVariant(i: 0).value<QList<int>>();
305 return m_availableRoles;
306 }
307
308 QHash<int, QByteArray> roleNames() const
309 {
310 QIntHash roles = propAsVariant(i: 1).value<QIntHash>();
311 return roles;
312 }
313
314 void setModel(QAbstractItemModelReplica *model);
315 bool clearCache(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles);
316
317Q_SIGNALS:
318 void availableRolesChanged();
319 void dataChanged(QtPrivate::IndexList topLeft, QtPrivate::IndexList bottomRight, QList<int> roles);
320 void rowsInserted(QtPrivate::IndexList parent, int first, int last);
321 void rowsRemoved(QtPrivate::IndexList parent, int first, int last);
322 void rowsMoved(QtPrivate::IndexList parent, int start, int end, QtPrivate::IndexList destination, int row);
323 void currentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
324 void modelReset();
325 void headerDataChanged(Qt::Orientation,int,int);
326 void columnsInserted(QtPrivate::IndexList parent, int first, int last);
327 void layoutChanged(QtPrivate::IndexList parents, QAbstractItemModel::LayoutChangeHint hint);
328public Q_SLOTS:
329 QRemoteObjectPendingReply<QSize> replicaSizeRequest(QtPrivate::IndexList parentList)
330 {
331 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaSizeRequest(QtPrivate::IndexList)");
332 QVariantList __repc_args;
333 __repc_args << QVariant::fromValue(value: parentList);
334 return QRemoteObjectPendingReply<QSize>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
335 }
336 QRemoteObjectPendingReply<QtPrivate::DataEntries> replicaRowRequest(QtPrivate::IndexList start, QtPrivate::IndexList end, QList<int> roles)
337 {
338 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaRowRequest(QtPrivate::IndexList,QtPrivate::IndexList,QList<int>)");
339 QVariantList __repc_args;
340 __repc_args << QVariant::fromValue(value: start) << QVariant::fromValue(value: end) << QVariant::fromValue(value: roles);
341 return QRemoteObjectPendingReply<QtPrivate::DataEntries>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
342 }
343 QRemoteObjectPendingReply<QVariantList> replicaHeaderRequest(QList<Qt::Orientation> orientations, QList<int> sections, QList<int> roles)
344 {
345 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaHeaderRequest(QList<Qt::Orientation>,QList<int>,QList<int>)");
346 QVariantList __repc_args;
347 __repc_args << QVariant::fromValue(value: orientations) << QVariant::fromValue(value: sections) << QVariant::fromValue(value: roles);
348 return QRemoteObjectPendingReply<QVariantList>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
349 }
350 void replicaSetCurrentIndex(QtPrivate::IndexList index, QItemSelectionModel::SelectionFlags command)
351 {
352 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaSetCurrentIndex(QtPrivate::IndexList,QItemSelectionModel::SelectionFlags)");
353 QVariantList __repc_args;
354 __repc_args << QVariant::fromValue(value: index) << QVariant::fromValue(value: command);
355 send(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args);
356 }
357 void replicaSetData(QtPrivate::IndexList index, const QVariant &value, int role)
358 {
359 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaSetData(QtPrivate::IndexList,QVariant,int)");
360 QVariantList __repc_args;
361 __repc_args << QVariant::fromValue(value: index) << QVariant::fromValue(value) << QVariant::fromValue(value: role);
362 send(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args);
363 }
364 QRemoteObjectPendingReply<QtPrivate::MetaAndDataEntries> replicaCacheRequest(size_t size, QList<int> roles)
365 {
366 static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot(slot: "replicaCacheRequest(size_t,QList<int>)");
367 QVariantList __repc_args;
368 __repc_args << QVariant::fromValue(value: size) << QVariant::fromValue(value: roles);
369 return QRemoteObjectPendingReply<QtPrivate::MetaAndDataEntries>(sendWithReply(call: QMetaObject::InvokeMetaMethod, index: __repc_index, args: __repc_args));
370 }
371 void onHeaderDataChanged(Qt::Orientation orientation, int first, int last);
372 void onDataChanged(const QtPrivate::IndexList &start, const QtPrivate::IndexList &end, const QList<int> &roles);
373 void onRowsInserted(const QtPrivate::IndexList &parent, int start, int end);
374 void onRowsRemoved(const QtPrivate::IndexList &parent, int start, int end);
375 void onColumnsInserted(const QtPrivate::IndexList &parent, int start, int end);
376 void onRowsMoved(QtPrivate::IndexList srcParent, int srcRow, int count, QtPrivate::IndexList destParent, int destRow);
377 void onCurrentChanged(QtPrivate::IndexList current, QtPrivate::IndexList previous);
378 void onModelReset();
379 void requestedData(QRemoteObjectPendingCallWatcher *);
380 void requestedHeaderData(QRemoteObjectPendingCallWatcher *);
381 void init();
382 void fetchPendingData();
383 void fetchPendingHeaderData();
384 void handleInitDone(QRemoteObjectPendingCallWatcher *watcher);
385 void handleModelResetDone(QRemoteObjectPendingCallWatcher *watcher);
386 void handleSizeDone(QRemoteObjectPendingCallWatcher *watcher);
387 void onReplicaCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
388 void fillCache(const QtPrivate::IndexValuePair &pair,const QList<int> &roles);
389 void onLayoutChanged(const QtPrivate::IndexList &parents, QAbstractItemModel::LayoutChangeHint hint);
390public:
391 QScopedPointer<QItemSelectionModel> m_selectionModel;
392 QList<CacheEntry> m_headerData[2];
393
394 CacheData m_rootItem;
395 inline CacheData* cacheData(const QModelIndex &index) const {
396 if (!index.isValid())
397 return const_cast<CacheData*>(&m_rootItem);
398 if (index.internalPointer()) {
399 auto parent = static_cast<CacheData*>(index.internalPointer());
400 if (m_activeParents.find(x: parent) != m_activeParents.end())
401 return parent->children.get(key: index.row());
402 }
403 return nullptr;
404 }
405 inline CacheData* cacheData(const QtPrivate::IndexList &index) const {
406 return cacheData(index: toQModelIndex(list: index, model: q));
407 }
408 inline CacheData* createCacheData(const QtPrivate::IndexList &index) const {
409 bool ok = false;
410 auto modelIndex = toQModelIndex(list: index, model: q, ok: &ok);
411 if (!ok)
412 return nullptr;
413 cacheData(index: modelIndex.parent())->ensureChildren(start: modelIndex.row() , end: modelIndex.row());
414 return cacheData(index: modelIndex);
415 }
416 inline CacheEntry* cacheEntry(const QModelIndex &index) const {
417 auto data = cacheData(index);
418 if (!data || index.column() < 0 || index.column() >= data->cachedRowEntry.size())
419 return nullptr;
420 CacheEntry &entry = data->cachedRowEntry[index.column()];
421 return &entry;
422 }
423 inline CacheEntry* cacheEntry(const QtPrivate::IndexList &index) const {
424 return cacheEntry(index: toQModelIndex(list: index, model: q));
425 }
426
427 QRemoteObjectPendingCallWatcher *doModelReset();
428 void initializeModelConnections();
429
430 bool m_initDone = false;
431 QList<RequestedData> m_requestedData;
432 QList<RequestedHeaderData> m_requestedHeaderData;
433 QList<QRemoteObjectPendingCallWatcher*> m_pendingRequests;
434 QAbstractItemModelReplica *q;
435 mutable QList<int> m_availableRoles;
436 std::unordered_set<CacheData*> m_activeParents;
437 QtRemoteObjects::InitialAction m_initialAction;
438 QList<int> m_initialFetchRolesHint;
439};
440
441QT_END_NAMESPACE
442
443#endif // QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
444

source code of qtremoteobjects/src/remoteobjects/qremoteobjectabstractitemmodelreplica_p.h