On 05/17/2011 04:06 PM, David Woodhouse wrote:
On Wed, 2011-05-11 at 10:27 +0200, Patrick Ohly wrote:
If QtContacts-EDS is ever meant to work with a) non-file backends or
b) a file backend which has longer IDs (perhaps because they were
created before upgrading to EDS with the 32 bit patch), then hashing
will be needed again.
I think we can rule out a) for MeeGo, at this time at least.
We are actively working on adding other back ends, for example for use
with Microsoft Exchange (via both EWS and ActiveSync). My primary focus
was MeeGo 1.3, but there are those who need it on 1.2 too. So I don't
think you can rule it out so easily.
Let's remove it. With all the rewriting of the code, the original design
for the hashing probably makes no sense at all anymore anyway. Besides
that, it also has the conceptual issues mentioned earlier (no checks for
hash collisions).
On the other hand even if it's not quite working it serves as
documentation of where this support needs to be added back.
To be honest, I'd rather see the hash support *fixed* rather than
removed.
Hi,
In response to David Woodhouse's feedback, please find attached a patch
for QtContacts-EDS that implements thread-safe and collision-safe ID
hashing to bring compatibility with unpatched EDS (that uses string ids).
I would appreciate if you could review it before I merge the change.
Kr,
--
Dr. Christophe Dumez
Linux Software Engineer
Intel Finland Oy - Open Source Technology Center
From 7cd63c7189f54141d2a62112492852b144ebc613 Mon Sep 17 00:00:00 2001
From: Christophe Dumez <[email protected]>
Date: Fri, 20 May 2011 05:30:04 -0700
Subject: [PATCH] QContactEBook: Implement safe ID hashing
Brings support for thread-safe and collision-safe ID
hashing. Unpatched EDS uses string ids and hashing is
needed to get 32 bit integers (QContactLocalId).
Define USE_ID_HASHING DEFINE when compiling to enable
hashing or used a patched version of EDS that uses
32 bit integer ids.
Note that ID hashing is less efficient because it
needs to fetch all contacts on startup for
initialization. it also consumes more memory because
it keeps the ID mapping in memory.
---
engine/engine.pro | 12 +++-
engine/qcontactebook.cpp | 70 +++++++++++++++++++---
engine/qcontactebook_p.h | 17 ++++-
engine/qcontactebookbackend.cpp | 10 ++--
engine/qcontactfetchbyidrequestworker.cpp | 8 +-
engine/qcontactfetchbyidrequestworker_p.h | 7 +-
engine/qcontactfetchrequestworker.cpp | 13 +++--
engine/qcontactfetchrequestworker_p.h | 11 ++-
engine/qcontactremoverequestworker.cpp | 8 ++-
engine/qcontactremoverequestworker_p.h | 7 +-
engine/qcontactsaverequestworker.cpp | 17 ++++--
engine/qcontactsaverequestworker_p.h | 6 +-
engine/qebookidhash.cpp | 79 +++++++++++++++++++++++++
engine/qebookidhash.h | 51 ++++++++++++++++
test/ut_propertymatching/ebook_helpers.h | 7 +--
test/ut_propertymatching/propertymatching.cpp | 25 +++++++-
16 files changed, 286 insertions(+), 62 deletions(-)
create mode 100644 engine/qebookidhash.cpp
create mode 100644 engine/qebookidhash.h
diff --git a/engine/engine.pro b/engine/engine.pro
index 7754344..5150cb4 100644
--- a/engine/engine.pro
+++ b/engine/engine.pro
@@ -9,6 +9,11 @@ CONFIG += mobility link_pkgconfig
MOBILITY = contacts versit
PKGCONFIG += libebook-1.2
+# ID hashing is required with unpatched EDS
+# because EDS uses string IDS and we need
+# 32 bit integers
+# DEFINES += USE_ID_HASHING
+
HEADERS += \
qcontactebook_p.h \
qcontactebookbackend_p.h \
@@ -17,7 +22,8 @@ HEADERS += \
qcontactremoverequestworker_p.h \
qcontactabstractrequestworker_p.h \
qcontactfetchrequestworker_p.h \
- qcontactfetchbyidrequestworker_p.h
+ qcontactfetchbyidrequestworker_p.h \
+ qebookidhash.h
SOURCES += \
qcontactebook.cpp \
@@ -25,5 +31,5 @@ SOURCES += \
qcontactsaverequestworker.cpp \
qcontactremoverequestworker.cpp \
qcontactfetchrequestworker.cpp \
- qcontactfetchbyidrequestworker.cpp
-
+ qcontactfetchbyidrequestworker.cpp \
+ qebookidhash.cpp
diff --git a/engine/qcontactebook.cpp b/engine/qcontactebook.cpp
index fb178ea..37702e4 100644
--- a/engine/qcontactebook.cpp
+++ b/engine/qcontactebook.cpp
@@ -71,23 +71,31 @@
*/
static const QString EBookFileIDPrefix = "pas-id-";
-QString QContactEBook::QContactLocalId2UID(QContactLocalId contactId)
+QString QContactEBook::QContactLocalId2UID(const QContactLocalId &contactId)
const
{
+#ifdef USE_ID_HASHING
+ return m_idHash.value(contactId);
+#else
return EBookFileIDPrefix + QString("%1").arg(contactId, 4, 16/*base*/,
QChar('0')).toUpper();
+#endif
}
/**
* Converts an EContact UID \a uid into a QContactLocalId.
* @return 0 if not a valid pas-id-ABCD UID
*/
-QContactLocalId QContactEBook::UID2QContactLocalId(const QString &uid)
+QContactLocalId QContactEBook::UID2QContactLocalId(const QString &uid) const
{
+#ifdef USE_ID_HASHING
+ return m_idHash.value(uid);
+#else
// Input validation
static QRegExp pasid_validator("^"+EBookFileIDPrefix+"[0-9a-f]{4}$",
Qt::CaseInsensitive);
if (!pasid_validator.exactMatch(uid))
return 0;
// The following returns 0 if the conversion fails
return uid.right(4).toUInt(0, 16/*base*/);
+#endif
}
static QContactManager::Error getErrorFromStatus(const EBookStatus status){
@@ -140,6 +148,9 @@ QContactManager::Error
QContactEBook::getErrorFromError(const GError *gError){
struct cbSharedData{
QContactEBook *that;
+#ifdef USE_ID_HASHING
+ QEbookIdHash *idHash;
+#endif
/**
* true while initial view is still loading, false once that is
* done; QtContacts change signals will be emitted only after the
@@ -170,12 +181,13 @@ QContactEBook::~QContactEBook()
delete m_cbSD;
}
-static QList<QContactLocalId> EContactListToLocalIdList(GList *next) {
+static QList<QContactLocalId> EContactListToLocalIdList(QContactEBook *ebook,
+ GList *next) {
QList<QContactLocalId> localIds;
while (next) {
EContact *contact = E_CONTACT(next->data);
const char *uid = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
- QContactLocalId id = QContactEBook::UID2QContactLocalId(uid);
+ QContactLocalId id = ebook->UID2QContactLocalId(uid);
if (id)
localIds << id;
else
@@ -199,7 +211,8 @@ static void contactsAddedCB(EBookView *view, gpointer
glist, gpointer data)
return;
}
- QList<QContactLocalId> contactIds =
EContactListToLocalIdList(static_cast<GList*>(glist));
+ QList<QContactLocalId> contactIds = EContactListToLocalIdList(d->that,
+
static_cast<GList*>(glist));
QCM5_DEBUG << "ADDED:" << contactIds;
if (!contactIds.isEmpty())
@@ -214,7 +227,8 @@ static void contactsChangedCB(EBookView *view, gpointer
glist, gpointer data)
cbSharedData* d = static_cast<cbSharedData*>(data);
Q_ASSERT(d);
- QList<QContactLocalId> contactIds =
EContactListToLocalIdList(static_cast<GList*>(glist));
+ QList<QContactLocalId> contactIds = EContactListToLocalIdList(d->that,
+
static_cast<GList*>(glist));
QCM5_DEBUG << "CHANGED:" << contactIds;
if (!contactIds.isEmpty())
@@ -233,8 +247,13 @@ static void contactsRemovedCB(EBookView *view, gpointer
glist, gpointer data)
QList<QContactLocalId> contactIds;
while (next) {
- const char *uid = CONST_CHAR(next->data);
- QContactLocalId id = QContactEBook::UID2QContactLocalId(uid);
+ QString uid = CONST_CHAR(next->data);
+#ifdef USE_ID_HASHING
+ // Remove from the id hash
+ QContactLocalId id = d->idHash->take(uid);
+#else
+ QContactLocalId id = d->that->UID2QContactLocalId(uid);
+#endif
if (id)
contactIds << id;
else
@@ -319,9 +338,17 @@ void QContactEBook::initAddressBook(const QMap<QString,
QString>& parameters){
if (!e_book_open(m_ebook, false, &gError))
FATAL_IF_ERROR(gError);
+ // Initialize local id hash
+#ifdef USE_ID_HASHING
+ initLocalIdHash();
+#endif
+
/* Initialize callbacks shared data */
m_cbSD = new cbSharedData;
m_cbSD->that = this;
+#ifdef USE_ID_HASHING
+ m_cbSD->idHash = &m_idHash;
+#endif
m_cbSD->initializing = true;
// catch-all query, optimized in EDS storage
@@ -470,7 +497,7 @@ QStringList QContactEBook::getDetailMapping(const QString
&detailDefinitionName,
* \return A valid QContact if the conversion was successful, an empty one
otherwise.
*/
QContact QContactEBook::toQContact(EContact *eContact, const QString
&managerURI,
- QContactManager::Error* error)
+ QContactManager::Error* error) const
{
char *vcard = e_vcard_to_string(&eContact->parent,
EVC_FORMAT_VCARD_30);
@@ -533,7 +560,8 @@ EContact* QContactEBook::getEContact(const QContactLocalId&
contactId, QContactM
* Converts a QContact \a contact into a EContact.
* \return A pointer to an EContact or NULL if the conversion failed.
*/
-EContact* QContactEBook::toEContact(const QContact *contact,
QContactManager::Error* error)
+EContact* QContactEBook::toEContact(const QContact *contact,
+ QContactManager::Error* error) const
{
QCM5_DEBUG << "converting contact:\n" << *contact;
@@ -561,3 +589,25 @@ EContact* QContactEBook::toEContact(const QContact
*contact, QContactManager::Er
return rtn;
}
+
+/*! Fetches all the contacts from EDS and initializes our hash map */
+void QContactEBook::initLocalIdHash()
+{
+#ifdef USE_ID_HASHING
+ // Fetch all contact ids
+ EBookQuery *query = e_book_query_any_field_contains("");
+ GList *contacts = NULL;
+ GError *gError = NULL;
+ e_book_get_contacts(m_ebook, query, &contacts, &gError);
+ FATAL_IF_ERROR(gError);
+ GList *item;
+ for (item = contacts; item; item = item->next) {
+ // Register the id in the hash map
+ EContact *eContact = E_CONTACT(item->data);
+ QString uid = (const char*)e_contact_get_const(eContact, E_CONTACT_UID);
+ m_idHash.insert(uid);
+ }
+ // Clean up
+ g_list_free_full(contacts, g_object_unref);
+#endif
+}
diff --git a/engine/qcontactebook_p.h b/engine/qcontactebook_p.h
index 485e1cc..8baf2bd 100644
--- a/engine/qcontactebook_p.h
+++ b/engine/qcontactebook_p.h
@@ -50,6 +50,9 @@
#include <libebook/e-book.h>
#include "qcontactebookdebug_p.h"
+#ifdef USE_ID_HASHING
+#include "qebookidhash.h"
+#endif
QTM_USE_NAMESPACE
@@ -71,13 +74,15 @@ public:
static QStringList getDetailMapping(const QString &detailDefinitionName,
const QString &detailFieldName);
- static QContactLocalId UID2QContactLocalId(const QString &uid);
- static QString QContactLocalId2UID(QContactLocalId contactId);
+ QContactLocalId UID2QContactLocalId(const QString &uid) const;
+ QString QContactLocalId2UID(const QContactLocalId &contactId) const;
static QContactManager::Error getErrorFromError(const GError *gError);
/* Saving - QContact to ebookContact */
- static EContact* toEContact(const QContact *contact, QContactManager::Error*
error);
+ EContact* toEContact(const QContact *contact, QContactManager::Error* error)
const;
/* Reading - eContact/ebookContact to QContact methods */
- static QContact toQContact(EContact *eContact, const QString &managerURI,
QContactManager::Error* error);
+ QContact toQContact(EContact *eContact, const QString &managerURI,
+ QContactManager::Error* error) const;
+ void initLocalIdHash();
signals:
void contactsAdded(const QList<QContactLocalId>& contactIds);
@@ -99,6 +104,10 @@ private:
EBook *m_ebook;
EBookView *m_ebookview;
cbSharedData *m_cbSD;
+
+#ifdef USE_ID_HASHING
+ QEbookIdHash m_idHash;
+#endif
};
#endif
diff --git a/engine/qcontactebookbackend.cpp b/engine/qcontactebookbackend.cpp
index 5d42a92..e642e88 100644
--- a/engine/qcontactebookbackend.cpp
+++ b/engine/qcontactebookbackend.cpp
@@ -574,19 +574,19 @@ QContactAbstractRequestWorker*
QContactEBookEngine::createAsyncWorker(QContactAb
switch(request->type()) {
case QContactAbstractRequest::ContactSaveRequest:
return new
QContactSaveRequestWorker(static_cast<QContactSaveRequest*>(request),
- d->m_ebook->ebook(), this);
+ d->m_ebook, this);
case QContactAbstractRequest::ContactRemoveRequest:
return new
QContactRemoveRequestWorker(static_cast<QContactRemoveRequest*>(request),
- d->m_ebook->ebook());
+ d->m_ebook);
case QContactAbstractRequest::ContactFetchRequest:
return new
QContactFetchRequestWorker(static_cast<QContactFetchRequest*>(request),
- d->m_ebook->ebook(), this);
+ d->m_ebook, this);
case QContactAbstractRequest::ContactLocalIdFetchRequest:
return new
QContactFetchRequestWorker(static_cast<QContactLocalIdFetchRequest*>(request),
- d->m_ebook->ebook(), this);
+ d->m_ebook, this);
case QContactAbstractRequest::ContactFetchByIdRequest:
return new
QContactFetchByIdRequestWorker(static_cast<QContactFetchByIdRequest*>(request),
- d->m_ebook->ebook(), this);
+ d->m_ebook, this);
default:
Q_ASSERT(0); // Should never happen
return 0;
diff --git a/engine/qcontactfetchbyidrequestworker.cpp
b/engine/qcontactfetchbyidrequestworker.cpp
index ae80b85..d0cf9aa 100644
--- a/engine/qcontactfetchbyidrequestworker.cpp
+++ b/engine/qcontactfetchbyidrequestworker.cpp
@@ -43,7 +43,7 @@ static void fetchContactByIdCB(EBook* book, const GError
*gError, EContact *cont
}
QContactFetchByIdRequestWorker::QContactFetchByIdRequestWorker(QContactFetchByIdRequest*
request,
- EBook* ebook,
+ QContactEBook*
ebook,
const
QContactManagerEngine *engine) :
QContactAbstractRequestWorker(request), m_engine(engine), m_ebook(ebook),
m_localIds(request->localIds())
{
@@ -75,8 +75,8 @@ bool QContactFetchByIdRequestWorker::start()
bool subRequestStarted = false;
for (int i=0; i<m_localIds.size(); ++i) {
const QContactLocalId &localId = m_localIds.at(i);
- char* uid =
strdup(QContactEBook::QContactLocalId2UID(localId).toUtf8().constData());
- if (e_book_get_contact_async(m_ebook, uid, fetchContactByIdCB,
m_sharedData[i])) {
+ char* uid =
strdup(m_ebook->QContactLocalId2UID(localId).toUtf8().constData());
+ if (e_book_get_contact_async(m_ebook->ebook(), uid, fetchContactByIdCB,
m_sharedData[i])) {
subRequestStarted = true;
} else {
m_errors.insert(i, QContactManager::UnspecifiedError);
@@ -105,7 +105,7 @@ void
QContactFetchByIdRequestWorker::handleSubRequestFinished(int index)
if (sd->error == QContactManager::NoError && sd->contact) {
// EContact -> QContact conversion
QContactManager::Error tmpErr;
- QContact c = QContactEBook::toQContact(E_CONTACT(sd->contact),
m_engine->managerUri(), &tmpErr);
+ QContact c = m_ebook->toQContact(E_CONTACT(sd->contact),
m_engine->managerUri(), &tmpErr);
// Check if conversion succeeded
if (tmpErr == QContactManager::NoError) {
m_result.replace(index, c);
diff --git a/engine/qcontactfetchbyidrequestworker_p.h
b/engine/qcontactfetchbyidrequestworker_p.h
index 5e26052..91df9ac 100644
--- a/engine/qcontactfetchbyidrequestworker_p.h
+++ b/engine/qcontactfetchbyidrequestworker_p.h
@@ -21,10 +21,10 @@
#define QCONTACTFETCHBYIDREQUESTWORKER_P_H
#include <QContactFetchByIdRequest>
-#include <libebook/e-book.h>
#include "qcontactabstractrequestworker_p.h"
struct FetchByIdJobSharedData;
+class QContactEBook;
/*!
* Worker class for asynchronous contact fetching by Id requests.
@@ -33,7 +33,8 @@ class QContactFetchByIdRequestWorker : public
QContactAbstractRequestWorker
{
Q_OBJECT
public:
- explicit QContactFetchByIdRequestWorker(QContactFetchByIdRequest* request,
EBook* ebook,
+ explicit QContactFetchByIdRequestWorker(QContactFetchByIdRequest* request,
+ QContactEBook* ebook,
const QContactManagerEngine *engine);
~QContactFetchByIdRequestWorker();
@@ -43,7 +44,7 @@ public slots:
private:
const QContactManagerEngine *m_engine;
- EBook *m_ebook;
+ QContactEBook *m_ebook;
QMap<int/*index*/, FetchByIdJobSharedData*> m_sharedData;
QList<QContactLocalId> m_localIds;
QList<QContact> m_result;
diff --git a/engine/qcontactfetchrequestworker.cpp
b/engine/qcontactfetchrequestworker.cpp
index fa38b2d..3459a96 100644
--- a/engine/qcontactfetchrequestworker.cpp
+++ b/engine/qcontactfetchrequestworker.cpp
@@ -71,7 +71,8 @@ static void fetchContactCB(EBook* book, const GError *gError,
GList *list, gpoin
sd->worker->handleSubRequestFinished();
}
-QContactFetchRequestWorker::QContactFetchRequestWorker(QContactFetchRequest*
request, EBook* ebook,
+QContactFetchRequestWorker::QContactFetchRequestWorker(QContactFetchRequest*
request,
+ QContactEBook* ebook,
const
QContactManagerEngine *engine) :
QContactAbstractRequestWorker(request), m_engine(engine), m_ebook(ebook),
m_sharedData(0),
m_filter(request->filter()), m_sortOrders(request->sorting()),
m_postProcessingRequired(false)
@@ -79,7 +80,8 @@
QContactFetchRequestWorker::QContactFetchRequestWorker(QContactFetchRequest* req
QContactManagerEngine::updateRequestState(m_request.data(),
QContactAbstractRequest::InactiveState);
}
-QContactFetchRequestWorker::QContactFetchRequestWorker(QContactLocalIdFetchRequest*
request, EBook* ebook,
+QContactFetchRequestWorker::QContactFetchRequestWorker(QContactLocalIdFetchRequest*
request,
+ QContactEBook* ebook,
const
QContactManagerEngine *engine) :
QContactAbstractRequestWorker(request), m_engine(engine), m_ebook(ebook),
m_sharedData(0),
m_filter(request->filter()), m_sortOrders(request->sorting()),
m_postProcessingRequired(false)
@@ -128,7 +130,8 @@ bool QContactFetchRequestWorker::start()
m_sharedData = new FetchJobSharedData(this);
- bool started = e_book_get_contacts_async(m_ebook, query, fetchContactCB,
m_sharedData);
+ bool started = e_book_get_contacts_async(m_ebook->ebook(), query,
fetchContactCB,
+ m_sharedData);
if (!started) {
updateAnyContactFetchRequest(m_request.data(),
QList<QContact>(),
QContactManager::BadArgumentError,
@@ -171,7 +174,7 @@ EBookQuery *
QContactFetchRequestWorker::convertFilter(const QtMobility::QContac
if (!eContactId[0])
return NULL;
#else
- QString eContactId = QContactEBook::QContactLocalId2UID(id);
+ QString eContactId = m_ebook->QContactLocalId2UID(id);
#endif
q = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS,
eContactId.toUtf8().data());
@@ -329,7 +332,7 @@ void QContactFetchRequestWorker::handleSubRequestFinished()
EContact *eContact = E_CONTACT(item->data);
// EContact -> QContact conversion
QContactManager::Error tmpErr;
- QContact c = QContactEBook::toQContact(eContact, m_engine->managerUri(),
&tmpErr);
+ QContact c = m_ebook->toQContact(eContact, m_engine->managerUri(),
&tmpErr);
// Check if conversion succeeded
if (tmpErr == QContactManager::NoError) {
// Contact post filtering (if applicable) and insertion sort in the
diff --git a/engine/qcontactfetchrequestworker_p.h
b/engine/qcontactfetchrequestworker_p.h
index 3720b4a..3e0830f 100644
--- a/engine/qcontactfetchrequestworker_p.h
+++ b/engine/qcontactfetchrequestworker_p.h
@@ -23,10 +23,11 @@
#include <QContactFetchRequest>
#include <QContactLocalIdFetchRequest>
#include <QContactDetailFilter>
-#include <libebook/e-book.h>
#include "qcontactabstractrequestworker_p.h"
struct FetchJobSharedData;
+struct EBookQuery;
+class QContactEBook;
/*!
* Worker class for asynchronous contact (or Contact localId) fetching
requests.
@@ -36,9 +37,11 @@ class QContactFetchRequestWorker : public
QContactAbstractRequestWorker
Q_OBJECT
public:
- explicit QContactFetchRequestWorker(QContactFetchRequest* request, EBook*
ebook,
+ explicit QContactFetchRequestWorker(QContactFetchRequest* request,
+ QContactEBook* ebook,
const QContactManagerEngine *engine = 0);
- explicit QContactFetchRequestWorker(QContactLocalIdFetchRequest* request,
EBook* ebook,
+ explicit QContactFetchRequestWorker(QContactLocalIdFetchRequest* request,
+ QContactEBook* ebook,
const QContactManagerEngine *engine = 0);
~QContactFetchRequestWorker();
@@ -54,7 +57,7 @@ private:
private:
const QContactManagerEngine *m_engine;
- EBook *m_ebook;
+ QContactEBook *m_ebook;
FetchJobSharedData *m_sharedData;
QContactFilter m_filter;
QList<QContactSortOrder> m_sortOrders;
diff --git a/engine/qcontactremoverequestworker.cpp
b/engine/qcontactremoverequestworker.cpp
index 20543b8..633a078 100644
--- a/engine/qcontactremoverequestworker.cpp
+++ b/engine/qcontactremoverequestworker.cpp
@@ -43,7 +43,8 @@ static void delContactCB(EBook *book, const GError *gError,
gpointer closure)
}
/*! Constructs the worker */
-QContactRemoveRequestWorker::QContactRemoveRequestWorker(QContactRemoveRequest*
request, EBook* ebook) :
+QContactRemoveRequestWorker::QContactRemoveRequestWorker(QContactRemoveRequest*
request,
+ QContactEBook* ebook)
:
QContactAbstractRequestWorker(request), m_ebook(ebook), m_sharedData(0),
m_contactIds(request->contactIds())
{
@@ -70,10 +71,11 @@ bool QContactRemoveRequestWorker::start()
GList *ids = NULL;
m_sharedData = new RemoveJobSharedData(this);
foreach (const QContactLocalId &localId, m_contactIds) {
- ids = g_list_append(ids,
strdup(QContactEBook::QContactLocalId2UID(localId).toUtf8().constData()));
+ ids = g_list_append(ids,
+
strdup(m_ebook->QContactLocalId2UID(localId).toUtf8().constData()));
}
- bool ok = e_book_remove_contacts_async(m_ebook, ids, delContactCB,
m_sharedData);
+ bool ok = e_book_remove_contacts_async(m_ebook->ebook(), ids, delContactCB,
m_sharedData);
if (!ok) {
QContactManagerEngine::updateContactRemoveRequest(static_cast<QContactRemoveRequest*>(m_request.data()),
QContactManager::UnspecifiedError, m_errors,
diff --git a/engine/qcontactremoverequestworker_p.h
b/engine/qcontactremoverequestworker_p.h
index 5d9e5d8..b5bf1c7 100644
--- a/engine/qcontactremoverequestworker_p.h
+++ b/engine/qcontactremoverequestworker_p.h
@@ -21,10 +21,10 @@
#define QCONTACTREMOVEREQUESTWORKER_P_H
#include <QContactRemoveRequest>
-#include <libebook/e-book.h>
#include "qcontactabstractrequestworker_p.h"
class RemoveJobSharedData;
+class QContactEBook;
/*!
* Worker class for asynchronous contact removal requests.
@@ -33,7 +33,8 @@ class QContactRemoveRequestWorker : public
QContactAbstractRequestWorker
{
Q_OBJECT
public:
- explicit QContactRemoveRequestWorker(QContactRemoveRequest* request, EBook*
ebook);
+ explicit QContactRemoveRequestWorker(QContactRemoveRequest* request,
+ QContactEBook* ebook);
~QContactRemoveRequestWorker();
public slots:
@@ -41,7 +42,7 @@ public slots:
void handleSubRequestFinished();
private:
- EBook *m_ebook;
+ QContactEBook *m_ebook;
RemoveJobSharedData* m_sharedData;
QList<QContactLocalId> m_contactIds;
};
diff --git a/engine/qcontactsaverequestworker.cpp
b/engine/qcontactsaverequestworker.cpp
index 48d2435..bb53e15 100644
--- a/engine/qcontactsaverequestworker.cpp
+++ b/engine/qcontactsaverequestworker.cpp
@@ -31,6 +31,7 @@ struct SaveJobSharedData {
QContactSaveRequestWorker* worker;
int index;
QContactLocalId localId;
+ QContactEBook *ebook;
QContactManager::Error error;
};
@@ -49,13 +50,14 @@ static void addContactCB(EBook* book, const GError *gError,
const char *uid, gp
Q_ASSERT(sd);
if (uid)
- sd->localId = QContactEBook::UID2QContactLocalId(uid);
+ sd->localId = sd->ebook->UID2QContactLocalId(uid);
commitContactCB(book, gError, user_data);
}
/*! Constructs the worker */
-QContactSaveRequestWorker::QContactSaveRequestWorker(QContactSaveRequest*
request, EBook* ebook,
+QContactSaveRequestWorker::QContactSaveRequestWorker(QContactSaveRequest*
request,
+ QContactEBook* ebook,
const
QContactManagerEngine *engine) :
QContactAbstractRequestWorker(request), m_engine(engine), m_ebook(ebook),
m_contacts(request->contacts())
@@ -81,6 +83,7 @@ bool QContactSaveRequestWorker::start()
// Prepare the shared data
for (int i=0; i<m_contacts.size(); ++i) {
SaveJobSharedData *sd = new SaveJobSharedData(this);
+ sd->ebook = m_ebook;
sd->index = i;
sd->localId = m_contacts.at(i).localId();
m_sharedData.insert(i, sd);
@@ -111,7 +114,7 @@ bool QContactSaveRequestWorker::start()
QString pasid;
if (c.localId()) {
QContactGuid guid = c.detail<QContactGuid>();
- pasid = QContactEBook::QContactLocalId2UID(c.localId());
+ pasid = m_ebook->QContactLocalId2UID(c.localId());
guid.setGuid(pasid);
c.saveDetail(&guid);
} else {
@@ -123,7 +126,7 @@ bool QContactSaveRequestWorker::start()
QContactManagerEngine::setContactDisplayLabel(&c,
m_engine->synthesizedDisplayLabel(c, &error));
// Convert QContact to EContact;
- EContact *eContact = QContactEBook::toEContact(&c, &error);
+ EContact *eContact = m_ebook->toEContact(&c, &error);
if (!eContact) {
m_errors.insert(i, error);
delete m_sharedData.take(i);
@@ -132,10 +135,12 @@ bool QContactSaveRequestWorker::start()
bool ok = false;
if (c.localId()) {
// Commit
- ok = e_book_commit_contact_async(m_ebook, eContact, commitContactCB,
m_sharedData[i]);
+ ok = e_book_commit_contact_async(m_ebook->ebook(), eContact,
commitContactCB,
+ m_sharedData[i]);
} else {
// Add
- ok = e_book_add_contact_async(m_ebook, eContact, addContactCB,
m_sharedData[i]);
+ ok = e_book_add_contact_async(m_ebook->ebook(), eContact, addContactCB,
+ m_sharedData[i]);
}
// Clean up eContact
g_object_unref(eContact);
diff --git a/engine/qcontactsaverequestworker_p.h
b/engine/qcontactsaverequestworker_p.h
index 7a0369f..bde7f87 100644
--- a/engine/qcontactsaverequestworker_p.h
+++ b/engine/qcontactsaverequestworker_p.h
@@ -23,10 +23,10 @@
#include <QWeakPointer>
#include <QContactSaveRequest>
#include <QMap>
-#include <libebook/e-book.h>
#include "qcontactabstractrequestworker_p.h"
struct SaveJobSharedData;
+class QContactEBook;
/*!
* Worker class for asynchronous contact saving requests.
@@ -35,7 +35,7 @@ class QContactSaveRequestWorker : public
QContactAbstractRequestWorker
{
Q_OBJECT
public:
- explicit QContactSaveRequestWorker(QContactSaveRequest* request, EBook*
ebook,
+ explicit QContactSaveRequestWorker(QContactSaveRequest* request,
QContactEBook* ebook,
const QContactManagerEngine *engine);
~QContactSaveRequestWorker();
@@ -45,7 +45,7 @@ public slots:
private:
const QContactManagerEngine *m_engine;
- EBook *m_ebook;
+ QContactEBook *m_ebook;
QMap<int/*index*/, SaveJobSharedData*> m_sharedData;
QList<QContact> m_contacts;
};
diff --git a/engine/qebookidhash.cpp b/engine/qebookidhash.cpp
new file mode 100644
index 0000000..c0a5359
--- /dev/null
+++ b/engine/qebookidhash.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) version 3.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <QMutexLocker>
+#include "qebookidhash.h"
+
+/*! Removes id \a eContactId from the hash table */
+QContactLocalId QEbookIdHash::take(const QString &eContactId)
+{
+ QMutexLocker locker(&m_hashMutex);
+ QHash<QString, QContactLocalId>::iterator pos = m_toQid.find(eContactId);
+ if (pos != m_toQid.end()) {
+ const QContactLocalId qContactId = pos.value();
+ m_toQid.erase(pos);
+ m_fromQid.remove(qContactId);
+ return qContactId;
+ }
+ return 0;
+}
+
+/*!
+ * Returns the QContactLocalId corresponding to \a eContactId.
+ * QContactLocalId is computed if not already in the hash
+ */
+QtMobility::QContactLocalId QEbookIdHash::value(const QString &eContactId)
const
+{
+ QMutexLocker locker(&m_hashMutex);
+ // Return corresponding QContactLocalId if existing
+ if (m_toQid.contains(eContactId))
+ return m_toQid.value(eContactId);
+ // eContactId is not known yet, generate one by hashing
+ quint32 hashed_qid = qHash(eContactId);
+ // Handle hashing collisions
+ while (hashed_qid == 0 || m_fromQid.contains(hashed_qid)) {
+ // Increment hashed_qid until it does not collide
+ // NOTE: simple incrementation is safe because unsigned integers "wrap
around"
+ // on overflow and we skip the (localId == 0) case which corresponds to an
+ // invalid QContactLocalId.
+ ++hashed_qid;
+ }
+ // Insert in hash tables
+ m_toQid[eContactId] = hashed_qid;
+ m_fromQid[hashed_qid] = eContactId;
+
+ return hashed_qid;
+}
+
+/*! Returns the EContact string id corresponding to \a qContactId.
+ * \return Null QString if the qContactId does not exist.
+ */
+QString QEbookIdHash::value(const QContactLocalId &qContactId) const
+{
+ QMutexLocker locker(&m_hashMutex);
+ return m_fromQid.value(qContactId);
+}
+
+ /*! Inserts \a eContactId in the hash map */
+void QEbookIdHash::insert(const QString &eContactId)
+{
+ // Call value() to avoid code duplication.
+ // value() will insert the eContactId if it does not already exist.
+ value(eContactId);
+}
diff --git a/engine/qebookidhash.h b/engine/qebookidhash.h
new file mode 100644
index 0000000..1f02181
--- /dev/null
+++ b/engine/qebookidhash.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) version 3.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef QEBOOKIDHASH_H
+#define QEBOOKIDHASH_H
+
+#include <QContact>
+#include <QHash>
+#include <QMutex>
+
+QTM_USE_NAMESPACE
+
+/*!
+ * Thread-safe ID hashing class to transform EContact string ids into
+ * 32bit integer ids usable for a QContact.
+ * Hashing collisions are properly handled.
+ *
+ * @author Christophe Dumez <[email protected]>
+ */
+class QEbookIdHash
+{
+ public:
+ void insert(const QString& eContactId);
+ QContactLocalId take(const QString& eContactId);
+ // Two-way id mapping
+ QContactLocalId value(const QString& eContactId) const;
+ QString value(const QContactLocalId &qContactId) const;
+
+ private:
+ mutable QMutex m_hashMutex;
+ mutable QHash<QContactLocalId, QString> m_fromQid;
+ mutable QHash<QString, QContactLocalId> m_toQid;
+};
+
+#endif // QEBOOKIDHASH_H
diff --git a/test/ut_propertymatching/ebook_helpers.h
b/test/ut_propertymatching/ebook_helpers.h
index 697bcdc..4707087 100644
--- a/test/ut_propertymatching/ebook_helpers.h
+++ b/test/ut_propertymatching/ebook_helpers.h
@@ -43,15 +43,12 @@ static const QString EBookFileIDPrefix = "pas-id-";
#define EC_GET_PHOTO_IMAGE(ec, field) getPhotoImageField(ec, field)
/*! Retrieves the EContact identified by \a contactId from the EBook. */
-EContact* getEContact(EBook* ebook, const QContactLocalId& contactId)
+EContact* getEContact(EBook* ebook, const char* uid)
{
EContact* rtn = NULL;
- QString num;
- num.sprintf("%04X", contactId);
- QString id = EBookFileIDPrefix + num;
GError *gError = NULL;
- e_book_get_contact(ebook, id.toUtf8().data(), &rtn, &gError);
+ e_book_get_contact(ebook, uid, &rtn, &gError);
g_clear_error(&gError);
return rtn;
diff --git a/test/ut_propertymatching/propertymatching.cpp
b/test/ut_propertymatching/propertymatching.cpp
index c17543f..dadd8cb 100644
--- a/test/ut_propertymatching/propertymatching.cpp
+++ b/test/ut_propertymatching/propertymatching.cpp
@@ -142,16 +142,33 @@ void PropertyMatching::propertyMatchingTest()
// be fixed.
validateContact(c);
- qDebug() << "Saving QContact...";
- QVERIFY2(manager->saveContact(&c), "Failed to save the contact");
-
qDebug() << "Initializing EBook...";
ebook = testEBook();
QVERIFY2(ebook, "Impossible to create the EBook, aborting!");
+ qDebug() << "Saving QContact...";
+ GError *gError = NULL;
+ GList* changes;
+ char changeId = '0';
+ QVERIFY(e_book_get_changes(ebook, &changeId, &changes, &gError));
+ e_book_free_change_list(changes);
+ QVERIFY2(manager->saveContact(&c), "Failed to save the contact");
+ QVERIFY(e_book_get_changes(ebook, &changeId, &changes, &gError));
+ GList* item = NULL;
+ char* uid = NULL;
+ // Trick to get the EContact UID if the added contact
+ for (item = changes; item; item = item->next) {
+ EBookChange *change = static_cast<EBookChange*>(item->data);
+ if(change->change_type == E_BOOK_CHANGE_CARD_ADDED) {
+ uid = (char*)e_contact_get_const(change->contact, E_CONTACT_UID);
+ break;
+ }
+ }
+ QVERIFY(uid != NULL);
qDebug() << "loading saved EContact using libebook...";
- ec = getEContact(ebook, c.localId());
+ ec = getEContact(ebook, uid);
QVERIFY2(ec, "Failed to retrieve the saved EContact!");
+ e_book_free_change_list(changes);
qDebug() << "Comparing QContact and EContact...";
checkContactsAreSame(c, ec);
--
1.7.2.2
_______________________________________________
MeeGo-dev mailing list
[email protected]
http://lists.meego.com/listinfo/meego-dev
http://wiki.meego.com/Mailing_list_guidelines