blob: 8f13091898c3910b3cae1f337b0d8bf795dc6e3f [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 Ford Motor Company
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtRemoteObjects module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
#define QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qremoteobjectabstractitemmodeltypes.h"
#include "qremoteobjectabstractitemmodelreplica.h"
#include "qremoteobjectreplica.h"
#include "qremoteobjectpendingcall.h"
#include <list>
#include <unordered_map>
#include <unordered_set>
QT_BEGIN_NAMESPACE
namespace {
const int DefaultNodesCacheSize = 1000;
}
struct CacheEntry
{
QHash<int, QVariant> data;
Qt::ItemFlags flags;
explicit CacheEntry()
: flags(Qt::NoItemFlags)
{}
};
typedef QVector<CacheEntry> CachedRowEntry;
template <class Key, class Value>
struct LRUCache
{
typedef std::pair<Key, Value*> Pair;
std::list<Pair> cachedItems;
typedef typename std::list<Pair>::iterator CacheIterator;
std::unordered_map<Key, CacheIterator> cachedItemsMap;
size_t cacheSize;
explicit LRUCache()
{
bool ok;
cacheSize = qEnvironmentVariableIntValue("QTRO_NODES_CACHE_SIZE" , &ok);
if (!ok)
cacheSize = DefaultNodesCacheSize;
}
~LRUCache()
{
clear();
}
inline void cleanCache()
{
Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
auto it = cachedItems.rbegin();
while (cachedItemsMap.size() > cacheSize) {
// Do not trash elements with children
// Workaround QTreeView bugs which caches the children indexes for very long time
while (it->second->hasChildren && it != cachedItems.rend())
++it;
if (it == cachedItems.rend())
break;
cachedItemsMap.erase(it->first);
delete it->second;
cachedItems.erase((++it).base());
}
Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
}
void setCacheSize(size_t rootCacheSize)
{
cacheSize = rootCacheSize;
cleanCache();
cachedItemsMap.reserve(rootCacheSize);
}
void changeKeys(Key key, Key delta) {
std::vector<std::pair<Key, CacheIterator>> changed;
auto it = cachedItemsMap.begin();
while (it != cachedItemsMap.end()) {
if (it->first >= key) {
changed.emplace_back(it->first + delta, it->second);
it->second->first += delta;
it = cachedItemsMap.erase(it);
} else {
++it;
}
}
for (auto pair : changed)
cachedItemsMap[pair.first] = pair.second;
}
void insert(Key key, Value *value)
{
changeKeys(key, 1);
ensure(key, value);
Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
}
void ensure(Key key, Value *value)
{
cachedItems.emplace_front(key, value);
cachedItemsMap[key] = cachedItems.begin();
cleanCache();
}
void remove(Key key)
{
auto it = cachedItemsMap.find(key);
if (it != cachedItemsMap.end()) {
delete it->second->second;
cachedItems.erase(it->second);
cachedItemsMap.erase(it);
}
changeKeys(key, -1);
Q_ASSERT(cachedItems.size() == cachedItemsMap.size());
}
Value *get(Key key)
{
auto it = cachedItemsMap.find(key);
if (it == cachedItemsMap.end())
return nullptr;
// Move the accessed item to front
cachedItems.splice(cachedItems.begin(), cachedItems, it->second);
Q_ASSERT(it->second->first == key);
return it->second->second;
}
Key find(Value *val)
{
for (auto it = cachedItemsMap.begin(); it != cachedItemsMap.end(); ++it) {
if (it->second->second == val)
return it->first;
}
Q_ASSERT_X(false, __FUNCTION__, "Value not found");
return Key{};
}
bool exists(Value *val)
{
for (const auto &pair : cachedItems)
if (pair.second == val)
return true;
return false;
}
bool exists(Key key)
{
return cachedItemsMap.find(key) != cachedItemsMap.end();
}
size_t size()
{
return cachedItemsMap.size();
}
void clear()
{
for (const auto &pair : cachedItems)
delete pair.second;
cachedItems.clear();
cachedItemsMap.clear();
}
};
class QAbstractItemModelReplicaImplementation;
struct CacheData
{
QAbstractItemModelReplicaImplementation *replicaModel;
CacheData *parent;
CachedRowEntry cachedRowEntry;
bool hasChildren;
LRUCache<int, CacheData> children;
int columnCount;
int rowCount;
explicit CacheData(QAbstractItemModelReplicaImplementation *model, CacheData *parentItem = nullptr);
~CacheData();
void ensureChildren(int start, int end)
{
for (int i = start; i <= end; ++i)
if (!children.exists(i))
children.ensure(i, new CacheData(replicaModel, this));
}
void insertChildren(int start, int end) {
Q_ASSERT_X(start >= 0 && start <= end, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2")).arg(start).arg(end)));
for (int i = start; i <= end; ++i) {
auto cacheData = new CacheData(replicaModel, this);
cacheData->columnCount = columnCount;
children.insert(i, cacheData);
++rowCount;
}
if (rowCount)
hasChildren = true;
}
void removeChildren(int start, int end) {
Q_ASSERT_X(start >= 0 && start <= end && end < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 <= %2 < %3")).arg(start).arg(end).arg(rowCount)));
for (int i = end; i >= start; --i) {
children.remove(i);
--rowCount;
}
hasChildren = rowCount;
}
void clear() {
cachedRowEntry.clear();
children.clear();
hasChildren = false;
columnCount = 0;
rowCount = 0;
}
};
struct RequestedData
{
IndexList start;
IndexList end;
QVector<int> roles;
};
struct RequestedHeaderData
{
int role;
int section;
Qt::Orientation orientation;
};
class SizeWatcher : public QRemoteObjectPendingCallWatcher
{
Q_OBJECT
public:
SizeWatcher(IndexList _parentList, const QRemoteObjectPendingReply<QSize> &reply)
: QRemoteObjectPendingCallWatcher(reply),
parentList(_parentList) {}
IndexList parentList;
};
class RowWatcher : public QRemoteObjectPendingCallWatcher
{
Q_OBJECT
public:
RowWatcher(IndexList _start, IndexList _end, QVector<int> _roles, const QRemoteObjectPendingReply<DataEntries> &reply)
: QRemoteObjectPendingCallWatcher(reply),
start(_start),
end(_end),
roles(_roles) {}
IndexList start, end;
QVector<int> roles;
};
class HeaderWatcher : public QRemoteObjectPendingCallWatcher
{
Q_OBJECT
public:
HeaderWatcher(QVector<Qt::Orientation> _orientations, QVector<int> _sections, QVector<int> _roles, const QRemoteObjectPendingReply<QVariantList> &reply)
: QRemoteObjectPendingCallWatcher(reply),
orientations(_orientations),
sections(_sections),
roles(_roles) {}
QVector<Qt::Orientation> orientations;
QVector<int> sections, roles;
};
class QAbstractItemModelReplicaImplementation : public QRemoteObjectReplica
{
Q_OBJECT
//TODO Use an input name for the model on the Replica side
Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "ServerModelAdapter")
Q_PROPERTY(QVector<int> availableRoles READ availableRoles NOTIFY availableRolesChanged)
Q_PROPERTY(QIntHash roleNames READ roleNames)
public:
QAbstractItemModelReplicaImplementation();
QAbstractItemModelReplicaImplementation(QRemoteObjectNode *node, const QString &name);
~QAbstractItemModelReplicaImplementation() override;
void initialize() override;
static void registerMetatypes();
inline const QVector<int> &availableRoles() const
{
if (m_availableRoles.isEmpty())
m_availableRoles = propAsVariant(0).value<QVector<int> >();
return m_availableRoles;
}
QHash<int, QByteArray> roleNames() const
{
QIntHash roles = propAsVariant(1).value<QIntHash>();
return roles;
}
void setModel(QAbstractItemModelReplica *model);
bool clearCache(const IndexList &start, const IndexList &end, const QVector<int> &roles);
Q_SIGNALS:
void availableRolesChanged();
void dataChanged(IndexList topLeft, IndexList bottomRight, QVector<int> roles);
void rowsInserted(IndexList parent, int first, int last);
void rowsRemoved(IndexList parent, int first, int last);
void rowsMoved(IndexList parent, int start, int end, IndexList destination, int row);
void currentChanged(IndexList current, IndexList previous);
void modelReset();
void headerDataChanged(Qt::Orientation,int,int);
void columnsInserted(IndexList parent, int first, int last);
public Q_SLOTS:
QRemoteObjectPendingReply<QSize> replicaSizeRequest(IndexList parentList)
{
static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaSizeRequest(IndexList)");
QVariantList __repc_args;
__repc_args << QVariant::fromValue(parentList);
return QRemoteObjectPendingReply<QSize>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
}
QRemoteObjectPendingReply<DataEntries> replicaRowRequest(IndexList start, IndexList end, QVector<int> roles)
{
static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaRowRequest(IndexList,IndexList,QVector<int>)");
QVariantList __repc_args;
__repc_args << QVariant::fromValue(start) << QVariant::fromValue(end) << QVariant::fromValue(roles);
return QRemoteObjectPendingReply<DataEntries>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
}
QRemoteObjectPendingReply<QVariantList> replicaHeaderRequest(QVector<Qt::Orientation> orientations, QVector<int> sections, QVector<int> roles)
{
static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaHeaderRequest(QVector<Qt::Orientation>,QVector<int>,QVector<int>)");
QVariantList __repc_args;
__repc_args << QVariant::fromValue(orientations) << QVariant::fromValue(sections) << QVariant::fromValue(roles);
return QRemoteObjectPendingReply<QVariantList>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
}
void replicaSetCurrentIndex(IndexList index, QItemSelectionModel::SelectionFlags command)
{
static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaSetCurrentIndex(IndexList,QItemSelectionModel::SelectionFlags)");
QVariantList __repc_args;
__repc_args << QVariant::fromValue(index) << QVariant::fromValue(command);
send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);
}
void replicaSetData(IndexList index, const QVariant &value, int role)
{
static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaSetData(IndexList,QVariant,int)");
QVariantList __repc_args;
__repc_args << QVariant::fromValue(index) << QVariant::fromValue(value) << QVariant::fromValue(role);
send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);
}
QRemoteObjectPendingReply<MetaAndDataEntries> replicaCacheRequest(size_t size, QVector<int> roles)
{
static int __repc_index = QAbstractItemModelReplicaImplementation::staticMetaObject.indexOfSlot("replicaCacheRequest(size_t,QVector<int>)");
QVariantList __repc_args;
__repc_args << QVariant::fromValue(size) << QVariant::fromValue(roles);
return QRemoteObjectPendingReply<MetaAndDataEntries>(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));
}
void onHeaderDataChanged(Qt::Orientation orientation, int first, int last);
void onDataChanged(const IndexList &start, const IndexList &end, const QVector<int> &roles);
void onRowsInserted(const IndexList &parent, int start, int end);
void onRowsRemoved(const IndexList &parent, int start, int end);
void onColumnsInserted(const IndexList &parent, int start, int end);
void onRowsMoved(IndexList srcParent, int srcRow, int count, IndexList destParent, int destRow);
void onCurrentChanged(IndexList current, IndexList previous);
void onModelReset();
void requestedData(QRemoteObjectPendingCallWatcher *);
void requestedHeaderData(QRemoteObjectPendingCallWatcher *);
void init();
void fetchPendingData();
void fetchPendingHeaderData();
void handleInitDone(QRemoteObjectPendingCallWatcher *watcher);
void handleModelResetDone(QRemoteObjectPendingCallWatcher *watcher);
void handleSizeDone(QRemoteObjectPendingCallWatcher *watcher);
void onReplicaCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
void fillCache(const IndexValuePair &pair,const QVector<int> &roles);
public:
QScopedPointer<QItemSelectionModel> m_selectionModel;
QVector<CacheEntry> m_headerData[2];
CacheData m_rootItem;
inline CacheData* cacheData(const QModelIndex &index) const {
if (!index.isValid())
return const_cast<CacheData*>(&m_rootItem);
if (index.internalPointer()) {
auto parent = static_cast<CacheData*>(index.internalPointer());
if (m_activeParents.find(parent) != m_activeParents.end())
return parent->children.get(index.row());
}
return nullptr;
}
inline CacheData* cacheData(const IndexList &index) const {
return cacheData(toQModelIndex(index, q));
}
inline CacheData* createCacheData(const IndexList &index) const {
auto modelIndex = toQModelIndex(index, q);
cacheData(modelIndex.parent())->ensureChildren(modelIndex.row() , modelIndex.row());
return cacheData(modelIndex);
}
inline CacheEntry* cacheEntry(const QModelIndex &index) const {
auto data = cacheData(index);
if (!data || index.column() < 0 || index.column() >= data->cachedRowEntry.size())
return nullptr;
CacheEntry &entry = data->cachedRowEntry[index.column()];
return &entry;
}
inline CacheEntry* cacheEntry(const IndexList &index) const {
return cacheEntry(toQModelIndex(index, q));
}
QRemoteObjectPendingCallWatcher *doModelReset();
void initializeModelConnections();
bool m_initDone = false;
QVector<RequestedData> m_requestedData;
QVector<RequestedHeaderData> m_requestedHeaderData;
QVector<QRemoteObjectPendingCallWatcher*> m_pendingRequests;
QAbstractItemModelReplica *q;
mutable QVector<int> m_availableRoles;
std::unordered_set<CacheData*> m_activeParents;
QtRemoteObjects::InitialAction m_initialAction;
QVector<int> m_initialFetchRolesHint;
};
QT_END_NAMESPACE
#endif // QREMOTEOBJECTS_ABSTRACT_ITEM_REPLICA_P_H