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

0001 /*
0002   SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "fakeakonadiservercommand.h"
0008 
0009 #include <QMetaMethod>
0010 #include <QStringList>
0011 
0012 #include "akranges.h"
0013 #include "entitydisplayattribute.h"
0014 #include "fakeserverdata.h"
0015 #include "tagattribute.h"
0016 
0017 using namespace Akonadi;
0018 using namespace AkRanges;
0019 
0020 FakeAkonadiServerCommand::FakeAkonadiServerCommand(FakeAkonadiServerCommand::Type type, FakeServerData *serverData)
0021     : m_type(type)
0022     , m_serverData(serverData)
0023     , m_model(serverData->model())
0024 {
0025     connectForwardingSignals();
0026 }
0027 
0028 bool FakeAkonadiServerCommand::isTagSignal(const QByteArray &signal) const
0029 {
0030     return signal.startsWith("emit_tag") || signal.startsWith("emit_monitoredTag");
0031 }
0032 
0033 bool FakeAkonadiServerCommand::isItemSignal(const QByteArray &signal) const
0034 {
0035     return signal.startsWith("emit_item") || signal.startsWith("emit_monitoredItem");
0036 }
0037 
0038 bool FakeAkonadiServerCommand::isCollectionSignal(const QByteArray &signal) const
0039 {
0040     return signal.startsWith("emit_collection") || signal.startsWith("emit_monitoredCollection");
0041 }
0042 
0043 void FakeAkonadiServerCommand::connectForwardingSignals()
0044 {
0045     const auto mo = FakeAkonadiServerCommand::metaObject();
0046     for (int methodIndex = 0; methodIndex < mo->methodCount(); ++methodIndex) {
0047         const QMetaMethod mm = mo->method(methodIndex);
0048         const QByteArray signature = mm.methodSignature();
0049         if (mm.methodType() == QMetaMethod::Signal) {
0050             if ((qobject_cast<TagModel *>(m_model) && isTagSignal(signature))
0051                 || (qobject_cast<EntityTreeModel *>(m_model) && (isCollectionSignal(signature) || isItemSignal(signature)))) {
0052                 const int modelSlotIndex = m_model->metaObject()->indexOfSlot(signature.mid(5).constData());
0053                 if (modelSlotIndex < 0) {
0054                     qWarning() << "Slot not found in" << m_model->metaObject()->className() << ":" << signature.mid(5).constData();
0055                     Q_ASSERT(modelSlotIndex >= 0);
0056                 }
0057                 mo->connect(this, methodIndex, m_model, modelSlotIndex);
0058             }
0059         }
0060     }
0061 }
0062 
0063 Collection FakeAkonadiServerCommand::getCollectionByDisplayName(const QString &displayName) const
0064 {
0065     Q_ASSERT(qobject_cast<EntityTreeModel *>(m_model));
0066     QModelIndexList list = m_model->match(m_model->index(0, 0), Qt::DisplayRole, displayName, 1, Qt::MatchRecursive);
0067     if (list.isEmpty()) {
0068         return Collection();
0069     }
0070     return list.first().data(EntityTreeModel::CollectionRole).value<Collection>();
0071 }
0072 
0073 Item FakeAkonadiServerCommand::getItemByDisplayName(const QString &displayName) const
0074 {
0075     Q_ASSERT(qobject_cast<EntityTreeModel *>(m_model));
0076     QModelIndexList list = m_model->match(m_model->index(0, 0), Qt::DisplayRole, displayName, 1, Qt::MatchRecursive);
0077     if (list.isEmpty()) {
0078         return Item();
0079     }
0080     return list.first().data(EntityTreeModel::ItemRole).value<Item>();
0081 }
0082 
0083 Tag FakeAkonadiServerCommand::getTagByDisplayName(const QString &displayName) const
0084 {
0085     Q_ASSERT(qobject_cast<TagModel *>(m_model));
0086     QModelIndexList list = m_model->match(m_model->index(0, 0), Qt::DisplayRole, displayName, 1, Qt::MatchRecursive);
0087     if (list.isEmpty()) {
0088         return Tag();
0089     }
0090 
0091     return list.first().data(TagModel::TagRole).value<Tag>();
0092 }
0093 
0094 void FakeJobResponse::doCommand()
0095 {
0096     if (m_type == RespondToCollectionFetch) {
0097         Q_EMIT emit_collectionsFetched(m_collections | Views::values | Actions::toQVector);
0098     } else if (m_type == RespondToItemFetch) {
0099         setProperty("FetchCollectionId", m_parentCollection.id());
0100         Q_EMIT emit_itemsFetched(m_items | Views::values | Actions::toQVector);
0101     } else if (m_type == RespondToTagFetch) {
0102         Q_EMIT emit_tagsFetched(m_tags | Views::values | Actions::toQVector);
0103     }
0104 }
0105 
0106 QList<FakeJobResponse::Token> FakeJobResponse::tokenize(const QString &treeString)
0107 {
0108     QStringList parts = treeString.split(QLatin1Char('-'));
0109 
0110     QList<Token> tokens;
0111     const QStringList::const_iterator begin = parts.constBegin();
0112     const QStringList::const_iterator end = parts.constEnd();
0113 
0114     QStringList::const_iterator it = begin;
0115     ++it;
0116     for (; it != end; ++it) {
0117         Token token;
0118         if (it->trimmed().isEmpty()) {
0119             token.type = Token::Branch;
0120         } else {
0121             token.type = Token::Leaf;
0122             token.content = it->trimmed();
0123         }
0124         tokens.append(token);
0125     }
0126     return tokens;
0127 }
0128 
0129 QList<FakeAkonadiServerCommand *> FakeJobResponse::interpret(FakeServerData *fakeServerData, const QString &serverData)
0130 {
0131     QList<FakeAkonadiServerCommand *> list;
0132     const QList<FakeJobResponse *> response = parseTreeString(fakeServerData, serverData);
0133 
0134     for (FakeJobResponse *command : response) {
0135         list.append(command);
0136     }
0137     return list;
0138 }
0139 
0140 QList<FakeJobResponse *> FakeJobResponse::parseTreeString(FakeServerData *fakeServerData, const QString &treeString)
0141 {
0142     int depth = 0;
0143 
0144     QList<FakeJobResponse *> collectionResponseList;
0145     QHash<Collection::Id, FakeJobResponse *> itemResponseMap;
0146     QList<FakeJobResponse *> tagResponseList;
0147 
0148     Collection::List recentCollections;
0149     Tag::List recentTags;
0150 
0151     recentCollections.append(Collection::root());
0152     recentTags.append(Tag());
0153 
0154     QList<Token> tokens = tokenize(treeString);
0155     while (!tokens.isEmpty()) {
0156         Token token = tokens.takeFirst();
0157 
0158         if (token.type == Token::Branch) {
0159             ++depth;
0160             continue;
0161         }
0162         Q_ASSERT(token.type == Token::Leaf);
0163         parseEntityString(collectionResponseList, itemResponseMap, tagResponseList, recentCollections, recentTags, fakeServerData, token.content, depth);
0164 
0165         depth = 0;
0166     }
0167     return collectionResponseList + tagResponseList;
0168 }
0169 
0170 void FakeJobResponse::parseEntityString(QList<FakeJobResponse *> &collectionResponseList,
0171                                         QHash<Collection::Id, FakeJobResponse *> &itemResponseMap,
0172                                         QList<FakeJobResponse *> &tagResponseList,
0173                                         Collection::List &recentCollections,
0174                                         Tag::List &recentTags,
0175                                         FakeServerData *fakeServerData,
0176                                         const QString &_entityString,
0177                                         int depth)
0178 {
0179     QString entityString = _entityString;
0180     if (entityString.startsWith(QLatin1Char('C'))) {
0181         Collection collection;
0182         entityString.remove(0, 2);
0183         Q_ASSERT(entityString.startsWith(QLatin1Char('(')));
0184         entityString.remove(0, 1);
0185         QStringList parts = entityString.split(QLatin1Char(')'));
0186 
0187         if (!parts.first().isEmpty()) {
0188             QString typesString = parts.takeFirst();
0189 
0190             QStringList types = typesString.split(QLatin1Char(','));
0191             types.replaceInStrings(QStringLiteral(" "), QLatin1StringView(""));
0192             collection.setContentMimeTypes(types);
0193         } else {
0194             parts.removeFirst();
0195         }
0196 
0197         collection.setId(fakeServerData->nextCollectionId());
0198         collection.setName(QStringLiteral("Collection %1").arg(collection.id()));
0199         collection.setRemoteId(QStringLiteral("remoteId %1").arg(collection.id()));
0200 
0201         if (depth == 0) {
0202             collection.setParentCollection(Collection::root());
0203         } else {
0204             collection.setParentCollection(recentCollections.at(depth));
0205         }
0206 
0207         if (recentCollections.size() == (depth + 1)) {
0208             recentCollections.append(collection);
0209         } else {
0210             recentCollections[depth + 1] = collection;
0211         }
0212 
0213         int order = 0;
0214         if (!parts.first().isEmpty()) {
0215             QString displayName;
0216             QString optionalSection = parts.first().trimmed();
0217             if (optionalSection.startsWith(QLatin1Char('\''))) {
0218                 optionalSection.remove(0, 1);
0219                 QStringList optionalParts = optionalSection.split(QLatin1Char('\''));
0220                 displayName = optionalParts.takeFirst();
0221                 auto eda = new EntityDisplayAttribute();
0222                 eda->setDisplayName(displayName);
0223                 collection.addAttribute(eda);
0224                 optionalSection = optionalParts.first();
0225             }
0226 
0227             QString orderString = optionalSection.trimmed();
0228             if (!orderString.isEmpty()) {
0229                 bool ok;
0230                 order = orderString.toInt(&ok);
0231                 Q_ASSERT(ok);
0232             }
0233         } else {
0234             order = 1;
0235         }
0236         while (collectionResponseList.size() < order) {
0237             collectionResponseList.append(new FakeJobResponse(recentCollections[depth], FakeJobResponse::RespondToCollectionFetch, fakeServerData));
0238         }
0239         collectionResponseList[order - 1]->appendCollection(collection);
0240     }
0241     if (entityString.startsWith(QLatin1Char('I'))) {
0242         Item item;
0243         int order = 0;
0244         entityString.remove(0, 2);
0245         entityString = entityString.trimmed();
0246         QString type;
0247         int iFirstSpace = entityString.indexOf(QLatin1Char(' '));
0248         type = entityString.left(iFirstSpace);
0249         entityString = entityString.remove(0, iFirstSpace + 1).trimmed();
0250         if (iFirstSpace > 0 && !entityString.isEmpty()) {
0251             QString displayName;
0252             QString optionalSection = entityString;
0253             if (optionalSection.startsWith(QLatin1Char('\''))) {
0254                 optionalSection.remove(0, 1);
0255                 QStringList optionalParts = optionalSection.split(QLatin1Char('\''));
0256                 displayName = optionalParts.takeFirst();
0257                 auto eda = new EntityDisplayAttribute();
0258                 eda->setDisplayName(displayName);
0259                 item.addAttribute(eda);
0260                 optionalSection = optionalParts.first();
0261             }
0262             QString orderString = optionalSection.trimmed();
0263             if (!orderString.isEmpty()) {
0264                 bool ok;
0265                 order = orderString.toInt(&ok);
0266                 Q_ASSERT(ok);
0267             }
0268         } else {
0269             type = entityString;
0270         }
0271         Q_UNUSED(order)
0272 
0273         item.setMimeType(type);
0274         item.setId(fakeServerData->nextItemId());
0275         item.setRemoteId(QStringLiteral("RId_%1 %2").arg(item.id()).arg(type));
0276         item.setParentCollection(recentCollections.at(depth));
0277 
0278         Collection::Id colId = recentCollections[depth].id();
0279         if (!itemResponseMap.contains(colId)) {
0280             auto newResponse = new FakeJobResponse(recentCollections[depth], FakeJobResponse::RespondToItemFetch, fakeServerData);
0281             itemResponseMap.insert(colId, newResponse);
0282             collectionResponseList.append(newResponse);
0283         }
0284         itemResponseMap[colId]->appendItem(item);
0285     }
0286     if (entityString.startsWith(QLatin1Char('T'))) {
0287         Tag tag;
0288         int order = 0;
0289         entityString.remove(0, 2);
0290         entityString = entityString.trimmed();
0291         int iFirstSpace = entityString.indexOf(QLatin1Char(' '));
0292         QString type = entityString.left(iFirstSpace);
0293         entityString = entityString.remove(0, iFirstSpace + 1).trimmed();
0294         tag.setType(type.toLatin1());
0295 
0296         if (iFirstSpace > 0 && !entityString.isEmpty()) {
0297             QString displayName;
0298             QString optionalSection = entityString;
0299             if (optionalSection.startsWith(QLatin1Char('\''))) {
0300                 optionalSection.remove(0, 1);
0301                 QStringList optionalParts = optionalSection.split(QLatin1Char('\''));
0302                 displayName = optionalParts.takeFirst();
0303                 auto ta = new TagAttribute();
0304                 ta->setDisplayName(displayName);
0305                 tag.addAttribute(ta);
0306                 optionalSection = optionalParts.first();
0307             }
0308             QString orderString = optionalSection.trimmed();
0309             if (!orderString.isEmpty()) {
0310                 bool ok;
0311                 order = orderString.toInt(&ok);
0312                 Q_ASSERT(ok);
0313             }
0314         } else {
0315             type = entityString;
0316         }
0317 
0318         tag.setId(fakeServerData->nextTagId());
0319         tag.setRemoteId("RID_" + QByteArray::number(tag.id()) + ' ' + type.toLatin1());
0320         tag.setType(type.toLatin1());
0321 
0322         if (depth == 0) {
0323             tag.setParent(Tag());
0324         } else {
0325             tag.setParent(recentTags.at(depth));
0326         }
0327 
0328         if (recentTags.size() == (depth + 1)) {
0329             recentTags.append(tag);
0330         } else {
0331             recentTags[depth + 1] = tag;
0332         }
0333 
0334         while (tagResponseList.size() < order) {
0335             tagResponseList.append(new FakeJobResponse(recentTags[depth], FakeJobResponse::RespondToTagFetch, fakeServerData));
0336         }
0337         tagResponseList[order - 1]->appendTag(tag);
0338     }
0339 }
0340 
0341 void FakeCollectionMovedCommand::doCommand()
0342 {
0343     Collection collection = getCollectionByDisplayName(m_collectionName);
0344     Collection source = getCollectionByDisplayName(m_sourceName);
0345     Collection target = getCollectionByDisplayName(m_targetName);
0346 
0347     Q_ASSERT(collection.isValid());
0348     Q_ASSERT(source.isValid());
0349     Q_ASSERT(target.isValid());
0350 
0351     collection.setParentCollection(target);
0352 
0353     Q_EMIT emit_monitoredCollectionMoved(collection, source, target);
0354 }
0355 
0356 void FakeCollectionAddedCommand::doCommand()
0357 {
0358     Collection parent = getCollectionByDisplayName(m_parentName);
0359 
0360     Q_ASSERT(parent.isValid());
0361 
0362     Collection collection;
0363     collection.setId(m_serverData->nextCollectionId());
0364     collection.setName(QStringLiteral("Collection %1").arg(collection.id()));
0365     collection.setRemoteId(QStringLiteral("remoteId %1").arg(collection.id()));
0366     collection.setParentCollection(parent);
0367 
0368     auto eda = new EntityDisplayAttribute();
0369     eda->setDisplayName(m_collectionName);
0370     collection.addAttribute(eda);
0371 
0372     Q_EMIT emit_monitoredCollectionAdded(collection, parent);
0373 }
0374 
0375 void FakeCollectionRemovedCommand::doCommand()
0376 {
0377     Collection collection = getCollectionByDisplayName(m_collectionName);
0378 
0379     Q_ASSERT(collection.isValid());
0380 
0381     Q_EMIT emit_monitoredCollectionRemoved(collection);
0382 }
0383 
0384 void FakeCollectionChangedCommand::doCommand()
0385 {
0386     if (m_collection.isValid()) {
0387         Q_EMIT emit_monitoredCollectionChanged(m_collection);
0388         return;
0389     }
0390     Collection collection = getCollectionByDisplayName(m_collectionName);
0391     Collection parent = getCollectionByDisplayName(m_parentName);
0392 
0393     Q_ASSERT(collection.isValid());
0394 
0395     Q_EMIT emit_monitoredCollectionChanged(collection);
0396 }
0397 
0398 void FakeItemMovedCommand::doCommand()
0399 {
0400     Item item = getItemByDisplayName(m_itemName);
0401     Collection source = getCollectionByDisplayName(m_sourceName);
0402     Collection target = getCollectionByDisplayName(m_targetName);
0403 
0404     Q_ASSERT(item.isValid());
0405     Q_ASSERT(source.isValid());
0406     Q_ASSERT(target.isValid());
0407 
0408     item.setParentCollection(target);
0409 
0410     Q_EMIT emit_monitoredItemMoved(item, source, target);
0411 }
0412 
0413 void FakeItemAddedCommand::doCommand()
0414 {
0415     Collection parent = getCollectionByDisplayName(m_parentName);
0416 
0417     Q_ASSERT(parent.isValid());
0418 
0419     Item item;
0420     item.setId(m_serverData->nextItemId());
0421     item.setRemoteId(QStringLiteral("remoteId %1").arg(item.id()));
0422     item.setParentCollection(parent);
0423 
0424     auto eda = new EntityDisplayAttribute();
0425     eda->setDisplayName(m_itemName);
0426     item.addAttribute(eda);
0427 
0428     Q_EMIT emit_monitoredItemAdded(item, parent);
0429 }
0430 
0431 void FakeItemRemovedCommand::doCommand()
0432 {
0433     Item item = getItemByDisplayName(m_itemName);
0434 
0435     Q_ASSERT(item.isValid());
0436 
0437     Q_EMIT emit_monitoredItemRemoved(item);
0438 }
0439 
0440 void FakeItemChangedCommand::doCommand()
0441 {
0442     Item item = getItemByDisplayName(m_itemName);
0443     Collection parent = getCollectionByDisplayName(m_parentName);
0444 
0445     Q_ASSERT(item.isValid());
0446     Q_ASSERT(parent.isValid());
0447 
0448     Q_EMIT emit_monitoredItemChanged(item, QSet<QByteArray>());
0449 }
0450 
0451 void FakeTagAddedCommand::doCommand()
0452 {
0453     const Tag parent = getTagByDisplayName(m_parentName);
0454 
0455     Tag tag;
0456     tag.setId(m_serverData->nextTagId());
0457     tag.setName(m_tagName);
0458     tag.setRemoteId("remoteId " + QByteArray::number(tag.id()));
0459     tag.setParent(parent);
0460 
0461     Q_EMIT emit_monitoredTagAdded(tag);
0462 }
0463 
0464 void FakeTagChangedCommand::doCommand()
0465 {
0466     const Tag tag = getTagByDisplayName(m_tagName);
0467 
0468     Q_ASSERT(tag.isValid());
0469 
0470     Q_EMIT emit_monitoredTagChanged(tag);
0471 }
0472 
0473 void FakeTagMovedCommand::doCommand()
0474 {
0475     Tag tag = getTagByDisplayName(m_tagName);
0476     Tag newParent = getTagByDisplayName(m_newParent);
0477 
0478     Q_ASSERT(tag.isValid());
0479 
0480     tag.setParent(newParent);
0481 
0482     Q_EMIT emit_monitoredTagChanged(tag);
0483 }
0484 
0485 void FakeTagRemovedCommand::doCommand()
0486 {
0487     const Tag tag = getTagByDisplayName(m_tagName);
0488 
0489     Q_ASSERT(tag.isValid());
0490 
0491     Q_EMIT emit_monitoredTagRemoved(tag);
0492 }
0493 
0494 #include "moc_fakeakonadiservercommand.cpp"