| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. |
| 4 | ** Contact: http://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtContacts module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
| 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 http://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at http://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 2.1 or version 3 as published by the Free |
| 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
| 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
| 22 | ** following information to ensure the GNU Lesser General Public License |
| 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
| 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 25 | ** |
| 26 | ** As a special exception, The Qt Company gives you certain additional |
| 27 | ** rights. These rights are described in The Qt Company LGPL Exception |
| 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| 29 | ** |
| 30 | ** $QT_END_LICENSE$ |
| 31 | ** |
| 32 | ****************************************************************************/ |
| 33 | |
| 34 | #include "qcontactmemorybackend_p.h" |
| 35 | |
| 36 | #ifndef QT_NO_DEBUG_STREAM |
| 37 | #include <QtCore/qdebug.h> |
| 38 | #endif |
| 39 | #include <QtCore/qpointer.h> |
| 40 | #include <QtCore/qstringbuilder.h> |
| 41 | #include <QtCore/quuid.h> |
| 42 | |
| 43 | #include <QtContacts/qcontactidfilter.h> |
| 44 | #include <QtContacts/qcontactrequests.h> |
| 45 | #include <QtContacts/qcontacttimestamp.h> |
| 46 | |
| 47 | QT_BEGIN_NAMESPACE_CONTACTS |
| 48 | |
| 49 | QContactManagerEngine* QContactMemoryEngineFactory::engine(const QMap<QString, QString> ¶meters, QContactManager::Error *error) |
| 50 | { |
| 51 | Q_UNUSED(error); |
| 52 | |
| 53 | QContactMemoryEngine *ret = QContactMemoryEngine::createMemoryEngine(parameters); |
| 54 | return ret; |
| 55 | } |
| 56 | |
| 57 | QString QContactMemoryEngineFactory::managerName() const |
| 58 | { |
| 59 | return QString::fromLatin1(str: "memory" ); |
| 60 | } |
| 61 | |
| 62 | /*! |
| 63 | \class QContactMemoryEngine |
| 64 | |
| 65 | \inmodule QtContacts |
| 66 | |
| 67 | \brief The QContactMemoryEngine class provides an in-memory implementation |
| 68 | of a contacts backend. |
| 69 | |
| 70 | \internal |
| 71 | |
| 72 | It may be used as a reference implementation, or when persistent storage is not required. |
| 73 | |
| 74 | During construction, it will load the in-memory data associated with the memory store |
| 75 | identified by the "id" parameter from the given parameters if it exists, or a new, |
| 76 | anonymous store if it does not. |
| 77 | |
| 78 | Data stored in this engine is only available in the current process. |
| 79 | |
| 80 | This engine supports sharing, so an internal reference count is increased |
| 81 | whenever a manager uses this backend, and is decreased when the manager |
| 82 | no longer requires this engine. |
| 83 | */ |
| 84 | |
| 85 | /* static data for manager class */ |
| 86 | QMap<QString, QContactMemoryEngineData*> QContactMemoryEngine::engineDatas; |
| 87 | |
| 88 | /*! |
| 89 | * Factory function for creating a new in-memory backend, based |
| 90 | * on the given \a parameters. |
| 91 | * |
| 92 | * The same engine will be returned for multiple calls with the |
| 93 | * same value for the "id" parameter, while one of them is in scope. |
| 94 | */ |
| 95 | QContactMemoryEngine* QContactMemoryEngine::createMemoryEngine(const QMap<QString, QString> ¶meters) |
| 96 | { |
| 97 | bool anonymous = false; |
| 98 | QString idValue = parameters.value(QStringLiteral("id" )); |
| 99 | if (idValue.isNull() || idValue.isEmpty()) { |
| 100 | // no store given? new, anonymous store. |
| 101 | idValue = QUuid::createUuid().toString(); |
| 102 | anonymous = true; |
| 103 | } |
| 104 | |
| 105 | QContactMemoryEngineData *data = engineDatas.value(akey: idValue); |
| 106 | if (data) { |
| 107 | data->m_refCount.ref(); |
| 108 | } else { |
| 109 | data = new QContactMemoryEngineData(); |
| 110 | data->m_id = idValue; |
| 111 | data->m_anonymous = anonymous; |
| 112 | engineDatas.insert(akey: idValue, avalue: data); |
| 113 | } |
| 114 | return new QContactMemoryEngine(data); |
| 115 | } |
| 116 | |
| 117 | /*! |
| 118 | * Constructs a new in-memory backend which shares the given \a data with |
| 119 | * other shared memory engines. |
| 120 | */ |
| 121 | QContactMemoryEngine::QContactMemoryEngine(QContactMemoryEngineData *data) |
| 122 | : d(data) |
| 123 | { |
| 124 | qRegisterMetaType<QContactAbstractRequest::State>(typeName: "QContactAbstractRequest::State" ); |
| 125 | qRegisterMetaType<QList<QContactId> >(typeName: "QList<QContactId>" ); |
| 126 | qRegisterMetaType<QContactId>(typeName: "QContactId" ); |
| 127 | d->m_managerUri = managerUri(); |
| 128 | d->m_sharedEngines.append(t: this); |
| 129 | |
| 130 | // the default collection always exists. |
| 131 | if (d->m_idToCollectionHash.isEmpty()) { |
| 132 | d->m_managerUri = managerUri(); |
| 133 | const QContactCollectionId defaultId = defaultCollectionId(); |
| 134 | QContactCollection defaultCollection; |
| 135 | defaultCollection.setId(defaultId); |
| 136 | defaultCollection.setMetaData(key: QContactCollection::KeyName, value: QString(QStringLiteral("Default Collection" ))); |
| 137 | d->m_idToCollectionHash.insert(akey: defaultId, avalue: defaultCollection); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /*! Frees any memory used by this engine */ |
| 142 | QContactMemoryEngine::~QContactMemoryEngine() |
| 143 | { |
| 144 | d->m_sharedEngines.removeAll(t: this); |
| 145 | if (!d->m_refCount.deref()) { |
| 146 | engineDatas.remove(akey: d->m_id); |
| 147 | delete d; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | /*! \reimp */ |
| 152 | QString QContactMemoryEngine::managerName() const |
| 153 | { |
| 154 | return QStringLiteral("memory" ); |
| 155 | } |
| 156 | |
| 157 | /*! \reimp */ |
| 158 | QMap<QString, QString> QContactMemoryEngine::managerParameters() const |
| 159 | { |
| 160 | QMap<QString, QString> params; |
| 161 | params.insert(QStringLiteral("id" ), avalue: d->m_id); |
| 162 | return params; |
| 163 | } |
| 164 | |
| 165 | /*! \reimp |
| 166 | */ |
| 167 | QMap<QString, QString> QContactMemoryEngine::idInterpretationParameters() const |
| 168 | { |
| 169 | return managerParameters(); |
| 170 | } |
| 171 | |
| 172 | /*! \reimp */ |
| 173 | bool QContactMemoryEngine::setSelfContactId(const QContactId &contactId, QContactManager::Error *error) |
| 174 | { |
| 175 | if (contactId.isNull() || d->m_contactIds.contains(t: contactId)) { |
| 176 | *error = QContactManager::NoError; |
| 177 | QContactId oldId = d->m_selfContactId; |
| 178 | d->m_selfContactId = contactId; |
| 179 | |
| 180 | QContactChangeSet changeSet; |
| 181 | changeSet.setOldAndNewSelfContactId(QPair<QContactId, QContactId>(oldId, contactId)); |
| 182 | d->emitSharedSignals(cs: &changeSet); |
| 183 | return true; |
| 184 | } |
| 185 | |
| 186 | *error = QContactManager::DoesNotExistError; |
| 187 | return false; |
| 188 | } |
| 189 | |
| 190 | /*! \reimp */ |
| 191 | QContactId QContactMemoryEngine::selfContactId(QContactManager::Error *error) const |
| 192 | { |
| 193 | *error = QContactManager::DoesNotExistError; |
| 194 | if (!d->m_selfContactId.isNull()) |
| 195 | *error = QContactManager::NoError; |
| 196 | return d->m_selfContactId; |
| 197 | } |
| 198 | |
| 199 | /*! \reimp */ |
| 200 | QContact QContactMemoryEngine::contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const |
| 201 | { |
| 202 | Q_UNUSED(fetchHint); // no optimizations are possible in the memory backend; ignore the fetch hint. |
| 203 | int index = d->m_contactIds.indexOf(t: contactId); |
| 204 | if (index != -1) { |
| 205 | // found the contact successfully. |
| 206 | *error = QContactManager::NoError; |
| 207 | return d->m_contacts.at(i: index); |
| 208 | } |
| 209 | |
| 210 | *error = QContactManager::DoesNotExistError; |
| 211 | return QContact(); |
| 212 | } |
| 213 | |
| 214 | /*! \reimp */ |
| 215 | QList<QContactId> QContactMemoryEngine::contactIds(const QContactFilter &filter, const QList<QContactSortOrder> &sortOrders, QContactManager::Error *error) const |
| 216 | { |
| 217 | /* Special case the fast case */ |
| 218 | if (filter.type() == QContactFilter::DefaultFilter && sortOrders.count() == 0) { |
| 219 | return d->m_contactIds; |
| 220 | } else { |
| 221 | QList<QContact> clist = contacts(filter, sortOrders, fetchHint: QContactFetchHint(), error); |
| 222 | |
| 223 | /* Extract the ids */ |
| 224 | QList<QContactId> ids; |
| 225 | foreach (const QContact &c, clist) |
| 226 | ids.append(t: c.id()); |
| 227 | |
| 228 | return ids; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | /*! \reimp */ |
| 233 | QList<QContact> QContactMemoryEngine::contacts(const QContactFilter &filter, const QList<QContactSortOrder> &sortOrders, const QContactFetchHint &fetchHint, QContactManager::Error *error) const |
| 234 | { |
| 235 | Q_UNUSED(fetchHint); // no optimizations are possible in the memory backend; ignore the fetch hint. |
| 236 | Q_UNUSED(error); |
| 237 | |
| 238 | QList<QContact> sorted; |
| 239 | |
| 240 | /* First filter out contacts - check for default filter first */ |
| 241 | if (filter.type() == QContactFilter::DefaultFilter) { |
| 242 | foreach(const QContact&c, d->m_contacts) { |
| 243 | QContactManagerEngine::addSorted(sorted: &sorted,toAdd: c, sortOrders); |
| 244 | } |
| 245 | } else { |
| 246 | foreach(const QContact&c, d->m_contacts) { |
| 247 | if (QContactManagerEngine::testFilter(filter, contact: c)) |
| 248 | QContactManagerEngine::addSorted(sorted: &sorted,toAdd: c, sortOrders); |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | return sorted; |
| 253 | } |
| 254 | |
| 255 | /*! Saves the given contact \a theContact, storing any error to \a error and |
| 256 | filling the \a changeSet with ids of changed contacts as required |
| 257 | Returns true if the operation was successful otherwise false. |
| 258 | */ |
| 259 | bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error) |
| 260 | { |
| 261 | return saveContact(theContact, changeSet, error, mask: QList<QContactDetail::DetailType>()); |
| 262 | } |
| 263 | |
| 264 | /*! \reimp */ |
| 265 | bool QContactMemoryEngine::saveContacts(QList<QContact> *contacts, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error) |
| 266 | { |
| 267 | return saveContacts(contacts, errorMap, error, mask: QList<QContactDetail::DetailType>()); |
| 268 | } |
| 269 | |
| 270 | /*! Removes the contact identified by the given \a contactId, storing any error to \a error and |
| 271 | filling the \a changeSet with ids of changed contacts and relationships as required. |
| 272 | Returns true if the operation was successful otherwise false. |
| 273 | */ |
| 274 | bool QContactMemoryEngine::removeContact(const QContactId &contactId, QContactChangeSet &changeSet, QContactManager::Error *error) |
| 275 | { |
| 276 | int index = d->m_contactIds.indexOf(t: contactId); |
| 277 | |
| 278 | if (index == -1) { |
| 279 | *error = QContactManager::DoesNotExistError; |
| 280 | return false; |
| 281 | } |
| 282 | |
| 283 | // remove the contact from any relationships it was in. |
| 284 | QContact thisContact = d->m_contacts.at(i: index); |
| 285 | QList<QContactRelationship> allRelationships = relationships(relationshipType: QString(), participantId: thisContact.id(), role: QContactRelationship::Either, error); |
| 286 | if (*error != QContactManager::NoError && *error != QContactManager::DoesNotExistError) { |
| 287 | *error = QContactManager::UnspecifiedError; // failed to clean up relationships |
| 288 | return false; |
| 289 | } |
| 290 | |
| 291 | // this is meant to be a transaction, so if any of these fail, we're in BIG TROUBLE. |
| 292 | // a real backend will use DBMS transactions to ensure database integrity. |
| 293 | removeRelationships(relationships: allRelationships, errorMap: 0, error); |
| 294 | |
| 295 | // having cleaned up the relationships, remove the contact from the lists. |
| 296 | d->m_contacts.removeAt(i: index); |
| 297 | d->m_contactIds.removeAt(i: index); |
| 298 | *error = QContactManager::NoError; |
| 299 | |
| 300 | // and if it was the self contact, reset the self contact id |
| 301 | if (contactId == d->m_selfContactId) { |
| 302 | d->m_selfContactId = QContactId(); |
| 303 | changeSet.setOldAndNewSelfContactId(QPair<QContactId, QContactId>(contactId, QContactId())); |
| 304 | } |
| 305 | |
| 306 | changeSet.insertRemovedContact(addedContactId: contactId); |
| 307 | return true; |
| 308 | } |
| 309 | |
| 310 | /*! \reimp */ |
| 311 | bool QContactMemoryEngine::removeContacts(const QList<QContactId> &contactIds, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error) |
| 312 | { |
| 313 | if (contactIds.count() == 0) { |
| 314 | *error = QContactManager::BadArgumentError; |
| 315 | return false; |
| 316 | } |
| 317 | |
| 318 | QContactChangeSet changeSet; |
| 319 | QContactId current; |
| 320 | QContactManager::Error operationError = QContactManager::NoError; |
| 321 | for (int i = 0; i < contactIds.count(); i++) { |
| 322 | current = contactIds.at(i); |
| 323 | if (!removeContact(contactId: current, changeSet, error)) { |
| 324 | operationError = *error; |
| 325 | if (errorMap) |
| 326 | errorMap->insert(akey: i, avalue: operationError); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | *error = operationError; |
| 331 | d->emitSharedSignals(cs: &changeSet); |
| 332 | // return false if some errors occurred |
| 333 | return (*error == QContactManager::NoError); |
| 334 | } |
| 335 | |
| 336 | /*! \reimp */ |
| 337 | QList<QContactRelationship> QContactMemoryEngine::relationships(const QString &relationshipType, const QContactId &participantId, QContactRelationship::Role role, QContactManager::Error *error) const |
| 338 | { |
| 339 | const QContactId defaultId; |
| 340 | QList<QContactRelationship> retn; |
| 341 | for (int i = 0; i < d->m_relationships.size(); i++) { |
| 342 | QContactRelationship curr = d->m_relationships.at(i); |
| 343 | |
| 344 | // check that the relationship type matches |
| 345 | if (curr.relationshipType() != relationshipType && !relationshipType.isEmpty()) |
| 346 | continue; |
| 347 | |
| 348 | // if the participantId argument is default constructed, then the relationship matches. |
| 349 | if (participantId == defaultId) { |
| 350 | retn.append(t: curr); |
| 351 | continue; |
| 352 | } |
| 353 | |
| 354 | // otherwise, check that the participant exists and plays the required role in the relationship. |
| 355 | if (role == QContactRelationship::First && curr.first() == participantId) { |
| 356 | retn.append(t: curr); |
| 357 | } else if (role == QContactRelationship::Second && curr.second() == participantId) { |
| 358 | retn.append(t: curr); |
| 359 | } else if (role == QContactRelationship::Either && (curr.first() == participantId || curr.second() == participantId)) { |
| 360 | retn.append(t: curr); |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | *error = QContactManager::NoError; |
| 365 | if (retn.isEmpty()) |
| 366 | *error = QContactManager::DoesNotExistError; |
| 367 | return retn; |
| 368 | } |
| 369 | |
| 370 | /*! Saves the given relationship \a relationship, storing any error to \a error and |
| 371 | filling the \a changeSet with ids of changed contacts and relationships as required |
| 372 | Returns true if the operation was successful otherwise false. |
| 373 | */ |
| 374 | bool QContactMemoryEngine::saveRelationship(QContactRelationship *relationship, QContactChangeSet &changeSet, QContactManager::Error *error) |
| 375 | { |
| 376 | // Attempt to validate the relationship. |
| 377 | // first, check that the source contact exists and is in this manager. |
| 378 | QString myUri = managerUri(); |
| 379 | int firstContactIndex = d->m_contactIds.indexOf(t: relationship->first()); |
| 380 | if ((!relationship->first().managerUri().isEmpty() && relationship->first().managerUri() != myUri) |
| 381 | ||firstContactIndex == -1) { |
| 382 | *error = QContactManager::InvalidRelationshipError; |
| 383 | return false; |
| 384 | } |
| 385 | |
| 386 | // second, check that the second contact exists (if it's local); we cannot check other managers' contacts. |
| 387 | QContactId dest = relationship->second(); |
| 388 | int secondContactIndex = d->m_contactIds.indexOf(t: dest); |
| 389 | |
| 390 | if (dest.managerUri().isEmpty() || dest.managerUri() == myUri) { |
| 391 | // this entry in the destination list is supposedly stored in this manager. |
| 392 | // check that it exists, and that it isn't the source contact (circular) |
| 393 | if (secondContactIndex == -1 || dest == relationship->first()) { |
| 394 | *error = QContactManager::InvalidRelationshipError; |
| 395 | return false; |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | // the relationship is valid. We need to update the manager URIs in the second contact if it is empty to our URI. |
| 400 | if (dest.managerUri().isEmpty()) { |
| 401 | // need to update the URI |
| 402 | relationship->setSecond(dest); |
| 403 | } |
| 404 | |
| 405 | // check to see if the relationship already exists in the database. If so, replace. |
| 406 | // We do this because we don't want duplicates in our lists / maps of relationships. |
| 407 | *error = QContactManager::NoError; |
| 408 | QList<QContactRelationship> allRelationships = d->m_relationships; |
| 409 | for (int i = 0; i < allRelationships.size(); i++) { |
| 410 | QContactRelationship curr = allRelationships.at(i); |
| 411 | if (curr == *relationship) { |
| 412 | return true; |
| 413 | // TODO: set error to AlreadyExistsError and return false? |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | // no matching relationship; must be new. append it to lists in our map of relationships where required. |
| 418 | QList<QContactRelationship> firstRelationships = d->m_orderedRelationships.value(akey: relationship->first()); |
| 419 | QList<QContactRelationship> secondRelationships = d->m_orderedRelationships.value(akey: relationship->second()); |
| 420 | firstRelationships.append(t: *relationship); |
| 421 | secondRelationships.append(t: *relationship); |
| 422 | d->m_orderedRelationships.insert(akey: relationship->first(), avalue: firstRelationships); |
| 423 | d->m_orderedRelationships.insert(akey: relationship->second(), avalue: secondRelationships); |
| 424 | changeSet.insertAddedRelationshipsContact(affectedContactId: relationship->first()); |
| 425 | changeSet.insertAddedRelationshipsContact(affectedContactId: relationship->second()); |
| 426 | |
| 427 | // update the contacts involved |
| 428 | QContactManagerEngine::setContactRelationships(contact: &d->m_contacts[firstContactIndex], relationships: firstRelationships); |
| 429 | QContactManagerEngine::setContactRelationships(contact: &d->m_contacts[secondContactIndex], relationships: secondRelationships); |
| 430 | |
| 431 | // finally, insert into our list of all relationships, and return. |
| 432 | d->m_relationships.append(t: *relationship); |
| 433 | return true; |
| 434 | } |
| 435 | |
| 436 | /*! \reimp */ |
| 437 | bool QContactMemoryEngine::saveRelationships(QList<QContactRelationship> *relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error) |
| 438 | { |
| 439 | *error = QContactManager::NoError; |
| 440 | QContactManager::Error functionError; |
| 441 | QContactChangeSet changeSet; |
| 442 | |
| 443 | for (int i = 0; i < relationships->size(); i++) { |
| 444 | QContactRelationship curr = relationships->at(i); |
| 445 | saveRelationship(relationship: &curr, changeSet, error: &functionError); |
| 446 | if (functionError != QContactManager::NoError && errorMap) |
| 447 | errorMap->insert(akey: i, avalue: functionError); |
| 448 | |
| 449 | // and replace the current relationship with the updated version. |
| 450 | relationships->replace(i, t: curr); |
| 451 | |
| 452 | // also, update the total error if it did not succeed. |
| 453 | if (functionError != QContactManager::NoError) |
| 454 | *error = functionError; |
| 455 | } |
| 456 | |
| 457 | d->emitSharedSignals(cs: &changeSet); |
| 458 | return (*error == QContactManager::NoError); |
| 459 | } |
| 460 | |
| 461 | /*! Removes the given relationship \a relationship, storing any error to \a error and |
| 462 | filling the \a changeSet with ids of changed contacts and relationships as required |
| 463 | Returns true if the operation was successful otherwise false. |
| 464 | */ |
| 465 | bool QContactMemoryEngine::removeRelationship(const QContactRelationship &relationship, QContactChangeSet &changeSet, QContactManager::Error *error) |
| 466 | { |
| 467 | // attempt to remove it from our list of relationships. |
| 468 | if (!d->m_relationships.removeOne(t: relationship)) { |
| 469 | *error = QContactManager::DoesNotExistError; |
| 470 | return false; |
| 471 | } |
| 472 | |
| 473 | // if that worked, then we need to remove it from the two locations in our map, also. |
| 474 | QList<QContactRelationship> firstRelationships = d->m_orderedRelationships.value(akey: relationship.first()); |
| 475 | QList<QContactRelationship> secondRelationships = d->m_orderedRelationships.value(akey: relationship.second()); |
| 476 | firstRelationships.removeOne(t: relationship); |
| 477 | secondRelationships.removeOne(t: relationship); |
| 478 | d->m_orderedRelationships.insert(akey: relationship.first(), avalue: firstRelationships); |
| 479 | d->m_orderedRelationships.insert(akey: relationship.second(), avalue: secondRelationships); |
| 480 | |
| 481 | // Update the contacts as well |
| 482 | int firstContactIndex = d->m_contactIds.indexOf(t: relationship.first()); |
| 483 | int secondContactIndex = relationship.second().managerUri() == managerUri() ? d->m_contactIds.indexOf(t: relationship.second()) : -1; |
| 484 | if (firstContactIndex != -1) |
| 485 | QContactMemoryEngine::setContactRelationships(contact: &d->m_contacts[firstContactIndex], relationships: firstRelationships); |
| 486 | if (secondContactIndex != -1) |
| 487 | QContactMemoryEngine::setContactRelationships(contact: &d->m_contacts[secondContactIndex], relationships: secondRelationships); |
| 488 | |
| 489 | // set our changes, and return. |
| 490 | changeSet.insertRemovedRelationshipsContact(affectedContactId: relationship.first()); |
| 491 | changeSet.insertRemovedRelationshipsContact(affectedContactId: relationship.second()); |
| 492 | *error = QContactManager::NoError; |
| 493 | return true; |
| 494 | } |
| 495 | |
| 496 | /*! \reimp */ |
| 497 | bool QContactMemoryEngine::removeRelationships(const QList<QContactRelationship> &relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error) |
| 498 | { |
| 499 | QContactManager::Error functionError; |
| 500 | QContactChangeSet cs; |
| 501 | for (int i = 0; i < relationships.size(); i++) { |
| 502 | removeRelationship(relationship: relationships.at(i), changeSet&: cs, error: &functionError); |
| 503 | |
| 504 | // update the total error if it did not succeed. |
| 505 | if (functionError != QContactManager::NoError) { |
| 506 | if (errorMap) |
| 507 | errorMap->insert(akey: i, avalue: functionError); |
| 508 | *error = functionError; |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | d->emitSharedSignals(cs: &cs); |
| 513 | return (*error == QContactManager::NoError); |
| 514 | } |
| 515 | |
| 516 | QContactCollectionId QContactMemoryEngine::defaultCollectionId() const |
| 517 | { |
| 518 | static const QByteArray id("Personal" ); |
| 519 | return collectionId(localId: id); |
| 520 | } |
| 521 | |
| 522 | QContactCollection QContactMemoryEngine::collection(const QContactCollectionId &collectionId, QContactManager::Error *error) |
| 523 | { |
| 524 | if (d->m_idToCollectionHash.contains(akey: collectionId)) { |
| 525 | *error = QContactManager::NoError; |
| 526 | return d->m_idToCollectionHash.value(akey: collectionId); |
| 527 | } |
| 528 | |
| 529 | *error = QContactManager::DoesNotExistError; |
| 530 | return QContactCollection(); |
| 531 | } |
| 532 | |
| 533 | QList<QContactCollection> QContactMemoryEngine::collections(QContactManager::Error *error) |
| 534 | { |
| 535 | Q_ASSERT(!d->m_idToCollectionHash.isEmpty()); |
| 536 | *error = QContactManager::NoError; |
| 537 | return d->m_idToCollectionHash.values(); |
| 538 | } |
| 539 | |
| 540 | bool QContactMemoryEngine::saveCollection(QContactCollection *collection, QContactManager::Error *error) |
| 541 | { |
| 542 | QContactCollectionId collectionId = collection->id(); |
| 543 | |
| 544 | QContactCollectionChangeSet cs; |
| 545 | if (d->m_idToCollectionHash.contains(akey: collectionId)) { |
| 546 | // this collection already exists. update our internal list |
| 547 | // if the collection has been modified. |
| 548 | if (d->m_idToCollectionHash.value(akey: collectionId) == *collection) { |
| 549 | *error = QContactManager::NoError; |
| 550 | return true; |
| 551 | } |
| 552 | |
| 553 | cs.insertChangedCollection(collectionId); |
| 554 | } else { |
| 555 | // this must be a new collection. check that the id is null. |
| 556 | if (!collectionId.isNull() && collectionId.managerUri() != d->m_managerUri) { |
| 557 | // nope, this collection belongs in another manager, or has been deleted. |
| 558 | *error = QContactManager::DoesNotExistError; |
| 559 | return false; |
| 560 | } |
| 561 | |
| 562 | // this is a new collection with a null id; create a new id, add it to our list. |
| 563 | QUuid id = QUuid::createUuid(); |
| 564 | collectionId = this->collectionId(localId: id.toByteArray()); |
| 565 | collection->setId(collectionId); |
| 566 | cs.insertAddedCollection(collectionId); |
| 567 | } |
| 568 | |
| 569 | d->m_idToCollectionHash.insert(akey: collectionId, avalue: *collection); |
| 570 | d->emitSharedSignals(cs: &cs); |
| 571 | *error = QContactManager::NoError; |
| 572 | return true; |
| 573 | } |
| 574 | |
| 575 | bool QContactMemoryEngine::removeCollection(const QContactCollectionId &collectionId, QContactManager::Error *error) |
| 576 | { |
| 577 | if (collectionId == defaultCollectionId()) { |
| 578 | // attempting to remove the default collection. this is not allowed in the memory engine. |
| 579 | *error = QContactManager::PermissionsError; |
| 580 | return false; |
| 581 | } |
| 582 | |
| 583 | // try to find the collection to remove it (and the items it contains) |
| 584 | if (d->m_idToCollectionHash.contains(akey: collectionId)) { |
| 585 | // found the collection to remove. remove the items in the collection. |
| 586 | const QList<QContactId> contactsToRemove = d->m_contactsInCollections.values(akey: collectionId); |
| 587 | if (!contactsToRemove.isEmpty()) { |
| 588 | QMap<int, QContactManager::Error> errorMap; |
| 589 | if (!removeContacts(contactIds: contactsToRemove, errorMap: &errorMap, error)) { |
| 590 | // without transaction support, we can't back out. but the operation should fail. |
| 591 | return false; |
| 592 | } |
| 593 | } |
| 594 | |
| 595 | // now remove the collection from our lists. |
| 596 | d->m_idToCollectionHash.remove(akey: collectionId); |
| 597 | d->m_contactsInCollections.remove(akey: collectionId); |
| 598 | QContactCollectionChangeSet cs; |
| 599 | cs.insertRemovedCollection(collectionId); |
| 600 | d->emitSharedSignals(cs: &cs); |
| 601 | *error = QContactManager::NoError; |
| 602 | return true; |
| 603 | } |
| 604 | |
| 605 | // the collection doesn't exist... |
| 606 | *error = QContactManager::DoesNotExistError; |
| 607 | return false; |
| 608 | } |
| 609 | |
| 610 | /*! \reimp */ |
| 611 | void QContactMemoryEngine::requestDestroyed(QContactAbstractRequest *req) |
| 612 | { |
| 613 | Q_UNUSED(req); |
| 614 | } |
| 615 | |
| 616 | /*! \reimp */ |
| 617 | bool QContactMemoryEngine::startRequest(QContactAbstractRequest *req) |
| 618 | { |
| 619 | updateRequestState(req, state: QContactAbstractRequest::ActiveState); |
| 620 | performAsynchronousOperation(request: req); |
| 621 | |
| 622 | return true; |
| 623 | } |
| 624 | |
| 625 | bool QContactMemoryEngine::cancelRequest(QContactAbstractRequest *req) |
| 626 | { |
| 627 | Q_UNUSED(req); // we can't cancel since we complete immediately |
| 628 | return false; |
| 629 | } |
| 630 | |
| 631 | /*! \reimp */ |
| 632 | bool QContactMemoryEngine::waitForRequestFinished(QContactAbstractRequest *req, int msecs) |
| 633 | { |
| 634 | // in our implementation, we always complete any operation we start. |
| 635 | Q_UNUSED(msecs); |
| 636 | Q_UNUSED(req); |
| 637 | |
| 638 | return true; |
| 639 | } |
| 640 | |
| 641 | /*! |
| 642 | * This slot is called some time after an asynchronous request is started. |
| 643 | * It performs the required operation, sets the result and returns. |
| 644 | */ |
| 645 | void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest *currentRequest) |
| 646 | { |
| 647 | // store up changes, and emit signals once at the end of the (possibly batch) operation. |
| 648 | QContactChangeSet changeSet; |
| 649 | |
| 650 | // Now perform the active request and emit required signals. |
| 651 | Q_ASSERT(currentRequest->state() == QContactAbstractRequest::ActiveState); |
| 652 | switch (currentRequest->type()) { |
| 653 | case QContactAbstractRequest::ContactFetchRequest: |
| 654 | { |
| 655 | QContactFetchRequest *r = static_cast<QContactFetchRequest*>(currentRequest); |
| 656 | QContactFilter filter = r->filter(); |
| 657 | QList<QContactSortOrder> sorting = r->sorting(); |
| 658 | QContactFetchHint fetchHint = r->fetchHint(); |
| 659 | |
| 660 | QContactManager::Error operationError = QContactManager::NoError; |
| 661 | QList<QContact> requestedContacts = contacts(filter, sortOrders: sorting, fetchHint, error: &operationError); |
| 662 | |
| 663 | // update the request with the results. |
| 664 | if (!requestedContacts.isEmpty() || operationError != QContactManager::NoError) |
| 665 | updateContactFetchRequest(req: r, result: requestedContacts, error: operationError, QContactAbstractRequest::FinishedState); |
| 666 | else |
| 667 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 668 | } |
| 669 | break; |
| 670 | |
| 671 | case QContactAbstractRequest::ContactFetchByIdRequest: |
| 672 | { |
| 673 | QContactFetchByIdRequest *r = static_cast<QContactFetchByIdRequest*>(currentRequest); |
| 674 | QContactIdFilter idFilter; |
| 675 | idFilter.setIds(r->contactIds()); |
| 676 | QList<QContactSortOrder> sorting; |
| 677 | QContactFetchHint fetchHint = r->fetchHint(); |
| 678 | QContactManager::Error error = QContactManager::NoError; |
| 679 | QList<QContact> requestedContacts = contacts(filter: idFilter, sortOrders: sorting, fetchHint, error: &error); |
| 680 | // Build an index into the results |
| 681 | QHash<QContactId, int> idMap; // value is index into unsorted |
| 682 | if (error == QContactManager::NoError) { |
| 683 | for (int i = 0; i < requestedContacts.size(); i++) { |
| 684 | idMap.insert(akey: requestedContacts[i].id(), avalue: i); |
| 685 | } |
| 686 | } |
| 687 | // Find the order in which the results should be presented |
| 688 | // Build up the results and errors |
| 689 | QList<QContact> results; |
| 690 | QMap<int, QContactManager::Error> errorMap; |
| 691 | int index = 0; |
| 692 | foreach (const QContactId &id, r->contactIds()) { |
| 693 | if (!idMap.contains(akey: id)) { |
| 694 | errorMap.insert(akey: index, avalue: QContactManager::DoesNotExistError); |
| 695 | error = QContactManager::DoesNotExistError; |
| 696 | results.append(t: QContact()); |
| 697 | } else { |
| 698 | results.append(t: requestedContacts[idMap[id]]); |
| 699 | } |
| 700 | index++; |
| 701 | } |
| 702 | |
| 703 | // update the request with the results. |
| 704 | if (!requestedContacts.isEmpty() || error != QContactManager::NoError) |
| 705 | QContactManagerEngine::updateContactFetchByIdRequest(req: r, result: results, error, errorMap, QContactAbstractRequest::FinishedState); |
| 706 | else |
| 707 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 708 | } |
| 709 | break; |
| 710 | |
| 711 | case QContactAbstractRequest::ContactIdFetchRequest: |
| 712 | { |
| 713 | QContactIdFetchRequest *r = static_cast<QContactIdFetchRequest*>(currentRequest); |
| 714 | QContactFilter filter = r->filter(); |
| 715 | QList<QContactSortOrder> sorting = r->sorting(); |
| 716 | |
| 717 | QContactManager::Error operationError = QContactManager::NoError; |
| 718 | QList<QContactId> requestedContactIds = contactIds(filter, sortOrders: sorting, error: &operationError); |
| 719 | |
| 720 | if (!requestedContactIds.isEmpty() || operationError != QContactManager::NoError) |
| 721 | updateContactIdFetchRequest(req: r, result: requestedContactIds, error: operationError, QContactAbstractRequest::FinishedState); |
| 722 | else |
| 723 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 724 | } |
| 725 | break; |
| 726 | |
| 727 | case QContactAbstractRequest::ContactSaveRequest: |
| 728 | { |
| 729 | QContactSaveRequest *r = static_cast<QContactSaveRequest*>(currentRequest); |
| 730 | QList<QContact> contacts = r->contacts(); |
| 731 | |
| 732 | QContactManager::Error operationError = QContactManager::NoError; |
| 733 | QMap<int, QContactManager::Error> errorMap; |
| 734 | saveContacts(contacts: &contacts, errorMap: &errorMap, error: &operationError, mask: r->typeMask()); |
| 735 | |
| 736 | updateContactSaveRequest(req: r, result: contacts, error: operationError, errorMap, QContactAbstractRequest::FinishedState); |
| 737 | } |
| 738 | break; |
| 739 | |
| 740 | case QContactAbstractRequest::ContactRemoveRequest: |
| 741 | { |
| 742 | // this implementation provides scant information to the user |
| 743 | // the operation either succeeds (all contacts matching the filter were removed) |
| 744 | // or it fails (one or more contacts matching the filter could not be removed) |
| 745 | // if a failure occurred, the request error will be set to the most recent |
| 746 | // error that occurred during the remove operation. |
| 747 | QContactRemoveRequest *r = static_cast<QContactRemoveRequest*>(currentRequest); |
| 748 | QContactManager::Error operationError = QContactManager::NoError; |
| 749 | QList<QContactId> contactsToRemove = r->contactIds(); |
| 750 | QMap<int, QContactManager::Error> errorMap; |
| 751 | |
| 752 | for (int i = 0; i < contactsToRemove.size(); i++) { |
| 753 | QContactManager::Error tempError; |
| 754 | removeContact(contactId: contactsToRemove.at(i), changeSet, error: &tempError); |
| 755 | |
| 756 | if (tempError != QContactManager::NoError) { |
| 757 | errorMap.insert(akey: i, avalue: tempError); |
| 758 | operationError = tempError; |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | if (!errorMap.isEmpty() || operationError != QContactManager::NoError) |
| 763 | updateContactRemoveRequest(req: r, error: operationError, errorMap, QContactAbstractRequest::FinishedState); |
| 764 | else |
| 765 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 766 | } |
| 767 | break; |
| 768 | |
| 769 | case QContactAbstractRequest::RelationshipFetchRequest: |
| 770 | { |
| 771 | QContactRelationshipFetchRequest *r = static_cast<QContactRelationshipFetchRequest*>(currentRequest); |
| 772 | QContactManager::Error operationError = QContactManager::NoError; |
| 773 | QList<QContactManager::Error> operationErrors; |
| 774 | QList<QContactRelationship> allRelationships = relationships(relationshipType: QString(), participantId: QContactId(), role: QContactRelationship::Either, error: &operationError); |
| 775 | QList<QContactRelationship> requestedRelationships; |
| 776 | |
| 777 | // select the requested relationships. |
| 778 | for (int i = 0; i < allRelationships.size(); i++) { |
| 779 | QContactRelationship currRel = allRelationships.at(i); |
| 780 | if (r->first() != QContactId() && r->first() != currRel.first()) |
| 781 | continue; |
| 782 | if (r->second() != QContactId() && r->second() != currRel.second()) |
| 783 | continue; |
| 784 | if (!r->relationshipType().isEmpty() && r->relationshipType() != currRel.relationshipType()) |
| 785 | continue; |
| 786 | requestedRelationships.append(t: currRel); |
| 787 | } |
| 788 | |
| 789 | // update the request with the results. |
| 790 | if (!requestedRelationships.isEmpty() || operationError != QContactManager::NoError) |
| 791 | updateRelationshipFetchRequest(req: r, result: requestedRelationships, error: operationError, QContactAbstractRequest::FinishedState); |
| 792 | else |
| 793 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 794 | } |
| 795 | break; |
| 796 | |
| 797 | case QContactAbstractRequest::RelationshipRemoveRequest: |
| 798 | { |
| 799 | QContactRelationshipRemoveRequest *r = static_cast<QContactRelationshipRemoveRequest*>(currentRequest); |
| 800 | QContactManager::Error operationError = QContactManager::NoError; |
| 801 | QList<QContactRelationship> relationshipsToRemove = r->relationships(); |
| 802 | QMap<int, QContactManager::Error> errorMap; |
| 803 | |
| 804 | removeRelationships(relationships: r->relationships(), errorMap: &errorMap, error: &operationError); |
| 805 | |
| 806 | if (!errorMap.isEmpty() || operationError != QContactManager::NoError) |
| 807 | updateRelationshipRemoveRequest(req: r, error: operationError, errorMap, QContactAbstractRequest::FinishedState); |
| 808 | else |
| 809 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 810 | } |
| 811 | break; |
| 812 | |
| 813 | case QContactAbstractRequest::RelationshipSaveRequest: |
| 814 | { |
| 815 | QContactRelationshipSaveRequest *r = static_cast<QContactRelationshipSaveRequest*>(currentRequest); |
| 816 | QContactManager::Error operationError = QContactManager::NoError; |
| 817 | QMap<int, QContactManager::Error> errorMap; |
| 818 | QList<QContactRelationship> requestRelationships = r->relationships(); |
| 819 | |
| 820 | saveRelationships(relationships: &requestRelationships, errorMap: &errorMap, error: &operationError); |
| 821 | |
| 822 | // update the request with the results. |
| 823 | updateRelationshipSaveRequest(req: r, result: requestRelationships, error: operationError, errorMap, QContactAbstractRequest::FinishedState); |
| 824 | } |
| 825 | break; |
| 826 | |
| 827 | case QContactAbstractRequest::CollectionFetchRequest: |
| 828 | { |
| 829 | QContactCollectionFetchRequest* r = static_cast<QContactCollectionFetchRequest*>(currentRequest); |
| 830 | QContactManager::Error operationError = QContactManager::NoError; |
| 831 | QList<QContactCollection> requestedContactCollections = collections(error: &operationError); |
| 832 | |
| 833 | // update the request with the results. |
| 834 | updateCollectionFetchRequest(request: r, result: requestedContactCollections, error: operationError, newState: QContactAbstractRequest::FinishedState); |
| 835 | } |
| 836 | break; |
| 837 | |
| 838 | case QContactAbstractRequest::CollectionSaveRequest: |
| 839 | { |
| 840 | QContactCollectionSaveRequest* r = static_cast<QContactCollectionSaveRequest*>(currentRequest); |
| 841 | QList<QContactCollection> collections = r->collections(); |
| 842 | QList<QContactCollection> retn; |
| 843 | |
| 844 | QContactManager::Error operationError = QContactManager::NoError; |
| 845 | QMap<int, QContactManager::Error> errorMap; |
| 846 | for (int i = 0; i < collections.size(); ++i) { |
| 847 | QContactManager::Error tempError = QContactManager::NoError; |
| 848 | QContactCollection curr = collections.at(i); |
| 849 | if (!saveCollection(collection: &curr, error: &tempError)) { |
| 850 | errorMap.insert(akey: i, avalue: tempError); |
| 851 | operationError = tempError; |
| 852 | } |
| 853 | retn.append(t: curr); |
| 854 | } |
| 855 | |
| 856 | updateCollectionSaveRequest(request: r, result: retn, error: operationError, errorMap, newState: QContactAbstractRequest::FinishedState); |
| 857 | } |
| 858 | break; |
| 859 | |
| 860 | case QContactAbstractRequest::CollectionRemoveRequest: |
| 861 | { |
| 862 | // removes the collections identified in the list of ids. |
| 863 | QContactCollectionRemoveRequest* r = static_cast<QContactCollectionRemoveRequest*>(currentRequest); |
| 864 | QContactManager::Error operationError = QContactManager::NoError; |
| 865 | QList<QContactCollectionId> collectionsToRemove = r->collectionIds(); |
| 866 | QMap<int, QContactManager::Error> errorMap; |
| 867 | |
| 868 | for (int i = 0; i < collectionsToRemove.size(); i++) { |
| 869 | QContactManager::Error tempError = QContactManager::NoError; |
| 870 | removeCollection(collectionId: collectionsToRemove.at(i), error: &tempError); |
| 871 | |
| 872 | if (tempError != QContactManager::NoError) { |
| 873 | errorMap.insert(akey: i, avalue: tempError); |
| 874 | operationError = tempError; |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | if (!errorMap.isEmpty() || operationError != QContactManager::NoError) |
| 879 | updateCollectionRemoveRequest(request: r, error: operationError, errorMap, newState: QContactAbstractRequest::FinishedState); |
| 880 | else |
| 881 | updateRequestState(req: currentRequest, state: QContactAbstractRequest::FinishedState); |
| 882 | } |
| 883 | break; |
| 884 | |
| 885 | |
| 886 | default: // unknown request type. |
| 887 | break; |
| 888 | } |
| 889 | |
| 890 | // now emit any signals we have to emit |
| 891 | d->emitSharedSignals(cs: &changeSet); |
| 892 | } |
| 893 | |
| 894 | void QContactMemoryEngine::partiallySyncDetails(QContact *to, const QContact &from, const QList<QContactDetail::DetailType> &mask) |
| 895 | { |
| 896 | // these details in old contact |
| 897 | QList<QContactDetail> fromDetails; |
| 898 | // these details in new contact |
| 899 | QList<QContactDetail> toDetails; |
| 900 | // Collect details that match mask |
| 901 | foreach (QContactDetail::DetailType type, mask) { |
| 902 | fromDetails.append(t: from.details(type)); |
| 903 | toDetails.append(t: to->details(type)); |
| 904 | } |
| 905 | // check details to remove |
| 906 | foreach (QContactDetail detail, toDetails) { |
| 907 | if (!fromDetails.contains(t: detail)) |
| 908 | to->removeDetail(detail: &detail); |
| 909 | } |
| 910 | // check details to save |
| 911 | foreach (QContactDetail detail, fromDetails) { |
| 912 | if (!toDetails.contains(t: detail)) |
| 913 | to->saveDetail(detail: &detail); |
| 914 | } |
| 915 | } |
| 916 | |
| 917 | /*! |
| 918 | * \reimp |
| 919 | */ |
| 920 | bool QContactMemoryEngine::isRelationshipTypeSupported(const QString& relationshipType, QContactType::TypeValues contactType) const |
| 921 | { |
| 922 | // the memory backend supports arbitrary relationship types |
| 923 | // but some relationship types don't make sense for groups or facets. |
| 924 | if (contactType == QContactType::TypeGroup || contactType == QContactType::TypeFacet) { |
| 925 | if (relationshipType == QContactRelationship::HasSpouse() || relationshipType == QContactRelationship::HasAssistant()) { |
| 926 | return false; |
| 927 | } |
| 928 | |
| 929 | if (contactType == QContactType::TypeGroup) { |
| 930 | if (relationshipType == QContactRelationship::Aggregates()) |
| 931 | return false; |
| 932 | } else { |
| 933 | if (relationshipType == QContactRelationship::HasMember()) |
| 934 | return false; |
| 935 | } |
| 936 | } |
| 937 | |
| 938 | // all other relationship types for all contact types are supported. |
| 939 | return true; |
| 940 | } |
| 941 | |
| 942 | /*! |
| 943 | * \reimp |
| 944 | */ |
| 945 | QList<QVariant::Type> QContactMemoryEngine::supportedDataTypes() const |
| 946 | { |
| 947 | QList<QVariant::Type> st; |
| 948 | st.append(t: QVariant::String); |
| 949 | st.append(t: QVariant::Date); |
| 950 | st.append(t: QVariant::DateTime); |
| 951 | st.append(t: QVariant::Time); |
| 952 | st.append(t: QVariant::Bool); |
| 953 | st.append(t: QVariant::Char); |
| 954 | st.append(t: QVariant::Int); |
| 955 | st.append(t: QVariant::UInt); |
| 956 | st.append(t: QVariant::LongLong); |
| 957 | st.append(t: QVariant::ULongLong); |
| 958 | st.append(t: QVariant::Double); |
| 959 | |
| 960 | return st; |
| 961 | } |
| 962 | |
| 963 | /*! |
| 964 | * The function returns true if the backend natively supports the given filter \a filter, otherwise false. |
| 965 | */ |
| 966 | bool QContactMemoryEngine::isFilterSupported(const QContactFilter &filter) const |
| 967 | { |
| 968 | Q_UNUSED(filter); |
| 969 | // Until we add hashes for common stuff, fall back to slow code |
| 970 | return false; |
| 971 | } |
| 972 | |
| 973 | bool QContactMemoryEngine::saveContacts(QList<QContact> *contacts, QMap<int, QContactManager::Error> *errorMap, |
| 974 | QContactManager::Error *error, const QList<QContactDetail::DetailType> &mask) |
| 975 | { |
| 976 | if (!contacts) { |
| 977 | *error = QContactManager::BadArgumentError; |
| 978 | return false; |
| 979 | } |
| 980 | |
| 981 | QContactChangeSet changeSet; |
| 982 | QContact current; |
| 983 | QContactManager::Error operationError = QContactManager::NoError; |
| 984 | for (int i = 0; i < contacts->count(); i++) { |
| 985 | current = contacts->at(i); |
| 986 | if (!saveContact(theContact: ¤t, changeSet, error, mask)) { |
| 987 | operationError = *error; |
| 988 | if (errorMap) |
| 989 | errorMap->insert(akey: i, avalue: operationError); |
| 990 | } else { |
| 991 | (*contacts)[i] = current; |
| 992 | } |
| 993 | } |
| 994 | |
| 995 | *error = operationError; |
| 996 | d->emitSharedSignals(cs: &changeSet); |
| 997 | // return false if some error occurred |
| 998 | return (*error == QContactManager::NoError); |
| 999 | } |
| 1000 | |
| 1001 | bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &changeSet, |
| 1002 | QContactManager::Error *error, const QList<QContactDetail::DetailType> &mask) |
| 1003 | { |
| 1004 | // ensure that the contact's details conform to their definitions |
| 1005 | if (!validateContact(contact: *theContact, error)) { |
| 1006 | return false; |
| 1007 | } |
| 1008 | |
| 1009 | QContactId id(theContact->id()); |
| 1010 | if (!id.managerUri().isEmpty() && id.managerUri() != managerUri()) { |
| 1011 | // the contact doesn't belong to this manager |
| 1012 | *error = QContactManager::DoesNotExistError; |
| 1013 | return false; |
| 1014 | } |
| 1015 | |
| 1016 | // check to see if this contact already exists |
| 1017 | int index = d->m_contactIds.indexOf(t: id); |
| 1018 | if (index != -1) { |
| 1019 | /* We also need to check that there are no modified create only details */ |
| 1020 | QContact oldContact = d->m_contacts.at(i: index); |
| 1021 | |
| 1022 | if (oldContact.type() != theContact->type()) { |
| 1023 | *error = QContactManager::AlreadyExistsError; |
| 1024 | return false; |
| 1025 | } |
| 1026 | |
| 1027 | // check if this is partial save |
| 1028 | if (!mask.isEmpty()) { |
| 1029 | QContact tempContact = oldContact; |
| 1030 | partiallySyncDetails(to: &tempContact, from: *theContact, mask); |
| 1031 | *theContact = tempContact; |
| 1032 | } |
| 1033 | |
| 1034 | QContactTimestamp ts = theContact->detail(type: QContactTimestamp::Type); |
| 1035 | ts.setLastModified(QDateTime::currentDateTime()); |
| 1036 | QContactManagerEngine::setDetailAccessConstraints(detail: &ts, constraints: QContactDetail::ReadOnly | QContactDetail::Irremovable); |
| 1037 | theContact->saveDetail(detail: &ts); |
| 1038 | |
| 1039 | // Looks ok, so continue |
| 1040 | d->m_contacts.replace(i: index, t: *theContact); |
| 1041 | changeSet.insertChangedContact(addedContactId: theContact->id(), typesChanged: mask); |
| 1042 | } else { |
| 1043 | // id does not exist; if not zero, fail. |
| 1044 | QContactId newId; |
| 1045 | if (theContact->id() != QContactId() && theContact->id() != newId) { |
| 1046 | // the ID is not empty, and it doesn't identify an existing contact in our database either. |
| 1047 | *error = QContactManager::DoesNotExistError; |
| 1048 | return false; |
| 1049 | } |
| 1050 | |
| 1051 | // check the contact collection |
| 1052 | QContactCollectionId collectionId = theContact->collectionId(); |
| 1053 | // if is null use default collection |
| 1054 | if (collectionId.isNull()) { |
| 1055 | collectionId = this->defaultCollectionId(); |
| 1056 | theContact->setCollectionId(collectionId); |
| 1057 | } else { |
| 1058 | // check if the collection exists |
| 1059 | QContactCollection collection = this->collection(collectionId, error); |
| 1060 | if (collection.id().isNull()) { |
| 1061 | return false; |
| 1062 | } |
| 1063 | } |
| 1064 | |
| 1065 | // check if this is partial save |
| 1066 | if (!mask.isEmpty()) { |
| 1067 | QContact tempContact; |
| 1068 | partiallySyncDetails(to: &tempContact, from: *theContact, mask); |
| 1069 | *theContact = tempContact; |
| 1070 | } |
| 1071 | |
| 1072 | /* New contact */ |
| 1073 | QContactTimestamp ts = theContact->detail(type: QContactTimestamp::Type); |
| 1074 | ts.setLastModified(QDateTime::currentDateTime()); |
| 1075 | ts.setCreated(ts.lastModified()); |
| 1076 | setDetailAccessConstraints(detail: &ts, constraints: QContactDetail::ReadOnly | QContactDetail::Irremovable); |
| 1077 | theContact->saveDetail(detail: &ts); |
| 1078 | |
| 1079 | // update the contact item - set its ID |
| 1080 | QContactId newContactId = contactId(localId: QByteArray(reinterpret_cast<const char *>(&d->m_nextContactId), sizeof(quint32))); |
| 1081 | ++(d->m_nextContactId); |
| 1082 | theContact->setId(newContactId); |
| 1083 | |
| 1084 | // finally, add the contact to our internal lists and return |
| 1085 | d->m_contacts.append(t: *theContact); // add contact to list |
| 1086 | d->m_contactIds.append(t: theContact->id()); // track the contact id. |
| 1087 | d->m_contactsInCollections.insert(akey: collectionId, avalue: newContactId); // link contact to collection |
| 1088 | |
| 1089 | changeSet.insertAddedContact(addedContactId: theContact->id()); |
| 1090 | } |
| 1091 | |
| 1092 | *error = QContactManager::NoError; // successful. |
| 1093 | return true; |
| 1094 | } |
| 1095 | |
| 1096 | #include "moc_qcontactmemorybackend_p.cpp" |
| 1097 | |
| 1098 | QT_END_NAMESPACE_CONTACTS |
| 1099 | |