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"