File indexing completed on 2024-11-10 04:40:30

0001 /*
0002     SPDX-FileCopyrightText: 2009 Tobias Koenig <tokoe@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "itemsearchjob.h"
0008 
0009 #include "itemfetchscope.h"
0010 #include "job_p.h"
0011 #include "private/protocol_p.h"
0012 #include "protocolhelper_p.h"
0013 #include "searchquery.h"
0014 #include "tagfetchscope.h"
0015 
0016 #include <QCoreApplication>
0017 #include <QThreadStorage>
0018 #include <QTimer>
0019 
0020 using namespace Akonadi;
0021 
0022 class Akonadi::ItemSearchJobPrivate : public JobPrivate
0023 {
0024 public:
0025     ItemSearchJobPrivate(ItemSearchJob *parent, const SearchQuery &query)
0026         : JobPrivate(parent)
0027         , mQuery(query)
0028     {
0029         mEmitTimer.setSingleShot(true);
0030         mEmitTimer.setInterval(std::chrono::milliseconds{100});
0031     }
0032 
0033     void init()
0034     {
0035         QObject::connect(&mEmitTimer, &QTimer::timeout, q_ptr, [this]() {
0036             timeout();
0037         });
0038     }
0039 
0040     void aboutToFinish() override
0041     {
0042         timeout();
0043     }
0044 
0045     void timeout()
0046     {
0047         Q_Q(Akonadi::ItemSearchJob);
0048 
0049         mEmitTimer.stop(); // in case we are called by result()
0050         if (!mPendingItems.isEmpty()) {
0051             if (!q->error()) {
0052                 Q_EMIT q->itemsReceived(mPendingItems);
0053             }
0054             mPendingItems.clear();
0055         }
0056     }
0057     QString jobDebuggingString() const override
0058     {
0059         QStringList flags;
0060         if (mRecursive) {
0061             flags.append(QStringLiteral("recursive"));
0062         }
0063         if (mRemote) {
0064             flags.append(QStringLiteral("remote"));
0065         }
0066         if (mCollections.isEmpty()) {
0067             flags.append(QStringLiteral("all collections"));
0068         } else {
0069             flags.append(QStringLiteral("%1 collections").arg(mCollections.count()));
0070         }
0071         return QStringLiteral("%1,json=%2").arg(flags.join(QLatin1Char(',')), QString::fromUtf8(mQuery.toJSON()));
0072     }
0073 
0074     Q_DECLARE_PUBLIC(ItemSearchJob)
0075 
0076     SearchQuery mQuery;
0077     Collection::List mCollections;
0078     QStringList mMimeTypes;
0079     bool mRecursive = false;
0080     bool mRemote = false;
0081     ItemFetchScope mItemFetchScope;
0082     TagFetchScope mTagFetchScope;
0083 
0084     Item::List mItems;
0085     Item::List mPendingItems; // items pending for emitting itemsReceived()
0086 
0087     QTimer mEmitTimer;
0088 };
0089 
0090 QThreadStorage<Session *> instances;
0091 
0092 static void cleanupDefaultSearchSession()
0093 {
0094     instances.setLocalData(nullptr);
0095 }
0096 
0097 static Session *defaultSearchSession()
0098 {
0099     if (!instances.hasLocalData()) {
0100         const QByteArray sessionName = Session::defaultSession()->sessionId() + "-SearchSession";
0101         instances.setLocalData(new Session(sessionName));
0102         qAddPostRoutine(cleanupDefaultSearchSession);
0103     }
0104     return instances.localData();
0105 }
0106 
0107 static QObject *sessionForJob(QObject *parent)
0108 {
0109     if (qobject_cast<Job *>(parent) || qobject_cast<Session *>(parent)) {
0110         return parent;
0111     }
0112     return defaultSearchSession();
0113 }
0114 
0115 ItemSearchJob::ItemSearchJob(QObject *parent)
0116     : Job(new ItemSearchJobPrivate(this, SearchQuery()), sessionForJob(parent))
0117 {
0118     Q_D(ItemSearchJob);
0119     d->init();
0120 }
0121 
0122 ItemSearchJob::ItemSearchJob(const SearchQuery &query, QObject *parent)
0123     : Job(new ItemSearchJobPrivate(this, query), sessionForJob(parent))
0124 {
0125     Q_D(ItemSearchJob);
0126     d->init();
0127 }
0128 
0129 ItemSearchJob::~ItemSearchJob() = default;
0130 
0131 void ItemSearchJob::setQuery(const SearchQuery &query)
0132 {
0133     Q_D(ItemSearchJob);
0134 
0135     d->mQuery = query;
0136 }
0137 
0138 void ItemSearchJob::setFetchScope(const ItemFetchScope &fetchScope)
0139 {
0140     Q_D(ItemSearchJob);
0141 
0142     d->mItemFetchScope = fetchScope;
0143 }
0144 
0145 ItemFetchScope &ItemSearchJob::fetchScope()
0146 {
0147     Q_D(ItemSearchJob);
0148 
0149     return d->mItemFetchScope;
0150 }
0151 
0152 void ItemSearchJob::setTagFetchScope(const TagFetchScope &fetchScope)
0153 {
0154     Q_D(ItemSearchJob);
0155 
0156     d->mTagFetchScope = fetchScope;
0157 }
0158 
0159 TagFetchScope &ItemSearchJob::tagFetchScope()
0160 {
0161     Q_D(ItemSearchJob);
0162 
0163     return d->mTagFetchScope;
0164 }
0165 
0166 void ItemSearchJob::setSearchCollections(const Collection::List &collections)
0167 {
0168     Q_D(ItemSearchJob);
0169 
0170     d->mCollections = collections;
0171 }
0172 
0173 Collection::List ItemSearchJob::searchCollections() const
0174 {
0175     return d_func()->mCollections;
0176 }
0177 
0178 void ItemSearchJob::setMimeTypes(const QStringList &mimeTypes)
0179 {
0180     Q_D(ItemSearchJob);
0181 
0182     d->mMimeTypes = mimeTypes;
0183 }
0184 
0185 QStringList ItemSearchJob::mimeTypes() const
0186 {
0187     return d_func()->mMimeTypes;
0188 }
0189 
0190 void ItemSearchJob::setRecursive(bool recursive)
0191 {
0192     Q_D(ItemSearchJob);
0193 
0194     d->mRecursive = recursive;
0195 }
0196 
0197 bool ItemSearchJob::isRecursive() const
0198 {
0199     return d_func()->mRecursive;
0200 }
0201 
0202 void ItemSearchJob::setRemoteSearchEnabled(bool enabled)
0203 {
0204     Q_D(ItemSearchJob);
0205 
0206     d->mRemote = enabled;
0207 }
0208 
0209 bool ItemSearchJob::isRemoteSearchEnabled() const
0210 {
0211     return d_func()->mRemote;
0212 }
0213 
0214 void ItemSearchJob::doStart()
0215 {
0216     Q_D(ItemSearchJob);
0217 
0218     auto cmd = Protocol::SearchCommandPtr::create();
0219     cmd->setMimeTypes(d->mMimeTypes);
0220     if (!d->mCollections.isEmpty()) {
0221         QList<qint64> ids;
0222         ids.reserve(d->mCollections.size());
0223         for (const Collection &col : std::as_const(d->mCollections)) {
0224             ids << col.id();
0225         }
0226         cmd->setCollections(ids);
0227     }
0228     cmd->setRecursive(d->mRecursive);
0229     cmd->setRemote(d->mRemote);
0230     cmd->setQuery(QString::fromUtf8(d->mQuery.toJSON()));
0231     cmd->setItemFetchScope(ProtocolHelper::itemFetchScopeToProtocol(d->mItemFetchScope));
0232     cmd->setTagFetchScope(ProtocolHelper::tagFetchScopeToProtocol(d->mTagFetchScope));
0233 
0234     d->sendCommand(cmd);
0235 }
0236 
0237 bool ItemSearchJob::doHandleResponse(qint64 tag, const Protocol::CommandPtr &response)
0238 {
0239     Q_D(ItemSearchJob);
0240 
0241     if (response->isResponse() && response->type() == Protocol::Command::FetchItems) {
0242         const Item item = ProtocolHelper::parseItemFetchResult(Protocol::cmdCast<Protocol::FetchItemsResponse>(response));
0243         if (!item.isValid()) {
0244             return false;
0245         }
0246         d->mItems.append(item);
0247         d->mPendingItems.append(item);
0248         if (!d->mEmitTimer.isActive()) {
0249             d->mEmitTimer.start();
0250         }
0251 
0252         return false;
0253     }
0254 
0255     if (response->isResponse() && response->type() == Protocol::Command::Search) {
0256         return true;
0257     }
0258 
0259     return Job::doHandleResponse(tag, response);
0260 }
0261 
0262 Item::List ItemSearchJob::items() const
0263 {
0264     Q_D(const ItemSearchJob);
0265 
0266     return d->mItems;
0267 }
0268 
0269 #include "moc_itemsearchjob.cpp"