Changes: Only create TrackerChangeListener when the user actually connects signals.
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.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 "contactidfetchrequest.h"
46 #include "contactremoverequest.h"
47 #include "contactsaverequest.h"
48 #include "relationshipfetchrequest.h"
49 #include "relationshipremoverequest.h"
50 #include "relationshipsaverequest.h"
51 #include "trackerchangelistener.h"
52
53 #include <dao/contactdetail.h>
54 #include <dao/contactdetailschema.h>
55 #include <dao/querybuilder.h>
56 #include <dao/settings.h>
57 #include <dao/support.h>
58
59 #include <qcontactdetails.h>
60
61 QTM_USE_NAMESPACE;
62
63 ////////////////////////////////////////////////////////////////////////////////////////////////////
64
65 #define REPORT_UNSUPPORTED_FUNCTION(error) do {\
66     qctWarn(QString::fromLatin1("Method not implemented yet: %1").arg(Q_FUNC_INFO)); \
67     propagate(QContactManager::NotSupportedError, error); \
68 } while(0)
69
70 ////////////////////////////////////////////////////////////////////////////////////////////////////
71
72 static QContactManager::Error *const noErrorResult(0);
73 static QThreadStorage<DisplayNameDetailStack*> displayNameStack;
74
75 ////////////////////////////////////////////////////////////////////////////////////////////////////
76
77 // added here as list was not working because of
78 // [warn ] QObject::connect: Cannot queue arguments of type 'QContactAbstractRequest::State'
79 // (Make sure 'QContactAbstractRequest::State' is registered using qRegisterMetaType().)
80 Q_DECLARE_METATYPE(QContactAbstractRequest::State);
81
82 ////////////////////////////////////////////////////////////////////////////////////////////////////
83
84 QContactTrackerEngineData::QContactTrackerEngineData(const QMap<QString, QString>& parameters,
85                                                      const QString& engineName, int engineVersion)
86     : QSharedData()
87     , m_refCount(QAtomicInt(1))
88     , m_engineName(engineName)
89     , m_engineVersion(engineVersion)
90     , m_requestTimeout(QContactTrackerEngine::DefaultRequestTimeout)
91     , m_trackerTimeout(QContactTrackerEngine::DefaultTrackerTimeout)
92     , m_coalescingDelay(QContactTrackerEngine::DefaultCoalescingDelay)
93     , m_concurrencyLevel(QTrackerContactSettings().concurrencyLevel())
94     , m_batchSize(QContactTrackerEngine::DefaultBatchSize)
95     , m_trackerChangeListener(0)
96     , m_genericSchema(m_classHierarchy)
97     , m_parameters(parameters)
98 {
99     static const QMap<QString, QString> environmentParameters(readEnvironment());
100     m_parameters.unite(environmentParameters);
101     parseParameters();
102
103     QTrackerPersonContactDetailSchema personContactSchema(m_classHierarchy);
104     m_detailSchemas.insert(QContactType::TypeContact, personContactSchema);
105
106     if (m_debugFlags.testFlag(QContactTrackerEngine::SupportGroups)) {
107         QTrackerContactGroupDetailSchema contactGroupSchema(m_classHierarchy);
108         m_detailSchemas.insert(QContactType::TypeGroup, contactGroupSchema);
109     }
110
111     setupDisplayNameFields();
112 }
113
114 QContactTrackerEngineData::QContactTrackerEngineData(const QContactTrackerEngineData& other)
115     : QSharedData(other)
116     , m_refCount(QAtomicInt(1))
117     , m_engineName(other.m_engineName)
118     , m_engineVersion(other.m_engineVersion)
119     , m_requestTimeout(other.m_requestTimeout)
120     , m_trackerTimeout(other.m_trackerTimeout)
121     , m_coalescingDelay(other.m_coalescingDelay)
122     , m_concurrencyLevel(other.m_concurrencyLevel)
123     , m_batchSize(other.m_batchSize)
124     , m_trackerChangeListener(0) // must create our own when needed
125     , m_classHierarchy(other.m_classHierarchy)
126     , m_genericSchema(other.m_genericSchema)
127     , m_detailSchemas(other.m_detailSchemas)
128     , m_parameters(other.m_parameters)
129     , m_displayNameDetails(other.m_displayNameDetails)
130     , m_debugFlags(other.m_debugFlags)
131     , m_supportedDataTypes(other.m_supportedDataTypes)
132     , m_requests() // don't copy the request workers to prevent double frees
133 {
134 }
135
136 QContactTrackerEngineData::~QContactTrackerEngineData()
137 {
138     QMutexLocker locker(&m_mutex);
139     RequestList workers(m_requests.values());
140     m_requests.clear();
141     locker.unlock(); // silence gcc
142
143     foreach(QTrackerAbstractRequest *w, workers) {
144         delete w;
145     }
146 }
147
148 QMap<QString, QString>
149 QContactTrackerEngineData::readEnvironment()
150 {
151     const QStringList tokenList(QProcessEnvironment::systemEnvironment().
152                                 value(QLatin1String("QT_CONTACTS_TRACKER")).
153                                 replace(QChar(' '), QString()).
154                                 split(QLatin1String(";")));
155
156     QMap<QString, QString> parameters;
157
158     foreach(const QString &token, tokenList) {
159         const int eq = token.indexOf(QLatin1Char('='));
160         const QString key((eq >= 0 ? token.left(eq) : token).trimmed());
161         const QString value((eq >= 0 ? token.mid(eq + 1).trimmed() : QString()));
162
163         if (not key.isEmpty()) {
164             parameters.insert(key, value);
165         }
166     }
167
168     return parameters;
169 }
170
171 static void
172 parseParameter(int &result, const QString &key, const QString &text)
173 {
174     bool ok = false;
175     const int value = text.toInt(&ok);
176
177     if (ok) {
178         result = value;
179     } else {
180         qctWarn(QString::fromLatin1("Invalid value for %1 argument: %2").arg(key, text));
181     }
182 }
183
184 void
185 QContactTrackerEngineData::parseParameters()
186 {
187     QContactTrackerEngine::DebugFlags debugFlags;
188     QContactTrackerEngine::DebugFlags queryBuilderFlags;
189
190     for(QMap<QString, QString>::ConstIterator i(m_parameters.begin()); i != m_parameters.end(); ++i) {
191         if (QLatin1String("timeout") == i.key()) {
192             parseParameter(m_requestTimeout, i.key(), i.value());
193             continue;
194         }
195
196         if (QLatin1String("tracker-timeout") == i.key()) {
197             parseParameter(m_trackerTimeout, i.key(), i.value());
198             continue;
199         }
200
201         if (QLatin1String("coalescing-delay") == i.key()) {
202             parseParameter(m_coalescingDelay, i.key(), i.value());
203             continue;
204         }
205
206         if (QLatin1String("writeback") == i.key()) {
207             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
208             const bool all(values.contains(QLatin1String("all")));
209
210             if (all || values.contains(QLatin1String("presence"))) {
211                 foreach(QTrackerContactDetailSchema schema, m_detailSchemas) {
212                     schema.setWriteBackPresence(true);
213                 }
214             }
215
216             continue;
217         }
218
219         if (QLatin1String("debug") == i.key()) {
220             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
221             const bool all(values.contains(QLatin1String("all")));
222
223             if (all || values.contains(QLatin1String("queries"))) {
224                 debugFlags |= QContactTrackerEngine::ShowSelects;
225                 debugFlags |= QContactTrackerEngine::ShowUpdates;
226             }
227             if (all || values.contains(QLatin1String("selects"))) {
228                 debugFlags |= QContactTrackerEngine::ShowSelects;
229             }
230             if (all || values.contains(QLatin1String("updates"))) {
231                 debugFlags |= QContactTrackerEngine::ShowUpdates;
232             }
233             if (all || values.contains(QLatin1String("models"))) {
234                 debugFlags |= QContactTrackerEngine::ShowModels;
235             }
236             if (all || values.contains(QLatin1String("notes"))) {
237                 debugFlags |= QContactTrackerEngine::ShowNotes;
238             }
239             if (all || values.contains(QLatin1String("timing"))) {
240                 debugFlags |= QContactTrackerEngine::ShowTiming;
241             }
242
243             continue;
244         }
245
246         if (QLatin1String("features") == i.key()) {
247             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
248             const bool all(values.contains(QLatin1String("all")));
249
250             if (all || values.contains(QLatin1String("groups"))) {
251                 debugFlags |= QContactTrackerEngine::SupportGroups;
252             }
253
254             continue;
255         }
256
257         if (QLatin1String("concurrency") == i.key()) {
258             if ((m_concurrencyLevel = i.value().toInt()) < 1) {
259                 m_concurrencyLevel = QTrackerContactSettings().concurrencyLevel();
260             }
261
262             continue;
263         }
264
265         if (QLatin1String("batch-size") == i.key()) {
266             if ((m_batchSize = i.value().toInt()) < 1) {
267                 m_batchSize = QContactTrackerEngine::DefaultBatchSize;
268             }
269
270             continue;
271         }
272
273         if (QLatin1String("com.nokia.qt.mobility.contacts.implementation.version") == i.key()) {
274             continue;
275         }
276
277         qctWarn(QString::fromLatin1("Unknown parameter: %1").arg(i.key()));
278     }
279
280     m_debugFlags = debugFlags | queryBuilderFlags;
281     m_classHierarchy.setMutexTimeout(m_trackerTimeout);
282 }
283
284 static QPair<QString, QStringList>
285 makeDisplayNameField(const QString &detailName, const QString &field1,
286                      const QString &field2 = QString())
287 {
288     QStringList fieldNames(field1);
289
290     if (not field2.isEmpty()) {
291         fieldNames.append(field2);
292     }
293
294     return qMakePair(detailName, fieldNames);
295 }
296
297 void
298 QContactTrackerEngineData::setupDisplayNameFields()
299 {
300     // TODO Get name display rules from contacts settings, described in UI Spec
301     // v1.1 (section 4.9).
302
303     if (m_displayNameDetails.isEmpty()) {
304         m_displayNameDetails[QLatin1String("first-last")]
305                 // 0) If CustomLabel is set it dominates all other rules
306                 << makeDisplayNameField(QContactName::DefinitionName,
307                                         QContactName::FieldCustomLabel)
308
309                 // 1) First and last name, e.g. "Hans Wurst"
310                 << makeDisplayNameField(QContactName::DefinitionName,
311                                         QContactName::FieldFirstName,
312                                         QContactName::FieldLastName)
313
314                 // 2) The company name, e.g. "Nokia"
315                 << makeDisplayNameField(QContactOrganization::DefinitionName,
316                                         QContactOrganization::FieldName)
317
318                 // 3) The most available IM nickname, e.g "The Dude"
319                 << makeDisplayNameField(QContactGlobalPresence::DefinitionName,
320                                         QContactGlobalPresence::FieldNickname)
321
322                 // 4) The email address, e.g. "hans.wurst@nokia.com"
323                 << makeDisplayNameField(QContactEmailAddress::DefinitionName,
324                                         QContactEmailAddress::FieldEmailAddress)
325
326                 // 5) The phone number, e.g. "+3581122334455
327                 << makeDisplayNameField(QContactPhoneNumber::DefinitionName,
328                                         QContactPhoneNumber::FieldNumber)
329
330                 // 6) The contact's homepage URL, e.g. "http://www.nokia.com/"
331                 << makeDisplayNameField(QContactUrl::DefinitionName,
332                                         QContactUrl::FieldUrl)
333
334                 // 7) A last desparate fallback, this could be a long incomprehensible URI.
335                 //    Usually it should be something like "thedude@ovi.com".
336                 << makeDisplayNameField(QContactOnlineAccount::DefinitionName,
337                                         QContactOnlineAccount::FieldAccountUri);
338
339         m_displayNameDetails.insert(QLatin1String("none"), DisplayNameDetailList());
340     }
341 }
342
343 ///////////////////////////////////////////////////////////////////////////////
344 // Synchronous API helpers
345 ///////////////////////////////////////////////////////////////////////////////
346
347 class ContactTrackerRequestTask : public QThread
348 {
349 public:
350     ContactTrackerRequestTask(const QContactTrackerEngine *engine,
351                               QContactAbstractRequest *request,
352                               int msecs);
353
354     bool isFinished() const;
355
356 protected:
357     virtual void run();
358
359 private:
360     const QContactTrackerEngine *const m_engine;
361     QWeakPointer<QContactAbstractRequest> const m_request;
362     QThread *m_requestThread;
363     int m_timeout;
364 };
365
366 ContactTrackerRequestTask::ContactTrackerRequestTask(const QContactTrackerEngine *engine,
367                                                      QContactAbstractRequest *request,
368                                                      int msecs)
369     : m_engine(engine),
370       m_request(request),
371       m_timeout(msecs)
372 {
373     m_requestThread = m_request.data()->thread();
374     m_request.data()->moveToThread(this);
375 }
376
377 bool
378 ContactTrackerRequestTask::isFinished() const
379 {
380     // protect the case that request got deleted outside
381     if (m_request.isNull()) {
382         return false;
383     }
384
385     return m_request.data()->isFinished();
386 }
387
388 void
389 ContactTrackerRequestTask::run()
390 {
391     // Avoid altering the engine's public state.
392     // XXX: using QSharedDataPointer::operator-> in non-const methods
393     // will copy the state on various light pretexts, but this could be
394     // considered a punishment to sync API users.
395
396     QContactTrackerEngine taskEngine(*m_engine);
397     taskEngine.startRequest(m_request.data());
398
399     // protect the case that request got deleted during startRequest()
400     // because of a slot with direct connection
401     if (m_request.isNull()) {
402         return;
403     }
404
405     if (not m_request.data()->isFinished()) {
406         RequestEventLoop eventLoop;
407
408         eventLoop.connect(m_request.data(),
409                           SIGNAL(stateChanged(QContactAbstractRequest::State)),
410                           SLOT(stateChanged(QContactAbstractRequest::State)));
411
412         if (m_timeout > 0) {
413             QTimer::singleShot(m_timeout, &eventLoop, SLOT(quit()));
414         }
415
416         // Spin it, baby!
417         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
418     }
419
420     // destroy the worker to prevent late signals being sent to the void
421     taskEngine.requestDestroyed(m_request.data());
422
423     // move back to original thread
424     m_request.data()->moveToThread(m_requestThread);
425
426     // jgeorg: force cleanup of thread-local storage
427     // qttracker should do this automatically, see nb#175281
428     // consider it a workaround to relief the leak in nb#179622
429     // since the ClassUpdateSignaller is still leaked
430     SopranoLive::BackEnds::Tracker::trackerRelease();
431 }
432
433 ////////////////////////////////////////////////////////////////////////////////////////////////////
434
435 void
436 RequestEventLoop::stateChanged(QContactAbstractRequest::State newState)
437 {
438     switch (newState) {
439     case QContactAbstractRequest::FinishedState:
440     case QContactAbstractRequest::CanceledState:
441         exit();
442         break;
443     default:
444         break;
445     }
446 }
447
448 ////////////////////////////////////////////////////////////////////////////////////////////////////
449 // Instances which manage contact engine instances
450 ////////////////////////////////////////////////////////////////////////////////////////////////////
451
452 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString>& parameters,
453                                              const QString& engineName, int engineVersion)
454     : d(new QContactTrackerEngineData(parameters, engineName, engineVersion))
455 {
456 }
457
458 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
459     : d(other.d)
460 {
461 }
462
463 QContactTrackerEngine::~QContactTrackerEngine()
464 {
465 }
466
467 QContactTrackerEngine&
468 QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
469 {
470     d = other.d;
471     return *this;
472 }
473
474 ////////////////////////////////////////////////////////////////////////////////////////////////////
475 // Methods which describe the contact engine's capabilities
476 ////////////////////////////////////////////////////////////////////////////////////////////////////
477
478 QMap<QString, QString>
479 QContactTrackerEngine::managerParameters() const
480 {
481     return d->m_parameters;
482 }
483
484 QString
485 QContactTrackerEngine::managerName() const
486 {
487     return d->m_engineName;
488 }
489
490 int
491 QContactTrackerEngine::managerVersion() const
492 {
493     return d->m_engineVersion;
494 }
495
496 bool
497 QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature,
498                                   const QString& contactType) const
499 {
500     if (not supportedContactTypes().contains(contactType)) {
501         return false;
502     }
503
504     switch (feature) {
505     case QContactManager::ArbitraryRelationshipTypes:
506     case QContactManager::ActionPreferences:
507     case QContactManager::ChangeLogs:
508     case QContactManager::Groups:
509     case QContactManager::Relationships:
510     case QContactManager::SelfContact:
511         return true;
512
513     default:
514         return false;
515     }
516 }
517
518 bool
519 QContactTrackerEngine::isFilterSupported(const QContactFilter& filter) const
520 {
521     return QTrackerContactQueryBuilder::isFilterSupported(filter);
522 }
523
524 QList<QVariant::Type>
525 QContactTrackerEngine::supportedDataTypes() const
526 {
527     if (d->m_supportedDataTypes.isEmpty()) {
528         QSet<QVariant::Type> typeSet;
529
530         foreach(const QTrackerContactDetailSchema& schema, d->m_detailSchemas) {
531             typeSet += schema.supportedDataTypes();
532         }
533
534         d->m_supportedDataTypes = typeSet.toList();
535     }
536
537     return d->m_supportedDataTypes;
538 }
539
540 QStringList
541 QContactTrackerEngine::supportedContactTypes() const
542 {
543     return d->m_detailSchemas.keys();
544 }
545
546 QContactDetailDefinitionMap
547 QContactTrackerEngine::detailDefinitions(const QString& contactType,
548                                          QContactManager::Error* error) const
549 {
550     const QTrackerContactDetailSchemaMap::ConstIterator schema =
551             d->m_detailSchemas.find(contactType);
552
553     if (schema == d->m_detailSchemas.end()) {
554         propagate(QContactManager::InvalidContactTypeError, error);
555         return QContactDetailDefinitionMap();
556     }
557
558     propagate(QContactManager::NoError, error);
559     return schema->detailDefinitions();
560 }
561
562 QContactDetailDefinition
563 QContactTrackerEngine::detailDefinition(const QString& definitionName,
564                                         const QString& contactType,
565                                         QContactManager::Error* error) const
566 {
567     const QTrackerContactDetailSchemaMap::ConstIterator schema =
568             d->m_detailSchemas.find(contactType);
569
570     if (schema == d->m_detailSchemas.end()) {
571         propagate(QContactManager::InvalidContactTypeError, error);
572         return QContactDetailDefinition();
573     }
574
575     const QContactDetailDefinitionMap &definitions(schema->detailDefinitions());
576     const QContactDetailDefinitionMap::ConstIterator detail(definitions.find(definitionName));
577
578     if (definitions.end() == detail) {
579         propagate(QContactManager::DoesNotExistError, error);
580         return QContactDetailDefinition();
581     }
582
583     propagate(QContactManager::NoError, error);
584     return detail.value();
585 }
586
587 const QTrackerClassHierarchy &
588 QContactTrackerEngine::classes() const
589 {
590     return d->m_classHierarchy;
591 }
592
593 const QTrackerContactDetailSchemaMap &
594 QContactTrackerEngine::schemas() const
595 {
596     return d->m_detailSchemas;
597 }
598
599 const QTrackerContactDetailSchema &
600 QContactTrackerEngine::schema(const QString& contactType) const
601 {
602     QTrackerContactDetailSchemaMap::ConstIterator schema = d->m_detailSchemas.find(contactType);
603
604     if (schema == d->m_detailSchemas.end()) {
605         qctFail(QString::fromLatin1("Unexpected contact type %1. Aborting.").arg(contactType));
606         schema = d->m_detailSchemas.begin();
607     }
608
609     return schema.value();
610 }
611
612 const QTrackerContactDetailSchema &
613 QContactTrackerEngine::genericSchema() const
614 {
615     return d->m_genericSchema;
616 }
617
618 bool
619 QContactTrackerEngine::setSelfContactId(const QContactLocalId&,
620                                         QContactManager::Error* error)
621 {
622     REPORT_UNSUPPORTED_FUNCTION(error);
623     return false;
624 }
625
626 QContactLocalId
627 QContactTrackerEngine::selfContactId(QContactManager::Error* error) const
628 {
629     propagate(QContactManager::NoError, error);
630     return QTrackerContactSubject::SelfContactId;
631 }
632
633 const QContactTrackerEngine::DebugFlags &
634 QContactTrackerEngine::debugFlags() const
635 {
636     return d->m_debugFlags;
637 }
638
639 bool
640 QContactTrackerEngine::hasDebugFlag(DebugFlag flag) const
641 {
642     return d->m_debugFlags.testFlag(flag);
643 }
644
645 int
646 QContactTrackerEngine::concurrencyLevel() const
647 {
648     return d->m_concurrencyLevel;
649 }
650
651 int
652 QContactTrackerEngine::batchSize() const
653 {
654     return d->m_batchSize;
655 }
656
657 int
658 QContactTrackerEngine::requestTimeout() const
659 {
660     return d->m_requestTimeout;
661 }
662
663 int
664 QContactTrackerEngine::trackerTimeout() const
665 {
666     return d->m_trackerTimeout;
667 }
668
669 int
670 QContactTrackerEngine::coalescingDelay() const
671 {
672     return d->m_coalescingDelay;
673 }
674
675 ////////////////////////////////////////////////////////////////////////////////////////////////////
676 // Synchronous data access methods
677 ////////////////////////////////////////////////////////////////////////////////////////////////////
678
679 QList<QContactLocalId>
680 QContactTrackerEngine::contactIds(const QList<QContactSortOrder>& sortOrders,
681                                   QContactManager::Error* error) const
682 {
683     return contactIds(QContactFilter(), sortOrders, error);
684 }
685
686 QList<QContactLocalId>
687 QContactTrackerEngine::contactIds(const QContactFilter& filter,
688                                   const QList<QContactSortOrder>& sortOrders,
689                                   QContactManager::Error* error) const
690 {
691     QContactLocalIdFetchRequest request;
692
693     request.setFilter(filter);
694     request.setSorting(sortOrders);
695
696     runSyncRequest(&request, error);
697
698     return request.ids();
699 }
700
701 QList<QContact>
702 QContactTrackerEngine::contacts(const QContactFilter& filter,
703                                 const QList<QContactSortOrder>& sortOrders,
704                                 const QContactFetchHint& fetchHint,
705                                 QContactManager::Error* error) const
706 {
707     QContactFetchRequest request;
708
709     request.setFetchHint(fetchHint);
710     request.setFilter(filter);
711     request.setSorting(sortOrders);
712
713     runSyncRequest(&request, error);
714
715     return request.contacts();
716 }
717
718 QContact
719 QContactTrackerEngine::contact(const QContactLocalId& contactId,
720                                const QContactFetchHint& fetchHint,
721                                QContactManager::Error* error) const
722 {
723     // FIXME: Maybe only print this message on excessive use: e.g. more than 3 calls per second?
724     // Plan to keep this warning for a while - as message to customers using the API
725     qctWarn(QString::fromLatin1(Q_FUNC_INFO) + "\n"
726             "=============================================================================\n"
727             "API is blocking on dbus roundtrip while accessing tracker. Please, consider\n"
728             "using asynchronous API QContactFetchRequest and not fetching contacts by id -\n"
729             "reading 100 ids and 100 contact by ids is ~100 times slower then reading 100\n"
730             "contacts at once with QContactFetchRequest.\n"
731             "=============================================================================");
732     return contactImpl(contactId, fetchHint, error);
733 }
734
735 // Used in tests, removed warning while decided if to provide sync api.
736 // Until then customers are advised to use async.
737 QContact
738 QContactTrackerEngine::contactImpl(const QContactLocalId& contactId,
739                                    const QContactFetchHint& fetchHint,
740                                    QContactManager::Error* error) const
741 {
742     QContactLocalIdFilter idFilter;
743     idFilter.setIds(QList<QContactLocalId>() << contactId);
744
745     QContactFetchRequest request;
746     request.setFetchHint(fetchHint);
747     request.setFilter(idFilter);
748
749     if (not runSyncRequest(&request, error)) {
750         return QContact();
751     }
752
753     QList<QContact> contacts(request.contacts());
754
755     if (contacts.isEmpty()) {
756         propagate(QContactManager::DoesNotExistError, error);
757         return QContact();
758     }
759
760     if (contacts.count() > 1) {
761         qctWarn(QString::fromLatin1("Expected only one contact, but got %1").arg(contacts.count()));
762     }
763
764     return contacts.first();
765 }
766
767 bool
768 QContactTrackerEngine::saveContact(QContact* contact, QContactManager::Error* error)
769 {
770     if (0 == contact) {
771         propagate(QContactManager::BadArgumentError, error);
772         return false;
773     }
774
775     QList<QContact> contactList(QList<QContact>() << *contact);
776     bool success = saveContacts(&contactList, 0, error);
777
778     if (not contactList.isEmpty()) {
779         contact->setId(contactList.first().id());
780     }
781
782     return success;
783 }
784
785 bool
786 QContactTrackerEngine::saveContacts(QList<QContact>* contacts,
787                                     QMap<int, QContactManager::Error>* errorMap,
788                                     QContactManager::Error* error)
789 {
790     if (0 == contacts) {
791         propagate(QContactManager::BadArgumentError, error);
792         return false;
793     }
794
795     QContactSaveRequest request;
796     request.setContacts(*contacts);
797
798     const bool hasFinished = runSyncRequest(&request, error);
799     propagate(request.errorMap(), errorMap);
800
801     if (not hasFinished) {
802         return false;
803     }
804
805     const QList<QContact> savedContacts(request.contacts());
806
807     // only update the contact id, all other updates must be fetched
808     for (int i = 0; i < contacts->size(); ++i) {
809         (*contacts)[i].setId(savedContacts[i].id());
810     }
811
812     return QContactManager::NoError == request.error();
813 }
814
815 bool
816 QContactTrackerEngine::removeContact(const QContactLocalId& contactId,
817                                      QContactManager::Error* error)
818 {
819     return removeContacts(QList<QContactLocalId>() << contactId, 0, error);
820 }
821
822 bool
823 QContactTrackerEngine::removeContacts(const QList<QContactLocalId>& contactIds,
824                                       QMap<int, QContactManager::Error>* errorMap,
825                                       QContactManager::Error* error)
826 {
827     QContactRemoveRequest request;
828
829     request.setContactIds(contactIds);
830
831     runSyncRequest(&request, error);
832
833     propagate(request.errorMap(), errorMap);
834
835     return QContactManager::NoError == request.error();
836 }
837
838 QList<QContactRelationship>
839 QContactTrackerEngine::relationships(const QString &relationshipType,
840                                      const QContactId &participantId, QContactRelationship::Role role,
841                                      QContactManager::Error *error) const
842 {
843     QList<QContactRelationship> result;
844
845     // Mapping of this call to QContactRelationshipFetchRequest is not directly possible
846     // as QContactRelationshipFetchRequest gets set first and second contact but not
847     // just one participantId and a role.
848     //
849     // So in the case that role is QContactRelationship::Either two separate requests need to be done.
850
851     if (role==QContactRelationship::First
852         || role==QContactRelationship::Either) {
853         QContactRelationshipFetchRequest request;
854
855         request.setRelationshipType(relationshipType);
856         request.setFirst(participantId);
857
858         QContactManager::Error fetchError = QContactManager::UnspecifiedError;
859         runSyncRequest(&request, &fetchError);
860         propagate(fetchError, error);
861
862         if (fetchError != QContactManager::NoError) {
863             return QList<QContactRelationship>();
864         }
865
866         result += request.relationships();
867     }
868
869     if (role==QContactRelationship::Second
870         || role==QContactRelationship::Either) {
871         QContactRelationshipFetchRequest request;
872
873         request.setRelationshipType(relationshipType);
874         request.setSecond(participantId);
875
876         QContactManager::Error fetchError = QContactManager::UnspecifiedError;
877         runSyncRequest(&request, &fetchError);
878         propagate(fetchError, error);
879
880         if (fetchError != QContactManager::NoError) {
881             return QList<QContactRelationship>();
882         }
883
884         result += request.relationships();
885     }
886
887     return result;
888 }
889
890 bool
891 QContactTrackerEngine::saveRelationship(QContactRelationship *relationship, QContactManager::Error *error)
892 {
893     if (0 == relationship) {
894         propagate(QContactManager::BadArgumentError, error);
895         return false;
896     }
897
898     QList<QContactRelationship> relationships =
899             QList<QContactRelationship>() << *relationship;
900  
901     return saveRelationships(&relationships, 0, error);
902 }
903
904 bool
905 QContactTrackerEngine::saveRelationships(QList<QContactRelationship> *relationships,
906                                          QMap<int, QContactManager::Error> *errorMap,
907                                          QContactManager::Error *error)
908 {
909     if (0 == relationships) {
910         propagate(QContactManager::BadArgumentError, error);
911         return false;
912     }
913
914     QContactRelationshipSaveRequest request;
915     request.setRelationships(*relationships);
916
917     runSyncRequest(&request, error);
918     propagate(request.errorMap(), errorMap);
919
920     return QContactManager::NoError == request.error();
921 }
922
923 bool
924 QContactTrackerEngine::removeRelationship(const QContactRelationship &relationship, QContactManager::Error *error)
925 {
926     return removeRelationships(QList<QContactRelationship>() << relationship, 0, error);
927 }
928
929 bool
930 QContactTrackerEngine::removeRelationships(const QList<QContactRelationship> &relationships,
931                                            QMap<int, QContactManager::Error> *errorMap,
932                                            QContactManager::Error *error)
933 {
934     QContactRelationshipRemoveRequest request;
935     request.setRelationships(relationships);
936
937     runSyncRequest(&request, error);
938     propagate(request.errorMap(), errorMap);
939
940     return QContactManager::NoError == request.error();
941 }
942
943
944 static const DisplayNameDetailList &
945 findDisplayLabelDetails(const DisplayNameDetailMap &displayNameDetails,
946                         const QSet<QString> &definitionHints)
947 {
948     QString nameOrder;
949
950     foreach(const QString &detailName, definitionHints) {
951         if (detailName.startsWith(QContactDisplayLabel::DefinitionName.latin1())) {
952             const int n(sizeof QContactDisplayLabel::DefinitionName.chars);
953
954             if (detailName.length() >= n && QLatin1Char(';') == detailName[n - 1]) {
955                 nameOrder = detailName.mid(n);
956             }
957
958             break;
959         }
960     }
961
962     DisplayNameDetailMap::ConstIterator i(displayNameDetails.find(nameOrder));
963
964     if (i == displayNameDetails.end()) {
965         i = displayNameDetails.begin();
966     }
967
968     return i.value();
969 }
970
971 void
972 QContactTrackerEngine::ensureDisplayLabelDetails(QSet<QString> &definitionHints)
973 {
974     foreach(const DisplayNameDetail &detail,
975             findDisplayLabelDetails(d->m_displayNameDetails, definitionHints)) {
976         definitionHints.insert(detail.first);
977     }
978 }
979
980 void
981 QContactTrackerEngine::updateDisplayLabel(QContact &contact, const QSet<QString> &definitionHints)
982 {
983     const QContactDisplayLabel label(contact.detail<QContactDisplayLabel>());
984
985     if (label.label().isEmpty()) {
986         DisplayNameDetailStack *detailStack(displayNameStack.localData());
987
988         if (0 == detailStack) {
989             displayNameStack.setLocalData(detailStack = new DisplayNameDetailStack);
990         }
991
992         detailStack->enqueue(findDisplayLabelDetails(d->m_displayNameDetails, definitionHints));
993
994         QContactManager::Error synthError;
995         setContactDisplayLabel(&contact, synthesizedDisplayLabel(contact, &synthError));
996
997         detailStack->dequeue();
998     }
999 }
1000
1001 // gives a ranking for the presence state: the higher the value,
1002 // the more "available" is a contact with that presence state
1003 static int
1004 availabilityRank(QContactPresence::PresenceState state)
1005 {
1006     switch(state) {
1007     case QContactPresence::PresenceUnknown:
1008         return 0;
1009     case QContactPresence::PresenceHidden:
1010     case QContactPresence::PresenceOffline:
1011         return 1;
1012     case QContactPresence::PresenceAway:
1013     case QContactPresence::PresenceExtendedAway:
1014         return 2;
1015     case QContactPresence::PresenceBusy:
1016         return 3;
1017     case QContactPresence::PresenceAvailable:
1018         return 4;
1019     }
1020
1021     return availabilityRank(QContactPresence::PresenceUnknown);
1022 }
1023
1024 template <class T> static void
1025 transfer(const T &key, const QContactDetail &source, QContactDetail &target)
1026 {
1027     QVariant value(source.variantValue(key));
1028
1029     if (not value.isNull()) {
1030         target.setValue(key, value);
1031     }
1032 }
1033
1034 void
1035 QContactTrackerEngine::updateGlobalPresence(QContact &contact)
1036 {
1037     const QList<QContactPresence> presenceDetails(contact.details<QContactPresence>());
1038     const QContactPresence *mostAvailable(0);
1039     const QContactPresence *mostRecent(0);
1040
1041     if (not presenceDetails.isEmpty()) {
1042         mostAvailable = mostRecent = &presenceDetails.first();
1043     }
1044
1045     foreach(const QContactPresence &detail, presenceDetails) {
1046         if (availabilityRank(detail.presenceState()) > availabilityRank(mostAvailable->presenceState())) {
1047             if (detail.timestamp() == mostRecent->timestamp()) {
1048                 mostRecent = &detail;
1049             }
1050
1051             mostAvailable = &detail;
1052         }
1053
1054         if (detail.timestamp() > mostRecent->timestamp()) {
1055             mostRecent = &detail;
1056         }
1057     }
1058
1059     QContactGlobalPresence global(contact.detail<QContactGlobalPresence>());
1060
1061     if (mostRecent && mostAvailable) {
1062         QSet<QString> linkedDetails;
1063         linkedDetails << mostRecent->detailUri();
1064         linkedDetails << mostAvailable->detailUri();
1065         global.setLinkedDetailUris(linkedDetails.toList());
1066
1067         transfer(QContactGlobalPresence::FieldNickname, *mostRecent, global);
1068         transfer(QContactGlobalPresence::FieldTimestamp, *mostRecent, global);
1069         transfer(QContactGlobalPresence::FieldCustomMessage, *mostRecent, global);
1070
1071         transfer(QContactGlobalPresence::FieldPresenceState, *mostAvailable, global);
1072         transfer(QContactGlobalPresence::FieldPresenceStateText, *mostAvailable, global);
1073         transfer(QContactGlobalPresence::FieldPresenceStateImageUrl, *mostAvailable, global);
1074
1075         contact.saveDetail(&global);
1076     } else {
1077         contact.removeDetail(&global);
1078     }
1079 }
1080
1081 ////////////////////////////////////////////////////////////////////////////////////////////////////
1082 // Asynchronous data access methods
1083 ////////////////////////////////////////////////////////////////////////////////////////////////////
1084
1085 bool
1086 QContactTrackerEngine::finishDetailDefinitions()
1087 {
1088     foreach(QTrackerContactDetailSchema schema, d->m_detailSchemas) {
1089         if (not schema.finishDetailDefinitions()) {
1090             return false;
1091         }
1092     }
1093
1094     return true;
1095 }
1096
1097 bool
1098 QContactTrackerEngine::startRequest(QContactAbstractRequest* request)
1099 {
1100     if (not finishDetailDefinitions()) {
1101         qctWarn(QString::fromLatin1("Cannot finish detail definitions. Rejecting %1 request.").
1102                 arg(request->metaObject()->className()));
1103
1104         return false;
1105     }
1106
1107     // ensure old worker got destroyed when request gets reused
1108     requestDestroyed(request);
1109
1110     QTrackerAbstractRequest *worker = 0;
1111     QTime t; t.start();
1112
1113     // choose proper request implementation
1114     switch(request->type())
1115     {
1116     case QContactAbstractRequest::ContactFetchRequest:
1117         worker = new QTrackerContactFetchRequest(request, this);
1118         break;
1119
1120     case QContactAbstractRequest::ContactLocalIdFetchRequest:
1121         worker = new QTrackerContactIdFetchRequest(request, this);
1122         break;
1123
1124     case QContactAbstractRequest::ContactRemoveRequest:
1125         worker = new QTrackerContactRemoveRequest(request, this);
1126         break;
1127
1128     case QContactAbstractRequest::ContactSaveRequest:
1129         worker = new QTrackerContactSaveRequest(request, this);
1130         break;
1131
1132     case QContactAbstractRequest::RelationshipFetchRequest:
1133         worker = new QTrackerRelationshipFetchRequest(request, this);
1134         break;
1135
1136     case QContactAbstractRequest::RelationshipRemoveRequest:
1137         worker = new QTrackerRelationshipRemoveRequest(request, this);
1138         break;
1139
1140     case QContactAbstractRequest::RelationshipSaveRequest:
1141         worker = new QTrackerRelationshipSaveRequest(request, this);
1142         break;
1143
1144     case QContactAbstractRequest::InvalidRequest:
1145     case QContactAbstractRequest::DetailDefinitionFetchRequest:
1146     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
1147     case QContactAbstractRequest::DetailDefinitionSaveRequest:
1148         break;
1149     }
1150
1151     if (0 == worker) {
1152         qctWarn(QString::fromLatin1("Unsupported request type: %1").
1153                 arg(request->metaObject()->className()));
1154         return false;
1155     }
1156
1157     if (hasDebugFlag(ShowNotes)) {
1158         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
1159     }
1160
1161     // the contacts engine must bring requests into Activate state as soon as it is dealing with them
1162     // the (secret) state machine only permits this transitions Inactive > Active > Canceled/Finished
1163     // because in case of an error the state can go to Canceled/Finished, set to Active now
1164     // before starting the worker
1165     updateRequestState(request, QContactAbstractRequest::ActiveState);
1166
1167     if (hasDebugFlag(ShowNotes)) {
1168         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
1169     }
1170
1171     // XXX The unit tests directly access the engine. Therefore requests created by our unit
1172     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
1173     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
1174     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
1175     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
1176     // a request without contact manager is found.
1177     //
1178     // XXX This all of course is an ugly workaround. We probably should change the unit tests
1179     // to create use QContactManager accessing the engine via a static plugin.
1180     if (0 == request->manager()) {
1181         connect(request, SIGNAL(destroyed(QObject*)),
1182                 SLOT(requestDestroyed(QObject*)));
1183     }
1184
1185     // store the request and start it
1186     QMutexLocker locker(&d->m_mutex);
1187     d->m_requests.insert(request, worker);
1188     locker.unlock(); // silence gcc
1189
1190     bool success = worker->start();
1191
1192     if (hasDebugFlag(ShowNotes)) {
1193         qDebug() << Q_FUNC_INFO << "time elapsed until start returned:" << request << t.elapsed();
1194     }
1195
1196     return success;
1197 }
1198
1199 void
1200 QContactTrackerEngine::connectNotify(const char *signal)
1201 {
1202     if (0 == d->m_trackerChangeListener) {
1203         // Create the change listener on demand:
1204         // Creating the listener is expensive as we must subscribe to some DBus signals.
1205         // Also watching DBus without any specific need wastes energy by wakeing up processes.
1206         d->m_trackerChangeListener = new TrackerChangeListener(this, this);
1207
1208         connect(d->m_trackerChangeListener,
1209                 SIGNAL(contactsAdded(const QList<QContactLocalId>&)),
1210                 SIGNAL(contactsAdded(const QList<QContactLocalId>&)));
1211         connect(d->m_trackerChangeListener,
1212                 SIGNAL(contactsChanged(const QList<QContactLocalId>&)),
1213                 SIGNAL(contactsChanged(const QList<QContactLocalId>&)));
1214         connect(d->m_trackerChangeListener,
1215                 SIGNAL(contactsRemoved(const QList<QContactLocalId>&)),
1216                 SIGNAL(contactsRemoved(const QList<QContactLocalId>&)));
1217     }
1218
1219     QContactManagerEngine::connectNotify(signal);
1220 }
1221
1222 /*! \reimp */
1223 void
1224 QContactTrackerEngine::requestDestroyed(QContactAbstractRequest *req)
1225 {
1226     if (0 != req) {
1227         // the worker itself must be deleted outside of the mutex to prevent
1228         // deadlocks if the worker should use additional internal requests
1229         // for doing its job
1230         QMutexLocker locker(&d->m_mutex);
1231         QTrackerAbstractRequest *worker(d->m_requests.take(req));
1232         locker.unlock(); // silence gcc
1233
1234         if (hasDebugFlag(ShowNotes)) {
1235             qDebug()
1236                     << metaObject()->className() << ": request destroyed:"
1237                     << (void* ) req << worker;
1238         }
1239
1240         delete worker;
1241     }
1242 }
1243
1244 void
1245 QContactTrackerEngine::requestDestroyed(QObject *req)
1246 {
1247     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
1248     // derived parts of the class have already been destroyed by the time the base QObject's
1249     // destructor has emitted this signal. So we do a regular static case, because
1250     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
1251     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
1252 }
1253
1254 bool
1255 QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest *request, int msecs)
1256 {
1257     if (0 == request) {
1258         return false;
1259     }
1260
1261     if (not request->isFinished()) {
1262         RequestEventLoop eventLoop;
1263
1264         eventLoop.connect(request,
1265                           SIGNAL(stateChanged(QContactAbstractRequest::State)),
1266                           SLOT(stateChanged(QContactAbstractRequest::State)));
1267
1268         if (msecs > 0) {
1269             QTimer::singleShot(msecs, &eventLoop, SLOT(quit()));
1270         }
1271
1272         // Spin it, baby!
1273         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1274     }
1275
1276     return request->isFinished();
1277 }
1278
1279 bool
1280 QContactTrackerEngine::runSyncRequest(QContactAbstractRequest *request,
1281                                       QContactManager::Error *error) const
1282 {
1283     ContactTrackerRequestTask task(this, request, requestTimeout());
1284
1285     task.start();
1286     task.wait(); // ContactTrackerRequestTask does the timeout more reliably
1287
1288     if (not task.isFinished()) {
1289         propagate(QContactManager::UnspecifiedError, error);
1290         return false;
1291     }
1292
1293     propagate(request->error(), error);
1294
1295     return true;
1296 }
1297
1298 ////////////////////////////////////////////////////////////////////////////////////////////////////
1299 // Synthesized contact details
1300 ////////////////////////////////////////////////////////////////////////////////////////////////////
1301
1302 QString
1303 QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact,
1304                                                QContactManager::Error* error) const
1305 {
1306     propagate(QContactManager::NoError, error);
1307
1308     const DisplayNameDetailStack *stack(displayNameStack.localData());
1309     const DisplayNameDetailList &details(stack && not stack->isEmpty() ? stack->last() :
1310                                          d->m_displayNameDetails.begin().value());
1311
1312     QString label;
1313
1314     foreach(const DisplayNameDetail &dnd, details) {
1315         const QContactDetail detail(contact.detail(dnd.first));
1316
1317         foreach(const QString &fieldName, dnd.second) {
1318             const QString value(detail.value(fieldName));
1319
1320             if (value.isEmpty()) {
1321                 continue;
1322             }
1323
1324             if (not label.isEmpty()) {
1325                 label.append(QLatin1Char(' '));
1326             }
1327
1328             label.append(value);
1329         }
1330
1331         if (not label.isEmpty()) {
1332             break;
1333         }
1334     }
1335
1336     return label;
1337 }
1338
1339 ////////////////////////////////////////////////////////////////////////////////////////////////////
1340 // Unsupported functions
1341 ////////////////////////////////////////////////////////////////////////////////////////////////////
1342
1343 QContact
1344 QContactTrackerEngine::compatibleContact(const QContact&, QContactManager::Error* error) const
1345 {
1346     REPORT_UNSUPPORTED_FUNCTION(error);
1347     return QContact();
1348 }
1349
1350 bool
1351 QContactTrackerEngine::validateContact(const QContact&, QContactManager::Error* error) const
1352 {
1353     REPORT_UNSUPPORTED_FUNCTION(error);
1354     return false;
1355 }
1356
1357 bool
1358 QContactTrackerEngine::validateDefinition(const QContactDetailDefinition&,
1359                                           QContactManager::Error* error) const
1360 {
1361     REPORT_UNSUPPORTED_FUNCTION(error);
1362     return false;
1363 }
1364
1365 bool
1366 QContactTrackerEngine::saveDetailDefinition(const QContactDetailDefinition&, const QString&,
1367                                             QContactManager::Error* error)
1368 {
1369     REPORT_UNSUPPORTED_FUNCTION(error);
1370     return false;
1371 }
1372
1373 bool
1374 QContactTrackerEngine::removeDetailDefinition(const QString&, const QString&,
1375                                               QContactManager::Error* error)
1376 {
1377     REPORT_UNSUPPORTED_FUNCTION(error);
1378     return false;
1379 }
1380
1381 bool
1382 QContactTrackerEngine::cancelRequest(QContactAbstractRequest *request)
1383 {
1384     if (0 == request) {
1385         return false;
1386     }
1387
1388     updateRequestState(request, QContactAbstractRequest::CanceledState);
1389
1390     QTrackerAbstractRequest *worker = d->m_requests.take(request);
1391
1392     if (0 == worker) {
1393         return false;
1394     }
1395
1396     delete worker;
1397     return true;
1398 }
1399
1400 bool
1401 QContactTrackerEngine::isRelationshipTypeSupported(const QString& relationshipType,
1402                                                    const QString& contactType) const
1403 {
1404     if (relationshipType == QContactRelationship::HasMember
1405         && (contactType == QContactType::TypeContact
1406             || contactType == QContactType::TypeGroup)) {
1407         return true;
1408     }
1409
1410     return false;
1411 }