Hello!

I got permission to publish the bridge code between QtContacts resp.
KCalCore and EDS. Sorry for the delay, I had to take care of some other
business first. The code is now here:

http://meego.gitorious.org/meego-middleware/qtcontacts-eds
http://meego.gitorious.org/meego-middleware/kcal-eds

There's also an overview diagram in the Wiki showing the key difference
between the two approaches:
http://wiki.meego.com/images/Qtcontacts-kcal-eds.png

The difference is that QtContacts allows plugging a new backend into the
existing API. KCal doesn't allow that: an app needs to reference some
additional class, using their specific API, to load and store a
calendar.

mKCal is one such additional API. I thought a bit about emulating that
API, but eventually decided against it because much of that API is by
design mKCal specific and wouldn't translate well to EDS (example: the
notebook concept).

QtContacts-EDS
==============

This is derived from the Maemo 5 contact manager. The code wasn't very
clean to start with (IMHO, for example aborting the process on errors)
and uses a extra private implementation class which I find unnecessary.
Usually that is done to ABI compatibility, but the only public interface
in this case is the QtContacts plugin API, which remains stable even if
just one class is used.

I kept the structure for now, to document how individual aspects of the
code can be translated back to upstream EDS. Long term I'd like to see
QContactEBook and QContactEBookEngine merged.

Code quality aspects aside, right now reading/writing and change
notifications work. To install qtcontacts-eds, compile it and ensure
that /usr/lib/qt4/plugins/contacts/ has the resulting .so (or a symlink
to it). It depends on a patched EDS, see below.

To use the new storage, use "eds" as manager name. There is a test
program for the change tracking included. Manipulating data in EDS can
be done via Evolution or the SyncEvolution command line:

http://syncevolution.org/blogs/pohly/2010/manipulate-evolution-kcalextendedmkcal-qtcontacts-pim-items-uniform-command-line

To access EDS via QtContacts-EDS in the SyncEvolution command line, use
the following "database" value for 
syncevolution backend=qtcontacts \
              database=qtcontacts:eds:source=local:/system \
              --print-items \
              @foo bar

Never mind the "@foo bar" part, the values don't matter in this context.

Open work items:
      * add a QtVersit formatter which
             A. maps QtContactDetails to the corresponding properties as
                used in EDS
             B. loads/stores arbitrary QtContactDetails as X-
                extensions, similar to the SyncEvoQtContactsHandler in
                [1]
      * refactor the code (rewrite from scratch?), add error handling,
        consistent resource tracking (RAII as in SyncEvolution and
        KCal-EDS [2]?)
      * have Evolution maintainers review the EDS patch (attached) that
        QtContacts-EDS depends on (no mapping between int32 QContactId
        and traditional EDS string ID) and get it into MeeGo

[1] 
http://meego.gitorious.org/meego-middleware/syncevolution/blobs/master/src/backends/qtcontacts/QtContactsSource.cpp
[2] 
http://meego.gitorious.org/meego-middleware/kcal-eds/blobs/master/src/gnome-support.h


KCal-EDS
========

This code was written from scratch. It currently supports reading from
EDS, but not writing. Change notifications work on a per-item level by
updating the opened calendar with changes made in the underlying EDS.
This is an improvement over mKCal, where apps currently need to reload
the whole calendar each time the database changes.

Open work items:
      * add writing
      * clarify endDate() semantic

My plan is to look into that next week.

-- 
Best Regards, Patrick Ohly

The content of this message is my personal opinion only and although
I am an employee of Intel, the statements I make here in no way
represent Intel's position on the issue, nor am I authorized to speak
on behalf of Intel on this matter.

>From 65438bd3c4b706535f83304d94b3c018c8899a5d Mon Sep 17 00:00:00 2001
From: Patrick Ohly <patrick.o...@intel.com>
Date: Fri, 18 Mar 2011 16:07:23 +0100
Subject: [PATCH] addressbook file backend: use 32 bit integers as IDs

The motiviation for this change is primarily that it'll simplify
wrapping EDS in QtContacts, which only supports 32 bit integers for
the storages that it wraps.

Previously the ID was generated as a combination of time and running
counter, without checking for conflicts. This patch changes this such
that the ID is only a running counter. ID conflicts are detected during
an attempt to save, leading to retries with the next counter value.

