qtmobility/plugins/contacts/qtcontacts-tracker/qtrackercontactfetchrequest.cpp
changeset 4 90517678cc4f
child 11 06b8e2af4411
equal deleted inserted replaced
1:2b40d63a9c3d 4:90517678cc4f
       
     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 
       
    42 #include "qtrackercontactfetchrequest.h"
       
    43 
       
    44 #include <qtcontacts.h>
       
    45 
       
    46 #include <QtTracker/ontologies/nie.h>
       
    47 #include <QtTracker/ontologies/nco.h>
       
    48 
       
    49 using namespace SopranoLive;
       
    50 
       
    51 class ConversionLookup: public QHash<QString,QContactPresence::PresenceState>
       
    52 {
       
    53 public:
       
    54     ConversionLookup& operator<<(const QPair<QString, QContactPresence::PresenceState> &conversion)
       
    55     {
       
    56         this->insert(conversion.first, conversion.second);
       
    57         return *this;
       
    58     }
       
    59 };
       
    60 
       
    61 const QString FieldQContactLocalId("QContactLocalId");
       
    62 const QString FieldAccountPath("AccountPath");
       
    63 const ConversionLookup presenceConversion(ConversionLookup()
       
    64             <<QPair<QString, QContactPresence::PresenceState>("presence-status-offline", QContactPresence::PresenceOffline)
       
    65             <<QPair<QString, QContactPresence::PresenceState>("presence-status-available", QContactPresence::PresenceAvailable)
       
    66             <<QPair<QString, QContactPresence::PresenceState>("presence-status-away", QContactPresence::PresenceAway)
       
    67             <<QPair<QString, QContactPresence::PresenceState>("presence-status-extended-away", QContactPresence::PresenceExtendedAway)
       
    68             <<QPair<QString, QContactPresence::PresenceState>("presence-status-busy", QContactPresence::PresenceBusy)
       
    69             <<QPair<QString, QContactPresence::PresenceState>("presence-status-unknown", QContactPresence::PresenceUnknown)
       
    70             <<QPair<QString, QContactPresence::PresenceState>("presence-status-hidden", QContactPresence::PresenceHidden)
       
    71             <<QPair<QString, QContactPresence::PresenceState>("presence-status-dnd", QContactPresence::PresenceBusy)
       
    72 );
       
    73 
       
    74 void matchPhoneNumber(RDFVariable &variable, QContactDetailFilter &filter)
       
    75 {
       
    76     // This here is the first implementation of filtering that takes into account also affiliations.
       
    77     // needs to be applied for other filters too - TODO
       
    78     RDFVariable officeContact;
       
    79     RDFVariable homeContact;
       
    80 
       
    81     RDFVariable rdfPhoneNumber;
       
    82     rdfPhoneNumber = homeContact.property<nco::hasPhoneNumber>().property<nco::phoneNumber>();
       
    83 
       
    84     RDFVariable rdfOfficePhoneNumber;
       
    85     rdfOfficePhoneNumber = officeContact.property<nco::hasAffiliation>().property<nco::hasPhoneNumber>().property<nco::phoneNumber>();
       
    86 
       
    87     QString filterValue = filter.value().toString();
       
    88     if (filter.matchFlags() == Qt::MatchEndsWith)
       
    89     {
       
    90         QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Nokia","Trackerplugin");
       
    91         int matchDigitCount = settings.value("phoneNumberMatchDigitCount", "7").toInt();
       
    92         filterValue = filterValue.right(matchDigitCount);
       
    93         qDebug() << "match with:" << matchDigitCount << ":" << filterValue;
       
    94         rdfPhoneNumber.hasSuffix(filterValue);
       
    95         rdfOfficePhoneNumber.hasSuffix(filterValue);
       
    96     }
       
    97     else
       
    98     {   // default to exact match
       
    99         rdfOfficePhoneNumber.matchesRegexp(filterValue);
       
   100         rdfPhoneNumber.matchesRegexp(filterValue);
       
   101     }
       
   102     // This is the key part, including both contacts and affiliations
       
   103     variable.isMemberOf(RDFVariableList()<<homeContact);// TODO report bug doesnt work in tracker <<officeContact);
       
   104 }
       
   105 
       
   106 /*!
       
   107  * To define RDFquery graph for this one is tricky:
       
   108  * need to find IMAccount -> hasIMContact -> IMAddress - the same IMAddress as contact \a variable
       
   109  * has as PersonContact -> hasIMAddress -> IMAddress
       
   110  */
       
   111 void matchOnlineAccount(RDFVariable &variable, QContactDetailFilter &filter)
       
   112 {
       
   113     if ((filter.matchFlags() & QContactFilter::MatchExactly) == QContactFilter::MatchExactly)
       
   114     {
       
   115         // \a variable PersonContact -> hasIMAddress -> imaddress
       
   116         RDFVariable imaddress = variable.property<nco::hasIMAddress>();
       
   117         if (filter.detailFieldName() == "Account" || filter.detailFieldName() == QContactOnlineAccount::FieldAccountUri)
       
   118         {
       
   119             imaddress.property<nco::imID> ().isMemberOf(QStringList() << filter.value().toString());
       
   120         }
       
   121         else if (filter.detailFieldName() == FieldAccountPath)
       
   122         {
       
   123             // need to find IMAccount -> hasIMContact -> imaddress
       
   124             RDFVariable imaccount;
       
   125             imaccount.property<nco::hasIMContact>() = imaddress;
       
   126             imaccount.equal(QUrl(QString("telepathy:")+filter.value().toString()));
       
   127         }
       
   128         else if (filter.detailFieldName() == QContactOnlineAccount::FieldServiceProvider)
       
   129         {
       
   130             // need to find IMAccount -> hasIMContact -> imaddress
       
   131             RDFVariable imaccount;
       
   132             imaccount.property<nco::hasIMContact>() = imaddress;
       
   133             imaccount.property<nco::imDisplayName> ().isMemberOf(QStringList() << filter.value().toString());
       
   134         }
       
   135         else
       
   136             qWarning() << "QTrackerContactFetchRequest," << __FUNCTION__
       
   137                     << "Unsupported detail filter by QContactOnlineAccount.";
       
   138     }
       
   139     else
       
   140     {
       
   141         qWarning() << "QTrackerContactFetchRequest," << __FUNCTION__
       
   142                 << "Unsupported match flag in detail filter by QContactOnlineAccount. Use QContactFilter::MatchExactly";
       
   143     }
       
   144 }
       
   145 
       
   146 void matchName(RDFVariable &variable, QContactDetailFilter &filter)
       
   147 {
       
   148     if (filter.detailDefinitionName() != QContactName::DefinitionName) {
       
   149         qWarning() << "QTrackerContactFetchRequest," << __FUNCTION__
       
   150                    << "Unsupported definition name in detail filter, should be QContactName::DefinitionName";
       
   151         return;
       
   152     }
       
   153     QString filterValue = filter.value().toString();
       
   154     QString field = filter.detailFieldName();
       
   155     if ((filter.matchFlags() & QContactFilter::MatchExactly) == QContactFilter::MatchExactly) {
       
   156         if (field == QContactName::FieldFirstName) {
       
   157             variable.property<nco::nameGiven>() = LiteralValue(filterValue);
       
   158         } else if (field == QContactName::FieldLastName) {
       
   159             variable.property<nco::nameFamily>() = LiteralValue(filterValue);
       
   160         } else if (field == QContactName::FieldMiddleName) {
       
   161             variable.property<nco::nameAdditional>() = LiteralValue(filterValue);
       
   162         } else if (field == QContactName::FieldPrefix) {
       
   163             variable.property<nco::nameHonorificPrefix>() = LiteralValue(filterValue);
       
   164         } else if (field == QContactName::FieldSuffix) {
       
   165             variable.property<nco::nameHonorificSuffix>() = LiteralValue(filterValue);
       
   166         }
       
   167     } else {
       
   168         qWarning() << "QTrackerContactFetchRequest," << __FUNCTION__
       
   169                    << "Unsupported match flag in detail filter by QContactName";
       
   170     }
       
   171 }
       
   172 
       
   173 /*
       
   174  * RDFVariable describes all contacts in tracker before filter is applied.
       
   175  * This method translates QContactFilter to tracker rdf filter. When query is made
       
   176  * after this method, it would return only contacts that fit the filter.
       
   177  */
       
   178 QContactManager::Error QTrackerContactFetchRequest::applyFilterToContact(RDFVariable &variable,
       
   179         const QContactFilter &filter)
       
   180 {
       
   181 
       
   182     QContactFetchRequest* r = qobject_cast<QContactFetchRequest*> (req);
       
   183 
       
   184     if (!r) {
       
   185             return QContactManager::BadArgumentError;
       
   186     }
       
   187 
       
   188     if (filter.type() == QContactFilter::LocalIdFilter) {
       
   189         QContactLocalIdFilter filt = filter;
       
   190         if (!filt.ids().isEmpty()) {
       
   191             if (!isMeContact(r->filter())) {
       
   192             variable.property<nco::contactUID>().isMemberOf(filt.ids());
       
   193             } else {
       
   194                 variable == nco::default_contact_me::iri();
       
   195             }
       
   196         } else {
       
   197             qWarning() << Q_FUNC_INFO << "QContactLocalIdFilter idlist is empty";
       
   198             return QContactManager::BadArgumentError;
       
   199         }
       
   200     } else if (filter.type() == QContactFilter::ContactDetailFilter) {
       
   201         // this one is tricky as we need to match in contacts or in affiliations
       
   202 
       
   203         QContactDetailFilter filt = filter;
       
   204         if ( QContactPhoneNumber::DefinitionName == filt.detailDefinitionName()
       
   205              && QContactPhoneNumber::FieldNumber == filt.detailFieldName()) {
       
   206             matchPhoneNumber(variable, filt);
       
   207         }
       
   208         else if(QContactOnlineAccount::DefinitionName == filt.detailDefinitionName())
       
   209         {
       
   210             matchOnlineAccount(variable, filt);
       
   211         }
       
   212         else if (QContactName::DefinitionName == filt.detailDefinitionName()) {
       
   213             matchName(variable, filt);
       
   214         }
       
   215         else if (filt.matchFlags() == Qt::MatchExactly) {
       
   216             if (QContactEmailAddress::DefinitionName == filt.detailDefinitionName()
       
   217                        && QContactEmailAddress::FieldEmailAddress == filt.detailFieldName()) {
       
   218                 RDFVariable rdfEmailAddress;
       
   219                 rdfEmailAddress = variable.property<nco::hasEmailAddress>();
       
   220                 rdfEmailAddress.property<nco::emailAddress>() = LiteralValue(filt.value().toString());
       
   221             } else {
       
   222                 qWarning() << __PRETTY_FUNCTION__ << "QContactTrackerEngine: Unsupported QContactFilter::ContactDetail"
       
   223                     << filt.detailDefinitionName();
       
   224                 return QContactManager::NotSupportedError;
       
   225             }
       
   226         }
       
   227     }
       
   228     else if (filter.type() == QContactFilter::ContactDetailRangeFilter)
       
   229     {
       
   230         return applyDetailRangeFilterToContact(variable, filter);
       
   231     }
       
   232     else if (filter.type() == QContactFilter::ChangeLogFilter) {
       
   233         const QContactChangeLogFilter& clFilter = static_cast<const QContactChangeLogFilter&>(filter);
       
   234         // do not return facebook and telepathy contacts here
       
   235         // it is a temp implementation for the what to offer to synchronization constraint
       
   236         variable.property<nao::hasTag>().property<nao::prefLabel>() = LiteralValue("addressbook");
       
   237 
       
   238         if (clFilter.eventType() == QContactChangeLogFilter::EventRemoved) { // Removed since
       
   239             qWarning() << "QContactTrackerEngine: Unsupported QContactChangeLogFilter::Removed (contacts removed since)";
       
   240             return QContactManager::NotSupportedError;
       
   241         } else if (clFilter.eventType() == QContactChangeLogFilter::EventAdded) { // Added since
       
   242             variable.property<nie::contentCreated>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
       
   243         } else if (clFilter.eventType() == QContactChangeLogFilter::EventChanged) { // Changed since
       
   244             variable.property<nie::contentLastModified>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
       
   245         }
       
   246     } else if (filter.type() == QContactFilter::UnionFilter) {
       
   247         const QContactUnionFilter unionFilter(filter);
       
   248         foreach (const QContactFilter& f, unionFilter.filters()) {
       
   249             QContactManager::Error error = applyFilterToContact(variable, f);
       
   250             if (QContactManager::NoError != error)
       
   251                 return error;
       
   252         }
       
   253     }
       
   254     else if(filter.type() == QContactFilter::InvalidFilter || filter.type() == QContactFilter::DefaultFilter)
       
   255         return QContactManager::NoError;
       
   256     else
       
   257         return QContactManager::NotSupportedError;
       
   258     return QContactManager::NoError;
       
   259 }
       
   260 
       
   261 //!\sa applyFilterToContact
       
   262 QContactManager::Error QTrackerContactFetchRequest::applyDetailRangeFilterToContact(RDFVariable &variable, const QContactFilter &filter)
       
   263 {
       
   264     Q_ASSERT(filter.type() == QContactFilter::ContactDetailRangeFilter);
       
   265     if (filter.type() == QContactFilter::ContactDetailRangeFilter) {
       
   266         QContactDetailRangeFilter filt = filter;
       
   267         // birthday range
       
   268         if (QContactBirthday::DefinitionName == filt.detailDefinitionName()
       
   269                 && QContactBirthday::FieldBirthday == filt.detailFieldName())
       
   270         {
       
   271             RDFVariable time = variable.property<nco::birthDate>();
       
   272             if (filt.rangeFlags() & QContactDetailRangeFilter::IncludeUpper)
       
   273                 time <= LiteralValue(filt.maxValue().toDateTime().toString(Qt::ISODate));
       
   274             else
       
   275                 time < LiteralValue(filt.maxValue().toDateTime().toString(Qt::ISODate));
       
   276             if (filt.rangeFlags() & QContactDetailRangeFilter::ExcludeLower)
       
   277                 time > LiteralValue(filt.minValue().toDateTime().toString(Qt::ISODate));
       
   278             else
       
   279                 time >= LiteralValue(filt.minValue().toDateTime().toString(Qt::ISODate));
       
   280             return QContactManager::NoError;
       
   281         }
       
   282     }
       
   283     qWarning() << __PRETTY_FUNCTION__ << "Unsupported detail range filter";
       
   284     return QContactManager::NotSupportedError;
       
   285 }
       
   286 
       
   287 
       
   288 /*
       
   289  * To understand why all the following methods have for affiliation param, check nco ontology:
       
   290  * every contact has all these properties and also linked to affiliations (also contacts - nco:Role)
       
   291  * that again have the same properties. So it was needed to make the same query 2-ce - once for contact
       
   292  * and once for affiliations
       
   293  */
       
   294 RDFSelect preparePhoneNumbersQuery(RDFVariable &rdfcontact1, bool forAffiliations)
       
   295 {
       
   296     RDFVariable phone;
       
   297     if (!forAffiliations)
       
   298         phone = rdfcontact1.property<nco::hasPhoneNumber>();
       
   299     else
       
   300         phone = rdfcontact1.property<nco::hasAffiliation>().property<nco::hasPhoneNumber>();
       
   301     RDFVariable type = phone.type();
       
   302     type.property<rdfs::subClassOf>().notEqual(nco::ContactMedium::iri()); // sparql cannot handle exact type but returns all super types as junk rows
       
   303     type.property<rdfs::subClassOf>().notEqual(rdfs::Resource::iri()); // sparql cannot handle exact type but returns all super types as junk rows
       
   304     // therefore we eliminate those rows that are not of interest
       
   305     // columns
       
   306     RDFSelect queryidsnumbers;
       
   307     queryidsnumbers.addColumn("contactId", rdfcontact1.property<nco::contactUID> ());
       
   308     queryidsnumbers.addColumn("phoneno", phone.property<nco::phoneNumber> ());
       
   309     queryidsnumbers.addColumn("type", type);
       
   310     queryidsnumbers.distinct();
       
   311     return queryidsnumbers;
       
   312 }
       
   313 
       
   314 RDFSelect prepareEmailAddressesQuery(RDFVariable &rdfcontact1, bool forAffiliations)
       
   315 {
       
   316     RDFVariable email;
       
   317     if (!forAffiliations)
       
   318         email = rdfcontact1.property<nco::hasEmailAddress>();
       
   319     else
       
   320         email = rdfcontact1.property<nco::hasAffiliation>().property<nco::hasEmailAddress>();
       
   321     const RDFVariable& type = email.type();
       
   322     type.property<rdfs::subClassOf>().notEqual(nco::Resource::iri()); // sparql cannot handle exact type but returns all super types as junk rows
       
   323     // therefore we eliminate those rows that are not of interest
       
   324     // columns
       
   325     RDFSelect queryidsnumbers;
       
   326     queryidsnumbers.addColumn("contactId", rdfcontact1.property<nco::contactUID> ());
       
   327     queryidsnumbers.addColumn("emailaddress", email.property<nco::emailAddress> ());
       
   328     rdfcontact1.property<nco::hasEmailAddress> ().isOfType( nco::EmailAddress::iri(), true);
       
   329     queryidsnumbers.addColumn("type", type);
       
   330     queryidsnumbers.distinct();
       
   331     return queryidsnumbers;
       
   332 }
       
   333 
       
   334 
       
   335 /*!
       
   336  * \a contact here describes rdf graph - when making query, depending on filters applied
       
   337  * to \a contact, query results to any set of contacts
       
   338  */
       
   339 RDFSelect prepareIMAddressesQuery(RDFVariable  &contact)
       
   340 {
       
   341     RDFSelect queryidsimacccounts;
       
   342     // this establishes query graph relationship: imaddress that we want is a property in contact
       
   343     RDFVariable imaddress = contact.property<nco::hasIMAddress>();
       
   344     contact != nco::default_contact_me::iri();
       
   345 
       
   346     // in the query, we need to get imaccount where this imaddress resides.
       
   347     // i.e. from which local account we have established connection to imaddress
       
   348     RDFVariable imaccount = RDFVariable::fromType<nco::IMAccount>();
       
   349     // define link between imaccount to imaddress.
       
   350     imaccount.property<nco::hasIMContact>() = imaddress;
       
   351 
       
   352     queryidsimacccounts.addColumn("imaddress", imaddress);
       
   353     queryidsimacccounts.addColumn("contactId", contact.property<nco::contactUID> ());
       
   354     queryidsimacccounts.groupBy(imaddress);
       
   355     queryidsimacccounts.addColumn("IMId", imaddress.property<nco::imID> ());
       
   356     queryidsimacccounts.addColumn("status", imaddress.optional().property<nco::imPresence> ());
       
   357     queryidsimacccounts.addColumn("message", imaddress.optional().property<nco::imStatusMessage> ());
       
   358     queryidsimacccounts.addColumn("nick", imaddress.optional().property<nco::imNickname> ());
       
   359     queryidsimacccounts.addColumn("type", imaccount); // account path
       
   360     queryidsimacccounts.addColumn("capabilities",
       
   361               imaddress.optional().property<nco::imCapability>().filter("GROUP_CONCAT", LiteralValue(",")));
       
   362     queryidsimacccounts.addColumn("serviceprovider", imaccount.property<nco::imDisplayName>());
       
   363     return queryidsimacccounts;
       
   364 }
       
   365 
       
   366 RDFSelect prepareIMAccountsQuery(RDFVariable  &contact)
       
   367 {
       
   368     RDFVariable imAccount;
       
   369     imAccount = RDFVariable::fromType<nco::IMAccount>();
       
   370     RDFSelect queryidsimaccounts;
       
   371 
       
   372     RDFVariable address = imAccount.property<nco::imAccountAddress>();
       
   373     queryidsimaccounts.addColumn("uri", contact);
       
   374     queryidsimaccounts.addColumn("presence", address.property<nco::imPresence>());
       
   375     queryidsimaccounts.addColumn("message", address.property<nco::imStatusMessage>());
       
   376     queryidsimaccounts.addColumn("nick", address.property<nco::imNickname>());
       
   377     queryidsimaccounts.addColumn("distinct ", address.property<nco::imID>());
       
   378     queryidsimaccounts.addColumn("address_uri", address);
       
   379     queryidsimaccounts.addColumn("photo",address.optional().property<nco::imAvatar>());
       
   380 
       
   381     return queryidsimaccounts;
       
   382 }
       
   383 
       
   384 
       
   385 QTrackerContactAsyncRequest::QTrackerContactAsyncRequest(QContactAbstractRequest* request)
       
   386 : req(request)
       
   387 {
       
   388 }
       
   389 
       
   390 const QString rdfPhoneType2QContactSubtype(const QString rdfPhoneType)
       
   391 {
       
   392     if( rdfPhoneType.endsWith("VoicePhoneNumber") )
       
   393         return QContactPhoneNumber::SubTypeVoice;
       
   394     else if ( rdfPhoneType.endsWith("CarPhoneNumber") )
       
   395         return QContactPhoneNumber::SubTypeCar;
       
   396     else if ( rdfPhoneType.endsWith("CellPhoneNumber") )
       
   397         return QContactPhoneNumber::SubTypeMobile;
       
   398     else if ( rdfPhoneType.endsWith("BbsPhoneNumber") )
       
   399         return QContactPhoneNumber::SubTypeBulletinBoardSystem;
       
   400     else if ( rdfPhoneType.endsWith("FaxNumber") )
       
   401         return QContactPhoneNumber::SubTypeFax;
       
   402     else if ( rdfPhoneType.endsWith("ModemNumber") )
       
   403         return QContactPhoneNumber::SubTypeModem;
       
   404     else if ( rdfPhoneType.endsWith("PagerNumber") )
       
   405         return QContactPhoneNumber::SubTypePager;
       
   406     else if ( rdfPhoneType.endsWith("MessagingNumber") )
       
   407         return QContactPhoneNumber::SubTypeMessagingCapable;
       
   408     else
       
   409         qWarning() << Q_FUNC_INFO << "Not handled phone number type:" << rdfPhoneType;
       
   410     return "";
       
   411 }
       
   412 
       
   413 QTrackerContactAsyncRequest::~QTrackerContactAsyncRequest()
       
   414 {
       
   415 
       
   416 }
       
   417 
       
   418 /*!
       
   419  * The method was initially created to add default fields in case client did not supply
       
   420  * fields constraint - in that case the constraint is that default contact fields (ones
       
   421  * being edited in contact card and synchronized) are queried.
       
   422  */
       
   423 void QTrackerContactFetchRequest::validateRequest()
       
   424 {
       
   425     Q_ASSERT(req);
       
   426     Q_ASSERT(req->type() == QContactAbstractRequest::ContactFetchRequest);
       
   427     QContactFetchRequest* r = qobject_cast<QContactFetchRequest*> (req);
       
   428     QContactFetchHint fetchHint = r->fetchHint();
       
   429     if (r && fetchHint.detailDefinitionsHint().isEmpty()) {
       
   430         QStringList definitionNames;
       
   431         definitionNames << QContactAvatar::DefinitionName
       
   432             << QContactBirthday::DefinitionName
       
   433             << QContactAddress::DefinitionName
       
   434             << QContactEmailAddress::DefinitionName
       
   435             << QContactGender::DefinitionName
       
   436             << QContactAnniversary::DefinitionName
       
   437             << QContactName::DefinitionName
       
   438             << QContactOnlineAccount::DefinitionName
       
   439             << QContactOrganization::DefinitionName
       
   440             << QContactPhoneNumber::DefinitionName
       
   441             << QContactUrl::DefinitionName;
       
   442         fetchHint.setDetailDefinitionsHint(definitionNames);
       
   443         r->setFetchHint(fetchHint);
       
   444     }
       
   445 }
       
   446 
       
   447 QTrackerContactFetchRequest::QTrackerContactFetchRequest(QContactAbstractRequest* request,
       
   448                                                          QContactManagerEngine* parent) :
       
   449     QObject(parent),QTrackerContactAsyncRequest(request),
       
   450     queryPhoneNumbersNodesPending(0),
       
   451     queryEmailAddressNodesPending(0)
       
   452 {
       
   453     Q_ASSERT(parent);
       
   454     Q_ASSERT(request);
       
   455     QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState);
       
   456 
       
   457     QTimer::singleShot(0, this, SLOT(run()));
       
   458 }
       
   459 
       
   460 void QTrackerContactFetchRequest::run()
       
   461 {
       
   462     validateRequest();
       
   463     QContactFetchRequest* r = qobject_cast<QContactFetchRequest*> (req);
       
   464     QContactFetchHint fetchHint = r->fetchHint();
       
   465     QStringList definitionNames = fetchHint.detailDefinitionsHint();
       
   466 
       
   467     RDFVariable RDFContact = RDFVariable::fromType<nco::PersonContact>();
       
   468     QContactManager::Error error = applyFilterToContact(RDFContact, r->filter());
       
   469     if (error != QContactManager::NoError)
       
   470     {
       
   471         emitFinished(error);
       
   472         return;
       
   473     }
       
   474     if (definitionNames.contains(QContactPhoneNumber::DefinitionName)) {
       
   475         queryPhoneNumbersNodes.clear();
       
   476         queryPhoneNumbersNodesPending = 2;
       
   477         for(int forAffiliations = 0; forAffiliations <= 1; forAffiliations++) {
       
   478             // prepare query to get all phone numbers
       
   479             RDFVariable rdfcontact1 = RDFVariable::fromType<nco::PersonContact>();
       
   480             applyFilterToContact(rdfcontact1, r->filter());
       
   481             // criteria - only those with phone numbers
       
   482             RDFSelect queryidsnumbers = preparePhoneNumbersQuery(rdfcontact1, forAffiliations);
       
   483             queryPhoneNumbersNodes << ::tracker()->modelQuery(queryidsnumbers);
       
   484             // need to store LiveNodes in order to receive notification from model
       
   485             QObject::connect(queryPhoneNumbersNodes[forAffiliations].model(),
       
   486                              SIGNAL(modelUpdated()), this, SLOT(phoneNumbersReady()));
       
   487         }
       
   488     }
       
   489 
       
   490     if (definitionNames.contains(QContactEmailAddress::DefinitionName)) {
       
   491         queryEmailAddressNodes.clear();
       
   492         queryEmailAddressNodesPending = 2;
       
   493         for(int forAffiliations = 0; forAffiliations <= 1; forAffiliations++) {
       
   494             // prepare query to get all email addresses
       
   495             RDFVariable rdfcontact1 = RDFVariable::fromType<nco::PersonContact>();
       
   496             applyFilterToContact(rdfcontact1, r->filter());
       
   497             // criteria - only those with email addresses
       
   498             RDFSelect queryidsnumbers = prepareEmailAddressesQuery(rdfcontact1,forAffiliations);
       
   499             queryEmailAddressNodes << ::tracker()->modelQuery(queryidsnumbers);
       
   500             // need to store LiveNodes in order to receive notification from model
       
   501             QObject::connect(queryEmailAddressNodes[forAffiliations].model(),
       
   502                              SIGNAL(modelUpdated()), this, SLOT(emailAddressesReady()));
       
   503         }
       
   504     }
       
   505 
       
   506     if (definitionNames.contains( QContactOnlineAccount::DefinitionName)) {
       
   507         queryIMAccountNodesPending = 1;
       
   508 
       
   509         RDFSelect queryidsimaccounts;
       
   510         if(isMeContact(r->filter())) {
       
   511             // Prepare a query to get all IMAccounts
       
   512             RDFVariable meContact = RDFVariable::fromInstance<nco::default_contact_me>();
       
   513             queryidsimaccounts = prepareIMAccountsQuery(meContact);
       
   514         } else {
       
   515             RDFVariable rdfIMContact;
       
   516             rdfIMContact = rdfIMContact.fromType<nco::PersonContact> ();
       
   517             applyFilterToContact(rdfIMContact, r->filter());
       
   518             queryidsimaccounts = prepareIMAddressesQuery(rdfIMContact);
       
   519         }
       
   520         queryIMAccountNodes = ::tracker()->modelQuery(queryidsimaccounts);
       
   521         QObject::connect(queryIMAccountNodes.model(),
       
   522                 SIGNAL(modelUpdated()), SLOT(iMAcountsReady()));
       
   523     }
       
   524 
       
   525     QList<QContactLocalId> ids;
       
   526     RDFVariable RDFContact1 = RDFVariable::fromType<nco::PersonContact>();
       
   527     applyFilterToContact(RDFContact1, r->filter());
       
   528     RDFSelect quer;
       
   529     RDFVariable prefix = RDFContact1.optional().property<nco::nameHonorificPrefix> ();
       
   530     RDFVariable lastname = RDFContact1.optional().property<nco::nameFamily> ();
       
   531     RDFVariable middlename = RDFContact1.optional().property<nco::nameAdditional> ();
       
   532     RDFVariable firstname = RDFContact1.optional().property<nco::nameGiven> ();
       
   533     RDFVariable nickname = RDFContact1.optional().property<nco::nickname> ();
       
   534     quer.addColumn("contactId", RDFContact1.optional().property<nco::contactUID> ());
       
   535     quer.addColumn("prefix", prefix);
       
   536     quer.addColumn("firstname", firstname);
       
   537     quer.addColumn("middlename", middlename);
       
   538     quer.addColumn("secondname", lastname);
       
   539     quer.addColumn("photo", RDFContact1.optional().property<nco::photo> ());
       
   540     quer.addColumn("nickname", nickname);
       
   541 
       
   542     // for now adding columns to main query. later separate queries
       
   543     if (definitionNames.contains(QContactAddress::DefinitionName)) {
       
   544         RDFVariable address = RDFContact.optional().property< nco::hasPostalAddress> ();
       
   545         quer.addColumn("street",address.optional().property<nco::streetAddress> ());
       
   546         quer.addColumn("city", address.optional().property<nco::locality> ());
       
   547         quer.addColumn("country", address.optional().property<nco::country> ());
       
   548         quer.addColumn("pcode", address.optional().property<nco::postalcode> ());
       
   549         quer.addColumn("reg", address.optional().property<nco::region> ());
       
   550     }
       
   551     if (definitionNames.contains(QContactUrl::DefinitionName)) {
       
   552         quer.addColumn("homepage", RDFContact.optional().property<nco::websiteUrl> ());
       
   553         quer.addColumn("url", RDFContact.optional().property<nco::url> ());
       
   554         quer.addColumn("work_homepage", RDFContact.optional().property<nco::hasAffiliation> ().property<nco::websiteUrl> ());
       
   555         quer.addColumn("work_url", RDFContact.optional().property<nco::hasAffiliation> ().property<nco::url> ());
       
   556     }
       
   557     if (definitionNames.contains(QContactBirthday::DefinitionName)) {
       
   558         quer.addColumn("birth",RDFContact.optional().property<nco::birthDate> ());
       
   559     }
       
   560     if (definitionNames.contains(QContactGender::DefinitionName)) {
       
   561         quer.addColumn("gender", RDFContact.optional().property<nco::gender> ());
       
   562     }
       
   563     if (definitionNames.contains(QContactOrganization::DefinitionName)) {
       
   564         RDFVariable rdforg = RDFContact.optional().property<nco::hasAffiliation> ().optional().property<nco::org> ();
       
   565         quer.addColumn("org", rdforg.optional().property<nco::fullname> ());
       
   566         quer.addColumn("logo", rdforg.optional().property<nco::logo> ());
       
   567     }
       
   568 
       
   569 
       
   570     // QContactAnniversary - no such thing in tracker
       
   571     // QContactGeolocation - nco:hasLocation is not having class defined in nco yet. no properties. maybe rdfs:Resource:label
       
   572 
       
   573     // supporting sorting only here, difficult and no requirements in UI for sorting in multivalue details (phones, emails)
       
   574     foreach(const QContactSortOrder& sort, r->sorting()) {
       
   575         if (sort.detailDefinitionName() == QContactName::DefinitionName) {
       
   576             if (sort.detailFieldName() == QContactName::FieldFirstName)
       
   577                 quer.orderBy(firstname);
       
   578             else if (sort.detailFieldName() == QContactName::FieldLastName)
       
   579                 quer.orderBy(lastname);
       
   580             else
       
   581                 qWarning() << "QTrackerContactFetchRequest" << "sorting by"
       
   582                     << sort.detailDefinitionName()
       
   583                     << sort.detailFieldName() << "is not yet supported";
       
   584         } else {
       
   585             qWarning() << "QTrackerContactFetchRequest" << "sorting by"
       
   586                 << sort.detailDefinitionName()
       
   587                 << "is not yet supported";
       
   588         }
       
   589     }
       
   590     query = ::tracker()->modelQuery(quer);
       
   591     // need to store LiveNodes in order to receive notification from model
       
   592     QObject::connect(query.model(), SIGNAL(modelUpdated()), this, SLOT(contactsReady()));
       
   593 }
       
   594 
       
   595 bool detailExisting(const QString &definitionName, const QContact &contact, const QContactDetail &adetail)
       
   596 {
       
   597     QList<QContactDetail> details = contact.details(definitionName);
       
   598     foreach(const QContactDetail &detail, details) {
       
   599         if (detail == adetail) {
       
   600             return true;
       
   601         }
       
   602     }
       
   603     return false;
       
   604 }
       
   605 
       
   606 void QTrackerContactFetchRequest::contactsReady()
       
   607 {
       
   608     // 1) process contacts:
       
   609     // 2) process IMAddresses: queryIMAccountNodes
       
   610     // 3) process phonenumbers: queryPhoneNumbersNodes
       
   611     // 4) process emails: queryPhoneNumbersNodes
       
   612     // 5) update display label details
       
   613 
       
   614     QContactFetchRequest* request = qobject_cast<QContactFetchRequest*> (req);
       
   615     Q_ASSERT( request ); // signal is supposed to be used only for contact fetch
       
   616     // fastest way to get this working. refactor
       
   617     if (!request) {
       
   618         QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::FinishedState);
       
   619         return;
       
   620     }
       
   621 
       
   622     QContactManagerEngine *engine = qobject_cast<QContactManagerEngine *>(parent());
       
   623     Q_ASSERT(engine);
       
   624 
       
   625         // 1) process contacts:
       
   626     QContactFetchHint fetchHint = request->fetchHint();
       
   627     QStringList definitionNames = fetchHint.detailDefinitionsHint();
       
   628     for(int i = 0; i < query->rowCount(); i++) {
       
   629         bool ok;
       
   630         QContactLocalId contactid = query->index(i, 0).data().toUInt(&ok);
       
   631 
       
   632 
       
   633         QContact contact; // one we will be filling with this row
       
   634         if (isMeContact(request->filter())) {
       
   635             /* Me Contact does not have any cotactUID so assigning 0*/
       
   636             if(engine) {
       
   637                QContactManager::Error error;
       
   638                contactid = engine->selfContactId(&error);
       
   639                ok = true;
       
   640             }
       
   641         }
       
   642 
       
   643         if (!ok) {
       
   644             qWarning()<< Q_FUNC_INFO <<"Invalid contact ID: "<< query->index(i, 0).data().toString();
       
   645             continue;
       
   646         }
       
   647         QContactId id; id.setLocalId(contactid);
       
   648 
       
   649         if(engine)
       
   650             id.setManagerUri(engine->managerUri());
       
   651 
       
   652         contact.setId(id);
       
   653 
       
   654         // using redundancy to get less queries to tracker - it is possible
       
   655         // that rows are repeating: information for one contact is in several rows
       
   656         // that's why we update existing contact and append details to it if they
       
   657         // are not already in QContact
       
   658         QHash<quint32, int>::const_iterator it = id2ContactLookup.find(contactid);
       
   659         //
       
   660         int index = result.size(); // where to place new contact
       
   661         if (id2ContactLookup.end() != it) {
       
   662             if (it.value() < result.size() && it.value() >= 0) {
       
   663                 index = it.value();
       
   664                 contact = result[index];
       
   665             }
       
   666             Q_ASSERT(query->index(i, 0).data().toUInt() == contact.localId());
       
   667         }
       
   668 
       
   669         readFromQueryRowToContact(contact ,i);
       
   670         //to prevent me contact getting list in the contact list
       
   671         if (!isMeContact(request->filter())) {
       
   672             addContactToResultSet(contact);
       
   673         }
       
   674     }
       
   675 
       
   676     /*
       
   677      * 2) process IMAddresses _ To be replaced with derivedObjects
       
   678      */
       
   679      if (definitionNames.contains(QContactOnlineAccount::DefinitionName)) {
       
   680          processQueryIMContacts(queryIMAccountNodes);
       
   681      }
       
   682 
       
   683     // 3) process phonenumbers: queryPhoneNumbersNodes
       
   684     if (definitionNames.contains(QContactPhoneNumber::DefinitionName)) {
       
   685         Q_ASSERT(queryPhoneNumbersNodes.size() == 2);
       
   686         for( int cnt = 0; cnt < queryPhoneNumbersNodes.size(); cnt++) {
       
   687             processQueryPhoneNumbers(queryPhoneNumbersNodes[cnt], cnt);
       
   688         }
       
   689     }
       
   690 
       
   691     // 4) process emails: queryPhoneNumbersNodes
       
   692     if (definitionNames.contains(QContactEmailAddress::DefinitionName)) {
       
   693         Q_ASSERT(queryEmailAddressNodes.size() == 2);
       
   694         for (int cnt = 0; cnt < queryEmailAddressNodes.size(); cnt++) {
       
   695             processQueryEmailAddresses(queryEmailAddressNodes[cnt], cnt);
       
   696         }
       
   697     }
       
   698 
       
   699     // 5) update display labels
       
   700     for(int i = 0; i < result.count(); i++)
       
   701     {
       
   702         QContact &cont(result[i]);
       
   703         QContactDisplayLabel dl = cont.detail(QContactDisplayLabel::DefinitionName);
       
   704         if (dl.label().isEmpty()) {
       
   705             QContactManager::Error synthError;
       
   706             QContactManagerEngine::setContactDisplayLabel(&cont, engine->synthesizedDisplayLabel(cont, &synthError));
       
   707         }
       
   708     }
       
   709     emitFinished();
       
   710 }
       
   711 
       
   712 void QTrackerContactFetchRequest::emitFinished(QContactManager::Error error)
       
   713 {
       
   714     QContactFetchRequest *fetchRequest = qobject_cast<QContactFetchRequest *>(req);
       
   715     Q_ASSERT(fetchRequest);
       
   716     if(fetchRequest) {
       
   717         QContactManagerEngine::updateContactFetchRequest(fetchRequest, result, error, QContactAbstractRequest::FinishedState);
       
   718     }
       
   719 }
       
   720 
       
   721 /*!
       
   722  *  Appending contact to \sa result, id2ContactLookup,  of the request - to add it as new or to replace existing in result.
       
   723  */
       
   724 void QTrackerContactFetchRequest::addContactToResultSet(QContact &contact)
       
   725 {
       
   726     QHash<quint32, int>::const_iterator it = id2ContactLookup.find(contact.localId());
       
   727     int index = -1; // where to place new contact, -1 - not defined
       
   728     if (id2ContactLookup.end() != it) {
       
   729         if (it.value() < result.size() && it.value() >= 0)
       
   730             index = it.value();
       
   731     }
       
   732     QContact *contact_ = &contact;
       
   733     if ( -1 == index ) {
       
   734         result.append(*contact_);
       
   735         id2ContactLookup[contact_->localId()] = result.size()-1;
       
   736     } else {
       
   737         result[index] = *contact_;
       
   738         id2ContactLookup[contact_->localId()] = index;
       
   739     }
       
   740 }
       
   741 
       
   742 /*!
       
   743  * brief Processes one query record-row during read from tracker to QContact.
       
   744  * Order or columns in query is fixed to order defined in \sa run()
       
   745  */
       
   746 void QTrackerContactFetchRequest::readFromQueryRowToContact(QContact &contact, int i)
       
   747 {
       
   748     QContactFetchRequest* request = qobject_cast<QContactFetchRequest*> (req);
       
   749     Q_ASSERT( request ); // this is handled already in caller
       
   750     if( !request )
       
   751         return;
       
   752 
       
   753     int column = 1; // 0 - for QContactLocalId
       
   754     QContactName name = contact.detail(QContactName::DefinitionName);
       
   755     name.setPrefix(query->index(i, column++).data().toString());
       
   756     name.setFirstName(query->index(i, column++).data().toString());
       
   757     name.setMiddleName(query->index(i, column++).data().toString());
       
   758     name.setLastName(query->index(i, column++).data().toString());
       
   759     contact.saveDetail(&name);
       
   760 
       
   761     QContactAvatar avatar = contact.detail(QContactAvatar::DefinitionName);
       
   762     avatar.setImageUrl(QUrl(query->index(i, column++).data().toString())); // FIXME!!!
       
   763     //avatar.setAvatar(query->index(i, column++).data().toString());
       
   764     if (!avatar.imageUrl().isValid()) { // FIXME?
       
   765         contact.saveDetail(&avatar);
       
   766     }
       
   767         
       
   768     QContactNickname nick = contact.detail(QContactNickname::DefinitionName);
       
   769     nick.setNickname(query->index(i, column++).data().toString());
       
   770     contact.saveDetail(&nick);
       
   771 
       
   772        // TODO extract generic from bellow ... mapping field names
       
   773     QContactFetchHint fetchHint = request->fetchHint();
       
   774     QStringList definitionNames = fetchHint.detailDefinitionsHint();
       
   775     if (definitionNames.contains(QContactAddress::DefinitionName)) {
       
   776         QString street = query->index(i, column++).data().toString();
       
   777         QString loc = query->index(i, column++).data().toString();
       
   778         QString country = query->index(i, column++).data().toString();
       
   779         QString pcode = query->index(i, column++).data().toString();
       
   780         QString region = query->index(i, column++).data().toString();
       
   781         if (!(country.isEmpty() && pcode.isEmpty() && region.isEmpty()
       
   782               && street.isEmpty() && loc.isEmpty())) {
       
   783             // for multivalue fields is a bit tricky - try to find existing an
       
   784             QContactAddress a;
       
   785             a.setStreet(street);
       
   786             a.setLocality(loc);
       
   787             a.setCountry(country);
       
   788             a.setPostcode(pcode);
       
   789             a.setRegion(region);
       
   790             if (!detailExisting(QContactAddress::DefinitionName, contact, a)) {
       
   791                 contact.saveDetail(&a);
       
   792             }
       
   793         }
       
   794     }
       
   795     if (definitionNames.contains(QContactUrl::DefinitionName)) {
       
   796         // check query preparation (at the moment in constructor TODO refactor)
       
   797         // home website
       
   798         // if it is websiteUrl then interpret as homepage, if it is nco:url then fovourite url
       
   799         QContactUrl url;
       
   800         url.setSubType(QContactUrl::SubTypeHomePage);
       
   801         url.setContexts(QContactUrl::ContextHome);
       
   802         url.setUrl(query->index(i, column++).data().toString());
       
   803         if (url.url().isEmpty()) {
       
   804             // website url is at the same time url, so we handle duplication here
       
   805             // if only url then set it as favourite
       
   806             url.setUrl(query->index(i, column++).data().toString());
       
   807             url.setSubType(QContactUrl::SubTypeFavourite);
       
   808         }
       
   809 
       
   810         if (!url.url().isEmpty() && !detailExisting(QContactUrl::DefinitionName, contact, url)) {
       
   811             contact.saveDetail(&url);
       
   812         }
       
   813         // office website
       
   814         QContactUrl workurl;
       
   815         workurl.setContexts(QContactUrl::ContextWork);
       
   816         workurl.setSubType(QContactUrl::SubTypeHomePage);
       
   817         workurl.setUrl(query->index(i, column++).data().toString());
       
   818         if (workurl.url().isEmpty()) {
       
   819             workurl.setUrl(query->index(i, column++).data().toString());
       
   820             workurl.setSubType(QContactUrl::SubTypeFavourite);
       
   821         }
       
   822         if (!workurl.url().isEmpty() && !detailExisting(QContactUrl::DefinitionName, contact, workurl)) {
       
   823             contact.saveDetail(&workurl);
       
   824         }
       
   825     }
       
   826     if (definitionNames.contains(QContactBirthday::DefinitionName)) {
       
   827         QVariant var = query->index(i, column++).data();
       
   828         if (!var.toString().isEmpty() /* enable reading wrong && var.toDate().isValid()*/) {
       
   829             QContactBirthday birth = contact.detail(QContactBirthday::DefinitionName);
       
   830             birth.setDate(var.toDate());
       
   831             contact.saveDetail(&birth);
       
   832         }
       
   833     }
       
   834     if (definitionNames.contains(QContactGender::DefinitionName)) {
       
   835         QString var = query->index(i, column++).data().toString();
       
   836         if (!var.isEmpty()) {
       
   837             QContactGender g = contact.detail(QContactGender::DefinitionName);
       
   838             g.setGender(var);
       
   839             contact.saveDetail(&g);
       
   840         }
       
   841     }
       
   842     if (definitionNames.contains(QContactOrganization::DefinitionName)) {
       
   843         QString org = query->index(i, column++).data().toString();
       
   844         QString logo = query->index(i, column++).data().toString();
       
   845         if (!( org.isEmpty() && logo.isEmpty())) {
       
   846             QContactOrganization o;
       
   847             o.setName(org);
       
   848             o.setLogoUrl(QUrl(logo));
       
   849             if (!detailExisting(QContactOrganization::DefinitionName, contact, o)) {
       
   850                 contact.saveDetail(&o);
       
   851             }
       
   852         }
       
   853     }
       
   854 
       
   855 }
       
   856 
       
   857 
       
   858 void QTrackerContactFetchRequest::phoneNumbersReady()
       
   859 {
       
   860     queryPhoneNumbersNodesPending--;
       
   861 }
       
   862 
       
   863 void QTrackerContactFetchRequest::emailAddressesReady()
       
   864 {
       
   865     queryEmailAddressNodesPending--;
       
   866 }
       
   867 
       
   868 void QTrackerContactFetchRequest::imAcountsReady()
       
   869 {
       
   870     queryIMAccountNodesPending--;
       
   871     // now we know that the query is ready before get all contacts, check how it works with transactions
       
   872 }
       
   873 
       
   874 /*!
       
   875  * An internal helper method for converting nco:PhoneNumber subtype to
       
   876  * QContactPhoneNumber:: subtype attribute
       
   877  */
       
   878 void QTrackerContactFetchRequest::processQueryPhoneNumbers(SopranoLive::LiveNodes queryPhoneNumbers,
       
   879                                                            bool affiliationNumbers )
       
   880 {
       
   881     Q_ASSERT_X( queryPhoneNumbersNodesPending==0, Q_FUNC_INFO, "Phonenumbers query was supposed to be ready and it is not." );
       
   882     for (int i = 0; i < queryPhoneNumbers->rowCount(); i++) {
       
   883         // ignore if next one is the same - asked iridian about making query to ignore supertypes
       
   884         // TODO remove after his answer
       
   885         if ( i-1 >= 0
       
   886              && (queryPhoneNumbers->index(i, 0).data().toString()
       
   887                  == queryPhoneNumbers->index(i-1, 0).data().toString())
       
   888              && (queryPhoneNumbers->index(i, 1).data().toString()
       
   889                  == queryPhoneNumbers->index(i-1, 1).data().toString())) {
       
   890             // this is for ignoring duplicates. bad approach, asked iridian about
       
   891             // how to eliminate super types in query results
       
   892             continue;
       
   893         }
       
   894 
       
   895         QString subtype = rdfPhoneType2QContactSubtype(queryPhoneNumbers->index(i, 2).data().toString());
       
   896         QContactLocalId contactid = queryPhoneNumbers->index(i, 0).data().toUInt();
       
   897 
       
   898         QHash<quint32, int>::const_iterator it = id2ContactLookup.find(contactid);
       
   899         if (it != id2ContactLookup.end() && it.key() == contactid && it.value() >= 0 && it.value() < result.size())
       
   900         {
       
   901             QContactPhoneNumber number;
       
   902             if( affiliationNumbers )
       
   903                 number.setContexts(QContactPhoneNumber::ContextWork);
       
   904             else
       
   905                 number.setContexts(QContactPhoneNumber::ContextHome);
       
   906             number.setNumber(queryPhoneNumbers->index(i, 1).data().toString());
       
   907             number.setSubTypes(subtype);
       
   908             result[it.value()].saveDetail(&number);
       
   909         }
       
   910         else
       
   911             Q_ASSERT(false);
       
   912     }
       
   913 }
       
   914 
       
   915 void QTrackerContactFetchRequest::processQueryEmailAddresses( SopranoLive::LiveNodes queryEmailAddresses,
       
   916                                                               bool affiliationEmails)
       
   917 {
       
   918     Q_ASSERT_X(queryEmailAddressNodesPending == 0, Q_FUNC_INFO, "Email query was supposed to be ready and it is not." );
       
   919     for (int i = 0; i < queryEmailAddresses->rowCount(); i++) {
       
   920         // ignore if next one is the same - asked iridian about making query to ignore supertypes
       
   921         // TODO remove after his answer
       
   922         if ( i-1 >= 0
       
   923              && (queryEmailAddresses->index(i, 0).data().toString()
       
   924                  == queryEmailAddresses->index(i-1, 0).data().toString())
       
   925              && (queryEmailAddresses->index(i, 1).data().toString()
       
   926                  == queryEmailAddresses->index(i-1, 1).data().toString())) {
       
   927             // this is for ignoring duplicates. bad approach, asked iridian
       
   928             // about how to eliminate super types in query results
       
   929             continue;
       
   930         }
       
   931 
       
   932         //QString subtype = rdfPhoneType2QContactSubtype(queryEmailAddresses->index(i, 2).data().toString());
       
   933         QContactLocalId contactid = queryEmailAddresses->index(i, 0).data().toUInt();
       
   934 
       
   935         QHash<quint32, int>::const_iterator it = id2ContactLookup.find(contactid);
       
   936         if (it != id2ContactLookup.end() && it.key() == contactid && it.value() >= 0 && it.value() < result.size())
       
   937         {
       
   938             QContactEmailAddress email;
       
   939             if (affiliationEmails)
       
   940                 email.setContexts(QContactEmailAddress::ContextWork);
       
   941             else
       
   942                 email.setContexts(QContactEmailAddress::ContextHome);
       
   943             email.setEmailAddress(queryEmailAddresses->index(i, 1).data().toString());
       
   944             //email.setSubTypes(subtype);
       
   945             result[it.value()].saveDetail(&email);
       
   946         }
       
   947         else
       
   948             Q_ASSERT(false);
       
   949     }
       
   950 }
       
   951 
       
   952 
       
   953 /*!
       
   954  * \brief Processes one query record-row during read from tracker to QContactOnlineAccount.
       
   955  * Order or columns in query is fixed to order defined in \sa prepareIMAddressesQuery()
       
   956  */
       
   957 QContactOnlineAccount QTrackerContactFetchRequest::getOnlineAccountFromIMQuery(LiveNodes imAccountQuery, int queryRow)
       
   958 {
       
   959     QContactOnlineAccount account;
       
   960     QContactFetchRequest* r = qobject_cast<QContactFetchRequest*> (req);
       
   961     if(isMeContact(r->filter())) {
       
   962         account = getIMAccountFromIMQuery(imAccountQuery, queryRow);
       
   963     } else {
       
   964         account = getIMContactFromIMQuery(imAccountQuery, queryRow);
       
   965     }
       
   966     return account;
       
   967 }
       
   968 
       
   969 /*!
       
   970  * \brief processes IMQuery results. \sa prepareIMAddressesQuery, contactsReady
       
   971  */
       
   972 void QTrackerContactFetchRequest::processQueryIMContacts(SopranoLive::LiveNodes queryIMContacts)
       
   973 {
       
   974     //Q_ASSERT_X(queryEmailAddressNodes == 0, Q_FUNC_INFO, "IMAccount query was supposed to be ready and it is not." );
       
   975     QContactFetchRequest* r = qobject_cast<QContactFetchRequest*> (req);
       
   976     QContactManagerEngine *engine = qobject_cast<QContactManagerEngine *>(parent());
       
   977     Q_ASSERT(engine);
       
   978     if (!r  || !engine) {
       
   979         return;
       
   980     }
       
   981 
       
   982     for (int i = 0; i < queryIMContacts->rowCount(); i++) {
       
   983         QContactOnlineAccount account = getOnlineAccountFromIMQuery(queryIMContacts, i);
       
   984         QContactLocalId contactid = queryIMContacts->index(i, IMContact::ContactId).data().toUInt();
       
   985         // Need special treatment for me contact since the avatar is not working :(
       
   986         //
       
   987         if (isMeContact(r->filter())) {
       
   988             QString avatarURI = queryIMAccountNodes->index(i, IMAccount::ContactAvatar).data().toString();
       
   989             QContact meContact;
       
   990             QContactLocalId meContactLocalId;
       
   991             QContactManager::Error error;
       
   992             meContactLocalId = engine->selfContactId(&error);
       
   993             QContactId id; id.setLocalId(meContactLocalId);
       
   994             meContact.setId(id);
       
   995             QContactAvatar avatar = meContact.detail(QContactAvatar::DefinitionName);
       
   996             avatar.setImageUrl(QUrl(avatarURI)); // FIXME?
       
   997             //nick
       
   998 
       
   999             QContactNickname qnick = meContact.detail(QContactNickname::DefinitionName);
       
  1000             QString nick = queryIMAccountNodes->index(i, IMAccount::ContactNickname).data().toString(); // nick
       
  1001             qnick.setNickname(nick);
       
  1002 
       
  1003               
       
  1004             if (!avatarURI.isEmpty()) {
       
  1005                 meContact.saveDetail(&account);
       
  1006                 meContact.saveDetail(&avatar);
       
  1007                 meContact.saveDetail(&qnick);
       
  1008                 addContactToResultSet(meContact);
       
  1009             }
       
  1010         }
       
  1011 
       
  1012         QHash<quint32, int>::const_iterator it = id2ContactLookup.find(contactid);
       
  1013         if (it != id2ContactLookup.end() && it.key() == contactid && it.value() >= 0 && it.value() < result.size())
       
  1014         {
       
  1015            result[it.value()].saveDetail(&account);
       
  1016         }
       
  1017     }
       
  1018 }
       
  1019 
       
  1020 bool  QTrackerContactFetchRequest::isMeContact(const QContactFilter &filter) {
       
  1021     if (filter.type() == QContactFilter::LocalIdFilter) {
       
  1022          QContactManagerEngine *engine = dynamic_cast<QContactManagerEngine*>(parent());
       
  1023          if(!engine) {
       
  1024              qWarning() << __PRETTY_FUNCTION__ << ": Could not get QContactManager. Cannot retrieve IMAccounts for me-contact.";
       
  1025              return false;
       
  1026          }
       
  1027 
       
  1028         QContactManager::Error e;
       
  1029         QContactLocalId selfId = engine->selfContactId(&e);
       
  1030         QContactLocalIdFilter filt = filter;
       
  1031         if (filt.ids().contains(selfId)) {
       
  1032             return true;
       
  1033         }
       
  1034     }
       
  1035     return false;
       
  1036 }
       
  1037 
       
  1038 
       
  1039 QContactOnlineAccount QTrackerContactFetchRequest::getIMAccountFromIMQuery(LiveNodes imAccountQuery, int queryRow) {
       
  1040     QContactOnlineAccount account;
       
  1041 
       
  1042     // Custom value in QContactrOnlineAccount detail to store the account path to - to determine in My Profile to ignore the ring-account.
       
  1043     account.setValue("Account", imAccountQuery->index(queryRow, IMAccount::ContactIMId).data().toString()); // IMId
       
  1044     // the same is supposed to be in FieldAccountUri field
       
  1045     account.setValue(QContactOnlineAccount::FieldAccountUri, imAccountQuery->index(queryRow, IMAccount::ContactIMId).data().toString()); // IMId
       
  1046 
       
  1047     // XXX FIXME -- TODO VIA PRESENCE
       
  1048     //account.setNickname(imAccountQuery->index(queryRow, IMAccount::ContactNickname).data().toString()); // nick
       
  1049     //qDebug() << Q_FUNC_INFO << imAccountQuery->index(queryRow, IMAccount::ContactNickname).data().toString();
       
  1050 
       
  1051     //QString presence = imAccountQuery->index(queryRow, IMAccount::ContactPresence).data().toString(); // imPresence iri
       
  1052     //presence = presence.right(presence.length() - presence.lastIndexOf("presence-status"));
       
  1053     //account.setPresence(presenceConversion[presence]);
       
  1054     //qDebug() << Q_FUNC_INFO << "Presence converted: " << account.presence() << "raw presence: " << presence;
       
  1055 
       
  1056     //account.setStatusMessage(imAccountQuery->index(queryRow, IMAccount::ContactMessage).data().toString()); // imStatusMessage
       
  1057 
       
  1058     return account;
       
  1059 }
       
  1060 
       
  1061 QContactOnlineAccount QTrackerContactFetchRequest::getIMContactFromIMQuery(LiveNodes imContactQuery, int queryRow) {
       
  1062     QContactOnlineAccount account;
       
  1063 
       
  1064     account.setValue("Account", imContactQuery->index(queryRow, IMContact::ContactIMId).data().toString()); // IMId
       
  1065     if (!imContactQuery->index(queryRow, IMContact::AccountType).data().toString().isEmpty()) {
       
  1066         QString accountPathURI = imContactQuery->index(queryRow, IMContact::AccountType).data().toString();
       
  1067         QStringList decoded = accountPathURI.split(":");
       
  1068         // taking out the prefix "telepathy:"
       
  1069         account.setValue(FieldAccountPath, decoded.value(1));
       
  1070     }
       
  1071 
       
  1072 
       
  1073     // XXX FIXME -- TODO VIA PRESENCE
       
  1074     //account.setNickname(imContactQuery->index(queryRow, IMContact::ContactNickname).data().toString()); // nick
       
  1075 
       
  1076     QString cap = imContactQuery->index(queryRow, IMContact::Capabilities).data().toString();
       
  1077     cap = cap.right(cap.length() - cap.lastIndexOf("im-capability"));
       
  1078     account.setValue(QContactOnlineAccount::FieldCapabilities, cap);
       
  1079 
       
  1080     // XXX FIXME -- TODO VIA PRESENCE
       
  1081     //QString presence = imContactQuery->index(queryRow, IMContact::ContactPresence).data().toString(); // imPresence iri
       
  1082     //presence = presence.right(presence.length() - presence.lastIndexOf("presence-status"));
       
  1083     //account.setPresence(presenceConversion[presence]);
       
  1084     //
       
  1085     //account.setStatusMessage(imContactQuery->index(queryRow, IMContact::ContactMessage).data().toString()); // imStatusMessage
       
  1086 
       
  1087     account.setServiceProvider(imContactQuery->index(queryRow, IMContact::ServiceProvider).data().toString()); // service name
       
  1088     return account;
       
  1089 }
       
  1090