Revert "New: Introduce batch size engine parameters"
[qtcontacts-tracker:murrayc-qtcontacts-tracker-for-merge-requests2.git] / src / engine / engine.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include "engine.h"
42 #include "engine_p.h"
43
44 #include "contactfetchrequest.h"
45 #include "contactfetchrequest2.h"
46 #include "contactidfetchrequest.h"
47 #include "contactremoverequest.h"
48 #include "contactremoverequest2.h"
49 #include "contactsaverequest.h"
50 #include "contactsaverequest2.h"
51 #include "relationshipfetchrequest.h"
52 #include "relationshipsaverequest.h"
53
54 #include <dao/contactdetail.h>
55 #include <dao/contactdetailschema.h>
56 #include <dao/querybuilder.h>
57 #include <dao/trackerchangelistener.h>
58
59 #include <qcontactdetails.h>
60
61 static QContactManager::Error *const noErrorResult(0);
62 static QThreadStorage<DisplayNameDetailStack*> displayNameStack;
63
64 #define REPORT_UNSUPPORTED_FUNCTION(error) do {\
65     propagate(QContactManager::NotSupportedError, error); \
66     qWarning() << Q_FUNC_INFO << "this function is not supported";\
67 } while(0)
68
69 QTM_USE_NAMESPACE;
70
71 QContactTrackerEngineData::QContactTrackerEngineData(const QMap<QString, QString>& parameters,
72                                                      const QString& engineName, int engineVersion)
73     : QSharedData(), m_refCount(QAtomicInt(1)), m_engineName(engineName),
74     m_engineVersion(engineVersion), m_requestTimeout(10000),
75     m_parameters(parameters)
76 {
77     static const QMap<QString, QString> environmentParameters(readEnvironment());
78     m_parameters.unite(environmentParameters);
79     parseParameters();
80
81     setupDisplayNameFields();
82 }
83
84 QContactTrackerEngineData::QContactTrackerEngineData(const QContactTrackerEngineData& other)
85     : QSharedData(other), m_refCount(QAtomicInt(1)),
86     m_engineName(other.m_engineName),
87     m_engineVersion(other.m_engineVersion),
88     m_requestTimeout(other.m_requestTimeout),
89     m_schema(other.m_schema),
90     m_parameters(other.m_parameters),
91     m_requests(other.m_requests),
92     m_debugFlags(other.m_debugFlags),
93     m_displayNameDetails(other.m_displayNameDetails)
94 {
95 }
96
97 QContactTrackerEngineData::~QContactTrackerEngineData()
98 {
99 }
100
101 QMap<QString, QString>
102 QContactTrackerEngineData::readEnvironment()
103 {
104     const QStringList tokenList(QProcessEnvironment::systemEnvironment().
105                                 value(QLatin1String("QT_CONTACTS_TRACKER")).
106                                 replace(QChar(' '), QString()).
107                                 split(QLatin1String(";")));
108
109     QMap<QString, QString> parameters;
110
111     foreach(const QString &token, tokenList) {
112         const int eq = token.indexOf(QLatin1Char('='));
113         const QString key((eq >= 0 ? token.left(eq) : token).trimmed());
114         const QString value((eq >= 0 ? token.mid(eq + 1).trimmed() : QString()));
115
116         if (not key.isEmpty()) {
117             parameters.insert(key, value);
118         }
119     }
120
121     return parameters;
122 }
123
124 void
125 QContactTrackerEngineData::parseParameters()
126 {
127     QContactTrackerEngine::DebugFlags debugFlags;
128     QContactTrackerEngine::DebugFlags queryBuilderFlags;
129
130     if (not m_parameters.contains(QLatin1String("query-builder"))) {
131         m_parameters.insert(QLatin1String("query-builder"), QLatin1String("save"));
132     }
133
134     for(QMap<QString, QString>::const_iterator i(m_parameters.begin()); i != m_parameters.end(); ++i) {
135         if (QLatin1String("timeout") == i.key()) {
136             bool ok; const int value(i.value().toInt(&ok));
137
138             if (ok) {
139                 m_requestTimeout = value;
140             } else {
141                 qWarning()
142                         << Q_FUNC_INFO << qPrintable(i.key())
143                         << "- Invalid argument:" << i.value();
144             }
145
146             continue;
147         }
148
149         if (QLatin1String("writeback") == i.key()) {
150             const QSet<QString> values(i.value().split(QLatin1Char(',')).toSet());
151             const bool all(values.contains(QLatin1String("all")));
152
153             if (all || values.contains(QLatin1String("location"))) {
154                 m_schema.setWriteBackLocation(true);
155             }
156
157             if (all || values.contains(QLatin1String("presence"))) {
158                 m_schema.setWriteBackPresence(true);
159             }
160
161             continue;
162         }
163
164         if (QLatin1String("changelog-tag") == i.key()) {
165             m_schema.setChangeLogTagLabel(i.value());
166             continue;
167         }
168
169         if (QLatin1String("query-builder") == i.key()) {
170             const QSet<QString> values(i.value().split(QLatin1Char(',')).toSet());
171             const bool all(values.contains(QLatin1String("all")));
172
173             if (all || values.contains(QLatin1String("fetch"))) {
174                 queryBuilderFlags |= QContactTrackerEngine::QueryBuilderFetch;
175             }
176             if (all || values.contains(QLatin1String("remove"))) {
177                 queryBuilderFlags |= QContactTrackerEngine::QueryBuilderRemove;
178             }
179             if (all || values.contains(QLatin1String("save"))) {
180                 queryBuilderFlags |= QContactTrackerEngine::QueryBuilderSave;
181             }
182
183             continue;
184         }
185
186         if (QLatin1String("debug") == i.key()) {
187             const QSet<QString> values(i.value().split(QLatin1Char(',')).toSet());
188             const bool all(values.contains(QLatin1String("all")));
189
190             if (all || values.contains(QLatin1String("queries"))) {
191                 debugFlags |= QContactTrackerEngine::ShowSelects;
192                 debugFlags |= QContactTrackerEngine::ShowUpdates;
193             }
194             if (all || values.contains(QLatin1String("selects"))) {
195                 debugFlags |= QContactTrackerEngine::ShowSelects;
196             }
197             if (all || values.contains(QLatin1String("updates"))) {
198                 debugFlags |= QContactTrackerEngine::ShowUpdates;
199             }
200             if (all || values.contains(QLatin1String("models"))) {
201                 debugFlags |= QContactTrackerEngine::ShowModels;
202             }
203             if (all || values.contains(QLatin1String("notes"))) {
204                 debugFlags |= QContactTrackerEngine::ShowNotes;
205             }
206             if (all || values.contains(QLatin1String("timing"))) {
207                 debugFlags |= QContactTrackerEngine::ShowTiming;
208             }
209
210             continue;
211         }
212
213         if (QLatin1String("com.nokia.qt.mobility.contacts.implementation.version") == i.key()) {
214             continue;
215         }
216
217         qWarning()
218                 << Q_FUNC_INFO << qPrintable(i.key())
219                 << "- Unknown parameter";
220     }
221
222     m_debugFlags = debugFlags | queryBuilderFlags;
223 }
224
225 static QPair<QString, QStringList>
226 makeDisplayNameField(const QString &detailName, const QString &field1,
227                      const QString &field2 = QString())
228 {
229     QStringList fieldNames(field1);
230
231     if (not field2.isEmpty()) {
232         fieldNames.append(field2);
233     }
234
235     return qMakePair(detailName, fieldNames);
236 }
237
238 void
239 QContactTrackerEngineData::setupDisplayNameFields()
240 {
241     // TODO Get name display rules from contacts settings, described in UI Spec
242     // v1.1 (section 4.9).
243
244     // TODO: If only the first detail is used, is this ineffecient?
245     if (m_displayNameDetails.isEmpty()) {
246         m_displayNameDetails[QLatin1String("first-last")]
247                 << makeDisplayNameField(QContactName::DefinitionName,
248                                         QContactName::FieldFirstName,
249                                         QContactName::FieldLastName)
250                 << makeDisplayNameField(QContactOrganization::DefinitionName,
251                                         QContactOrganization::FieldName)
252                 << makeDisplayNameField(QContactGlobalPresence::DefinitionName,
253                                         QContactGlobalPresence::FieldNickname)
254                 << makeDisplayNameField(QContactEmailAddress::DefinitionName,
255                                         QContactEmailAddress::FieldEmailAddress)
256                 << makeDisplayNameField(QContactPhoneNumber::DefinitionName,
257                                         QContactPhoneNumber::FieldNumber)
258                 << makeDisplayNameField(QContactUrl::DefinitionName,
259                                         QContactUrl::FieldUrl)
260
261                 // For instance, someoneorother@jabber.com
262                 // TODO: Put "Account" in a constant somewhere, instead of repeating this magic string.
263                 << makeDisplayNameField(QContactOnlineAccount::DefinitionName,
264                                         "Account")
265
266                 // A last desparate fallback. This could be a long incomprehensible URI:
267                 << makeDisplayNameField(QContactOnlineAccount::DefinitionName,
268                                         QContactOnlineAccount::FieldAccountUri);
269
270         m_displayNameDetails.insert(QLatin1String("none"), DisplayNameDetailList());
271     }
272 }
273
274 ///////////////////////////////////////////////////////////////////////////////
275 // Synchronous API helpers
276 ///////////////////////////////////////////////////////////////////////////////
277
278 class ContactTrackerRequestTask : public QThread
279 {
280 public:
281     ContactTrackerRequestTask(const QContactTrackerEngine *engine,
282                               QContactAbstractRequest *request);
283
284     virtual void run();
285
286 private:
287     const QContactTrackerEngine *const m_engine;
288     QContactAbstractRequest *const m_request;
289 };
290
291 ContactTrackerRequestTask::ContactTrackerRequestTask(const QContactTrackerEngine *engine,
292                                                      QContactAbstractRequest *request)
293     : m_engine(engine),
294       m_request(request)
295 {
296 }
297
298 void
299 ContactTrackerRequestTask::run()
300 {
301     // Avoid altering the engine's public state.
302     // XXX: using QSharedDataPointer::operator-> in non-const methods
303     // will copy the state on various light pretexts, but this could be
304     // considered a punishment to sync API users.
305
306     QContactTrackerEngine taskEngine(*m_engine);
307     taskEngine.startRequest(m_request);
308
309     if (not m_request->isFinished()) {
310         RequestEventLoop eventLoop;
311
312         eventLoop.connect(m_request,
313                 SIGNAL(stateChanged(QContactAbstractRequest::State)),
314                 SLOT(stateChanged(QContactAbstractRequest::State)));
315
316         // Spin it, baby!
317         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
318     }
319 }
320
321 void
322 RequestEventLoop::stateChanged(QContactAbstractRequest::State newState)
323 {
324     switch (newState) {
325     case QContactAbstractRequest::FinishedState:
326     case QContactAbstractRequest::CanceledState:
327         exit();
328         break;
329     default:
330         break;
331     }
332 }
333
334 ////////////////////////////////////////////////////////////////////////////////////////////////////
335 // Instances which manage contact engine instances
336 ////////////////////////////////////////////////////////////////////////////////////////////////////
337
338 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString>& parameters,
339                                              const QString& engineName, int engineVersion)
340     : d(new QContactTrackerEngineData(parameters, engineName, engineVersion))
341 {
342     connectToSignals();
343 }
344
345 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
346     : d(other.d)
347 {
348     connectToSignals();
349 }
350
351 void QContactTrackerEngine::connectToSignals()
352 {
353     // added here as list was not working because of
354     // [warn ] QObject::connect: Cannot queue arguments of type 'QContactAbstractRequest::State'
355     // (Make sure 'QContactAbstractRequest::State' is registered using qRegisterMetaType().)
356     qRegisterMetaType<QContactAbstractRequest::State>("QContactAbstractRequest::State");
357
358     TrackerChangeListener *listener = new TrackerChangeListener(this, this);
359
360     connect(listener, SIGNAL(contactsAdded(const QList<QContactLocalId>&)),
361             this, SIGNAL(contactsAdded(const QList<QContactLocalId>&)));
362     connect(listener, SIGNAL(contactsChanged(const QList<QContactLocalId>&)),
363             this, SIGNAL(contactsChanged(const QList<QContactLocalId>&)));
364     connect(listener, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)),
365             this, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)));
366 }
367
368 QContactTrackerEngine::~QContactTrackerEngine()
369 {
370 }
371
372 QContactTrackerEngine&
373 QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
374 {
375     d = other.d;
376     return *this;
377 }
378
379 ////////////////////////////////////////////////////////////////////////////////////////////////////
380 // Methods which describe the contact engine's capabilities
381 ////////////////////////////////////////////////////////////////////////////////////////////////////
382
383 QMap<QString, QString>
384 QContactTrackerEngine::managerParameters() const
385 {
386     return d->m_parameters;
387 }
388
389 QString
390 QContactTrackerEngine::managerName() const
391 {
392     return d->m_engineName;
393 }
394
395 int
396 QContactTrackerEngine::managerVersion() const
397 {
398     return d->m_engineVersion;
399 }
400
401 bool
402 QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature,
403                                   const QString& contactType) const
404 {
405     if (not supportedContactTypes().contains(contactType)) {
406         return false;
407     }
408
409     switch (feature) {
410     case QContactManager::ArbitraryRelationshipTypes:
411     case QContactManager::ActionPreferences:
412     case QContactManager::ChangeLogs:
413     case QContactManager::Groups:
414     case QContactManager::Relationships:
415     case QContactManager::SelfContact:
416         return true;
417
418     default:
419         return false;
420     }
421 }
422
423 bool
424 QContactTrackerEngine::isFilterSupported(const QContactFilter& filter) const
425 {
426     if (d->m_debugFlags.testFlag(QueryBuilderFetch)) {
427         return QTrackerContactQueryBuilder::isFilterSupported(filter);
428     }
429
430     switch (filter.type()) {
431     case QContactFilter::InvalidFilter:
432     case QContactFilter::DefaultFilter:
433     case QContactFilter::LocalIdFilter:
434     case QContactFilter::ContactDetailFilter:
435     case QContactFilter::ContactDetailRangeFilter:
436     case QContactFilter::ActionFilter:
437     case QContactFilter::ChangeLogFilter:
438     case QContactFilter::RelationshipFilter:
439         return true;
440
441
442     case QContactFilter::IntersectionFilter: // not yet done
443     case QContactFilter::UnionFilter: // not yet done
444         break;
445     }
446
447     return false;
448 }
449
450 QList<QVariant::Type>
451 QContactTrackerEngine::supportedDataTypes() const
452 {
453     return d->m_schema.supportedDataTypes();
454 }
455
456 QStringList
457 QContactTrackerEngine::supportedContactTypes() const
458 {
459     static const QStringList types(QStringList() << QContactType::TypeContact);
460     return types;
461 }
462
463 QContactDetailDefinitionMap
464 QContactTrackerEngine::detailDefinitions(const QString& contactType,
465                                          QContactManager::Error* error) const
466 {
467     if (contactType != QContactType::TypeContact) {
468         propagate(QContactManager::InvalidContactTypeError, error);
469         return QContactDetailDefinitionMap();
470     }
471
472     propagate(QContactManager::NoError, error);
473     return d->m_schema.detailDefinitions();
474 }
475
476 QContactDetailDefinition
477 QContactTrackerEngine::detailDefinition(const QString& definitionName,
478                                         const QString& contactType,
479                                         QContactManager::Error* error) const
480 {
481     if (contactType != QContactType::TypeContact) {
482         propagate(QContactManager::InvalidContactTypeError, error);
483         return QContactDetailDefinition();
484     }
485
486     const QContactDetailDefinitionMap &definitions(d->m_schema.detailDefinitions());
487     const QContactDetailDefinitionMapConstIter detail(definitions.find(definitionName));
488
489     if (definitions.end() == detail) {
490         propagate(QContactManager::DoesNotExistError, error);
491         return QContactDetailDefinition();
492     }
493
494     propagate(QContactManager::NoError, error);
495     return detail.value();
496 }
497
498 const QTrackerContactDetailSchema &
499 QContactTrackerEngine::schema() const
500 {
501     return d->m_schema;
502 }
503
504 bool
505 QContactTrackerEngine::setSelfContactId(const QContactLocalId&,
506                                         QContactManager::Error* error)
507 {
508     REPORT_UNSUPPORTED_FUNCTION(error);
509     return false;
510 }
511
512 QContactLocalId
513 QContactTrackerEngine::selfContactId(QContactManager::Error* error) const
514 {
515     propagate(QContactManager::NoError, error);
516     return QTrackerContactSubject::SelfContactId;
517 }
518
519 const QContactTrackerEngine::DebugFlags &
520 QContactTrackerEngine::debugFlags() const
521 {
522     return d->m_debugFlags;
523 }
524
525 ////////////////////////////////////////////////////////////////////////////////////////////////////
526 // Synchronous data access methods
527 ////////////////////////////////////////////////////////////////////////////////////////////////////
528
529 QList<QContactLocalId>
530 QContactTrackerEngine::contactIds(const QList<QContactSortOrder>& sortOrders,
531                                   QContactManager::Error* error) const
532 {
533     return contactIds(QContactFilter(), sortOrders, error);
534 }
535
536 QList<QContactLocalId>
537 QContactTrackerEngine::contactIds(const QContactFilter& filter,
538                                   const QList<QContactSortOrder>& sortOrders,
539                                   QContactManager::Error* error) const
540 {
541     QContactLocalIdFetchRequest request;
542
543     request.setFilter(filter);
544     request.setSorting(sortOrders);
545
546     if (runSyncRequest(&request, error)) {
547         propagate(request.error(), error);
548     }
549
550     return request.ids();
551 }
552
553 QList<QContact>
554 QContactTrackerEngine::contacts(const QContactFilter& filter,
555                                 const QList<QContactSortOrder>& sortOrders,
556                                 const QContactFetchHint& fetchHint,
557                                 QContactManager::Error* error) const
558 {
559     QContactFetchRequest request;
560
561     request.setFetchHint(fetchHint);
562     request.setFilter(filter);
563     request.setSorting(sortOrders);
564
565     if (runSyncRequest(&request, error)) {
566         propagate(request.error(), error);
567     }
568
569     return request.contacts();
570 }
571
572 QContact
573 QContactTrackerEngine::contact(const QContactLocalId& contactId,
574                                const QContactFetchHint& fetchHint,
575                                QContactManager::Error* error) const
576 {
577     // plan to keep this warning for a while - as message to customers using the API
578     qWarning() << Q_FUNC_INFO << "\n"
579             "=============================================================================\n"
580             "API is blocking on dbus roundtrip while accessing tracker. Please, consider\n"
581             "using asynchronous API QContactFetchRequest and not fetching contacts by id -\n"
582             "reading 100 ids and 100 contact by ids is ~100 times slower then reading 100\n"
583             "contacts at once with QContactFetchRequest.\n"
584             "=============================================================================";
585
586     return contactImpl(contactId, fetchHint, error);
587 }
588
589 // Used in tests, removed warning while decided if to provide sync api.
590 // Until then customers are advised to use async.
591 QContact
592 QContactTrackerEngine::contactImpl(const QContactLocalId& contactId,
593                                    const QContactFetchHint& fetchHint,
594                                    QContactManager::Error* error) const
595 {
596     QStringList definitionNames = fetchHint.detailDefinitionsHint();
597
598     if (not d->m_debugFlags.testFlag(QueryBuilderFetch) &&
599         fetchHint.detailDefinitionsHint().isEmpty()) {
600         definitionNames
601                 << QContactAvatar::DefinitionName
602                 << QContactBirthday::DefinitionName
603                 << QContactAddress::DefinitionName
604                 << QContactEmailAddress::DefinitionName
605                 << QContactDisplayLabel::DefinitionName
606                 << QContactGender::DefinitionName
607                 << QContactAnniversary::DefinitionName
608                 << QContactName::DefinitionName
609                 << QContactOnlineAccount::DefinitionName
610                 << QContactOrganization::DefinitionName
611                 << QContactPhoneNumber::DefinitionName
612                 << QContactOnlineAccount::DefinitionName
613                 << QContactPresence::DefinitionName
614                 << QContactUrl::DefinitionName
615                 << QContactTimestamp::DefinitionName
616                 << QContactGuid::DefinitionName;
617     }
618
619     QContactFetchHint modifiedHint(fetchHint);
620     modifiedHint.setDetailDefinitionsHint(definitionNames);
621
622     QContactLocalIdFilter idFilter;
623     idFilter.setIds(QList<QContactLocalId>() << contactId);
624
625     QContactFetchRequest request;
626     request.setFetchHint(modifiedHint);
627     request.setFilter(idFilter);
628
629     if (not runSyncRequest(&request, error)) {
630         return QContact();
631     }
632
633     QList<QContact> contacts(request.contacts());
634
635     if(contacts.isEmpty()) {
636         propagate(QContactManager::DoesNotExistError, error);
637         return QContact();
638     }
639
640     propagate(request.error(), error);
641     Q_ASSERT(1 == contacts.count());
642     return contacts.first();
643 }
644
645 bool
646 QContactTrackerEngine::saveContact(QContact* contact, QContactManager::Error* error)
647 {
648     if (0 == contact) {
649         propagate(QContactManager::BadArgumentError, error);
650         return false;
651     }
652
653     QList<QContact> contactList(QList<QContact>() << *contact);
654     bool ret = saveContacts(&contactList, 0, error);
655     if(contactList.size()) {
656         *contact = contactList[0];
657     }
658     return ret;
659 }
660
661 bool
662 QContactTrackerEngine::saveContacts(QList<QContact>* contacts,
663                                     QMap<int, QContactManager::Error>* errorMap,
664                                     QContactManager::Error* error)
665 {
666     if (not validateContacts(contacts, errorMap, error)) {
667         return false;
668     }
669
670     QContactSaveRequest request;
671     request.setContacts(*contacts);
672
673     if (not runSyncRequest(&request, error)) {
674         propagate(request.errorMap(), errorMap);
675         return false;
676     }
677
678     propagate(request.errorMap(), errorMap);
679     propagate(request.error(), error);
680
681     for (int i = 0; i < contacts->size(); ++i) {
682         contacts->replace(i, request.contacts().at(i));
683     }
684
685     return QContactManager::NoError == request.error();
686 }
687
688 bool
689 QContactTrackerEngine::validateContacts(QList<QContact>* contacts,
690                                         QMap<int, QContactManager::Error>* errorMap,
691                                         QContactManager::Error* error)
692 {
693     bool result = true;
694
695     if (0 == contacts) {
696         propagate(QContactManager::BadArgumentError, error);
697         return false;
698     }
699
700     for(int i = 0; i < contacts->count(); ++i) {
701         foreach(const QTrackerContactDetail &detail, schema().details()) {
702             if (detail.isUnique() && contacts->at(i).details(detail.name()).count() > 1) {
703                 qWarning()
704                         << "Cannot save contact at index" << i << ":"
705                         << detail.name() << "detail must be unique";
706
707                 propagate(QContactManager::InvalidDetailError, errorMap, i);
708                 propagate(QContactManager::InvalidDetailError, error);
709
710                 result = false;
711             }
712         }
713     }
714
715     return result;
716 }
717
718 bool
719 QContactTrackerEngine::removeContact(const QContactLocalId& contactId,
720                                      QContactManager::Error* error)
721 {
722     return removeContacts(QList<QContactLocalId>() << contactId, 0, error);
723 }
724
725 bool
726 QContactTrackerEngine::removeContacts(const QList<QContactLocalId>& contactIds,
727                                       QMap<int, QContactManager::Error>* errorMap,
728                                       QContactManager::Error* error)
729 {
730     QContactRemoveRequest request;
731
732     request.setContactIds(contactIds);
733
734     if (not runSyncRequest(&request, error)) {
735         propagate(request.errorMap(), errorMap);
736         return false;
737     }
738
739     propagate(request.errorMap(), errorMap);
740     propagate(request.error(), error);
741
742     return QContactManager::NoError == request.error();
743 }
744
745 static const DisplayNameDetailList &
746 findDisplayLabelDetails(const DisplayNameDetailMap &displayNameDetails,
747                         const QSet<QString> &definitionHints)
748 {
749     QString nameOrder;
750
751     foreach(const QString &detailName, definitionHints) {
752         if (detailName.startsWith(QContactDisplayLabel::DefinitionName.latin1())) {
753             const int n(sizeof QContactDisplayLabel::DefinitionName.chars);
754
755             if (detailName.length() >= n && QLatin1Char(';') == detailName[n - 1]) {
756                 nameOrder = detailName.mid(n);
757             }
758
759             break;
760         }
761     }
762
763     DisplayNameDetailMap::const_iterator i(displayNameDetails.find(nameOrder));
764
765     if (i == displayNameDetails.end()) {
766         i = displayNameDetails.begin();
767     }
768
769     return i.value();
770 }
771
772 void
773 QContactTrackerEngine::ensureDisplayLabelDetails(QSet<QString> &definitionHints)
774 {
775     foreach(const DisplayNameDetail &detail,
776             findDisplayLabelDetails(d->m_displayNameDetails, definitionHints)) {
777         definitionHints.insert(detail.first);
778     }
779 }
780
781 void
782 QContactTrackerEngine::updateDisplayLabel(QContact &contact, const QSet<QString> &definitionHints)
783 {
784     const QContactDisplayLabel label(contact.detail<QContactDisplayLabel>());
785
786     if (label.label().isEmpty()) {
787         DisplayNameDetailStack *detailStack(displayNameStack.localData());
788
789         if (0 == detailStack) {
790             displayNameStack.setLocalData(detailStack = new DisplayNameDetailStack);
791         }
792
793         detailStack->enqueue(findDisplayLabelDetails(d->m_displayNameDetails, definitionHints));
794
795         QContactManager::Error synthError;
796         setContactDisplayLabel(&contact, synthesizedDisplayLabel(contact, &synthError));
797
798         detailStack->dequeue();
799     }
800 }
801
802 static int
803 availability(QContactPresence::PresenceState state)
804 {
805     switch(state) {
806     case QContactPresence::PresenceUnknown:
807         return 0;
808     case QContactPresence::PresenceHidden:
809     case QContactPresence::PresenceOffline:
810         return 1;
811     case QContactPresence::PresenceAway:
812     case QContactPresence::PresenceExtendedAway:
813         return 2;
814     case QContactPresence::PresenceBusy:
815         return 3;
816     case QContactPresence::PresenceAvailable:
817         return 4;
818     }
819
820     return availability(QContactPresence::PresenceUnknown);
821 }
822
823 template <class T> static void
824 transfer(const T &key, const QContactDetail &source, QContactDetail &target)
825 {
826     QVariant value(source.variantValue(key));
827
828     if (not value.isNull()) {
829         target.setValue(key, value);
830     }
831 }
832
833 void
834 QContactTrackerEngine::updateGlobalPresence(QContact &contact)
835 {
836     const QList<QContactPresence> presenceDetails(contact.details<QContactPresence>());
837     const QContactPresence *mostAvailable(0);
838     const QContactPresence *mostRecent(0);
839
840     if (not presenceDetails.isEmpty()) {
841         mostAvailable = mostRecent = &presenceDetails.first();
842     }
843
844     foreach(const QContactPresence &detail, presenceDetails) {
845         if (availability(detail.presenceState()) > availability(mostAvailable->presenceState())) {
846             if (detail.timestamp() == mostRecent->timestamp()) {
847                 mostRecent = &detail;
848             }
849
850             mostAvailable = &detail;
851         }
852
853         if (detail.timestamp() > mostRecent->timestamp()) {
854             mostRecent = &detail;
855         }
856     }
857
858     QContactGlobalPresence global(contact.detail<QContactGlobalPresence>());
859
860     if (mostRecent && mostAvailable) {
861         QSet<QString> linkedDetails;
862         linkedDetails << mostRecent->detailUri();
863         linkedDetails << mostAvailable->detailUri();
864         global.setLinkedDetailUris(linkedDetails.toList());
865
866         transfer(QContactGlobalPresence::FieldNickname, *mostRecent, global);
867         transfer(QContactGlobalPresence::FieldTimestamp, *mostRecent, global);
868         transfer(QContactGlobalPresence::FieldCustomMessage, *mostRecent, global);
869
870         transfer(QContactGlobalPresence::FieldPresenceState, *mostAvailable, global);
871         transfer(QContactGlobalPresence::FieldPresenceStateText, *mostAvailable, global);
872         transfer(QContactGlobalPresence::FieldPresenceStateImageUrl, *mostAvailable, global);
873
874         contact.saveDetail(&global);
875     } else {
876         contact.removeDetail(&global);
877     }
878 }
879
880 ////////////////////////////////////////////////////////////////////////////////////////////////////
881 // Asynchronous data access methods
882 ////////////////////////////////////////////////////////////////////////////////////////////////////
883
884 bool
885 QContactTrackerEngine::startRequest(QContactAbstractRequest* request)
886 {
887     QTrackerAbstractRequest *worker = 0;
888     QTime t; t.start();
889
890     // choose proper request implementation
891     switch(request->type())
892     {
893     case QContactAbstractRequest::ContactFetchRequest:
894         if (d->m_debugFlags.testFlag(QueryBuilderFetch)) {
895             worker = new QTrackerContactFetchRequest2(request, this);
896         } else {
897             worker = new QTrackerContactFetchRequest(request, this);
898         }
899         break;
900
901     case QContactAbstractRequest::ContactLocalIdFetchRequest:
902         worker = new QTrackerContactIdFetchRequest(request, this);
903         break;
904
905     case QContactAbstractRequest::ContactRemoveRequest:
906         if (d->m_debugFlags.testFlag(QueryBuilderRemove)) {
907             worker = new QTrackerContactRemoveRequest2(request, this);
908         } else {
909             worker = new QTrackerContactRemoveRequest(request, this);
910         }
911         break;
912
913     case QContactAbstractRequest::ContactSaveRequest:
914         if (d->m_debugFlags.testFlag(QueryBuilderSave)) {
915             worker = new QTrackerContactSaveRequest2(request, this);
916         } else {
917             worker = new QTrackerContactSaveRequest(request, this);
918         }
919         break;
920
921     case QContactAbstractRequest::RelationshipFetchRequest:
922         worker = new QTrackerRelationshipFetchRequest(request, this);
923         break;
924
925     case QContactAbstractRequest::RelationshipSaveRequest:
926         worker = new QTrackerRelationshipSaveRequest(request, this);
927         break;
928
929     case QContactAbstractRequest::InvalidRequest:
930     case QContactAbstractRequest::DetailDefinitionFetchRequest:
931     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
932     case QContactAbstractRequest::DetailDefinitionSaveRequest:
933     case QContactAbstractRequest::RelationshipRemoveRequest:
934         break;
935     }
936
937     if (0 == worker) {
938         qWarning()
939                 << metaObject()->className() << "unsupported request"
940                 << request->metaObject()->className();
941
942         return false;
943     }
944
945     if (d->m_debugFlags.testFlag(ShowNotes)) {
946         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
947     }
948
949     Q_ASSERT(request->isActive());
950
951     if (d->m_debugFlags.testFlag(ShowNotes)) {
952         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
953     }
954
955     // XXX The unit tests directly access the engine. Therefore requests created by our unit
956     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
957     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
958     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
959     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
960     // a request without contact manager is found.
961     //
962     // XXX This all of course is an ugly workaround. We probably should change the unit tests
963     // to create use QContactManager accessing the engine via a static plugin.
964     if (0 == request->manager()) {
965         connect(request, SIGNAL(destroyed(QObject*)),
966                 SLOT(requestDestroyed(QObject*)));
967     }
968
969     // store the request and start it
970     d->m_requests.insert(request, worker);
971
972     bool success = worker->start();
973
974     if (d->m_debugFlags.testFlag(ShowNotes)) {
975         qDebug() << Q_FUNC_INFO << "time elapsed until start returned:"<< request << t.elapsed();
976     }
977
978     return success;
979 }
980
981 /*! \reimp */
982 void
983 QContactTrackerEngine::requestDestroyed(QContactAbstractRequest *req)
984 {
985     delete d->m_requests.take(req);
986 }
987
988 void
989 QContactTrackerEngine::requestDestroyed(QObject *req)
990 {
991     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
992     // derived parts of the class have already been destroyed by the time the base QObject's
993     // destructor has emitted this signal. So we do a regular static case, because
994     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
995     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
996 }
997
998 bool
999 QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
1000 {
1001     QWeakPointer<QContactAbstractRequest> request(req);
1002
1003     RequestEventLoop eventLoop;
1004
1005     connect(req, SIGNAL(stateChanged(QContactAbstractRequest::State)),
1006             &eventLoop, SLOT(stateChanged(QContactAbstractRequest::State)));
1007
1008     QTimer::singleShot(msecs, &eventLoop, SLOT(quit()));
1009
1010     eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1011
1012     // protect the case that request got deleted outside
1013     if (request.isNull()) {
1014         return false;
1015     }
1016
1017     if (d->m_debugFlags.testFlag(ShowNotes)) {
1018         qDebug() << Q_FUNC_INFO << "finished:" << request.data()->isFinished();
1019     }
1020
1021     return request.data()->isFinished();
1022 }
1023
1024 bool
1025 QContactTrackerEngine::runSyncRequest(QContactAbstractRequest *request,
1026                                       QContactManager::Error *error) const
1027 {
1028     ContactTrackerRequestTask task(this, request);
1029
1030     task.start();
1031
1032     if (not task.wait(d->m_requestTimeout)) {
1033         qWarning() << request->metaObject()->className() << "timed out, aborting";
1034         propagate(QContactManager::LimitReachedError, error);
1035         return false;
1036     }
1037
1038     return true;
1039 }
1040
1041 ////////////////////////////////////////////////////////////////////////////////////////////////////
1042 // Synthesized contact details
1043 ////////////////////////////////////////////////////////////////////////////////////////////////////
1044
1045 QString
1046 QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact,
1047                                                QContactManager::Error* error) const
1048 {
1049     propagate(QContactManager::NoError, error);
1050     QString label;
1051
1052     const DisplayNameDetailStack *stack(displayNameStack.localData());
1053     const DisplayNameDetailList &details(stack && not stack->isEmpty() ? stack->last() :
1054                                          d->m_displayNameDetails.begin().value());
1055
1056     foreach(const DisplayNameDetail &dnd, details) {
1057         const QContactDetail detail(contact.detail(dnd.first));
1058
1059         foreach(const QString &fieldName, dnd.second) {
1060             const QString value(detail.value(fieldName));
1061
1062             if (value.isEmpty()) {
1063                 continue;
1064             }
1065
1066             if (not label.isEmpty()) {
1067                 label.append(QLatin1Char(' '));
1068             }
1069
1070             label.append(value);
1071         }
1072
1073         if (not label.isEmpty()) {
1074             break;
1075         }
1076     }
1077
1078     return label;
1079 }
1080
1081 ////////////////////////////////////////////////////////////////////////////////////////////////////
1082 // Unsupported functions
1083 ////////////////////////////////////////////////////////////////////////////////////////////////////
1084
1085 QList<QContactRelationship>
1086 QContactTrackerEngine::relationships(const QString&, const QContactId&, QContactRelationship::Role,
1087                                      QContactManager::Error* error) const
1088 {
1089     REPORT_UNSUPPORTED_FUNCTION(error);
1090     return QList<QContactRelationship>();
1091 }
1092
1093 bool
1094 QContactTrackerEngine::saveRelationships(QList<QContactRelationship>*,
1095                                          QMap<int, QContactManager::Error>*,
1096                                          QContactManager::Error* error)
1097 {
1098     REPORT_UNSUPPORTED_FUNCTION(error);
1099     return false;
1100 }
1101
1102 bool
1103 QContactTrackerEngine::removeRelationships(const QList<QContactRelationship>&,
1104                                            QMap<int, QContactManager::Error>*,
1105                                            QContactManager::Error* error)
1106 {
1107     REPORT_UNSUPPORTED_FUNCTION(error);
1108     return false;
1109 }
1110
1111 QContact
1112 QContactTrackerEngine::compatibleContact(const QContact&, QContactManager::Error* error) const
1113 {
1114     REPORT_UNSUPPORTED_FUNCTION(error);
1115     return QContact();
1116 }
1117
1118 bool
1119 QContactTrackerEngine::validateContact(const QContact&, QContactManager::Error* error) const
1120 {
1121     REPORT_UNSUPPORTED_FUNCTION(error);
1122     return false;
1123 }
1124
1125 bool
1126 QContactTrackerEngine::validateDefinition(const QContactDetailDefinition&,
1127                                           QContactManager::Error* error) const
1128 {
1129     REPORT_UNSUPPORTED_FUNCTION(error);
1130     return false;
1131 }
1132
1133 bool
1134 QContactTrackerEngine::saveDetailDefinition(const QContactDetailDefinition&, const QString&,
1135                                             QContactManager::Error* error)
1136 {
1137     REPORT_UNSUPPORTED_FUNCTION(error);
1138     return false;
1139 }
1140
1141 bool
1142 QContactTrackerEngine::removeDetailDefinition(const QString&, const QString&,
1143                                               QContactManager::Error* error)
1144 {
1145     REPORT_UNSUPPORTED_FUNCTION(error);
1146     return false;
1147 }
1148
1149 bool
1150 QContactTrackerEngine::cancelRequest(QContactAbstractRequest*)
1151 {
1152     REPORT_UNSUPPORTED_FUNCTION(noErrorResult);
1153     return false;
1154 }
1155
1156 bool
1157 QContactTrackerEngine::isRelationshipTypeSupported(const QString&, const QString&) const
1158 {
1159     REPORT_UNSUPPORTED_FUNCTION(noErrorResult);
1160     return false;
1161 }