The "for future use" members of _EBookBackendFilePrivate are removed
because they serve no useful purpose in a structure which is allocated
and used only locally.
---
 addressbook/backends/file/e-book-backend-file.c |   48 +++++++++++++++-------
 1 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index dc6a703..d21c527 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -77,11 +77,7 @@ struct _EBookBackendFilePrivate {
 	DB       *file_db;
 	DB_ENV   *env;
 	EBookBackendSummary *summary;
-	/* for future use */
-	gpointer reserved1;
-	gpointer reserved2;
-	gpointer reserved3;
-	gpointer reserved4;
+	guint     id;
 };
 
 G_LOCK_DEFINE_STATIC (global_env);
@@ -169,13 +165,13 @@ build_summary (EBookBackendFilePrivate *bfpriv)
 }
 
 static gchar *
-e_book_backend_file_create_unique_id (void)
+e_book_backend_file_create_next_id (EBookBackendFile *bf)
 {
-	/* use a 32 counter and the 32 bit timestamp to make an id.
-	   it's doubtful 2^32 id's will be created in a second, so we
-	   should be okay. */
-	static guint c = 0;
-	return g_strdup_printf (PAS_ID_PREFIX "%08lX%08X", time(NULL), c++);
+	/* use a 32 counter, avoid 0; collision checks must be done by caller */
+	++bf->priv->id;
+	if (!bf->priv->id)
+		bf->priv->id = 1;
+	return g_strdup_printf (PAS_ID_PREFIX "%04X", bf->priv->id);
 }
 
 static void
@@ -210,7 +206,8 @@ do_create(EBookBackendFile  *bf,
 	g_assert (vcard_req);
 	g_assert (contact);
 
-	id = e_book_backend_file_create_unique_id ();
+ retry:
+	id = e_book_backend_file_create_next_id (bf);
 
 	string_to_dbt (id, &id_dbt);
 
@@ -224,9 +221,10 @@ do_create(EBookBackendFile  *bf,
 
 	string_to_dbt (vcard, &vcard_dbt);
 
-	db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, 0);
+	db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, DB_NOOVERWRITE);
 
 	g_free (vcard);
+	g_free (id);
 
 	if (0 == db_error) {
 		db_error = db->sync (db, 0);
@@ -234,12 +232,16 @@ do_create(EBookBackendFile  *bf,
 			g_warning ("db->sync failed with %s", db_strerror (db_error));
 		}
 	} else {
-		g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
 		g_object_unref (*contact);
 		*contact = NULL;
+		if (DB_KEYEXIST == db_error) {
+			/* did not guess a new ID, try again */
+			goto retry;
+		} else {
+			g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
+		}
 	}
 
-	g_free (id);
 	db_error_to_gerror (db_error, perror);
 
 	return db_error == 0;
@@ -1108,6 +1110,7 @@ e_book_backend_file_load_source (EBookBackend           *backend,
 	DB_ENV *env;
 	time_t db_mtime;
 	struct stat sb;
+	DB_HASH_STAT *hashstat;
 
 	dirname = e_book_backend_file_extract_path_from_source (source);
 	filename = g_build_filename (dirname, "addressbook.db", NULL);
@@ -1290,6 +1293,21 @@ e_book_backend_file_load_source (EBookBackend           *backend,
 	}
 	db_mtime = sb.st_mtime;
 
+	/*
+	 * Picking the number of records as base for the next ID avoids trying out
+	 * lots of already used IDs in a database with few deleted contacts.
+	 * Optional optimization, if this fails or returns no information, we simply
+	 * start with zero.
+	 *
+	 * Note that DB_FAST_STAT is not used, because without it we won't get
+	 * hash_nkeys. This makes the stat call slower, but should be worth it
+	 * because we need to access the content sooner or later.
+	 */
+	if (!bf->priv->file_db->stat(bf->priv->file_db, NULL, &hashstat, 0)) {
+		bf->priv->id = hashstat->hash_nkeys;
+		g_free(hashstat);
+	}
+
 	g_free (bf->priv->summary_filename);
 	bf->priv->summary_filename = g_strconcat (bf->priv->filename, ".summary", NULL);
 	bf->priv->summary = e_book_backend_summary_new (bf->priv->summary_filename, SUMMARY_FLUSH_TIMEOUT);
-- 
1.7.2.5

_______________________________________________
MeeGo-dev mailing list
MeeGo-dev@meego.com
http://lists.meego.com/listinfo/meego-dev
http://wiki.meego.com/Mailing_list_guidelines

Reply via email to