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

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