[evolution-data-server] Patch from Stanislav Slusny as part of the Google Summer of Code project to optimize eds calendar me
- From: Chenthill Palanisamy <pchen src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Patch from Stanislav Slusny as part of the Google Summer of Code project to optimize eds calendar me
- Date: Tue, 7 Sep 2010 07:17:56 +0000 (UTC)
commit 1bec75476162b04acbb7e01fd708638ebfebc334
Author: Stanislav Slusny <slusnys gmail com>
Date: Mon Sep 6 19:59:53 2010 +0530
Patch from Stanislav Slusny as part of the Google Summer of
Code project to optimize eds calendar memory usage and speed.
Punit Jain <jpunit novell com> worked upon this patch and fixed
issues to be able to be upstreamed. Thanks to both of them.
calendar/backends/caldav/e-cal-backend-caldav.c | 54 ++-
calendar/backends/file/Makefile.am | 19 +
calendar/backends/file/e-cal-backend-file.c | 434 +++++++++++-
.../backends/groupwise/e-cal-backend-groupwise.c | 56 ++-
calendar/backends/http/e-cal-backend-http.c | 59 ++-
calendar/backends/weather/e-cal-backend-weather.c | 50 ++-
calendar/libecal/e-cal-recur.c | 94 ++-
calendar/libecal/e-cal-recur.h | 6 +
calendar/libecal/e-cal-util.c | 163 +++++-
calendar/libecal/e-cal-util.h | 7 +
calendar/libedata-cal/Makefile.am | 55 ++-
calendar/libedata-cal/e-cal-backend-cache.c | 61 ++-
calendar/libedata-cal/e-cal-backend-cache.h | 5 +-
calendar/libedata-cal/e-cal-backend-file-store.c | 52 +-
calendar/libedata-cal/e-cal-backend-intervaltree.c | 765 ++++++++++++++++++++
calendar/libedata-cal/e-cal-backend-intervaltree.h | 95 +++
calendar/libedata-cal/e-cal-backend-sexp.c | 68 ++-
calendar/libedata-cal/e-cal-backend-sexp.h | 6 +-
calendar/libedata-cal/e-cal-backend-store.c | 94 +++-
calendar/libedata-cal/e-cal-backend-store.h | 9 +-
calendar/libedata-cal/e-data-cal-view.c | 3 +-
calendar/libedata-cal/e-data-cal.c | 6 +-
calendar/libedata-cal/test-intervaltree.c | 431 +++++++++++
libedataserver/Makefile.am | 2 +
libedataserver/e-debug-log.c | 633 ++++++++++++++++
libedataserver/e-debug-log.h | 56 ++
libedataserver/e-sexp.c | 285 ++++++++-
libedataserver/e-sexp.h | 4 +
28 files changed, 3469 insertions(+), 103 deletions(-)
---
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index 505bc12..05d202a 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -266,6 +266,31 @@ caldav_debug_setup (SoupSession *session)
g_object_unref (logger);
}
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+ return (!strcmp (tzid, "UTC"))
+ ? icaltimezone_get_utc_timezone ()
+ : icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+static gboolean
+put_component_to_store (ECalBackendCalDAV *cbdav,
+ ECalComponent *comp)
+{
+ time_t time_start, time_end;
+ ECalBackendCalDAVPrivate *priv;
+
+ priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, NULL, icaltimezone_get_utc_timezone (),
+ e_cal_backend_get_kind (E_CAL_BACKEND (cbdav)));
+
+ return e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
+
static ECalBackendSyncClass *parent_class = NULL;
static icaltimezone *caldav_internal_get_default_timezone (ECalBackend *backend);
@@ -1873,7 +1898,7 @@ synchronize_cache (ECalBackendCalDAV *cbdav, time_t start_time, time_t end_time)
g_free (nc_rid);
}
- e_cal_backend_store_put_component (priv->store, new_comp);
+ put_component_to_store (cbdav, new_comp);
if (old_comp == NULL) {
gchar *new_str = e_cal_component_get_as_string (new_comp);
@@ -2594,7 +2619,7 @@ put_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp, const gcha
if (etag)
ecalcomp_set_etag (comp, etag);
- if (e_cal_backend_store_put_component (priv->store, comp))
+ if (put_component_to_store (cbdav, comp))
res = TRUE;
}
}
@@ -2607,7 +2632,7 @@ put_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp, const gcha
if (etag)
ecalcomp_set_etag (comp, etag);
- res = e_cal_backend_store_put_component (priv->store, comp);
+ res = put_component_to_store (cbdav, comp);
}
}
@@ -3974,6 +3999,8 @@ caldav_get_object_list (ECalBackendSync *backend,
ECalBackend *bkend;
gboolean do_search;
GSList *list, *iter;
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
cbdav = E_CAL_BACKEND_CALDAV (backend);
priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
@@ -3993,7 +4020,15 @@ caldav_get_object_list (ECalBackendSync *backend,
*objects = NULL;
- list = e_cal_backend_store_get_components (priv->store);
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp,
+ &occur_start,
+ &occur_end);
+
+ bkend = E_CAL_BACKEND (backend);
+
+ list = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
bkend = E_CAL_BACKEND (backend);
@@ -4023,7 +4058,8 @@ caldav_start_query (ECalBackend *backend,
gboolean do_search;
GSList *list, *iter;
const gchar *sexp_string;
-
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
cbdav = E_CAL_BACKEND_CALDAV (backend);
priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
@@ -4037,11 +4073,17 @@ caldav_start_query (ECalBackend *backend,
} else {
do_search = TRUE;
}
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp,
+ &occur_start,
+ &occur_end);
- list = e_cal_backend_store_get_components (priv->store);
bkend = E_CAL_BACKEND (backend);
+ list = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
+
for (iter = list; iter; iter = g_slist_next (iter)) {
ECalComponent *comp = E_CAL_COMPONENT (iter->data);
diff --git a/calendar/backends/file/Makefile.am b/calendar/backends/file/Makefile.am
index c58c6a3..316a070 100644
--- a/calendar/backends/file/Makefile.am
+++ b/calendar/backends/file/Makefile.am
@@ -9,6 +9,8 @@ libecalbackendfile_la_CPPFLAGS = \
-I$(top_builddir)/calendar \
$(EVOLUTION_CALENDAR_CFLAGS)
+noinst_PROGRAMS = test-interval-searches
+
libecalbackendfile_la_SOURCES = \
e-cal-backend-file-factory.c \
e-cal-backend-file-factory.h \
@@ -27,7 +29,24 @@ libecalbackendfile_la_LIBADD = \
$(top_builddir)/libedataserver/libedataserver-1.2.la \
$(EVOLUTION_CALENDAR_LIBS)
+libecalbackendfile_la_CFLAGS = $(AM_CFLAGS)
+
libecalbackendfile_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED)
+test_interval_searches_SOURCES = e-cal-backend-file.c
+
+test_interval_searches_LDADD = \
+ $(top_builddir)/calendar/libecal/libecal-1.2.la \
+ $(top_builddir)/calendar/libedata-cal/libedata-cal-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(top_builddir)/calendar/backends/file/libecalbackendfile.la \
+ $(EVOLUTION_CALENDAR_LIBS)
+
+test_interval_searches_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_builddir)/calendar \
+ $(EVOLUTION_CALENDAR_CFLAGS) \
+ -DTEST_QUERY_RESULT=1
+
-include $(top_srcdir)/git.mk
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index 7d7c511..a78dea2 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -33,12 +33,14 @@
#include <gio/gio.h>
#include "libedataserver/e-data-server-util.h"
#include "libedataserver/e-xml-hash-utils.h"
+#include "libedataserver/e-debug-log.h"
#include <libecal/e-cal-recur.h>
#include <libecal/e-cal-time-util.h>
#include <libecal/e-cal-util.h>
#include <libecal/e-cal-check-timezones.h>
#include <libedata-cal/e-cal-backend-util.h>
#include <libedata-cal/e-cal-backend-sexp.h>
+#include <libedata-cal/e-cal-backend-intervaltree.h>
#include "e-cal-backend-file-events.h"
#ifndef O_BINARY
@@ -85,6 +87,8 @@ struct _ECalBackendFilePrivate {
*/
GHashTable *comp_uid_hash;
+ EIntervalTree *interval_tree;
+
GList *comp;
/* The calendar's default timezone, used for resolving DATE and
@@ -114,7 +118,7 @@ struct _ECalBackendFilePrivate {
-#define d(x)
+#define d(x) x
static void e_cal_backend_file_dispose (GObject *object);
static void e_cal_backend_file_finalize (GObject *object);
@@ -126,6 +130,12 @@ e_cal_backend_file_add_timezone (ECalBackendSync *backend, EDataCal *cal, const
static void free_refresh_data (ECalBackendFile *cbfile);
+static icaltimezone *
+e_cal_backend_file_internal_get_timezone (ECalBackend *backend, const char *tzid);
+
+static icaltimezone *
+e_cal_backend_file_internal_get_default_timezone (ECalBackend *backend);
+
/* g_hash_table_foreach() callback to destroy a ECalBackendFileObject */
static void
free_object_data (gpointer data)
@@ -287,6 +297,9 @@ free_calendar_data (ECalBackendFile *cbfile)
priv = cbfile->priv;
+ e_intervaltree_destroy (priv->interval_tree);
+ priv->interval_tree = NULL;
+
free_calendar_components (priv->comp_uid_hash, priv->icalcomp);
priv->comp_uid_hash = NULL;
priv->icalcomp = NULL;
@@ -439,13 +452,19 @@ static icaltimezone *
resolve_tzid (const gchar *tzid, gpointer user_data)
{
icalcomponent *vcalendar_comp = user_data;
+ icaltimezone* zone;
if (!tzid || !tzid[0])
return NULL;
else if (!strcmp (tzid, "UTC"))
return icaltimezone_get_utc_timezone ();
- return icalcomponent_get_timezone (vcalendar_comp, tzid);
+ zone = icalcomponent_get_timezone (vcalendar_comp, tzid);
+
+ if (!zone)
+ zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+
+ return zone;
}
/* Checks if the specified component has a duplicated UID and if so changes it */
@@ -498,6 +517,63 @@ get_rid_icaltime (ECalComponent *comp)
return tt;
}
+
+/* Adds component to the interval tree
+ */
+static gboolean
+add_component_to_intervaltree (ECalBackendFile *cbfile, ECalComponent *comp)
+{
+ time_t time_start = -1, time_end = -1;
+ ECalBackendFilePrivate *priv;
+
+ g_return_val_if_fail (cbfile != NULL, FALSE);
+ g_return_val_if_fail (comp != NULL, FALSE);
+
+ priv = cbfile->priv;
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, priv->icalcomp, priv->default_zone,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cbfile)));
+
+ if (time_end != -1 && time_start > time_end)
+ g_print ("Bogus component %s\n", e_cal_component_get_as_string (comp));
+ else
+ e_intervaltree_insert (priv->interval_tree, time_start, time_end, comp);
+
+ return FALSE;
+}
+
+static gboolean
+remove_component_from_intervaltree (ECalBackendFile *cbfile, ECalComponent *comp)
+{
+ time_t time_start = -1, time_end = -1;
+ const char *uid = NULL;
+ gchar *rid;
+ ECalBackendFilePrivate *priv;
+
+ g_return_val_if_fail (cbfile != NULL, FALSE);
+ g_return_val_if_fail (comp != NULL, FALSE);
+
+ priv = cbfile->priv;
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, priv->icalcomp, priv->default_zone,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cbfile)));
+
+
+ if (time_end != -1 && time_start > time_end) {
+ g_error ("Bogus component %s\n", e_cal_component_get_as_string (comp));
+ return FALSE;
+ } else {
+ gboolean res;
+ rid = e_cal_component_get_recurid_as_string (comp);
+ e_cal_component_get_uid (comp, &uid);
+ res = e_intervaltree_remove (priv->interval_tree, uid, rid);
+ g_free (rid);
+ return res;
+ }
+}
+
/* Tries to add an icalcomponent to the file backend. We only store the objects
* of the types we support; all others just remain in the toplevel component so
* that we don't lose them.
@@ -536,6 +612,7 @@ add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_top
g_hash_table_insert (priv->comp_uid_hash, g_strdup (uid), obj_data);
}
+ add_component_to_intervaltree (cbfile, comp);
g_hash_table_insert (obj_data->recurrences, rid, comp);
obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
} else {
@@ -557,6 +634,7 @@ add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_top
obj_data->recurrences = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
g_hash_table_insert (priv->comp_uid_hash, g_strdup (uid), obj_data);
+ add_component_to_intervaltree (cbfile, comp);
}
}
@@ -590,6 +668,9 @@ remove_recurrence_cb (gpointer key, gpointer value, gpointer data)
icalcomp = e_cal_component_get_icalcomponent (comp);
g_assert (icalcomp != NULL);
+ if (!remove_component_from_intervaltree (cbfile, comp)) {
+ g_message (G_STRLOC " Could not remove component from interval tree!");
+ }
icalcomponent_remove_component (priv->icalcomp, icalcomp);
/* remove it from our mapping */
@@ -623,6 +704,10 @@ remove_component (ECalBackendFile *cbfile, const gchar *uid, ECalBackendFileObje
l = g_list_find (priv->comp, obj_data->full_object);
g_assert (l != NULL);
priv->comp = g_list_delete_link (priv->comp, l);
+
+ if (!remove_component_from_intervaltree (cbfile, obj_data->full_object)) {
+ g_message (G_STRLOC " Could not remove component from interval tree!");
+ }
}
/* remove the recurrences also */
@@ -911,6 +996,7 @@ open_cal (ECalBackendFile *cbfile, const gchar *uristr, GError **perror)
priv->custom_file = g_strdup (uristr);
priv->comp_uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_object_data);
+ priv->interval_tree = e_intervaltree_new ();
scan_vcalendar (cbfile);
prepare_refresh_data (cbfile);
@@ -1058,6 +1144,7 @@ reload_cal (ECalBackendFile *cbfile, const gchar *uristr, GError **perror)
priv->icalcomp = icalcomp;
priv->comp_uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_object_data);
+ priv->interval_tree = e_intervaltree_new ();
scan_vcalendar (cbfile);
priv->path = uri_to_path (E_CAL_BACKEND (cbfile));
@@ -1096,6 +1183,7 @@ create_cal (ECalBackendFile *cbfile, const gchar *uristr, GError **perror)
/* Create our internal data */
priv->comp_uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_object_data);
+ priv->interval_tree = e_intervaltree_new ();
priv->path = uri_to_path (E_CAL_BACKEND (cbfile));
@@ -1621,6 +1709,33 @@ typedef struct {
} MatchObjectData;
static void
+match_object_sexp_to_component (gpointer value, gpointer data)
+{
+ ECalComponent* comp = value;
+ MatchObjectData *match_data = data;
+ const gchar *uid;
+ ECalBackendFile *cbfile;
+ ECalBackendFilePrivate *priv;
+ e_cal_component_get_uid (comp, &uid);
+
+ g_return_if_fail (comp != NULL);
+
+ cbfile = E_CAL_BACKEND_FILE (match_data->backend);
+
+ g_return_if_fail (match_data->backend != NULL);
+
+ priv = cbfile->priv;
+
+ g_return_if_fail (priv != NULL);
+
+ if ((!match_data->search_needed) ||
+ (e_cal_backend_sexp_match_comp (match_data->obj_sexp, comp, match_data->backend))) {
+ match_data->obj_list = g_list_append (match_data->obj_list,
+ e_cal_component_get_as_string (comp));
+ }
+}
+
+static void
match_recurrence_sexp (gpointer key, gpointer value, gpointer data)
{
ECalComponent *comp = value;
@@ -1653,6 +1768,7 @@ match_object_sexp (gpointer key, gpointer value, gpointer data)
match_data);
}
+
/* Get_objects_in_range handler for the file backend */
static void
e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, const gchar *sexp, GList **objects, GError **perror)
@@ -1660,7 +1776,9 @@ e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
ECalBackendFile *cbfile;
ECalBackendFilePrivate *priv;
MatchObjectData match_data;
-
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
+ GList* objs_occuring_in_tw;
cbfile = E_CAL_BACKEND_FILE (backend);
priv = cbfile->priv;
@@ -1682,11 +1800,33 @@ e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
}
g_static_rec_mutex_lock (&priv->idle_save_rmutex);
- g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
+
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(match_data.obj_sexp,
+ &occur_start,
+ &occur_end);
+
+ objs_occuring_in_tw = NULL;
+
+ if (!prunning_by_time) {
+ g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp,
+ &match_data);
+ } else {
+ objs_occuring_in_tw = e_intervaltree_search (priv->interval_tree,
+ occur_start, occur_end);
+
+ g_list_foreach(objs_occuring_in_tw, (GFunc) match_object_sexp_to_component,
+ &match_data);
+ }
+
g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
*objects = match_data.obj_list;
+ if (objs_occuring_in_tw) {
+ g_list_foreach(objs_occuring_in_tw, (GFunc)g_object_unref, NULL);
+ g_list_free (objs_occuring_in_tw);
+ }
+
g_object_unref (match_data.obj_sexp);
}
@@ -1705,7 +1845,9 @@ e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
ECalBackendFile *cbfile;
ECalBackendFilePrivate *priv;
MatchObjectData match_data;
-
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
+ GList* objs_occuring_in_tw;
cbfile = E_CAL_BACKEND_FILE (backend);
priv = cbfile->priv;
@@ -1717,20 +1859,46 @@ e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
match_data.obj_list = NULL;
match_data.backend = backend;
match_data.default_zone = priv->default_zone;
+ match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
if (!strcmp (match_data.query, "#t"))
match_data.search_needed = FALSE;
- match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
if (!match_data.obj_sexp) {
GError *error = EDC_ERROR (InvalidQuery);
e_data_cal_view_notify_done (query, error);
g_error_free (error);
return;
}
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(match_data.obj_sexp,
+ &occur_start,
+ &occur_end);
+
+ objs_occuring_in_tw = NULL;
g_static_rec_mutex_lock (&priv->idle_save_rmutex);
- g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
+
+ if (!prunning_by_time) {
+ /* full scan */
+ g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp,
+ &match_data);
+
+ e_debug_log(FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "---;%p;QUERY-ITEMS;%s;%s;%d", query,
+ e_data_cal_view_get_text (query), G_OBJECT_TYPE_NAME (backend),
+ g_hash_table_size(priv->comp_uid_hash));
+ } else {
+ /* matches objects in new "interval tree" way */
+ /* events occuring in time window */
+ objs_occuring_in_tw = e_intervaltree_search (priv->interval_tree, occur_start, occur_end);
+
+ g_list_foreach(objs_occuring_in_tw, (GFunc) match_object_sexp_to_component,
+ &match_data);
+
+ e_debug_log(FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "---;%p;QUERY-ITEMS;%s;%s;%d", query,
+ e_data_cal_view_get_text (query), G_OBJECT_TYPE_NAME (backend),
+ g_list_length (objs_occuring_in_tw));
+ }
+
g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
/* notify listeners of all objects */
@@ -1741,6 +1909,11 @@ e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
g_list_foreach (match_data.obj_list, (GFunc) g_free, NULL);
g_list_free (match_data.obj_list);
}
+
+ if (objs_occuring_in_tw) {
+ g_list_foreach(objs_occuring_in_tw, (GFunc)g_object_unref, NULL);
+ g_list_free (objs_occuring_in_tw);
+ }
g_object_unref (match_data.obj_sexp);
e_data_cal_view_notify_done (query, NULL /* Success */);
@@ -3048,6 +3221,7 @@ e_cal_backend_file_init (ECalBackendFile *cbfile)
priv->icalcomp = NULL;
priv->comp_uid_hash = NULL;
priv->comp = NULL;
+ priv->interval_tree = NULL;
priv->custom_file = NULL;
priv->refresh_lock = g_mutex_new ();
@@ -3236,3 +3410,249 @@ e_cal_backend_file_reload (ECalBackendFile *cbfile, GError **perror)
if (err)
g_propagate_error (perror, err);
}
+
+#ifdef TEST_QUERY_RESULT
+#include <glib.h>
+
+static void
+test_query_by_scanning_all_objects (ECalBackendFile* cbfile, const char *sexp, GList **objects)
+{
+ MatchObjectData match_data;
+ ECalBackendFilePrivate *priv;
+
+ priv = cbfile->priv;
+
+ match_data.search_needed = TRUE;
+ match_data.query = sexp;
+ match_data.obj_list = NULL;
+ match_data.default_zone = priv->default_zone;
+ match_data.backend = E_CAL_BACKEND (cbfile);
+
+ if (!strcmp (sexp, "#t"))
+ match_data.search_needed = FALSE;
+
+ match_data.obj_sexp = e_cal_backend_sexp_new (sexp);
+ if (!match_data.obj_sexp)
+ return;
+
+ g_static_rec_mutex_lock (&priv->idle_save_rmutex);
+
+ if (!match_data.obj_sexp)
+ {
+ g_message (G_STRLOC ": Getting object list (%s)", sexp);
+ exit (-1);
+ }
+
+ g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp,
+ &match_data);
+
+ g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+
+ *objects = match_data.obj_list;
+
+ g_object_unref (match_data.obj_sexp);
+}
+
+static void
+write_list (GList* list)
+{
+ GList *l;
+
+ for (l = list; l; l = l->next)
+ {
+ const char *str = l->data;
+ ECalComponent *comp = e_cal_component_new_from_string (str);
+ const char *uid;
+ e_cal_component_get_uid (comp, &uid);
+ g_print ("%s\n", uid);
+ }
+}
+
+static void
+get_difference_of_lists (ECalBackendFile* cbfile, GList* smaller, GList* bigger)
+{
+ GList *l, *lsmaller;
+
+ for (l = bigger ; l; l = l->next) {
+ char *str = l->data;
+ const char *uid;
+ ECalComponent *comp = e_cal_component_new_from_string (str);
+ gboolean found = FALSE;
+ e_cal_component_get_uid (comp, &uid);
+
+ for (lsmaller = smaller; lsmaller && !found; lsmaller = lsmaller->next)
+ {
+ char *strsmaller = lsmaller->data;
+ const char *uidsmaller;
+ ECalComponent *compsmaller = e_cal_component_new_from_string (strsmaller);
+ e_cal_component_get_uid (compsmaller, &uidsmaller);
+
+ found = strcmp (uid, uidsmaller) == 0;
+
+ g_object_unref (compsmaller);
+ }
+
+ if (!found)
+ {
+ time_t time_start, time_end;
+ printf ("%s IS MISSING\n", uid);
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, cbfile->priv->icalcomp,
+ cbfile->priv->default_zone,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cbfile)));
+
+ d (printf ("start %s\n", asctime(gmtime(&time_start))));
+ d (printf ("end %s\n", asctime(gmtime(&time_end))));
+ }
+
+ g_object_unref (comp);
+ }
+}
+
+static void
+test_query (ECalBackendFile* cbfile, const char* query)
+{
+ GList *objects = NULL, *all_objects = NULL;
+
+ g_return_if_fail (query != NULL);
+
+ d (g_print ("Query %s\n", query));
+
+ test_query_by_scanning_all_objects (cbfile, query, &all_objects);
+ e_cal_backend_file_get_object_list (E_CAL_BACKEND_SYNC (cbfile), NULL, query, &objects, NULL);
+ if (objects == NULL)
+ {
+ g_message (G_STRLOC " failed to get objects\n");
+ exit(0);
+ }
+
+ if (g_list_length (objects) < g_list_length (all_objects) )
+ {
+ g_print ("ERROR\n");
+ get_difference_of_lists (cbfile, objects, all_objects);
+ exit (-1);
+ }
+ else if (g_list_length (objects) > g_list_length (all_objects) )
+ {
+ g_print ("ERROR\n");
+ write_list (all_objects);
+ get_difference_of_lists (cbfile, all_objects, objects);
+ exit (-1);
+ }
+
+
+ g_list_foreach(objects, (GFunc) g_free, NULL);
+ g_list_free (objects);
+ g_list_foreach(all_objects, (GFunc) g_free, NULL);
+ g_list_free (all_objects);
+}
+
+static void
+execute_query (ECalBackendFile* cbfile, const char* query)
+{
+ GList *objects = NULL;
+
+ g_return_if_fail (query != NULL);
+
+ d (g_print ("Query %s\n", query));
+ e_cal_backend_file_get_object_list (E_CAL_BACKEND_SYNC (cbfile), NULL, query, &objects, NULL);
+ if(objects == NULL)
+ {
+ g_message (G_STRLOC " failed to get objects\n");
+ exit(0);
+ }
+
+ g_list_foreach(objects, (GFunc) g_free, NULL);
+ g_list_free (objects);
+}
+
+static gchar *fname = NULL;
+static gboolean only_execute = FALSE;
+static gchar *calendar_fname = NULL;
+
+static GOptionEntry entries[] =
+{
+ { "test-file", 't', 0, G_OPTION_ARG_STRING, &fname, "File with prepared queries", NULL },
+ { "only-execute", 'e', 0, G_OPTION_ARG_NONE, &only_execute, "Only execute, do not test query", NULL },
+ { "calendar-file", 'c', 0, G_OPTION_ARG_STRING, &calendar_fname, "Path to the calendar.ics file", NULL },
+ { NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+ char * line = NULL;
+ size_t len = 0;
+ ssize_t read;
+ ECalBackendFile* cbfile;
+ int num = 0;
+ GError *error = NULL;
+ GOptionContext *context;
+ FILE* fin = NULL;
+
+ g_type_init ();
+ g_thread_init (NULL);
+
+ context = g_option_context_new ("- test utility for e-d-s file backend");
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_print ("option parsing failed: %s\n", error->message);
+ exit (1);
+ }
+
+ calendar_fname = g_strdup("calendar.ics");
+
+ if (!calendar_fname)
+ {
+ g_message (G_STRLOC " Please, use -c parameter");
+ exit (-1);
+ }
+
+ cbfile = g_object_new (E_TYPE_CAL_BACKEND_FILE, NULL);
+ open_cal (cbfile, calendar_fname, NULL);
+ if (cbfile == NULL)
+ {
+ g_message (G_STRLOC " Could not open calendar %s", calendar_fname);
+ exit (-1);
+ }
+
+ if (fname)
+ {
+ fin = fopen (fname, "r");
+
+ if (!fin)
+ {
+ g_message (G_STRLOC " Could not open file %s", fname);
+ goto err0;
+ }
+ }
+ else
+ {
+ g_message (G_STRLOC " Reading from stdin");
+ fin = stdin;
+ }
+
+ while ((read = getline(&line, &len, fin)) != -1) {
+ g_print ("Query %d: %s", num++, line);
+
+ if (only_execute)
+ execute_query(cbfile, line);
+ else
+ test_query(cbfile, line);
+ }
+
+ if (line)
+ free(line);
+
+ if (fname)
+ fclose (fin);
+
+err0:
+ g_object_unref (cbfile);
+
+ return 0;
+}
+#endif
+
diff --git a/calendar/backends/groupwise/e-cal-backend-groupwise.c b/calendar/backends/groupwise/e-cal-backend-groupwise.c
index 00db568..7dbbdc3 100644
--- a/calendar/backends/groupwise/e-cal-backend-groupwise.c
+++ b/calendar/backends/groupwise/e-cal-backend-groupwise.c
@@ -191,6 +191,30 @@ get_element_type (icalcomponent_kind kind)
}
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+ return (!strcmp (tzid, "UTC"))
+ ? icaltimezone_get_utc_timezone ()
+ : icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+static void
+put_component_to_store (ECalBackendGroupwise *cbgw,
+ ECalComponent *comp)
+{
+ time_t time_start, time_end;
+ ECalBackendGroupwisePrivate *priv;
+
+ priv = cbgw->priv;
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, NULL, priv->default_zone,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cbgw)));
+
+ e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
/* Initialy populate the cache from the server */
static EGwConnectionStatus
populate_cache (ECalBackendGroupwise *cbgw)
@@ -302,7 +326,7 @@ populate_cache (ECalBackendGroupwise *cbgw)
comp_str = e_cal_component_get_as_string (comp);
e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), (const gchar *) comp_str);
g_free (comp_str);
- e_cal_backend_store_put_component (priv->store, comp);
+ put_component_to_store (cbgw, comp);
g_object_unref (comp);
}
g_free (progress_string);
@@ -503,7 +527,7 @@ get_deltas (gpointer handle)
g_free (modif_comp_str);
g_free (cache_comp_str);
cache_comp_str = NULL;
- e_cal_backend_store_put_component (store, modified_comp);
+ put_component_to_store (cbgw, modified_comp);
}
e_cal_component_free_icaltimetype (tt);
@@ -653,8 +677,7 @@ get_deltas (gpointer handle)
comp = e_gw_item_to_cal_component (item, cbgw);
if (comp) {
e_cal_component_commit_sequence (comp);
- e_cal_backend_store_put_component (store, comp);
-
+ put_component_to_store (cbgw, comp);
if (kind == icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
tmp = e_cal_component_get_as_string (comp);
e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), tmp);
@@ -1344,6 +1367,7 @@ e_cal_backend_groupwise_open (ECalBackendSync *backend, EDataCal *cal, gboolean
}
}
+ e_cal_backend_store_load (priv->store);
PRIV_UNLOCK (priv);
return;
}
@@ -1594,6 +1618,8 @@ e_cal_backend_groupwise_get_object_list (ECalBackendSync *backend, EDataCal *cal
GSList *components, *l;
ECalBackendSExp *cbsexp;
gboolean search_needed = TRUE;
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
cbgw = E_CAL_BACKEND_GROUPWISE (backend);
priv = cbgw->priv;
@@ -1611,8 +1637,15 @@ e_cal_backend_groupwise_get_object_list (ECalBackendSync *backend, EDataCal *cal
}
*objects = NULL;
- components = e_cal_backend_store_get_components (priv->store);
- for (l = components; l != NULL; l = l->next) {
+
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(cbsexp,
+ &occur_start,
+ &occur_end);
+ components = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
+
+ for (l = components; l != NULL; l = l->next) {
ECalComponent *comp = E_CAL_COMPONENT (l->data);
if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) ==
@@ -1873,7 +1906,7 @@ update_from_server (ECalBackendGroupwise *cbgw, GSList *uid_list, gchar **calobj
item = (EGwItem *) tmp->data;
e_cal_comp = e_gw_item_to_cal_component (item, cbgw);
e_cal_component_commit_sequence (e_cal_comp);
- e_cal_backend_store_put_component (priv->store, e_cal_comp);
+ put_component_to_store (cbgw, e_cal_comp);
if (i == 0) {
*calobj = e_cal_component_get_as_string (e_cal_comp);
@@ -2089,7 +2122,7 @@ e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal,
return;
}
- e_cal_backend_store_put_component (priv->store, comp);
+ put_component_to_store (cbgw, comp);
*new_object = e_cal_component_get_as_string (comp);
break;
}
@@ -2121,7 +2154,7 @@ e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal,
g_propagate_error (error, EDC_ERROR_FAILED_STATUS (OtherError, status));
return;
}
- e_cal_backend_store_put_component (priv->store, comp);
+ put_component_to_store (cbgw, comp);
break;
}
}
@@ -2145,7 +2178,7 @@ e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal,
case CAL_MODE_LOCAL :
/* in offline mode, we just update the cache */
- e_cal_backend_store_put_component (priv->store, comp);
+ put_component_to_store (cbgw, comp);
break;
default :
break;
@@ -2521,8 +2554,7 @@ receive_object (ECalBackendGroupwise *cbgw, EDataCal *cal, icalcomponent *icalco
change_status (component, pstatus, e_gw_connection_get_user_email (priv->cnc));
e_cal_component_get_transparency (comp, &transp);
e_cal_component_set_transparency (component, transp);
-
- e_cal_backend_store_put_component (priv->store, component);
+ put_component_to_store (cbgw, comp);
comp_str = e_cal_component_get_as_string (component);
if (found)
diff --git a/calendar/backends/http/e-cal-backend-http.c b/calendar/backends/http/e-cal-backend-http.c
index 738ba8c..2f50c51 100644
--- a/calendar/backends/http/e-cal-backend-http.c
+++ b/calendar/backends/http/e-cal-backend-http.c
@@ -274,6 +274,36 @@ empty_cache (ECalBackendHttp *cbhttp)
e_cal_backend_store_clean (priv->store);
}
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+ icalcomponent *vcalendar_comp = user_data;
+
+ if (!tzid || !tzid[0])
+ return NULL;
+ else if (!strcmp (tzid, "UTC"))
+ return icaltimezone_get_utc_timezone ();
+
+ return icalcomponent_get_timezone (vcalendar_comp, tzid);
+}
+
+static void
+put_component_to_store (ECalBackendHttp *cb,
+ ECalComponent *comp)
+{
+ time_t time_start, time_end;
+ ECalBackendHttpPrivate *priv;
+
+ priv = cb->priv;
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, NULL, priv->default_zone,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cb)));
+
+ e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
+
static void
retrieval_done (SoupSession *session, SoupMessage *msg, ECalBackendHttp *cbhttp)
{
@@ -397,8 +427,7 @@ retrieval_done (SoupSession *session, SoupMessage *msg, ECalBackendHttp *cbhttp)
const gchar *uid, *orig_key, *orig_value;
gchar *obj;
- e_cal_backend_store_put_component (priv->store, comp);
-
+ put_component_to_store (cbhttp, comp);
e_cal_component_get_uid (comp, &uid);
/* middle (gpointer) cast only because of 'dereferencing type-punned pointer will break strict-aliasing rules' */
if (g_hash_table_lookup_extended (old_cache, uid, (gpointer *)(gpointer)&orig_key, (gpointer *)(gpointer)&orig_value)) {
@@ -907,6 +936,8 @@ e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
ECalBackendHttpPrivate *priv;
GSList *components, *l;
ECalBackendSExp *cbsexp;
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
cbhttp = E_CAL_BACKEND_HTTP (backend);
priv = cbhttp->priv;
@@ -920,7 +951,14 @@ e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
cbsexp = e_cal_backend_sexp_new (sexp);
*objects = NULL;
- components = e_cal_backend_store_get_components (priv->store);
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(cbsexp,
+ &occur_start,
+ &occur_end);
+
+ components = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
+
for (l = components; l != NULL; l = g_slist_next (l)) {
if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend))) {
*objects = g_list_append (*objects, e_cal_component_get_as_string (l->data));
@@ -941,6 +979,8 @@ e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
GSList *components, *l;
GList *objects = NULL;
ECalBackendSExp *cbsexp;
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
cbhttp = E_CAL_BACKEND_HTTP (backend);
priv = cbhttp->priv;
@@ -958,7 +998,14 @@ e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
cbsexp = e_cal_backend_sexp_new (e_data_cal_view_get_text (query));
objects = NULL;
- components = e_cal_backend_store_get_components (priv->store);
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(cbsexp,
+ &occur_start,
+ &occur_end);
+
+ components = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
+
for (l = components; l != NULL; l = g_slist_next (l)) {
if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend))) {
objects = g_list_append (objects, e_cal_component_get_as_string (l->data));
@@ -976,7 +1023,7 @@ e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
e_data_cal_view_notify_done (query, NULL /* Success */);
}
-static icaltimezone *
+/***** static icaltimezone *
resolve_tzid (const gchar *tzid, gpointer user_data)
{
icalcomponent *vcalendar_comp = user_data;
@@ -987,7 +1034,7 @@ resolve_tzid (const gchar *tzid, gpointer user_data)
return icaltimezone_get_utc_timezone ();
return icalcomponent_get_timezone (vcalendar_comp, tzid);
-}
+} *****/
static gboolean
free_busy_instance (ECalComponent *comp,
diff --git a/calendar/backends/weather/e-cal-backend-weather.c b/calendar/backends/weather/e-cal-backend-weather.c
index ffc0a41..025b0b6 100644
--- a/calendar/backends/weather/e-cal-backend-weather.c
+++ b/calendar/backends/weather/e-cal-backend-weather.c
@@ -139,6 +139,30 @@ maybe_start_reload_timeout (ECalBackendWeather *cbw)
}
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+ return (!strcmp (tzid, "UTC"))
+ ? icaltimezone_get_utc_timezone ()
+ : icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+static void
+put_component_to_store (ECalBackendWeather *cb,
+ ECalComponent *comp)
+{
+ time_t time_start, time_end;
+ ECalBackendWeatherPrivate *priv;
+
+ priv = cb->priv;
+
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, NULL, priv->default_zone,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cb)));
+
+ e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
static void
finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
{
@@ -157,6 +181,7 @@ finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
/* update cache */
l = e_cal_backend_store_get_components (priv->store);
+
for (; l != NULL; l = g_slist_next (l)) {
ECalComponentId *id;
gchar *obj;
@@ -181,7 +206,7 @@ finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
if (comp) {
GSList *forecasts;
- e_cal_backend_store_put_component (priv->store, comp);
+ put_component_to_store (cbw, comp);
icomp = e_cal_component_get_icalcomponent (comp);
obj = icalcomponent_as_ical_string_r (icomp);
e_cal_backend_notify_object_created (E_CAL_BACKEND (cbw), obj);
@@ -199,7 +224,7 @@ finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
if (nfo) {
comp = create_weather (cbw, nfo, TRUE);
if (comp) {
- e_cal_backend_store_put_component (priv->store, comp);
+ put_component_to_store (cbw, comp);
icomp = e_cal_component_get_icalcomponent (comp);
obj = icalcomponent_as_ical_string_r (icomp);
e_cal_backend_notify_object_created (E_CAL_BACKEND (cbw), obj);
@@ -487,6 +512,7 @@ e_cal_backend_weather_open (ECalBackendSync *backend, EDataCal *cal, gboolean on
g_propagate_error (perror, EDC_ERROR_EX (OtherError, _("Could not create cache file")));
return;
}
+ /* do we require to load this new store*/
e_cal_backend_store_load (priv->store);
if (priv->default_zone) {
@@ -589,6 +615,8 @@ e_cal_backend_weather_get_object_list (ECalBackendSync *backend, EDataCal *cal,
ECalBackendWeatherPrivate *priv = cbw->priv;
ECalBackendSExp *sexp = e_cal_backend_sexp_new (sexp_string);
GSList *components, *l;
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
if (!sexp) {
g_propagate_error (perror, EDC_ERROR (InvalidQuery));
@@ -597,6 +625,14 @@ e_cal_backend_weather_get_object_list (ECalBackendSync *backend, EDataCal *cal,
*objects = NULL;
components = e_cal_backend_store_get_components (priv->store);
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp,
+ &occur_start,
+ &occur_end);
+
+ components = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
+
for (l = components; l != NULL; l = g_slist_next (l)) {
if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend)))
*objects = g_list_append (*objects, e_cal_component_get_as_string (l->data));
@@ -717,6 +753,8 @@ static void e_cal_backend_weather_start_query (ECalBackend *backend, EDataCalVie
GSList *components, *l;
GList *objects;
GError *error;
+ time_t occur_start = -1, occur_end = -1;
+ gboolean prunning_by_time;
cbw = E_CAL_BACKEND_WEATHER (backend);
priv = cbw->priv;
@@ -737,9 +775,13 @@ static void e_cal_backend_weather_start_query (ECalBackend *backend, EDataCalVie
}
objects = NULL;
- components = e_cal_backend_store_get_components (priv->store);
+ prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp, &occur_start, &occur_end);
+ components = prunning_by_time ?
+ e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+ : e_cal_backend_store_get_components (priv->store);
+
for (l = components; l != NULL; l = g_slist_next (l)) {
- if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), backend))
+ if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), backend))
objects = g_list_append (objects, e_cal_component_get_as_string (l->data));
}
diff --git a/calendar/libecal/e-cal-recur.c b/calendar/libecal/e-cal-recur.c
index 5d80b66..c504faf 100644
--- a/calendar/libecal/e-cal-recur.c
+++ b/calendar/libecal/e-cal-recur.c
@@ -907,6 +907,65 @@ array_to_list (gshort *array, gint max_elements)
return g_list_reverse (l);
}
+/**
+ * e_cal_recur_get_enddate
+ *
+ * @ir: RRULE or EXRULE recurrence
+ * @prop: An RRULE or EXRULE #icalproperty.
+ * @zone: The DTSTART timezone, used for converting the UNTIL property if it
+ * is given as a DATE value.
+ * @convert_end_date: TRUE if the saved end date needs to be converted to the
+ * given @zone timezone. This is needed if the DTSTART is a DATE or floating
+ * time.
+ *
+ * @return End timepoint of giben event
+ *
+ * Finds out end time of (reccurent) event.
+ */
+time_t
+e_cal_recur_obtain_enddate (struct icalrecurrencetype *ir, icalproperty *prop,
+ icaltimezone *zone, gboolean convert_end_date)
+{
+ time_t enddate = -1;
+
+ g_return_val_if_fail (prop != NULL, 0);
+ g_return_val_if_fail (ir != NULL, 0);
+
+ if (ir->count != 0) {
+ /* If COUNT is set, we use the pre-calculated enddate.
+ Note that this can be 0 if the RULE doesn't actually
+ generate COUNT instances. */
+ enddate = e_cal_recur_get_rule_end_date (prop, convert_end_date ? zone : NULL);
+ } else {
+ if (icaltime_is_null_time (ir->until)) {
+ /* If neither COUNT or UNTIL is set, the event
+ recurs forever. */
+ } else if (ir->until.is_date) {
+ /* If UNTIL is a DATE, we stop at the end of
+ the day, in local time (with the DTSTART timezone).
+ Note that UNTIL is inclusive so we stop before
+ midnight. */
+ ir->until.hour = 23;
+ ir->until.minute = 59;
+ ir->until.second = 59;
+ ir->until.is_date = FALSE;
+
+ enddate = icaltime_as_timet_with_zone (ir->until, zone);
+#if 0
+ g_print (" until: %li - %s", r->enddate, ctime (&r->enddate));
+#endif
+
+ } else {
+ /* If UNTIL is a DATE-TIME, it must be in UTC. */
+ icaltimezone *utc_zone;
+ utc_zone = icaltimezone_get_utc_timezone ();
+ enddate = icaltime_as_timet_with_zone (ir->until, utc_zone);
+ }
+ }
+
+ return enddate;
+}
+
/**
* e_cal_recur_from_icalproperty:
* @prop: An RRULE or EXRULE #icalproperty.
@@ -943,40 +1002,7 @@ e_cal_recur_from_icalproperty (icalproperty *prop, gboolean exception,
r->freq = ir.freq;
r->interval = ir.interval;
- if (ir.count != 0) {
- /* If COUNT is set, we use the pre-calculated enddate.
- Note that this can be 0 if the RULE doesn't actually
- generate COUNT instances. */
- r->enddate = e_cal_recur_get_rule_end_date (prop, convert_end_date ? zone : NULL);
- } else {
- if (icaltime_is_null_time (ir.until)) {
- /* If neither COUNT or UNTIL is set, the event
- recurs forever. */
- r->enddate = 0;
- } else if (ir.until.is_date) {
- /* If UNTIL is a DATE, we stop at the end of
- the day, in local time (with the DTSTART timezone).
- Note that UNTIL is inclusive so we stop before
- midnight. */
- ir.until.hour = 23;
- ir.until.minute = 59;
- ir.until.second = 59;
- ir.until.is_date = FALSE;
-
- r->enddate = icaltime_as_timet_with_zone (ir.until,
- zone);
-#if 0
- g_print (" until: %li - %s", r->enddate, ctime (&r->enddate));
-#endif
-
- } else {
- /* If UNTIL is a DATE-TIME, it must be in UTC. */
- icaltimezone *utc_zone;
- utc_zone = icaltimezone_get_utc_timezone ();
- r->enddate = icaltime_as_timet_with_zone (ir.until,
- utc_zone);
- }
- }
+ r->enddate = e_cal_recur_obtain_enddate (&ir, prop, zone, convert_end_date);
r->week_start_day = e_cal_recur_ical_weekday_to_weekday (ir.week_start);
diff --git a/calendar/libecal/e-cal-recur.h b/calendar/libecal/e-cal-recur.h
index f80d3c2..7af97c5 100644
--- a/calendar/libecal/e-cal-recur.h
+++ b/calendar/libecal/e-cal-recur.h
@@ -45,6 +45,12 @@ void e_cal_recur_generate_instances (ECalComponent *comp,
gpointer tz_cb_data,
icaltimezone *default_timezone);
+time_t
+e_cal_recur_obtain_enddate (struct icalrecurrencetype *ir,
+ icalproperty *prop,
+ icaltimezone *zone,
+ gboolean convert_end_date);
+
/* Localized nth-day-of-month strings. (Use with _() ) */
#ifdef G_OS_WIN32
extern const gchar **e_cal_get_recur_nth (void);
diff --git a/calendar/libecal/e-cal-util.c b/calendar/libecal/e-cal-util.c
index b7e60c1..e7a8720 100644
--- a/calendar/libecal/e-cal-util.c
+++ b/calendar/libecal/e-cal-util.c
@@ -28,7 +28,8 @@
#include "e-cal-util.h"
#include "e-cal-system-timezone.h"
-
+#define _TIME_MIN ((time_t) 0) /* Min valid time_t */
+#define _TIME_MAX ((time_t) INT_MAX)
/**
* cal_obj_instance_list_free:
@@ -424,6 +425,7 @@ add_alarm_occurrences_cb (ECalComponent *comp, time_t start, time_t end, gpointe
return TRUE;
}
+
/* Generates the absolute triggers for a component */
static void
generate_absolute_triggers (ECalComponent *comp, struct alarm_occurrence_data *aod,
@@ -1166,3 +1168,162 @@ e_cal_util_get_system_timezone (void)
return zone;
}
+
+static time_t
+componenttime_to_utc_timet (const ECalComponentDateTime* dt_time,
+ ECalRecurResolveTimezoneFn tz_cb,
+ gpointer tz_cb_data,
+ const icaltimezone *default_zone)
+{
+ time_t timet = -1;
+ icaltimezone *zone = NULL;
+
+ g_return_val_if_fail (dt_time != NULL, -1);
+
+ if (dt_time->value) {
+ if (dt_time->tzid)
+ zone = tz_cb(dt_time->tzid, tz_cb_data);
+
+ // zone = icaltimezone_get_utc_timezone ();
+ timet = icaltime_as_timet_with_zone (*dt_time->value, zone ? zone : default_zone);
+ }
+
+
+ return timet;
+}
+
+/**
+ * Find out, when the component starts and stops. Be careful about recurrences.
+ */
+void
+get_component_occur_times (ECalComponent *comp,
+ time_t* start,
+ time_t* end,
+ ECalRecurResolveTimezoneFn tz_cb,
+ gpointer tz_cb_data,
+ const icaltimezone *default_timezone,
+ icalcomponent_kind kind)
+{
+ struct icalrecurrencetype ir;
+ ECalComponentDateTime dt_start, dt_end;
+ GSList *rrules = NULL, *exrules = NULL, *elem, *rdates = NULL;
+
+ g_return_if_fail (comp != NULL);
+ g_return_if_fail (start != NULL);
+ g_return_if_fail (end != NULL);
+
+ /* Get dtstart of the component and convert it to UTC */
+ e_cal_component_get_dtstart (comp, &dt_start);
+
+ if ( (*start = componenttime_to_utc_timet (&dt_start, tz_cb, tz_cb_data, default_timezone)) == -1)
+ *start = _TIME_MIN;
+
+ e_cal_component_free_datetime (&dt_start);
+
+ /* find out end date of component */
+ *end = _TIME_MAX;
+
+ if (kind == ICAL_VTODO_COMPONENT) {
+ /* max from COMPLETED and DUE properties */
+ struct icaltimetype *tt = NULL;
+ time_t completed_time = -1, due_time = -1, max_time;
+ ECalComponentDateTime dt_due;
+
+ e_cal_component_get_completed (comp, &tt);
+ if (tt) {
+ /* COMPLETED must be in UTC. */
+ completed_time = icaltime_as_timet_with_zone (*tt,
+ icaltimezone_get_utc_timezone());
+ e_cal_component_free_icaltimetype (tt);
+ }
+
+ e_cal_component_get_due (comp, &dt_due);
+ if(dt_due.value != NULL)
+ due_time = componenttime_to_utc_timet (&dt_due, tz_cb, tz_cb_data,
+ default_timezone);
+
+ e_cal_component_free_datetime (&dt_due);
+
+ max_time = MAX(completed_time, due_time);
+
+ if (max_time != -1)
+ *end = max_time;
+
+ } else {
+ /* ALARMS, EVENTS: DTEND and reccurences */
+
+ if (e_cal_component_has_recurrences (comp)) {
+ /* Do the RRULEs, EXRULEs and RDATEs*/
+ e_cal_component_get_rrule_property_list (comp, &rrules);
+ e_cal_component_get_exrule_property_list (comp, &exrules);
+ e_cal_component_get_rdate_list (comp, &rdates);
+
+ for (elem = rrules; elem; elem = elem->next) {
+ time_t rule_end;
+ icaltimezone *utc_zone;
+ icalproperty *prop = elem->data;
+ ir = icalproperty_get_rrule (prop);
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+ rule_end = e_cal_recur_obtain_enddate (&ir, prop, utc_zone, TRUE);
+
+ if (rule_end == -1) /* repeats forever */
+ *end = -1;
+ else if (rule_end > *end) /* new maximum */
+ *end = rule_end;
+ }
+
+ /* Do the EXRULEs. */
+ for (elem = exrules; elem; elem = elem->next) {
+ icalproperty *prop = elem->data;
+ time_t rule_end;
+ icaltimezone *utc_zone;
+ ir = icalproperty_get_exrule (prop);
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+ rule_end =
+ e_cal_recur_obtain_enddate (&ir, prop, utc_zone, TRUE);
+
+ if (rule_end == -1) /* repeats forever */
+ *end = _TIME_MAX;
+ else if (rule_end > *end)
+ *end = rule_end;
+ }
+
+ /* Do the RDATEs */
+ for (elem = rdates; elem; elem = elem->next) {
+ ECalComponentPeriod *p = elem->data;
+ time_t rdate_end = _TIME_MAX;
+
+ /* FIXME: We currently assume RDATEs are in the same timezone
+ as DTSTART. We should get the RDATE timezone and convert
+ to the DTSTART timezone first. */
+
+ /* Check if the end date or duration is set, libical seems to set
+ second to -1 to denote an unset time */
+ if (p->type != E_CAL_COMPONENT_PERIOD_DATETIME || p->u.end.second != -1)
+ rdate_end = icaltime_as_timet (icaltime_add(p->start, p->u.duration));
+ else
+ rdate_end = icaltime_as_timet (p->u.end);
+
+ if (rdate_end == -1) /* repeats forever */
+ *end = _TIME_MAX;
+ else if (rdate_end > *end)
+ *end = rdate_end;
+ }
+ }
+
+ /* Get dtend of the component and convert it to UTC */
+ e_cal_component_get_dtend (comp, &dt_end);
+
+ if (dt_end.value) {
+ time_t dtend_time = componenttime_to_utc_timet (&dt_end, tz_cb, tz_cb_data, default_timezone);
+
+ if (dtend_time == -1 || (dtend_time > *end))
+ *end = dtend_time;
+ }
+
+ e_cal_component_free_datetime (&dt_end);
+ }
+}
+
diff --git a/calendar/libecal/e-cal-util.h b/calendar/libecal/e-cal-util.h
index c63e76b..5de7400 100644
--- a/calendar/libecal/e-cal-util.h
+++ b/calendar/libecal/e-cal-util.h
@@ -151,6 +151,13 @@ void e_cal_util_remove_instances (icalcomponent *icalcomp,
gchar *e_cal_util_get_system_timezone_location (void);
icaltimezone *e_cal_util_get_system_timezone (void);
+void get_component_occur_times (ECalComponent *comp,
+ time_t* start,
+ time_t* end,
+ ECalRecurResolveTimezoneFn tz_cb,
+ gpointer tz_cb_data,
+ const icaltimezone *default_timezone,
+ icalcomponent_kind kind);
G_END_DECLS
diff --git a/calendar/libedata-cal/Makefile.am b/calendar/libedata-cal/Makefile.am
index 01527e6..adbe4b2 100644
--- a/calendar/libedata-cal/Makefile.am
+++ b/calendar/libedata-cal/Makefile.am
@@ -7,6 +7,7 @@ ENUM_GENERATED = e-data-cal-enumtypes.h e-data-cal-enumtypes.c
# The libraray
lib_LTLIBRARIES = libedata-cal-1.2.la
+noinst_PROGRAMS = test-e-sexp test-intervaltree test-intervaltree-coverage
libedata_cal_1_2_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
@@ -25,6 +26,7 @@ libedata_cal_1_2_la_SOURCES = \
e-cal-backend.c \
e-cal-backend-cache.c \
e-cal-backend-factory.c \
+ e-cal-backend-intervaltree.c \
e-cal-backend-sexp.c \
e-cal-backend-sync.c \
e-cal-backend-util.c \
@@ -41,7 +43,7 @@ libedata_cal_1_2_la_LIBADD = \
$(LIBICAL_LIBS) \
$(EVOLUTION_CALENDAR_LIBS)
-libedata_cal_1_2_la_LDFLAGS = \
+libedata_cal_1_2_la_LDFLAGS = \
-version-info $(LIBEDATACAL_CURRENT):$(LIBEDATACAL_REVISION):$(LIBEDATACAL_AGE) $(NO_UNDEFINED)
libedata_calincludedir = $(privincludedir)/libedata-cal
@@ -51,6 +53,7 @@ libedata_calinclude_HEADERS = \
e-cal-backend.h \
e-cal-backend-cache.h \
e-cal-backend-factory.h \
+ e-cal-backend-intervaltree.h \
e-cal-backend-sync.h \
e-cal-backend-util.h \
e-cal-backend-sexp.h \
@@ -64,6 +67,56 @@ libedata_calinclude_HEADERS = \
%-$(API_VERSION).pc: %.pc
cp $< $@
+test_e_sexp_SOURCES = e-cal-backend-sexp.c e-cal-backend-sexp.h
+test_e_sexp_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir)/calendar \
+ -I$(top_builddir)/calendar \
+ $(EVOLUTION_CALENDAR_CFLAGS) \
+ -DTESTER=1
+
+test_e_sexp_LDADD = libedata-cal-1.2.la $(E_DATA_SERVER_LIBS)
+
+test_intervaltree_SOURCES = test-intervaltree.c e-cal-backend-intervaltree.c
+
+test_intervaltree_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir)/calendar \
+ -I$(top_builddir)/calendar \
+ $(EVOLUTION_CALENDAR_CFLAGS)
+
+test_intervaltree_LDADD = \
+ $(top_builddir)/calendar/libecal/libecal-1.2.la \
+ $(top_builddir)/calendar/libedata-cal/libedata-cal-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(EVOLUTION_CALENDAR_LIBS)
+
+test_intervaltree_coverage_SOURCES = test-intervaltree.c e-cal-backend-intervaltree.c
+
+test_intervaltree_coverage_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir)/calendar \
+ -I$(top_builddir)/calendar \
+ $(EVOLUTION_CALENDAR_CFLAGS)
+
+test_intervaltree_coverage_LDADD = \
+ $(top_builddir)/calendar/libecal/libecal-1.2.la \
+ $(top_builddir)/calendar/libedata-cal/libedata-cal-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(EVOLUTION_CALENDAR_LIBS) \
+ -lgcov
+
+.PHONY: coverage
+coverage:
+ mkdir -p ./coverage
+ lcov --directory . --zerocounters
+ ./test-intervaltree-coverage
+ lcov --directory . --capture --output-file ./coverage/*.info
+ genhtml -o ./coverage --legend --num-spaces 2 ./coverage/*.info
+
+clean-local:
+ rm -f *.gcda *.gcno
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libedata-cal-$(API_VERSION).pc
diff --git a/calendar/libedata-cal/e-cal-backend-cache.c b/calendar/libedata-cal/e-cal-backend-cache.c
index dd242d4..bbaffb0 100644
--- a/calendar/libedata-cal/e-cal-backend-cache.c
+++ b/calendar/libedata-cal/e-cal-backend-cache.c
@@ -29,6 +29,7 @@
#include <libecal/e-cal-util.h>
#include <libedataserver/e-data-server-util.h>
#include "e-cal-backend-cache.h"
+#include "e-cal-backend-intervaltree.h"
#define E_CAL_BACKEND_CACHE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -36,6 +37,7 @@
struct _ECalBackendCachePrivate {
GHashTable *timezones;
+ EIntervalTree *intervaltree;
};
G_DEFINE_TYPE (ECalBackendCache, e_cal_backend_cache, E_TYPE_FILE_CACHE)
@@ -46,7 +48,10 @@ e_cal_backend_cache_finalize (GObject *object)
ECalBackendCachePrivate *priv;
priv = E_CAL_BACKEND_CACHE_GET_PRIVATE (object);
-
+ if (priv->intervaltree) {
+ e_intervaltree_destroy (priv->intervaltree);
+ priv->intervaltree = NULL;
+ }
g_hash_table_destroy (priv->timezones);
/* Chain up to parent's finalize() method. */
@@ -79,6 +84,8 @@ e_cal_backend_cache_init (ECalBackendCache *cache)
g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) timezones_value_destroy);
+
+ cache->priv->intervaltree = e_intervaltree_new ();
}
/**
@@ -174,12 +181,14 @@ e_cal_backend_cache_get_component (ECalBackendCache *cache, const gchar *uid, co
*/
gboolean
e_cal_backend_cache_put_component (ECalBackendCache *cache,
- ECalComponent *comp)
+ ECalComponent *comp,
+ time_t occurence_start,
+ time_t occurence_end)
{
gchar *real_key, *uid, *comp_str;
gchar *rid;
gboolean retval;
-
+ ECalBackendCachePrivate *priv = cache->priv;
g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
@@ -197,6 +206,8 @@ e_cal_backend_cache_put_component (ECalBackendCache *cache,
else
retval = e_file_cache_add_object (E_FILE_CACHE (cache), real_key, comp_str);
+ e_intervaltree_insert (priv->intervaltree, occurence_start, occurence_end, comp);
+
g_free (real_key);
g_free (comp_str);
g_free (rid);
@@ -238,6 +249,9 @@ e_cal_backend_cache_remove_component (ECalBackendCache *cache,
retval = e_file_cache_remove_object (E_FILE_CACHE (cache), real_key);
g_free (real_key);
+ if (retval)
+ retval = e_intervaltree_remove (priv->intervaltree, uid, rid);
+
return retval;
}
@@ -291,6 +305,47 @@ e_cal_backend_cache_get_components (ECalBackendCache *cache)
}
/**
+ * e_cal_backend_cache_get_components_occuring_in_range:
+ * @cache: An #ECalBackendCache object.
+ * @start:
+ * @end:
+ *
+ * Retrieves a list of components stored in the cache, that are occuring
+ * in time range [start, end].
+ *
+ * Return value: A list of the components. Each item in the list is
+ * an #ECalComponent, which should be freed when no longer needed.
+ */
+GList *
+e_cal_backend_cache_get_components_occuring_in_range (ECalBackendCache *cache, time_t start, time_t end)
+{
+ GList *l;
+ GList *list = NULL;
+ icalcomponent *icalcomp;
+ ECalBackendCachePrivate *priv;
+ /* return null if cache is not a valid Backend Cache. */
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+ priv = cache->priv;
+ if (!(l = e_intervaltree_search (priv->intervaltree, start, end)))
+ return NULL;
+
+ for ( ; l != NULL; l = g_list_next (l)) {
+ ECalComponent *comp = l->data;
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+ if (icalcomp) {
+ icalcomponent_kind kind;
+ kind = icalcomponent_isa (icalcomp);
+ if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT) {
+ list = g_list_prepend (list, comp);
+ }
+ }
+ }
+ return list;
+}
+
+
+
+/**
* e_cal_backend_cache_get_components_by_uid:
* @cache: An #ECalBackendCache object.
* @uid: ID of the component to retrieve.
diff --git a/calendar/libedata-cal/e-cal-backend-cache.h b/calendar/libedata-cal/e-cal-backend-cache.h
index d699fd1..ba7202d 100644
--- a/calendar/libedata-cal/e-cal-backend-cache.h
+++ b/calendar/libedata-cal/e-cal-backend-cache.h
@@ -69,7 +69,9 @@ ECalComponent * e_cal_backend_cache_get_component
const gchar *rid);
gboolean e_cal_backend_cache_put_component
(ECalBackendCache *cache,
- ECalComponent *comp);
+ ECalComponent *compi,
+ time_t occurence_start,
+ time_t occurence_end);
gboolean e_cal_backend_cache_remove_component
(ECalBackendCache *cache,
const gchar *uid,
@@ -79,6 +81,7 @@ GList * e_cal_backend_cache_get_components
GSList * e_cal_backend_cache_get_components_by_uid
(ECalBackendCache *cache,
const gchar *uid);
+GList *e_cal_backend_cache_get_components_occuring_in_range (ECalBackendCache *cache, time_t start, time_t end);
const icaltimezone *
e_cal_backend_cache_get_timezone(ECalBackendCache *cache,
const gchar *tzid);
diff --git a/calendar/libedata-cal/e-cal-backend-file-store.c b/calendar/libedata-cal/e-cal-backend-file-store.c
index f1ac17b..bdf8c89 100644
--- a/calendar/libedata-cal/e-cal-backend-file-store.c
+++ b/calendar/libedata-cal/e-cal-backend-file-store.c
@@ -23,6 +23,7 @@
#include "e-cal-backend-file-store.h"
#include "libebackend/e-file-cache.h"
#include <glib/gstdio.h>
+#include "libecal/e-cal-util.c"
#define CACHE_FILE_NAME "calendar.ics"
#define KEY_FILE_NAME "keys.xml"
@@ -117,11 +118,9 @@ put_component (ECalBackendFileStore *fstore, ECalComponent *comp)
g_warning ("The component does not have a valid uid \n");
return FALSE;
}
-
+
g_static_rw_lock_writer_lock (&priv->lock);
-
obj = g_hash_table_lookup (priv->comp_uid_hash, uid);
-
if (obj == NULL) {
obj = create_new_full_object ();
g_hash_table_insert (priv->comp_uid_hash, g_strdup (uid), obj);
@@ -132,14 +131,13 @@ put_component (ECalBackendFileStore *fstore, ECalComponent *comp)
g_object_unref (obj->comp);
obj->comp = comp;
- g_object_ref (comp);
} else {
gchar *rid = e_cal_component_get_recurid_as_string (comp);
- g_object_ref (comp);
g_hash_table_insert (obj->recurrences, rid, comp);
}
+ g_object_ref (comp);
g_static_rw_lock_writer_unlock (&priv->lock);
return TRUE;
@@ -156,7 +154,7 @@ remove_component (ECalBackendFileStore *fstore, const gchar *uid, const gchar *r
priv = E_CAL_BACKEND_FILE_STORE_GET_PRIVATE (fstore);
g_static_rw_lock_writer_lock (&priv->lock);
-
+
obj = g_hash_table_lookup (priv->comp_uid_hash, uid);
if (obj == NULL) {
ret_val = FALSE;
@@ -173,10 +171,12 @@ remove_component (ECalBackendFileStore *fstore, const gchar *uid, const gchar *r
if (remove_completely)
g_hash_table_remove (priv->comp_uid_hash, uid);
-
+
end:
g_static_rw_lock_writer_unlock (&priv->lock);
+
return ret_val;
+
}
static ECalComponent *
@@ -608,11 +608,36 @@ add_timezone (ECalBackendFileStore *fstore, icalcomponent *vtzcomp)
g_static_rw_lock_writer_unlock (&priv->lock);
}
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+ return (!strcmp (tzid, "UTC"))
+ ? icaltimezone_get_utc_timezone ()
+ : icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+/*static icaltimezone *
+get_zone (icalcomponent *icalcomp)
+{
+ icalproperty *prop;
+ icaltimezone *zone;
+ const gchar *tzid;
+ prop = icalcomponent_get_first_property (icalcomp, ICAL_TZID_PROPERTY);
+ if (!prop)
+ return NULL;
+ tzid = icalproperty_get_tzid (prop);
+ zone = icalcomponent_get_timezone (icalcomp, tzid);
+ return zone;
+} */
+
+
static void
-scan_vcalendar (ECalBackendFileStore *fstore, icalcomponent *top_icalcomp)
+scan_vcalendar (ECalBackendStore *store, icalcomponent *top_icalcomp)
{
+ ECalBackendFileStore *fstore = E_CAL_BACKEND_FILE_STORE (store);
ECalBackendFileStorePrivate *priv;
icalcompiter iter;
+ time_t time_start, time_end;
priv = E_CAL_BACKEND_FILE_STORE_GET_PRIVATE (fstore);
@@ -622,7 +647,6 @@ scan_vcalendar (ECalBackendFileStore *fstore, icalcomponent *top_icalcomp)
icalcomponent *icalcomp;
icalcomponent_kind kind;
ECalComponent *comp;
-
icalcomp = icalcompiter_deref (&iter);
kind = icalcomponent_isa (icalcomp);
@@ -644,8 +668,10 @@ scan_vcalendar (ECalBackendFileStore *fstore, icalcomponent *top_icalcomp)
g_object_unref (comp);
continue;
}
+ get_component_occur_times (comp, &time_start, &time_end,
+ resolve_tzid, NULL, NULL, kind);
- put_component (fstore, comp);
+ e_cal_backend_store_put_component (store, comp, time_start, time_end);
g_object_unref (comp);
}
@@ -664,7 +690,7 @@ e_cal_backend_file_store_load (ECalBackendStore *store)
return FALSE;
/* Parse keys */
- priv->keys_cache = e_file_cache_new (priv->key_file_name);
+ //priv->keys_cache = e_file_cache_new (priv->key_file_name);
/* Parse components */
icalcomp = e_cal_util_parse_ics_file (priv->cache_file_name);
@@ -676,8 +702,7 @@ e_cal_backend_file_store_load (ECalBackendStore *store)
return FALSE;
}
-
- scan_vcalendar (fstore, icalcomp);
+ scan_vcalendar (store, icalcomp);
icalcomponent_free (icalcomp);
return TRUE;
@@ -881,6 +906,7 @@ cal_backend_file_store_constructed (GObject *object)
path = e_cal_backend_store_get_path (E_CAL_BACKEND_STORE (object));
priv->cache_file_name = g_build_filename (path, CACHE_FILE_NAME, NULL);
priv->key_file_name = g_build_filename (path, KEY_FILE_NAME, NULL);
+ priv->keys_cache = e_file_cache_new (priv->key_file_name);
}
static void
diff --git a/calendar/libedata-cal/e-cal-backend-intervaltree.c b/calendar/libedata-cal/e-cal-backend-intervaltree.c
new file mode 100644
index 0000000..6e5f936
--- /dev/null
+++ b/calendar/libedata-cal/e-cal-backend-intervaltree.c
@@ -0,0 +1,765 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Stanislav Slusny <slusnys gmail com>
+ *
+ * Copyright 2008
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "e-cal-backend-intervaltree.h"
+
+#define d(x) x
+
+#define _TIME_MIN ((time_t) 0) /* Min valid time_t */
+#define _TIME_MAX ((time_t) INT_MAX) /* Max valid time_t */
+#define DIRECTION_GO_LEFT 0
+#define DIRECTION_GO_RIGHT 1
+
+G_DEFINE_TYPE (EIntervalTree, e_intervaltree, G_TYPE_OBJECT)
+
+#define E_INTERVALTREE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_INTERVALTREE, EIntervalTreePrivate))
+
+EIntervalNode*
+e_intervaltree_node_next (EIntervalTree *tree, EIntervalNode *x);
+
+struct _EIntervalNode
+{
+ /* start of the interval - the key of the node */
+ time_t start;
+ /* end of the interval */
+ time_t end;
+ /* maximum value of any interval stored in subtree rooted at node */
+ time_t max;
+ /* minimum value of any interval stored in subtree rooted at node */
+ time_t min;
+ /* color of the node (red or black) */
+ gboolean red;
+
+ ECalComponent *comp;
+
+ /* left child */
+ EIntervalNode* left;
+ /* right child */
+ EIntervalNode* right;
+ EIntervalNode* parent;
+};
+
+
+struct _EIntervalTreePrivate
+{
+ EIntervalNode *root;
+ EIntervalNode *nil;
+ GHashTable *id_node_hash;
+ GStaticRecMutex mutex;
+};
+
+static inline int
+get_direction (EIntervalNode *x, time_t z_start, time_t z_end)
+{
+ if (x->start == z_start)
+ return x->end > z_end;
+
+ if (x->start > z_start)
+ return DIRECTION_GO_LEFT;
+ else
+ return DIRECTION_GO_RIGHT;
+}
+
+static inline gchar *
+component_key(const gchar *uid, const gchar *rid)
+{
+ return g_strdup_printf("%s_%s", uid, rid);
+}
+
+/**
+ * compare_intervals:
+ *
+ * Compares two intervals.
+ *
+ * Returns: 0 if interval overlaps, -1 if first interval ends before
+ * the second starts, 1 otherwise.
+ *
+ **/
+static inline int
+compare_intervals (time_t x_start, time_t x_end, time_t y_start, time_t y_end)
+{
+ /* assumption: x_start <= x_end */
+ /* assumption: y_start <= y_end */
+
+ /* x is left of y */
+ if (x_end < y_start)
+ return -1;
+
+ /* x is right of y */
+ if (y_end < x_start)
+ return 1;
+
+ /* x and y overlap */
+ return 0;
+}
+
+/**
+ * left_rotate:
+ * @tree: interval tree
+ * @x: Node, where will be applied the operation
+ *
+ * Carry out left rotation on the node @x in tree @tree.
+ **/
+static void
+left_rotate (EIntervalTree *tree, EIntervalNode *x)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *y;
+ EIntervalNode *nil = priv->nil;
+
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (x != NULL);
+
+ y = x->right;
+ x->right = y->left;
+
+ if (y->left != nil)
+ y->left->parent = x;
+
+ y->parent = x->parent;
+
+ /* instead of checking if x->parent is the root as in the book, we */
+ /* count on the root sentinel to implicitly take care of this case */
+ if (x == x->parent->left)
+ x->parent->left = y;
+ else
+ x->parent->right = y;
+
+ y->left = x;
+ x->parent = y;
+
+ /* update max and min field */
+ x->max = MAX (x->left->max, MAX (x->end, x->right->max));
+ y->max = MAX (x->max, MAX (y->end, y->right->max));
+ x->min = MIN (x->left->min, x->start);
+ y->min = MIN (x->min, y->start);
+}
+
+/**
+ * right_rotate:
+ * @tree: interval tree
+ * @y: Node, where will be applied the operation
+ *
+ * Carry out right rotation on the node @y in tree @tree.
+ **/
+static void
+right_rotate (EIntervalTree *tree, EIntervalNode *y)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *x;
+ EIntervalNode *nil = priv->nil;
+
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (y != NULL);
+
+ x = y->left;
+ y->left = x->right;
+
+ if (nil != x->right)
+ x->right->parent = y;
+
+ x->parent = y->parent;
+
+ if (y == y->parent->left)
+ y->parent->left = x;
+ else
+ y->parent->right = x;
+
+ x->right = y;
+ y->parent = x;
+
+ /* update max and min field */
+ y->max = MAX (y->left->max, MAX (y->right->max, y->end));
+ x->max = MAX (x->left->max, MAX (y->max, x->end));
+ y->min = MIN (y->left->min, y->start);
+ x->min = MIN (x->left->min, x->start);
+}
+
+static void
+fixup_min_max_fields (EIntervalTree *tree, EIntervalNode *node)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ while (node != priv->root)
+ {
+ node->max = MAX (node->end, MAX (node->left->max, node->right->max));
+ node->min = MIN (node->start, node->left->min);
+
+ node = node->parent;
+ }
+}
+
+static void
+binary_tree_insert (EIntervalTree *tree, EIntervalNode *z)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *x;
+ EIntervalNode *y;
+ EIntervalNode *nil = priv->nil;
+
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (z != NULL);
+
+ z->left = z->right = nil;
+ y = priv->root;
+ x = priv->root->left;
+
+ while( x != nil)
+ {
+ y = x;
+
+ if (get_direction (x, z->start, z->end) == DIRECTION_GO_LEFT)
+ x = x->left;
+ else
+ x = x->right;
+ }
+
+ z->parent = y;
+
+ if ( (y == priv->root) || (get_direction (y, z->start, z->end) == DIRECTION_GO_LEFT))
+ y->left = z;
+ else
+ y->right = z;
+
+ /* update min and max fields */
+ y->min = MIN (y->left->min, y->start);
+ y->max = MAX (y->left->max, MAX (y->end, y->right->max));
+}
+
+/**
+ * e_intervaltree_insert:
+ * @tree: interval tree
+ * @key: the key to insert.
+ * @comp: Component
+ *
+ **/
+gboolean
+e_intervaltree_insert (EIntervalTree *tree, time_t start, time_t end, ECalComponent *comp)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *y;
+ EIntervalNode *x;
+ EIntervalNode *newNode;
+ const gchar *uid;
+ gchar *rid;
+ g_return_val_if_fail (tree != NULL, FALSE);
+ g_return_val_if_fail (comp != NULL, FALSE);
+ g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+ g_static_rec_mutex_lock (&priv->mutex);
+ x = g_new (EIntervalNode, 1);
+ x->min = x->start = start;
+ x->max = x->end = end;
+ x->comp = g_object_ref (comp);
+
+ binary_tree_insert (tree, x);
+ newNode = x;
+ x->red = TRUE;
+
+ fixup_min_max_fields (tree, x->parent);
+ while (x->parent->red)
+ { /* use sentinel instead of checking for root */
+ if (x->parent == x->parent->parent->left)
+ {
+ y = x->parent->parent->right;
+
+ if (y->red)
+ {
+ x->parent->red = FALSE;
+ y->red = FALSE;
+ x->parent->parent->red = TRUE;
+ x = x->parent->parent;
+ }
+ else
+ {
+ if (x == x->parent->right)
+ {
+ x = x ->parent;
+ left_rotate (tree, x);
+ }
+
+ x->parent->red = FALSE;
+ x->parent->parent->red = TRUE;
+ right_rotate (tree, x->parent->parent);
+ }
+ }
+ else
+ { /* case for x->parent == x->parent->parent->right */
+ y = x->parent->parent->left;
+
+ if (y->red)
+ {
+ x->parent->red = FALSE;
+ y->red = FALSE;
+ x->parent->parent->red = TRUE;
+ x = x->parent->parent;
+ }
+ else
+ {
+ if (x == x->parent->left)
+ {
+ x = x->parent;
+ right_rotate (tree, x);
+ }
+
+ x->parent->red = FALSE;
+ x->parent->parent->red = TRUE;
+ left_rotate (tree, x->parent->parent);
+ }
+ }
+ }
+
+ priv->root->left->red = FALSE;
+ e_cal_component_get_uid (comp, &uid);
+ rid = e_cal_component_get_recurid_as_string (comp);
+ g_hash_table_insert (priv->id_node_hash, component_key(uid, rid), newNode);
+ g_free (rid);
+
+ g_static_rec_mutex_unlock (&priv->mutex);
+
+ return TRUE;
+}
+
+EIntervalNode*
+e_intervaltree_node_next (EIntervalTree *tree, EIntervalNode *x)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *y;
+ EIntervalNode *nil = priv->nil;
+ EIntervalNode *root = priv->root;
+
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (x != NULL, NULL);
+ g_return_val_if_fail (x != priv->nil, NULL);
+
+ if (nil != (y = x->right))
+ {
+ /* find out minimum of right subtree of x (assignment to y is ok) */
+ while (y->left != nil)
+ y = y->left;
+
+ return y;
+ }
+
+ y = x->parent;
+
+ while (x == y->right)
+ {
+ x = y;
+ y = y->parent;
+ }
+
+ if (y == root)
+ return nil;
+
+ return y;
+}
+
+void e_intervaltree_destroy (EIntervalTree *tree)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *node;
+ GList *stack_start = NULL, *pos;
+
+ g_return_if_fail (tree != NULL);
+
+ stack_start = pos = g_list_insert (stack_start, priv->root->left, -1);
+
+ while (pos != NULL)
+ {
+ node = (EIntervalNode*) pos->data;
+
+ if (node != priv->nil)
+ {
+ pos = g_list_insert (pos, node->left, -1);
+ pos = g_list_insert (pos, node->right, -1);
+
+ g_object_unref (node->comp);
+ g_free (node);
+ }
+
+ pos = pos->next;
+ }
+
+ g_list_free (stack_start);
+ g_object_unref (tree);
+}
+
+static void
+e_intervaltree_fixup_deletion (EIntervalTree *tree, EIntervalNode *x)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *root = priv->root->left;
+ EIntervalNode *w;
+
+ while ( (!x->red) && (root != x))
+ {
+ if (x == x->parent->left)
+ {
+ w = x->parent->right;
+
+ if (w->red)
+ {
+ w->red = FALSE;
+ x->parent->red = TRUE;
+ left_rotate (tree, x->parent);
+ w = x->parent->right;
+ }
+
+ if ( (!w->right->red) && (!w->left->red) )
+ {
+ w->red = TRUE;
+ x = x->parent;
+ }
+ else
+ {
+ if (!w->right->red)
+ {
+ w->left->red = FALSE;
+ w->red = TRUE;
+ right_rotate (tree, w);
+ w = x->parent->right;
+ }
+
+ w->red = x->parent->red;
+ x->parent->red = FALSE;
+ w->right->red = FALSE;
+ left_rotate (tree, x->parent);
+ x = root; /* this is to exit while loop */
+ }
+ } else {
+ w = x->parent->left;
+
+ if (w->red)
+ {
+ w->red = FALSE;
+ x->parent->red = TRUE;
+ right_rotate (tree, x->parent);
+ w = x->parent->left;
+ }
+
+ if ( (!w->right->red) && (!w->left->red) )
+ {
+ w->red = TRUE;
+ x = x->parent;
+ }
+ else
+ {
+ if (!w->left->red)
+ {
+ w->right->red = FALSE;
+ w->red = TRUE;
+ left_rotate (tree, w);
+ w = x->parent->left;
+ }
+
+ w->red = x->parent->red;
+ x->parent->red = FALSE;
+ w->left->red = FALSE;
+ right_rotate (tree, x->parent);
+ x = root; /* this is to exit while loop */
+ }
+ }
+ }
+
+ x->red = 0;
+}
+
+/**
+ * e_intervaltree_search:
+ * @tree: interval tree
+ * @start: start of the interval
+ * @end: end of the interval
+ *
+ * Returns list of nodes that overlaps given interval
+ * or NULL.
+ **/
+GList*
+e_intervaltree_search (EIntervalTree *tree, time_t start, time_t end)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *node;
+ GList *list = NULL;
+ GList *stack_start = NULL, *pos;
+
+ g_return_val_if_fail (tree != NULL, NULL);
+
+ g_static_rec_mutex_lock (&priv->mutex);
+
+ stack_start = pos = g_list_insert (stack_start, priv->root->left, -1);
+
+ while (pos != NULL)
+ {
+ node = (EIntervalNode*) pos->data;
+
+ if (node != priv->nil)
+ {
+ if (compare_intervals (node->start, node->end, start, end) == 0)
+ {
+ list = g_list_insert (list, node->comp, -1);
+ g_object_ref (node->comp);
+ }
+
+ if (compare_intervals (node->left->min, node->left->max, start, end) == 0)
+ pos = g_list_insert (pos, node->left, -1);
+
+ if (compare_intervals (node->right->min, node->right->max, start, end) == 0)
+ pos = g_list_insert (pos, node->right, -1);
+ }
+
+ pos = pos->next;
+ }
+
+ g_list_free (stack_start);
+
+ g_static_rec_mutex_unlock (&priv->mutex);
+
+ return list;
+}
+
+#ifdef E_INTERVALTREE_DEBUG
+static void
+e_intervaltree_node_dump (EIntervalTree *tree, EIntervalNode *node, gint indent)
+{
+ /*
+ char start_time[32] = {0}, end_time[32] = {0};
+ struct tm tm_start_time, tm_end_time;
+
+ localtime_r (&node->start, &tm_start_time);
+ localtime_r (&node->end, &tm_end_time);
+ strftime(start_time, sizeof (start_time), "%Y-%m-%d T%H:%M:%S", &tm_start_time);
+ strftime(end_time, sizeof (end_time), "%Y-%m-%d T%H:%M:%S", &tm_end_time);
+ g_print ("%*s[%s - %s]\n", indent, "", start_time, end_time);
+ */
+ EIntervalTreePrivate *priv = tree->priv;
+ if (node != priv->nil)
+ g_print ("%*s[%ld - %ld] [%ld - %ld] red %d\n", indent, "", node->start,
+ node->end, node->min, node->max, node->red);
+ else
+ {
+ g_print ("%*s[ - ]\n", indent, "");
+ return;
+ }
+
+ e_intervaltree_node_dump (tree, node->left, indent + 2);
+ e_intervaltree_node_dump (tree, node->right, indent + 2);
+}
+
+
+void
+e_intervaltree_dump (EIntervalTree *tree)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ if (priv->root)
+ e_intervaltree_node_dump (tree, priv->root, 0);
+}
+#endif
+
+
+/**
+ * FIXME
+ **/
+static EIntervalNode*
+e_intervaltree_search_component (EIntervalTree *tree,
+ const char *searched_uid,
+ const char *searched_rid)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (searched_uid != NULL, NULL);
+
+ if (!searched_uid)
+ {
+ g_warning ("Searching the interval tree, the component "
+ " does not have a valid UID skipping it\n");
+
+ return NULL;
+ }
+
+ return g_hash_table_lookup (priv->id_node_hash, component_key(searched_uid, searched_rid));
+}
+
+gboolean
+e_intervaltree_remove (EIntervalTree *tree,
+ const char *uid,
+ const char *rid)
+{
+ EIntervalTreePrivate *priv = tree->priv;
+ EIntervalNode *y;
+ EIntervalNode *x;
+ EIntervalNode *z;
+ EIntervalNode *nil = priv->nil;
+ EIntervalNode *root = priv->root;
+
+ g_return_val_if_fail (tree != NULL, FALSE);
+
+ g_static_rec_mutex_lock (&priv->mutex);
+
+ z = e_intervaltree_search_component (tree, uid, rid);
+
+ if (!z || z == nil)
+ {
+ g_message (G_STRLOC ": Cannot remove node - could not find node in tree\n");
+ return FALSE;
+ }
+
+ y = ((z->left == nil) || (z->right == nil)) ? z : e_intervaltree_node_next (tree, z);
+ x = (y->left == nil) ? y->right : y->left;
+ /* y is to be spliced out. x is it's only child */
+
+ x->parent = y->parent;
+
+ if (root == x->parent)
+ root->left = x;
+ else
+ {
+ if (y == y->parent->left)
+ y->parent->left = x;
+ else
+ y->parent->right = x;
+ }
+
+ if (y != z)
+ {
+ /* y (the succesor of z) is the node to be spliced out */
+ g_return_val_if_fail (y != priv->nil, FALSE);
+
+ y->max = _TIME_MIN;
+ y->min = _TIME_MAX;
+ y->left = z->left;
+ y->right = z->right;
+ y->parent = z->parent;
+ z->left->parent = z->right->parent = y;
+
+ if (z == z->parent->left)
+ z->parent->left = y;
+ else
+ z->parent->right = y;
+
+ fixup_min_max_fields (tree, x->parent);
+
+ if (!(y->red))
+ {
+ y->red = z->red;
+ e_intervaltree_fixup_deletion (tree, x);
+ }
+ else
+ y->red = z->red;
+ }
+ else
+ {
+ /* z is the node to be spliced out */
+
+ fixup_min_max_fields (tree, x->parent);
+
+ if (!(y->red))
+ e_intervaltree_fixup_deletion (tree, x);
+ }
+
+ g_hash_table_remove (priv->id_node_hash, component_key(uid, rid));
+
+ g_object_unref (z->comp);
+ g_free (z);
+ g_static_rec_mutex_unlock (&priv->mutex);
+
+ return TRUE;
+}
+
+static void
+e_intervaltree_finalize (GObject *object)
+{
+ EIntervalTreePrivate *priv = E_INTERVALTREE_GET_PRIVATE (object);
+ if (priv->root) {
+ g_free (priv->root);
+ priv->root = NULL;
+ }
+ if (priv->nil) {
+ g_free (priv->nil);
+ priv->nil = NULL;
+ }
+ if (priv->id_node_hash) {
+ g_hash_table_destroy (priv->id_node_hash);
+ priv->id_node_hash = NULL;
+ }
+ g_static_rec_mutex_free (&priv->mutex);
+ G_OBJECT_CLASS (e_intervaltree_parent_class)->finalize (object);
+
+}
+
+static void
+e_intervaltree_class_init (EIntervalTreeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ g_type_class_add_private (klass, sizeof (EIntervalTreePrivate));
+ object_class->finalize = e_intervaltree_finalize;
+}
+
+
+static void
+e_intervaltree_init (EIntervalTree *tree)
+{
+ EIntervalTreePrivate *priv;
+ EIntervalNode *root, *nil;
+ priv = g_new0 (EIntervalTreePrivate, 1);
+ tree->priv = priv;
+
+ priv->nil = nil = g_new (EIntervalNode, 1);
+ nil->parent = nil->left = nil->right = nil;
+ nil->red = FALSE;
+ nil->start = nil->end = nil->max = _TIME_MIN;
+ nil->min = _TIME_MAX;
+
+ priv->root = root = g_new (EIntervalNode, 1);
+ root->parent = root->left = root->right = nil;
+ root->red = FALSE;
+ root->start = _TIME_MAX;
+ root->end = 0;
+ root->max = _TIME_MAX;
+ root->min = _TIME_MIN;
+
+ g_static_rec_mutex_init (&priv->mutex);
+ priv->id_node_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+/**
+ * e_intervaltree_new:
+ *
+ * Creates a new #EIntervalTree.
+ *
+ * Returns: The newly-created #EIntervalTree.
+ **/
+EIntervalTree*
+e_intervaltree_new (void)
+{
+ EIntervalTree *tree;
+ tree = g_object_new (E_TYPE_INTERVALTREE, NULL);
+ return tree;
+}
diff --git a/calendar/libedata-cal/e-cal-backend-intervaltree.h b/calendar/libedata-cal/e-cal-backend-intervaltree.h
new file mode 100644
index 0000000..299b7a7
--- /dev/null
+++ b/calendar/libedata-cal/e-cal-backend-intervaltree.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Stanislav Slusny <slusnys gmail com>
+ *
+ * Copyright 2008
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __E_INTERVALTREE_H__
+#define __E_INTERVALTREE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <glib.h>
+//#include <glib/glist.h>
+// EDS opt
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <libecal/e-cal-component.h>
+#include <libecal/e-cal-recur.h>
+
+#define E_INTERVALTREE_DEBUG 1
+
+#define E_TYPE_INTERVALTREE (e_intervaltree_get_type ())
+#define E_INTERVALTREE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_INTERVALTREE, EIntervalTree))
+#define E_INTERVALTREE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), E_TYPE_INTERVALTREE, EIntervalTreeClass))
+#define E_IS_INTERVALTREE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_INTERVALTREE))
+#define E_IS_INTERVALTREE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_INTERVALTREE))
+#define E_INTERVALTREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_TYPE_INTERVALTREE, EIntervalTreeClass))
+/* #undef E_INTERVALTREE_DEBUG */
+/*
+ * Implementation of the interval node as described in Introduction to Algorithms
+ * book by Cormen et al, chapter 14.3.
+ *
+ * Basically, the interval tree is the red-black tree, the node key is the start
+ * of the interval.
+ */
+
+typedef struct _EIntervalTree EIntervalTree;
+typedef struct _EIntervalTreeClass EIntervalTreeClass;
+typedef struct _EIntervalTreePrivate EIntervalTreePrivate;
+typedef struct _EIntervalNode EIntervalNode;
+
+struct _EIntervalTree
+{
+ GObject parent;
+ EIntervalTreePrivate *priv;
+};
+
+struct _EIntervalTreeClass
+{
+ GObjectClass parent;
+};
+
+GType e_intervaltree_get_type (void);
+
+EIntervalTree* e_intervaltree_new(void);
+
+gboolean e_intervaltree_insert (EIntervalTree *tree, time_t start, time_t end, ECalComponent *comp);
+
+gboolean e_intervaltree_remove (EIntervalTree *tree, const char *uid, const char *rid);
+
+gpointer e_intervaltree_lookup (EIntervalTree *tree, time_t start, time_t end);
+
+void e_intervaltree_destroy (EIntervalTree *tree);
+
+GList*
+e_intervaltree_search (EIntervalTree *tree, time_t start, time_t end);
+#ifdef E_INTERVALTREE_DEBUG
+void e_intervaltree_dump (EIntervalTree *tree);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __E_INTERVALTREE_H__ */
diff --git a/calendar/libedata-cal/e-cal-backend-sexp.c b/calendar/libedata-cal/e-cal-backend-sexp.c
index f6fa9a6..7c35881 100644
--- a/calendar/libedata-cal/e-cal-backend-sexp.c
+++ b/calendar/libedata-cal/e-cal-backend-sexp.c
@@ -24,7 +24,7 @@
#include <string.h>
#include <glib/gi18n-lib.h>
-#include "libedataserver/e-data-server-util.h"
+#include <libedataserver/e-data-server-util.h>
#include <libecal/e-cal-time-util.h>
#include "e-cal-backend-sexp.h"
@@ -1273,6 +1273,25 @@ static struct {
};
/**
+ * e_cal_backend_sexp_evaluate_occur_times:
+ * @sexp: An #ESExp object.
+ * @start: Start of the time window will be stored here.
+ * @end: End of the time window will be stored here.
+ *
+ * Determines biggest time window given by expressions "occur-in-range" in sexp.
+ *
+ */
+gboolean
+e_cal_backend_sexp_evaluate_occur_times(ECalBackendSExp *sexp, time_t *start, time_t *end)
+{
+ g_return_val_if_fail (sexp != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ return e_sexp_evaluate_occur_times (sexp->priv->search_sexp, start, end);
+}
+
+/**
* e_cal_backend_sexp_match_comp:
* @sexp: An #ESExp object.
* @comp: Component to match against the expression.
@@ -1446,3 +1465,50 @@ e_cal_backend_sexp_init (ECalBackendSExp *sexp)
sexp->priv = priv;
priv->search_context = g_new (SearchContext, 1);
}
+#ifdef TESTER
+static void
+test_query (const char* query)
+{
+ ECalBackendSExp *sexp = e_cal_backend_sexp_new (query);
+ time_t start, end;
+
+ gboolean generator = e_cal_backend_sexp_evaluate_occur_times(sexp, &start, &end);
+
+ if (generator) {
+ printf ("%s: %ld - %ld\n", query, start, end);
+ } else {
+ printf ("%s: no time prunning possible\n", query);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ g_type_init();
+
+ /* e_sexp_add_variable(f, 0, "test", NULL); */
+
+ if (argc < 2 || !argv[1])
+ {
+ test_query ("(occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))");
+ test_query ("(due-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))");
+ test_query ("(has-alarms-in-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))");
+ test_query ("(completed-before? (make-time \"20080727T220000Z\") )");
+
+ test_query ("(and (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\")) #t)");
+ test_query ("(or (occur-in-time-range? (make-time \"20080727T220000Z\")(make-time \"20080907T220000Z\")) #t)");
+
+ test_query ("(and (contains? \"substring\") (has-categories? \"blah\"))");
+ test_query ("(or (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\")) (contains? \"substring\"))");
+
+ test_query ("(and (and (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))"
+ " (or (contains? \"substring\") (has-categories? \"blah\"))) (has-alarms?))");
+
+ test_query ("(or (and (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))"
+ " (or (contains? \"substring\") (has-categories? \"blah\"))) (has-alarms?))");
+ }
+ else
+ test_query(argv[1]);
+
+ return 0;
+}
+#endif
diff --git a/calendar/libedata-cal/e-cal-backend-sexp.h b/calendar/libedata-cal/e-cal-backend-sexp.h
index e25e7b6..71ae30e 100644
--- a/calendar/libedata-cal/e-cal-backend-sexp.h
+++ b/calendar/libedata-cal/e-cal-backend-sexp.h
@@ -28,7 +28,7 @@
#include <glib-object.h>
#include <libecal/e-cal-component.h>
#include <libedata-cal/e-cal-backend.h>
-#include "libedataserver/e-sexp.h"
+#include <libedataserver/e-sexp.h>
G_BEGIN_DECLS
@@ -70,7 +70,9 @@ ESExpResult *e_cal_backend_sexp_func_make_time (ESExp *esexp, gint argc, ES
ESExpResult *e_cal_backend_sexp_func_time_add_day (ESExp *esexp, gint argc, ESExpResult **argv, gpointer data);
ESExpResult *e_cal_backend_sexp_func_time_day_begin (ESExp *esexp, gint argc, ESExpResult **argv, gpointer data);
ESExpResult *e_cal_backend_sexp_func_time_day_end (ESExp *esexp, gint argc, ESExpResult **argv, gpointer data);
-
+gboolean e_cal_backend_sexp_evaluate_occur_times (ECalBackendSExp *sexp, time_t *start, time_t *end);
+gboolean e_cal_backend_sexp_is_a_time_function(const char *function_name);
+gboolean e_cal_backend_sexp_is_a_generator_function(const char *function_name);
G_END_DECLS
#endif /* __E_CAL_BACKEND_SEXP_H__ */
diff --git a/calendar/libedata-cal/e-cal-backend-store.c b/calendar/libedata-cal/e-cal-backend-store.c
index 443d6aa..3089601 100644
--- a/calendar/libedata-cal/e-cal-backend-store.c
+++ b/calendar/libedata-cal/e-cal-backend-store.c
@@ -20,7 +20,7 @@
*/
#include "e-cal-backend-store.h"
-
+#include "e-cal-backend-intervaltree.h"
#include <libedataserver/e-data-server-util.h>
#define E_CAL_BACKEND_STORE_GET_PRIVATE(obj) \
@@ -29,6 +29,7 @@
struct _ECalBackendStorePrivate {
gchar *path;
+ EIntervalTree *intervaltree;
gboolean loaded;
};
@@ -91,6 +92,10 @@ cal_backend_store_finalize (GObject *object)
priv = E_CAL_BACKEND_STORE_GET_PRIVATE (object);
g_free (priv->path);
+ if (priv->intervaltree) {
+ e_intervaltree_destroy (priv->intervaltree);
+ priv->intervaltree = NULL;
+ }
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_cal_backend_store_parent_class)->finalize (object);
@@ -123,7 +128,10 @@ e_cal_backend_store_class_init (ECalBackendStoreClass *class)
static void
e_cal_backend_store_init (ECalBackendStore *store)
{
- store->priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+ ECalBackendStorePrivate *priv;
+ priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+ store->priv = priv;
+ priv->intervaltree = e_intervaltree_new ();
}
/**
@@ -169,8 +177,14 @@ e_cal_backend_store_load (ECalBackendStore *store)
gboolean
e_cal_backend_store_remove (ECalBackendStore *store)
{
+ ECalBackendStorePrivate *priv;
g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
+ priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+ /* remove interval tree */
+ e_intervaltree_destroy (priv->intervaltree);
+ priv->intervaltree = NULL;
+
return (E_CAL_BACKEND_STORE_GET_CLASS (store))->remove (store);
}
@@ -182,8 +196,16 @@ e_cal_backend_store_remove (ECalBackendStore *store)
gboolean
e_cal_backend_store_clean (ECalBackendStore *store)
{
+ ECalBackendStorePrivate *priv;
g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
+ priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+
+ if (priv->intervaltree) {
+ e_intervaltree_destroy (priv->intervaltree);
+ priv->intervaltree = e_intervaltree_new ();
+ }
+
return (E_CAL_BACKEND_STORE_GET_CLASS (store))->clean (store);
}
@@ -221,13 +243,20 @@ e_cal_backend_store_has_component (ECalBackendStore *store, const gchar *uid, co
* Since: 2.28
**/
gboolean
-e_cal_backend_store_put_component (ECalBackendStore *store, ECalComponent *comp)
+e_cal_backend_store_put_component (ECalBackendStore *store, ECalComponent *comp, time_t occurence_start, time_t occurence_end)
{
+ ECalBackendStorePrivate *priv;
g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
- return (E_CAL_BACKEND_STORE_GET_CLASS (store))->put_component (store, comp);
-}
+ priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+
+ if ((E_CAL_BACKEND_STORE_GET_CLASS (store))->put_component (store, comp)) {
+ e_intervaltree_insert (priv->intervaltree, occurence_start, occurence_end, comp);
+ return TRUE;
+ }
+ return FALSE;
+}
/**
* e_cal_backend_store_remove_component:
@@ -237,10 +266,17 @@ e_cal_backend_store_put_component (ECalBackendStore *store, ECalComponent *comp)
gboolean
e_cal_backend_store_remove_component (ECalBackendStore *store, const gchar *uid, const gchar *rid)
{
+ ECalBackendStorePrivate *priv;
g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
+
+ priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
- return (E_CAL_BACKEND_STORE_GET_CLASS (store))->remove_component (store, uid, rid);
+ if ((E_CAL_BACKEND_STORE_GET_CLASS (store))->remove_component (store, uid, rid)) {
+ e_intervaltree_remove (priv->intervaltree, uid, rid);
+ return TRUE;
+ }
+ return FALSE;
}
/**
@@ -335,11 +371,55 @@ GSList *
e_cal_backend_store_get_components (ECalBackendStore *store)
{
g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), NULL);
-
return (E_CAL_BACKEND_STORE_GET_CLASS (store))->get_components (store);
}
/**
+ * e_cal_backend_store_get_components_occuring_in_range:
+ * @store: An #ECalBackendStore object.
+ * @start:
+ * @end:
+ *
+ * Retrieves a list of components stored in the store, that are occuring
+ * in time range [start, end].
+ *
+ * Return value: A list of the components. Each item in the list is
+ * an #ECalComponent, which should be freed when no longer needed.
+ */
+GSList *
+e_cal_backend_store_get_components_occuring_in_range (ECalBackendStore *store, time_t start, time_t end)
+{
+ ECalBackendStorePrivate *priv;
+ GList *l;
+ GSList *list = NULL;
+ icalcomponent *icalcomp;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), NULL);
+
+ priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+
+ if (!(l = e_intervaltree_search (priv->intervaltree, start, end)))
+ return NULL;
+
+ for ( ; l != NULL; l = g_list_next (l)) {
+ ECalComponent *comp = l->data;
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+ if (icalcomp) {
+ icalcomponent_kind kind;
+
+ kind = icalcomponent_isa (icalcomp);
+ if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT) {
+ list = g_slist_prepend (list, comp);
+ }
+ }
+ }
+
+ return list;
+}
+
+
+/**
* e_cal_backend_store_get_component_ids:
*
* Since: 2.28
diff --git a/calendar/libedata-cal/e-cal-backend-store.h b/calendar/libedata-cal/e-cal-backend-store.h
index 9eeb230..f28b733 100644
--- a/calendar/libedata-cal/e-cal-backend-store.h
+++ b/calendar/libedata-cal/e-cal-backend-store.h
@@ -82,6 +82,7 @@ struct _ECalBackendStoreClass {
GSList * (*get_components_by_uid)(ECalBackendStore *store,
const gchar *uid);
GSList * (*get_components) (ECalBackendStore *store);
+
GSList * (*get_component_ids) (ECalBackendStore *store);
const icaltimezone *
(*get_timezone) (ECalBackendStore *store,
@@ -115,7 +116,9 @@ ECalComponent * e_cal_backend_store_get_component
const gchar *rid);
gboolean e_cal_backend_store_put_component
(ECalBackendStore *store,
- ECalComponent *comp);
+ ECalComponent *comp,
+ time_t occurence_start,
+ time_t occurence_end);
gboolean e_cal_backend_store_remove_component
(ECalBackendStore *store,
const gchar *uid,
@@ -143,6 +146,10 @@ GSList * e_cal_backend_store_get_components_by_uid
const gchar *uid);
GSList * e_cal_backend_store_get_components
(ECalBackendStore *store);
+GSList * e_cal_backend_store_get_components_occuring_in_range
+ (ECalBackendStore *store,
+ time_t start,
+ time_t end);
GSList * e_cal_backend_store_get_component_ids
(ECalBackendStore *store);
const gchar * e_cal_backend_store_get_key_value
diff --git a/calendar/libedata-cal/e-data-cal-view.c b/calendar/libedata-cal/e-data-cal-view.c
index bc63d6b..fb61d5a 100644
--- a/calendar/libedata-cal/e-data-cal-view.c
+++ b/calendar/libedata-cal/e-data-cal-view.c
@@ -29,7 +29,7 @@
#include <glib.h>
#include <glib-object.h>
-
+#include <libedataserver/e-debug-log.h>
#include "e-cal-backend-sexp.h"
#include "e-data-cal-view.h"
#include "e-gdbus-egdbuscalview.h"
@@ -305,6 +305,7 @@ impl_DataCalView_start (EGdbusCalView *object, GDBusMethodInvocation *invocation
if (!priv->started) {
priv->started = TRUE;
+ e_debug_log(FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "---;%p;QUERY-START;%s;%s", query, e_data_cal_view_get_text (query), G_OBJECT_TYPE_NAME(priv->backend));
e_cal_backend_start_query (priv->backend, query);
}
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index ec1fadb..c8246f6 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -31,7 +31,7 @@
#include <unistd.h>
#include <glib-object.h>
-
+#include <libedataserver/e-debug-log.h>
#include "e-data-cal.h"
#include "e-data-cal-enumtypes.h"
#include "e-gdbus-egdbuscal.h"
@@ -479,12 +479,16 @@ impl_Cal_getQuery (EGdbusCal *object, GDBusMethodInvocation *invocation, const g
}
query = e_data_cal_view_new (cal->priv->backend, obj_sexp);
+ e_debug_log (FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "%p;%p;NEW;%s;%s", cal, query, sexp, G_OBJECT_TYPE_NAME (cal->priv->backend));
if (!query) {
g_object_unref (obj_sexp);
e_data_cal_notify_query (cal, invocation, EDC_ERROR (OtherError), NULL);
return TRUE;
}
+ /* log query to evaluate cache performance */
+ e_debug_log (FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "%p;%p;REUSED;%s;%s", cal, query, sexp, G_OBJECT_TYPE_NAME (cal->priv->backend));
+
path = construct_calview_path ();
e_data_cal_view_register_gdbus_object (query, g_dbus_method_invocation_get_connection (invocation), path, &error);
diff --git a/calendar/libedata-cal/test-intervaltree.c b/calendar/libedata-cal/test-intervaltree.c
new file mode 100644
index 0000000..8db8275
--- /dev/null
+++ b/calendar/libedata-cal/test-intervaltree.c
@@ -0,0 +1,431 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Stanislav Slusny <slusnys gmail com>
+ *
+ * Copyright 2008
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <glib.h>
+#include <string.h>
+#include <libecal/e-cal-recur.h>
+#include <libecal/e-cal-component.h>
+#include <libical/icalcomponent.h>
+#include "e-cal-backend-intervaltree.h"
+
+#define NUM_INTERVALS_CLOSED 100
+#define NUM_INTERVALS_OPEN 100
+#define NUM_SEARCHES 500
+#define _TIME_MIN ((time_t) 0) /* Min valid time_t */
+#define _TIME_MAX ((time_t) INT_MAX) /* Max valid time_t */
+
+GRand *myrand = NULL;
+const double pbality_delete = 0.3;
+
+struct _EInterval
+{
+ int start;
+ int end;
+ ECalComponent* comp;
+};
+
+typedef struct _EInterval EInterval;
+
+GList *list = NULL;
+
+static inline int
+compare_intervals (time_t x_start, time_t x_end, time_t y_start, time_t y_end)
+{
+ /* assumption: x_start <= x_end */
+ /* assumption: y_start <= y_end */
+
+ /* x is left of y */
+ if (x_end < y_start)
+ return -1;
+
+ /* x is right of y */
+ if (y_end < x_start)
+ return 1;
+
+ /* x and y overlap */
+ return 0;
+}
+
+static GList*
+search_in_list (GList* l, time_t start, time_t end)
+{
+ GList* result = NULL;
+ EInterval *i, *i_new;
+
+ while (l)
+ {
+ i = (EInterval*) l->data;
+
+ if (compare_intervals (start, end, i->start, i->end) == 0)
+ {
+ i_new = g_new (EInterval, 1);
+
+ i_new->start = i->start;
+ i_new->end = i->end;
+ i_new->comp = i->comp;
+ g_object_ref (i->comp);
+
+ result = g_list_insert (result, i_new, -1);
+ }
+
+ l = l->next;
+ }
+
+ return result;
+}
+
+/*
+ * list1 .... from list
+ * list2 ... from interval tree
+ */
+static gboolean
+compare_interval_lists (GList *list1, GList *list2)
+{
+ gboolean found;
+ GList *l2;
+ EInterval *i1;
+ ECalComponent *c2, *c1;
+
+ found = TRUE;
+
+ while (list1 != NULL && found)
+ {
+ found = FALSE;
+
+ l2 = list2;
+
+ i1 = (EInterval*) list1->data;
+ c1 = i1->comp;
+
+
+ while (!found && l2 != NULL)
+ {
+ c2 = (ECalComponent*) l2->data;
+
+ found = (c1 == c2);
+
+ l2 = l2->next;
+ }
+
+ if (found)
+ list1 = list1->next;
+ }
+
+ if (!found)
+ {
+ i1 = (EInterval*) list1->data;
+
+ g_message (G_STRLOC ": Not found %d - %d\n", i1->start, i1->end);
+ }
+
+ return found;
+}
+
+static ECalComponent*
+create_test_component (time_t start, time_t end)
+{
+ ECalComponent *comp = e_cal_component_new ();
+ ECalComponentText *summary;
+ struct icaltimetype current;
+ e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
+
+ /*
+ ECalComponentDateTime dtstart, dtend;
+ struct icaltimetype time_start, time_end;
+
+ time_start = icaltime_from_timet (start, 0);
+ dtstart.value = icaltime_from_timet (start, 0);
+ dtstart.zone = icaltimezone_get_utc_timezone ();
+
+ dtend.value = icaltime_from_timet (end, 0);
+ dtend.value = icaltimezone_get_utc_timezone ();
+ e_cal_component_set_dtstart (comp, &dtstart);
+ e_cal_component_set_dtend (comp, &dtend);
+ */
+
+ summary = g_new (ECalComponentText, 1);
+
+ summary->value = g_strdup_printf ("%ld - %ld", start, end);
+ summary->altrep = NULL;
+
+ e_cal_component_set_summary (comp, summary);
+
+ current = icaltime_from_timet (time (NULL), 0);
+ e_cal_component_set_created (comp, ¤t);
+ e_cal_component_set_last_modified (comp, ¤t);
+
+ return comp;
+}
+
+static void
+unref_comp (gpointer data, gpointer user_data)
+{
+ EInterval *interval = (EInterval*) data;
+ g_object_unref (interval->comp);
+ g_free(data);
+}
+
+static void
+print_nodes_list (GList *l1)
+{
+ while (l1)
+ {
+ ECalComponent *comp = l1->data;
+ ECalComponentText summary;
+
+ e_cal_component_get_summary (comp, &summary);
+ g_print ("%s\n", summary.value);
+ l1 = l1->next;
+ }
+}
+
+static void
+print_list (GList *l2)
+{
+ while (l2)
+ {
+ EInterval* i = l2->data;
+
+ g_print ("%d - %d\n", i->start, i->end);
+ l2 = l2->next;
+ }
+}
+
+static void
+random_test()
+{
+ /*
+ * outline:
+ * 1. create new tree and empty list of intervals
+ * 2. insert some intervals into tree and list
+ * 3. do various searches, compare results of both structures
+ * 4. delete some intervals
+ * 5. do various searches, compare results of both structures
+ * 6. free memory
+ */
+ int i, start, end;
+ EInterval *interval = NULL;
+ EIntervalTree *tree;
+ GList *l1, *l2, *next;
+ int num_deleted = 0;
+
+ tree = e_intervaltree_new ();
+
+ for (i = 0; i < NUM_INTERVALS_CLOSED; i++)
+ {
+ ECalComponent *comp;
+ start = g_rand_int_range (myrand, 0, 1000);
+ end = g_rand_int_range (myrand, start, 2000);
+ comp = create_test_component (start, end);
+ if (!comp)
+ {
+ g_message (G_STRLOC ": error");
+ exit (-1);
+ }
+
+ interval = g_new (EInterval, 1);
+ interval->start = start;
+ interval->end = end;
+ interval->comp = comp;
+ g_object_ref (comp);
+
+ list = g_list_insert (list, interval, -1);
+
+ e_intervaltree_insert (tree, start, end, comp);
+ }
+
+ /* insert open ended intervals */
+ for (i = 0; i < NUM_INTERVALS_OPEN; i++)
+ {
+ ECalComponent *comp;
+ start = g_rand_int_range (myrand, 0, 1000);
+ comp = create_test_component (start, end);
+ if (!comp)
+ {
+ g_message (G_STRLOC ": error");
+ exit (-1);
+ }
+
+ interval = g_new (EInterval, 1);
+ interval->start = start;
+ interval->end = _TIME_MAX;
+ interval->comp = comp;
+ g_object_ref (comp);
+ list = g_list_insert (list, interval, -1);
+
+ e_intervaltree_insert (tree, start, interval->end, comp);
+ /* g_print ("%d - %d\n", start, interval->end); */
+ }
+
+ g_print ("Number of intervals inserted: %d\n", NUM_INTERVALS_CLOSED + NUM_INTERVALS_OPEN);
+
+ for (i = 0; i < NUM_SEARCHES; i++)
+ {
+ start = g_rand_int_range (myrand, 0, 1000);
+ end = g_rand_int_range (myrand, 2000, _TIME_MAX);
+
+ /*
+ g_print ("Search for : %d - %d\n", start, end);
+ */
+
+ l1 = e_intervaltree_search (tree, start, end);
+
+
+ l2 = search_in_list (list, start, end);
+
+ if (!compare_interval_lists (l2, l1))
+ {
+ e_intervaltree_dump (tree);
+ g_message (G_STRLOC "Error");
+ exit(-1);
+ }
+
+ /* g_print ("OK\n"); */
+ g_list_foreach(l1, (GFunc)g_object_unref, NULL);
+ g_list_foreach(l2, (GFunc)unref_comp, NULL);
+ g_list_free (l1);
+ g_list_free (l2);
+ }
+
+ /* open-ended intervals */
+ for (i = 0; i < 20; i++)
+ {
+ start = g_rand_int_range (myrand, 0, 1000);
+ end = _TIME_MAX;
+
+ /*
+ g_print ("Search for : %d - %d\n", start, end);
+ */
+
+ l1 = e_intervaltree_search (tree, start, end);
+
+
+ l2 = search_in_list (list, start, end);
+
+ if (!compare_interval_lists (l2, l1))
+ {
+ e_intervaltree_dump (tree);
+ g_message (G_STRLOC "Error");
+ exit(-1);
+ }
+
+ /* g_print ("OK\n"); */
+ g_list_foreach(l1, (GFunc)g_object_unref, NULL);
+ g_list_foreach(l2, (GFunc)unref_comp, NULL);
+ g_list_free (l1);
+ g_list_free (l2);
+ }
+
+ l1 = list;
+
+ while (l1)
+ {
+ /* perhaps we will delete l1 */
+ next = l1->next;
+
+ if (g_rand_double(myrand) < pbality_delete)
+ {
+ ECalComponent *comp;
+ const gchar *uid = NULL;
+ gchar *rid;
+ interval = (EInterval*) l1->data;
+ comp = interval->comp;
+
+ /* delete l1 */
+ /*
+ g_print ("Deleting interval %d - %d\n", interval->start,
+ interval->end);
+ */
+
+ rid = e_cal_component_get_recurid_as_string (comp);
+ e_cal_component_get_uid (comp, &uid);
+ if (!e_intervaltree_remove (tree, uid, rid))
+ {
+ g_free (rid);
+ e_intervaltree_dump (tree);
+ g_print ("Deleting interval %d - %d ERROR\n", interval->start,
+ interval->end);
+ exit(-1);
+ }
+
+
+ g_free (rid);
+ g_object_unref (interval->comp);
+ g_free (l1->data);
+ list = g_list_delete_link (list, l1);
+ num_deleted++;
+ }
+
+ l1 = next;
+ }
+
+ g_print ("Number of intervals deleted: %d\n", num_deleted);
+
+ for (i = 0; i < NUM_SEARCHES; i++)
+ {
+ start = g_rand_int_range (myrand, 0, 1000);
+ end = g_rand_int_range (myrand, start, 2000);
+
+ /*
+ g_print ("Search for : %d - %d\n", start, end);
+ */
+
+ l1 = e_intervaltree_search (tree, start, end);
+
+ /*
+ g_print ("Results from tree:\n");
+ print_nodes_list (l1);
+ */
+
+ l2 = search_in_list (list, start, end);
+
+ if (!compare_interval_lists (l2, l1))
+ {
+ g_print ("ERROR!\n\n");
+ return;
+ }
+
+ g_list_foreach(l1, (GFunc)g_object_unref, NULL);
+ g_list_foreach(l2, (GFunc)unref_comp, NULL);
+ g_list_free (l1);
+ g_list_free (l2);
+
+ /* g_print ("OK\n"); */
+ }
+
+ e_intervaltree_destroy (tree);
+ g_list_foreach(list, (GFunc)unref_comp, NULL);
+ g_list_free (list);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_type_init ();
+
+ myrand = g_rand_new ();
+ random_test();
+ g_print ("Everything OK\n");
+
+ return 0;
+}
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 556603d..9dd222f 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -27,6 +27,7 @@ libedataserver_1_2_la_SOURCES = \
e-source-group.c \
e-source-list.c \
e-source.c \
+ e-debug-log.c \
e-time-utils.c \
e-uid.c \
e-url.c \
@@ -61,6 +62,7 @@ libedataserverinclude_HEADERS = \
e-source-group.h \
e-source-list.h \
e-source.h \
+ e-debug-log.h \
e-time-utils.h \
e-uid.h \
e-url.h \
diff --git a/libedataserver/e-debug-log.c b/libedataserver/e-debug-log.c
new file mode 100644
index 0000000..524a455
--- /dev/null
+++ b/libedataserver/e-debug-log.c
@@ -0,0 +1,633 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ e-debug-log.c: Ring buffer for logging debug messages
+
+ Copyright (C) 2006, 2007 Novell, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Federico Mena-Quintero <federico novell com>
+*/
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include "e-debug-log.h"
+
+#define DEFAULT_RING_BUFFER_NUM_LINES 1000
+
+#define KEY_FILE_GROUP "debug log"
+#define KEY_FILE_DOMAINS_KEY "enable domains"
+#define KEY_FILE_MAX_LINES_KEY "max lines"
+
+static GStaticMutex log_mutex = G_STATIC_MUTEX_INIT;
+
+static GHashTable *domains_hash;
+static char **ring_buffer;
+static int ring_buffer_next_index;
+static int ring_buffer_num_lines;
+static int ring_buffer_max_lines = DEFAULT_RING_BUFFER_NUM_LINES;
+
+static GSList *milestones_head;
+static GSList *milestones_tail;
+
+static void
+lock (void)
+{
+ g_static_mutex_lock (&log_mutex);
+}
+
+static void
+unlock (void)
+{
+ g_static_mutex_unlock (&log_mutex);
+}
+
+void
+e_debug_log (gboolean is_milestone, const char *domain, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ e_debug_logv (is_milestone, domain, format, args);
+ va_end (args);
+}
+
+static gboolean
+is_domain_enabled (const char *domain)
+{
+ /* User actions are always logged */
+ if (strcmp (domain, E_DEBUG_LOG_DOMAIN_USER) == 0)
+ return TRUE;
+
+ if (!domains_hash)
+ return FALSE;
+
+ return (g_hash_table_lookup (domains_hash, domain) != NULL);
+}
+
+static void
+ensure_ring (void)
+{
+ if (ring_buffer)
+ return;
+
+ ring_buffer = g_new0 (char *, ring_buffer_max_lines);
+ ring_buffer_next_index = 0;
+ ring_buffer_num_lines = 0;
+}
+
+static void
+add_to_ring (char *str)
+{
+ ensure_ring ();
+
+ g_assert (str != NULL);
+
+ if (ring_buffer_num_lines == ring_buffer_max_lines) {
+ /* We have an overlap, and the ring_buffer_next_index points to
+ * the "first" item. Free it to make room for the new item.
+ */
+
+ g_assert (ring_buffer[ring_buffer_next_index] != NULL);
+ g_free (ring_buffer[ring_buffer_next_index]);
+ } else
+ ring_buffer_num_lines++;
+
+ g_assert (ring_buffer_num_lines <= ring_buffer_max_lines);
+
+ ring_buffer[ring_buffer_next_index] = str;
+
+ ring_buffer_next_index++;
+ if (ring_buffer_next_index == ring_buffer_max_lines) {
+ ring_buffer_next_index = 0;
+ g_assert (ring_buffer_num_lines == ring_buffer_max_lines);
+ }
+}
+
+static void
+add_to_milestones (const char *str)
+{
+ char *str_copy;
+
+ str_copy = g_strdup (str);
+
+ if (milestones_tail) {
+ milestones_tail = g_slist_append (milestones_tail, str_copy);
+ milestones_tail = milestones_tail->next;
+ } else {
+ milestones_head = milestones_tail = g_slist_append (NULL, str_copy);
+ }
+
+ g_assert (milestones_head != NULL && milestones_tail != NULL);
+}
+
+void
+e_debug_logv (gboolean is_milestone, const char *domain, const char *format, va_list args)
+{
+ char *str;
+ char *debug_str;
+ struct timeval tv;
+ struct tm tm;
+
+ lock ();
+
+ if (!(is_milestone || is_domain_enabled (domain)))
+ goto out;
+
+ str = g_strdup_vprintf (format, args);
+ gettimeofday (&tv, NULL);
+
+ tm = *localtime (&tv.tv_sec);
+
+ debug_str = g_strdup_printf ("%p;%04d/%02d/%02d;%02d:%02d:%02d.%04d;(%s);%s",
+ g_thread_self (),
+ tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ (int) (tv.tv_usec / 100),
+ domain,
+ str);
+ g_free (str);
+
+ add_to_ring (debug_str);
+ if (is_milestone)
+ add_to_milestones (debug_str);
+
+ out:
+ unlock ();
+}
+
+gboolean
+e_debug_log_load_configuration (const char *filename, GError **error)
+{
+ GKeyFile *key_file;
+ char **strings;
+ gsize num_strings;
+ int num;
+ GError *my_error;
+
+ g_assert (filename != NULL);
+ g_assert (error == NULL || *error == NULL);
+
+ key_file = g_key_file_new ();
+
+ if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) {
+ g_key_file_free (key_file);
+ return FALSE;
+ }
+
+ /* Domains */
+
+ my_error = NULL;
+ strings = g_key_file_get_string_list (key_file, KEY_FILE_GROUP, KEY_FILE_DOMAINS_KEY, &num_strings, &my_error);
+ if (my_error)
+ g_error_free (my_error);
+ else {
+ int i;
+
+ for (i = 0; i < num_strings; i++)
+ strings[i] = g_strstrip (strings[i]);
+
+ e_debug_log_enable_domains ((const char **) strings, num_strings);
+ g_strfreev (strings);
+ }
+
+ /* Number of lines */
+
+ my_error = NULL;
+ num = g_key_file_get_integer (key_file, KEY_FILE_GROUP, KEY_FILE_MAX_LINES_KEY, &my_error);
+ if (my_error)
+ g_error_free (my_error);
+ else
+ e_debug_log_set_max_lines (num);
+
+ g_key_file_free (key_file);
+ return TRUE;
+}
+
+void
+e_debug_log_enable_domains (const char **domains, int n_domains)
+{
+ int i;
+
+ g_assert (domains != NULL);
+ g_assert (n_domains >= 0);
+
+ lock ();
+
+ if (!domains_hash)
+ domains_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0; i < n_domains; i++) {
+ g_assert (domains[i] != NULL);
+
+ if (strcmp (domains[i], E_DEBUG_LOG_DOMAIN_USER) == 0)
+ continue; /* user actions are always enabled */
+
+ if (g_hash_table_lookup (domains_hash, domains[i]) == NULL) {
+ char *domain;
+
+ domain = g_strdup (domains[i]);
+ g_hash_table_insert (domains_hash, domain, domain);
+ }
+ }
+
+ unlock ();
+}
+
+void
+e_debug_log_disable_domains (const char **domains, int n_domains)
+{
+ int i;
+
+ g_assert (domains != NULL);
+ g_assert (n_domains >= 0);
+
+ lock ();
+
+ if (domains_hash) {
+ for (i = 0; i < n_domains; i++) {
+ char *domain;
+
+ g_assert (domains[i] != NULL);
+
+ if (strcmp (domains[i], E_DEBUG_LOG_DOMAIN_USER) == 0)
+ continue; /* user actions are always enabled */
+
+ domain = g_hash_table_lookup (domains_hash, domains[i]);
+ if (domain) {
+ g_hash_table_remove (domains_hash, domain);
+ g_free (domain);
+ }
+ }
+ } /* else, there is nothing to disable */
+
+ unlock ();
+}
+
+gboolean
+e_debug_log_is_domain_enabled (const char *domain)
+{
+ gboolean retval;
+
+ g_assert (domain != NULL);
+
+ lock ();
+ retval = is_domain_enabled (domain);
+ unlock ();
+
+ return retval;
+}
+
+struct domains_dump_closure {
+ char **domains;
+ int num_domains;
+};
+
+static void
+domains_foreach_dump_cb (gpointer key, gpointer value, gpointer data)
+{
+ struct domains_dump_closure *closure;
+ char *domain;
+
+ closure = data;
+ domain = key;
+
+ closure->domains[closure->num_domains] = domain;
+ closure->num_domains++;
+}
+
+static GKeyFile *
+make_key_file_from_configuration (void)
+{
+ GKeyFile *key_file;
+ struct domains_dump_closure closure;
+ int num_domains;
+
+ key_file = g_key_file_new ();
+
+ /* domains */
+
+ if (domains_hash) {
+ num_domains = g_hash_table_size (domains_hash);
+ if (num_domains != 0) {
+ closure.domains = g_new (char *, num_domains);
+ closure.num_domains = 0;
+
+ g_hash_table_foreach (domains_hash, domains_foreach_dump_cb, &closure);
+ g_assert (num_domains == closure.num_domains);
+
+ g_key_file_set_string_list (key_file, KEY_FILE_GROUP, KEY_FILE_DOMAINS_KEY,
+ (const gchar * const *) closure.domains, closure.num_domains);
+ g_free (closure.domains);
+ }
+ }
+
+ /* max lines */
+
+ g_key_file_set_integer (key_file, KEY_FILE_GROUP, KEY_FILE_MAX_LINES_KEY, ring_buffer_max_lines);
+
+ return key_file;
+}
+
+static gboolean
+write_string (const char *filename, FILE *file, const char *str, GError **error)
+{
+ if (fputs (str, file) == EOF) {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "error when writing to log file %s", filename);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+dump_configuration (const char *filename, FILE *file, GError **error)
+{
+ GKeyFile *key_file;
+ char *data;
+ gsize length;
+ gboolean success;
+
+ if (!write_string (filename, file,
+ "\n\n"
+ "This configuration for the debug log can be re-created\n"
+ "by putting the following in ~/evolution-data-server-debug-log.conf\n"
+ "(use ';' to separate domain names):\n\n",
+ error)) {
+ return FALSE;
+ }
+
+ success = FALSE;
+
+ key_file = make_key_file_from_configuration ();
+
+ data = g_key_file_to_data (key_file, &length, error);
+ if (!data)
+ goto out;
+
+ if (!write_string (filename, file, data, error)) {
+ goto out;
+ }
+
+ success = TRUE;
+ out:
+ g_key_file_free (key_file);
+ return success;
+}
+
+static gboolean
+dump_milestones (const char *filename, FILE *file, GError **error)
+{
+ GSList *l;
+
+ if (!write_string (filename, file, "===== BEGIN MILESTONES =====\n", error))
+ return FALSE;
+
+ for (l = milestones_head; l; l = l->next) {
+ const char *str;
+
+ str = l->data;
+ if (!(write_string (filename, file, str, error)
+ && write_string (filename, file, "\n", error)))
+ return FALSE;
+ }
+
+ if (!write_string (filename, file, "===== END MILESTONES =====\n", error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+dump_ring_buffer (const char *filename, FILE *file, GError **error)
+{
+ int start_index;
+ int i;
+
+ if (!write_string (filename, file, "===== BEGIN RING BUFFER =====\n", error))
+ return FALSE;
+
+ if (ring_buffer_num_lines == ring_buffer_max_lines)
+ start_index = ring_buffer_next_index;
+ else
+ start_index = 0;
+
+ for (i = 0; i < ring_buffer_num_lines; i++) {
+ int idx;
+
+ idx = (start_index + i) % ring_buffer_max_lines;
+
+ if (!(write_string (filename, file, ring_buffer[idx], error)
+ && write_string (filename, file, "\n", error))) {
+ return FALSE;
+ }
+ }
+
+ if (!write_string (filename, file, "===== END RING BUFFER =====\n", error))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+e_debug_log_dump (const char *filename, GError **error)
+{
+ FILE *file;
+ gboolean success;
+
+ g_assert (error == NULL || *error == NULL);
+
+ lock ();
+
+ success = FALSE;
+
+ file = fopen (filename, "w");
+ if (!file) {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "could not open log file %s", filename);
+ goto out;
+ }
+
+ if (!(dump_milestones (filename, file, error)
+ && dump_ring_buffer (filename, file, error)
+ && dump_configuration (filename, file, error))) {
+ goto do_close;
+ }
+
+ success = TRUE;
+
+ do_close:
+
+ if (fclose (file) != 0) {
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (error && *error) {
+ g_error_free (*error);
+ *error = NULL;
+ }
+
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "error when closing log file %s", filename);
+ success = FALSE;
+ }
+
+ out:
+
+ unlock ();
+ return success;
+}
+
+gboolean
+e_debug_log_dump_to_dated_file (GError **error)
+{
+ time_t t;
+ struct tm tm;
+ char *basename;
+ char *filename;
+ gboolean retval;
+
+ g_assert (error == NULL || *error == NULL);
+
+ t = time (NULL);
+ tm = *localtime (&t);
+
+ basename = g_strdup_printf ("e-debug-log-%04d-%02d-%02d-%02d-%02d-%02d.txt",
+ tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec);
+ filename = g_build_filename (g_get_home_dir (), basename, NULL);
+
+ retval = e_debug_log_dump (filename, error);
+
+ g_free (basename);
+ g_free (filename);
+
+ return retval;
+}
+
+void
+e_debug_log_set_max_lines (int num_lines)
+{
+ char **new_buffer;
+ int lines_to_copy;
+
+ g_assert (num_lines > 0);
+
+ lock ();
+
+ if (num_lines == ring_buffer_max_lines)
+ goto out;
+
+ new_buffer = g_new0 (char *, num_lines);
+
+ lines_to_copy = MIN (num_lines, ring_buffer_num_lines);
+
+ if (ring_buffer) {
+ int start_index;
+ int i;
+
+ if (ring_buffer_num_lines == ring_buffer_max_lines)
+ start_index = (ring_buffer_next_index + ring_buffer_max_lines - lines_to_copy) % ring_buffer_max_lines;
+ else
+ start_index = ring_buffer_num_lines - lines_to_copy;
+
+ g_assert (start_index >= 0 && start_index < ring_buffer_max_lines);
+
+ for (i = 0; i < lines_to_copy; i++) {
+ int idx;
+
+ idx = (start_index + i) % ring_buffer_max_lines;
+
+ new_buffer[i] = ring_buffer[idx];
+ ring_buffer[idx] = NULL;
+ }
+
+ for (i = 0; i < ring_buffer_max_lines; i++)
+ g_free (ring_buffer[i]);
+
+ g_free (ring_buffer);
+ }
+
+ ring_buffer = new_buffer;
+ ring_buffer_next_index = lines_to_copy;
+ ring_buffer_num_lines = lines_to_copy;
+ ring_buffer_max_lines = num_lines;
+
+ out:
+
+ unlock ();
+}
+
+int
+e_debug_log_get_max_lines (void)
+{
+ int retval;
+
+ lock ();
+ retval = ring_buffer_max_lines;
+ unlock ();
+
+ return retval;
+}
+
+void
+e_debug_log_clear (void)
+{
+ int i;
+
+ lock ();
+
+ if (!ring_buffer)
+ goto out;
+
+ for (i = 0; i < ring_buffer_max_lines; i++) {
+ g_free (ring_buffer[i]);
+ ring_buffer[i] = NULL;
+ }
+
+ ring_buffer_next_index = 0;
+ ring_buffer_num_lines = 0;
+
+ out:
+ unlock ();
+}
+
diff --git a/libedataserver/e-debug-log.h b/libedataserver/e-debug-log.h
new file mode 100644
index 0000000..9241c12
--- /dev/null
+++ b/libedataserver/e-debug-log.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ e-debug-log.h: Ring buffer for logging debug messages
+
+ Copyright (C) 2006, 2007 Novell, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Federico Mena-Quintero <federico novell com>
+*/
+
+#ifndef E_DEBUG_LOG_H
+#define E_DEBUG_LOG_H
+
+#include <glib.h>
+
+#define E_DEBUG_LOG_DOMAIN_USER "USER" /* always enabled */
+#define E_DEBUG_LOG_DOMAIN_GLOG "GLog" /* used for GLog messages; don't use it yourself */
+#define E_DEBUG_LOG_DOMAIN_CAL_QUERIES "CalQueries" /* used for calendar queries analysis */
+
+void e_debug_log (gboolean is_milestone, const char *domain, const char *format, ...);
+
+void e_debug_logv (gboolean is_milestone, const char *domain, const char *format, va_list args);
+
+gboolean e_debug_log_load_configuration (const char *filename, GError **error);
+
+void e_debug_log_enable_domains (const char **domains, int n_domains);
+void e_debug_log_disable_domains (const char **domains, int n_domains);
+
+gboolean e_debug_log_is_domain_enabled (const char *domain);
+
+gboolean e_debug_log_dump (const char *filename, GError **error);
+
+gboolean e_debug_log_dump_to_dated_file (GError **error);
+
+void e_debug_log_set_max_lines (int num_lines);
+int e_debug_log_get_max_lines (void);
+
+/* For testing only */
+void e_debug_log_clear (void);
+
+#endif /* E_DEBUG_LOG_H */
+
diff --git a/libedataserver/e-sexp.c b/libedataserver/e-sexp.c
index f054ddb..785a199 100644
--- a/libedataserver/e-sexp.c
+++ b/libedataserver/e-sexp.c
@@ -113,6 +113,12 @@ static void parse_dump_term(struct _ESExpTerm *t, gint depth);
static GObjectClass *parent_class;
#endif
+typedef gboolean (ESGeneratorFunc)(int argc, struct _ESExpResult **argv, struct _ESExpResult *r);
+typedef gboolean (ESOperatorFunc)(int argc, struct _ESExpResult **argv, struct _ESExpResult *r);
+/* FIXME: constant _TIME_MAX used in different files, move it somewhere */
+#define _TIME_MAX ((time_t) INT_MAX) /* Max valid time_t */
+
+
static const GScannerConfig scanner_config =
{
( (gchar *) " \t\r\n") /* cset_skip_characters */,
@@ -177,6 +183,9 @@ e_sexp_result_new(struct _ESExp *f, gint type)
{
struct _ESExpResult *r = e_memchunk_alloc0(f->result_chunks);
r->type = type;
+ r->occuring_start = 0;
+ r->occuring_end = _TIME_MAX;
+ r->time_generator = FALSE;
return r;
}
@@ -703,7 +712,7 @@ e_sexp_term_eval(struct _ESExp *f, struct _ESExpTerm *t)
r->value.boolean = t->value.boolean;
break;
case ESEXP_TERM_TIME:
- r(printf(" (time_t %d)\n", t->value.time));
+ r(printf(" (time_t %ld)\n", t->value.time));
r = e_sexp_result_new (f, ESEXP_RES_TIME);
r->value.time = t->value.time;
break;
@@ -820,6 +829,236 @@ parse_dump_term(struct _ESExpTerm *t, gint depth)
}
#endif
+const char *time_functions[] = {
+ "time-now",
+ "make-time",
+ "time-add-day",
+ "time-day-begin",
+ "time-day-end"
+};
+
+static gboolean
+binary_generator (int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+ g_return_val_if_fail (r != NULL, FALSE);
+ g_return_val_if_fail (argc == 2, FALSE);
+
+ if ((argv[0]->type != ESEXP_RES_TIME) || (argv[1]->type != ESEXP_RES_TIME))
+ return FALSE;
+
+ r->occuring_start = argv[0]->value.time;
+ r->occuring_end = argv[1]->value.time;
+
+ return TRUE;
+}
+
+static gboolean
+unary_generator(int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+ /* unary generator with end time */
+ g_return_val_if_fail (r != NULL, FALSE);
+ g_return_val_if_fail (argc == 1, FALSE);
+
+ if (argv[0]->type != ESEXP_RES_TIME)
+ return FALSE;
+
+ r->occuring_start = 0;
+ r->occuring_end = argv[0]->value.time;
+
+ return TRUE;
+}
+
+static const struct {
+ char *name;
+ ESGeneratorFunc *func;
+} generators[] = {
+ {"occur-in-time-range?", binary_generator},
+ {"due-in-time-range?", binary_generator},
+ {"has-alarms-in-range?", binary_generator},
+ {"completed-before?", unary_generator},
+};
+
+const int generators_count = sizeof(generators) / sizeof(generators[0]);
+
+static gboolean
+or_operator(int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+ /*
+ A B A or B
+ ---- ---- ------
+ norm(0) norm(0) norm(0)
+ gen(1) norm(0) norm(0)
+ norm(0) gen(1) norm(0)
+ gen(1) gen(1) gen*(1)
+ */
+
+ g_return_val_if_fail (r != NULL, FALSE);
+ g_return_val_if_fail (argc == 2, FALSE);
+
+ if ((r->time_generator = argv[0]->time_generator & argv[1]->time_generator)) {
+ r->occuring_start = MIN(argv[0]->occuring_start, argv[1]->occuring_start);
+ r->occuring_end = MAX(argv[0]->occuring_end, argv[1]->occuring_end);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+and_operator(int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+ /*
+ A B A and B
+ ---- ---- ------- -
+ norm(0) norm(0) norm(0)
+ gen(1) norm(0) gen(1)
+ norm(0) gen(1) gen(1)
+ gen(1) gen(1) gen(1)
+ */
+
+ g_return_val_if_fail (r != NULL, FALSE);
+ g_return_val_if_fail (argc == 2, FALSE);
+
+ if ((r->time_generator = argv[0]->time_generator | argv[1]->time_generator)) {
+ /* constraint interval */
+ r->occuring_start = MAX(argv[0]->occuring_start, argv[1]->occuring_start);
+ r->occuring_end = MIN(argv[0]->occuring_end, argv[1]->occuring_end);
+ }
+
+ return TRUE;
+}
+
+static const struct {
+ char *name;
+ ESOperatorFunc *func;
+} operators[] = {
+ {"or", or_operator},
+ {"and", and_operator}
+};
+
+const int operators_count = sizeof(operators) / sizeof(operators[0]);
+
+
+static ESOperatorFunc*
+get_operator_function(const char *fname)
+{
+ int i;
+
+ g_return_val_if_fail (fname != NULL, NULL);
+
+ for (i = 0; i < sizeof(operators) / sizeof(operators[0]); i++)
+ if (strcmp(operators[i].name, fname) == 0)
+ return operators[i].func;
+
+ return NULL;
+}
+
+static inline gboolean
+is_time_function(const char *fname)
+{
+ int i;
+
+ g_return_val_if_fail (fname != NULL, FALSE);
+
+ for (i = 0; i < sizeof(time_functions) / sizeof(time_functions[0]); i++)
+ if (strcmp(time_functions[i], fname) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static ESGeneratorFunc*
+get_generator_function(const char *fname)
+{
+ int i;
+
+ g_return_val_if_fail (fname != NULL, NULL);
+
+ for (i = 0; i < sizeof(generators) / sizeof(generators[0]); i++)
+ if (strcmp(generators[i].name, fname) == 0)
+ return generators[i].func;
+
+ return NULL;
+}
+
+/* this must only be called from inside term evaluation callbacks! */
+static struct _ESExpResult *
+e_sexp_term_evaluate_occur_times(struct _ESExp *f, struct _ESExpTerm *t, time_t *start, time_t *end)
+{
+ struct _ESExpResult *r = NULL;
+ int i, argc;
+ struct _ESExpResult **argv;
+ gboolean ok = TRUE;
+
+ g_return_val_if_fail(t != NULL, NULL);
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ /*
+ printf("eval term :\n");
+ parse_dump_term(t, 0);
+ */
+
+ switch (t->type) {
+ case ESEXP_TERM_STRING:
+ r(printf(" (string \"%s\")\n", t->value.string));
+ r = e_sexp_result_new(f, ESEXP_RES_STRING);
+ r->value.string = g_strdup(t->value.string);
+ break;
+ case ESEXP_TERM_IFUNC:
+ case ESEXP_TERM_FUNC:
+ r(printf(" (function \"%s\"\n", t->value.func.sym->name));
+
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+ argc = t->value.func.termcount;
+ argv = alloca(sizeof(argv[0]) * argc);
+
+ for (i=0;i<t->value.func.termcount;i++) {
+ argv[i] = e_sexp_term_evaluate_occur_times (f, t->value.func.terms[i],
+ start, end);
+ }
+
+ ESGeneratorFunc *generator = NULL;
+ ESOperatorFunc *operator = NULL;
+
+ if (is_time_function(t->value.func.sym->name)) {
+ /* evaluate time */
+ if (t->value.func.sym->f.func)
+ r = t->value.func.sym->f.func(f, t->value.func.termcount,
+ argv, t->value.func.sym->data);
+ } else if ((generator = get_generator_function(t->value.func.sym->name)) != NULL) {
+ /* evaluate generator function */
+ r->time_generator = TRUE;
+ ok = generator(argc, argv, r);
+ } else if ((operator = get_operator_function(t->value.func.sym->name)) != NULL)
+ /* evaluate operator function */
+ ok = operator(argc, argv, r);
+ else {
+ /* normal function: we need to scan all objects */
+ r->time_generator = FALSE;
+ }
+
+ e_sexp_resultv_free(f, t->value.func.termcount, argv);
+ break;
+
+ case ESEXP_TERM_INT:
+ case ESEXP_TERM_BOOL:
+ case ESEXP_TERM_TIME:
+ break;
+ default:
+ ok = FALSE;
+ break;
+ }
+
+ if (!ok)
+ e_sexp_fatal_error(f, "Error in parse tree");
+
+ if (r==NULL)
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+ return r;
+}
+
+
/*
PARSER
*/
@@ -1308,6 +1547,45 @@ e_sexp_eval(ESExp *f)
}
/**
+ * e_cal_backend_sexp_evaluate_occur_times:
+ * @f: An #ESExp object.
+ * @start: Start of the time window will be stored here.
+ * @end: End of the time window will be stored here.
+ *
+ * Determines biggest time window given by expressions "occur-in-range" in sexp.
+ *
+ */
+gboolean
+e_sexp_evaluate_occur_times(ESExp *f, time_t *start, time_t *end)
+{
+ struct _ESExpResult *r;
+ gboolean generator;
+ g_return_val_if_fail (IS_E_SEXP (f), FALSE);
+ g_return_val_if_fail (f->tree != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ *start = *end = -1;
+
+ if (setjmp(f->failenv)) {
+ g_warning("Error in execution: %s", f->error);
+ return FALSE;
+ }
+
+ r = e_sexp_term_evaluate_occur_times(f, f->tree, start, end);
+ generator = r->time_generator;
+
+ if (generator) {
+ *start = r->occuring_start;
+ *end = r->occuring_end;
+ }
+
+ e_sexp_result_free(f, r);
+
+ return generator;
+}
+
+/**
* e_sexp_encode_bool:
* @s:
* @state:
@@ -1365,7 +1643,10 @@ gint main(gint argc, gchar **argv)
e_sexp_add_variable(f, 0, "test", NULL);
- e_sexp_input_text(f, t, strlen(t));
+ if (argc < 2 || !argv[1])
+ return;
+
+ e_sexp_input_text(f, t, t);
e_sexp_parse(f);
if (f->tree) {
diff --git a/libedataserver/e-sexp.h b/libedataserver/e-sexp.h
index 6c4d492..c10dc09 100644
--- a/libedataserver/e-sexp.h
+++ b/libedataserver/e-sexp.h
@@ -57,6 +57,9 @@ struct _ESExpResult {
gint boolean;
time_t time;
} value;
+ gboolean time_generator;
+ time_t occuring_start;
+ time_t occuring_end;
};
typedef struct _ESExpResult *(ESExpFunc)(struct _ESExp *sexp, gint argc,
@@ -173,6 +176,7 @@ const gchar *e_sexp_error (struct _ESExp *f);
ESExpTerm * e_sexp_parse_value(ESExp *f);
+gboolean e_sexp_evaluate_occur_times (ESExp *f, time_t *start, time_t *end);
G_END_DECLS
#endif /* _E_SEXP_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]