]> err.no Git - libchamplain/commitdiff
Introduce ChamplainCache to deal with caching tiles
authorPierre-Luc Beaudoin <pierre-luc@pierlux.com>
Sat, 28 Mar 2009 17:25:48 +0000 (19:25 +0200)
committerPierre-Luc Beaudoin <pierre-luc@pierlux.com>
Fri, 1 May 2009 16:23:48 +0000 (12:23 -0400)
champlain/Makefile.am
champlain/champlain-cache.c [new file with mode: 0644]
champlain/champlain-cache.h [new file with mode: 0644]
champlain/champlain-network-map-source.c
champlain/champlain-tile.c
champlain/champlain-tile.h

index 40bdb32720cd511af245b41b3cba43704ed046c9..7efe081e2e1967e63110b3d6fde377153dca2ca4 100644 (file)
@@ -42,7 +42,8 @@ libchamplain_0_3_la_SOURCES = \
        champlain-zoom-level.c          \
        champlain-tile.c                \
        champlain-map-source.c          \
-       champlain-network-map-source.c
+       champlain-network-map-source.c  \
+       champlain-cache.c
 
 noinst_HEADERS = \
        champlain-debug.h               \
@@ -56,7 +57,8 @@ noinst_HEADERS = \
        champlain-enum-types.h          \
        champlain-map-source.h          \
        champlain-network-map-source.h  \
-       champlain-version.h
+       champlain-version.h             \
+       champlain-cache.h
 
 libchamplain_include_HEADERS = \
        champlain.h                     \
@@ -70,7 +72,8 @@ libchamplain_include_HEADERS = \
        champlain-zoom-level.h          \
        champlain-base-marker.h         \
        champlain-marker.h              \
-       champlain-version.h
+       champlain-version.h             \
+       champlain-cache.h
 
 libchamplain_0_3_la_LIBADD = $(DEPS_LIBS) $(SOUP_LIBS) ../tidy/libtidy-1.0.la
 
diff --git a/champlain/champlain-cache.c b/champlain/champlain-cache.c
new file mode 100644 (file)
index 0000000..2576672
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+* Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "champlain-cache.h"
+
+#include "champlain-enum-types.h"
+#include "champlain-private.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_DEFINE_TYPE (ChamplainCache, champlain_cache, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHAMPLAIN_TYPE_CACHE, ChamplainCachePrivate))
+
+enum
+{
+  PROP_0,
+  PROP_SIZE_LIMIT,
+};
+
+typedef struct _ChamplainCachePrivate ChamplainCachePrivate;
+
+struct _ChamplainCachePrivate {
+  guint size_limit;
+};
+
+static void
+champlain_cache_get_property (GObject *object,
+                             guint property_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+  ChamplainCache *self = CHAMPLAIN_CACHE (object);
+  switch (property_id)
+    {
+      case PROP_SIZE_LIMIT:
+        g_value_set_uint (value, champlain_cache_get_size_limit (self));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+champlain_cache_set_property (GObject *object,
+                             guint property_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+  ChamplainCache *self = CHAMPLAIN_CACHE (object);
+  switch (property_id)
+    {
+      case PROP_SIZE_LIMIT:
+        champlain_cache_set_size_limit (self, g_value_get_uint (value));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+champlain_cache_dispose (GObject *object)
+{
+//  ChamplainCachePrivate *priv = GET_PRIVATE (object);
+
+  G_OBJECT_CLASS (champlain_cache_parent_class)->dispose (object);
+}
+
+static void
+champlain_cache_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (champlain_cache_parent_class)->finalize (object);
+}
+
+static void
+champlain_cache_class_init (ChamplainCacheClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ChamplainCachePrivate));
+
+  object_class->get_property = champlain_cache_get_property;
+  object_class->set_property = champlain_cache_set_property;
+  object_class->dispose = champlain_cache_dispose;
+  object_class->finalize = champlain_cache_finalize;
+
+  g_object_class_install_property (object_class,
+      PROP_SIZE_LIMIT,
+      g_param_spec_uint ("size-limit",
+        "Size Limit",
+        "The cache's size limit (Mb)",
+        1,
+        G_MAXINT,
+        10,
+        G_PARAM_READWRITE));
+}
+
+static void
+champlain_cache_init (ChamplainCache *self)
+{
+  champlain_cache_set_size_limit (self, 10);
+}
+
+ChamplainCache*
+champlain_cache_get_default (void)
+{
+  static ChamplainCache * instance = NULL;
+
+  if (G_UNLIKELY (instance == NULL))
+    {
+      instance = g_object_new (CHAMPLAIN_TYPE_CACHE, NULL);
+      return instance;
+    }
+
+  return g_object_ref (instance);
+}
+
+guint
+champlain_cache_get_size_limit (ChamplainCache *self)
+{
+  g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE);
+
+  ChamplainCachePrivate *priv = GET_PRIVATE (self);
+
+  return priv->size_limit;
+}
+
+void
+champlain_cache_set_size_limit (ChamplainCache *self,
+    guint size_limit)
+{
+  g_return_if_fail(CHAMPLAIN_CACHE (self));
+
+  ChamplainCachePrivate *priv = GET_PRIVATE (self);
+
+  priv->size_limit = size_limit;
+  g_object_notify (G_OBJECT (self), "size-limit");
+}
+
+gboolean
+champlain_cache_fill_tile (ChamplainCache *self,
+    ChamplainTile *tile)
+{
+  g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE);
+  g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE);
+
+  GFileInfo *info;
+  GFile *file;
+  GError *error = NULL;
+  ClutterActor *actor;
+  const gchar *etag = NULL;
+  const gchar *filename;
+  GTimeVal *modified_time = g_new0 (GTimeVal, 1);
+
+  filename = champlain_tile_get_filename (tile);
+
+  if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+    return FALSE;
+
+  file = g_file_new_for_path (filename);
+  info = g_file_query_info (file,
+      G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_ETAG_VALUE,
+      G_FILE_QUERY_INFO_NONE, NULL, NULL);
+  g_file_info_get_modification_time (info, modified_time);
+  champlain_tile_set_modified_time (tile, modified_time);
+
+  //TODO: retrieve etag
+  if (etag != NULL)
+    champlain_tile_set_etag (tile, etag);
+
+  /* Load the cached version */
+  actor = clutter_texture_new_from_file (filename, &error);
+  champlain_tile_set_actor (tile, actor);
+
+  g_object_unref (file);
+  g_object_unref (info);
+
+  return TRUE;
+}
+
+gboolean
+champlain_cache_tile_is_expired (ChamplainCache *self,
+    ChamplainTile *tile)
+{
+  g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE);
+  g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE);
+
+  GTimeVal *now = g_new0 (GTimeVal, 1);
+  const GTimeVal *modified_time = champlain_tile_get_modified_time (tile);
+  gboolean validate_cache = FALSE;
+
+  g_get_current_time (now);
+  g_time_val_add (now, (-60ul * 60ul * 1000ul * 1000ul)); // Cache expires 1 hour
+  validate_cache = modified_time->tv_sec < now->tv_sec;
+
+  g_free (now);
+  return validate_cache;
+}
+
+void
+champlain_cache_update_tile (ChamplainCache *self,
+    ChamplainTile *tile)
+{
+
+  //Save the etag 
+
+}
diff --git a/champlain/champlain-cache.h b/champlain/champlain-cache.h
new file mode 100644 (file)
index 0000000..6fbe7c4
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef CHAMPLAIN_CACHE_H
+#define CHAMPLAIN_CACHE_H
+
+#include <champlain/champlain-defines.h>
+
+#include "champlain-tile.h"
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define CHAMPLAIN_TYPE_CACHE champlain_cache_get_type()
+
+#define CHAMPLAIN_CACHE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_CACHE, ChamplainCache))
+
+#define CHAMPLAIN_CACHE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_CACHE, ChamplainCacheClass))
+
+#define CHAMPLAIN_IS_CACHE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_CACHE))
+
+#define CHAMPLAIN_IS_CACHE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_CACHE))
+
+#define CHAMPLAIN_CACHE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_CACHE, ChamplainCacheClass))
+
+typedef struct {
+  GObject parent;
+} ChamplainCache;
+
+typedef struct {
+  GObjectClass parent_class;
+} ChamplainCacheClass;
+
+GType champlain_cache_get_type (void);
+
+ChamplainCache* champlain_cache_get_default (void);
+
+void champlain_cache_update_tile (ChamplainCache *self,
+    ChamplainTile *tile);
+gboolean champlain_cache_fill_tile (ChamplainCache *self,
+    ChamplainTile *tile);
+gboolean champlain_cache_tile_is_expired (ChamplainCache *self,
+    ChamplainTile *tile);
+
+void champlain_cache_set_size_limit (ChamplainCache *self,
+    guint size_limit);
+guint champlain_cache_get_size_limit (ChamplainCache *self);
+
+void champlain_cache_purge (ChamplainCache *self);
+
+G_END_DECLS
+
+#endif /* CHAMPLAIN_CACHE_H */
+
index 3b107a51a25d6ced7e937eaeff64e322fe19adb0..179340837e2cb890575ab2243a0256a27d806881 100644 (file)
@@ -24,6 +24,7 @@
 #include "champlain-debug.h"
 
 #include "champlain.h"
+#include "champlain-cache.h"
 #include "champlain-defines.h"
 #include "champlain-enum-types.h"
 #include "champlain-map-source.h"
@@ -413,6 +414,7 @@ file_loaded_cb (SoupSession *session,
   ClutterActor *actor, *previous_actor = NULL;
   GFile *file;
   GFileInfo *info;
+  ChamplainCache *cache = champlain_cache_get_default ();
 
   filename = champlain_tile_get_filename (ctx->tile);
 
@@ -496,16 +498,9 @@ file_loaded_cb (SoupSession *session,
 
   if (etag != NULL)
     {
-      file = g_file_new_for_path (filename);
-      info = g_file_query_info (file, G_FILE_ATTRIBUTE_ETAG_VALUE,
-          G_FILE_QUERY_INFO_NONE, NULL, NULL);
-      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
-      g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL,
-          NULL);
-
-      g_object_unref (file);
-      g_object_unref (info);
+      champlain_tile_set_etag (ctx->tile, etag);
     }
+  champlain_cache_update_tile (cache, ctx->tile);
 
   /* Load the image into clutter */
   GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
@@ -558,50 +553,21 @@ champlain_network_map_source_get_tile (ChamplainMapSource *map_source,
   gchar* filename;
   gboolean in_cache = FALSE;
   gboolean validate_cache = FALSE;
-  GTimeVal *modified_time = g_new0 (GTimeVal, 1);
-  gchar * etag = NULL;
 
   ChamplainNetworkMapSource *network_map_source = CHAMPLAIN_NETWORK_MAP_SOURCE (map_source);
   ChamplainNetworkMapSourcePrivate *priv = network_map_source->priv;
+  ChamplainCache *cache = champlain_cache_get_default ();
 
   /* Try the cached version first */
   filename = get_filename (network_map_source, zoom_level, tile);
   champlain_tile_set_filename (tile, filename);
   champlain_tile_set_size (tile, champlain_map_source_get_tile_size (map_source));
 
-  if (g_file_test (filename, G_FILE_TEST_EXISTS))
+  in_cache = champlain_cache_fill_tile (cache, tile);
+
+  if (in_cache == TRUE)
     {
-      GTimeVal *now = g_new0 (GTimeVal, 1);
-      GFileInfo *info;
-      GFile *file;
-      GError *error = NULL;
-      ClutterActor *actor;
-      const gchar *etag_tmp;
-
-      in_cache = TRUE;
-
-      /* Verify since when is the file in cache */
-      file = g_file_new_for_path (filename);
-      info = g_file_query_info (file,
-          G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_ETAG_VALUE,
-          G_FILE_QUERY_INFO_NONE, NULL, NULL);
-      g_file_info_get_modification_time (info, modified_time);
-      etag_tmp = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE);
-      if (etag_tmp != NULL)
-        etag = g_strdup (etag_tmp);
-
-      g_get_current_time (now);
-      g_time_val_add (now, (-60ul * 60ul * 1000ul * 1000ul)); // Cache expires 1 hour
-      validate_cache = modified_time->tv_sec < now->tv_sec;
-      validate_cache = TRUE;
-
-      g_object_unref (file);
-      g_object_unref (info);
-      g_free (now);
-
-      /* Load the cached version */
-      actor = clutter_texture_new_from_file (filename, &error);
-      champlain_tile_set_actor (tile, actor);
+      validate_cache = champlain_cache_tile_is_expired (cache, tile);
 
       if (validate_cache == TRUE)
         champlain_tile_set_state (tile, CHAMPLAIN_STATE_VALIDATING_CACHE);
@@ -646,22 +612,22 @@ champlain_network_map_source_get_tile (ChamplainMapSource *map_source,
 
       if (in_cache == TRUE)
         {
-          char value [100];
-          struct tm *other_time = gmtime (&modified_time->tv_sec);
-
-          strftime (value, 100, "%a, %d %b %Y %T %Z", other_time);
+          const gchar *etag;
+          gchar *date;
 
-          DEBUG("If-Modified-Since %s", value);
+          date = champlain_tile_get_modified_time_string (tile);
+          DEBUG("If-Modified-Since %s", date);
           soup_message_headers_append (msg->request_headers,
-              "If-Modified-Since", value);
+              "If-Modified-Since", date);
+          g_free (date);
 
+          etag = champlain_tile_get_etag (tile);
           if (etag != NULL)
             {
               DEBUG("If-None-Match: %s", etag);
               soup_message_headers_append (msg->request_headers,
                   "If-None-Match", etag);
             }
-
         }
 
       soup_session_queue_message (soup_session, msg,
@@ -673,7 +639,5 @@ champlain_network_map_source_get_tile (ChamplainMapSource *map_source,
   /* If a tile is neither in cache or can be fetched, do nothing, it'll show up
    * as empty
    */
-  g_free (modified_time);
-  g_free (etag);
 }
 
index 0c928beb5121658d0507388997810f4a0a63a68a..bdde9219e5118fd6e0b6508e00d84b01ea060522 100644 (file)
@@ -43,7 +43,8 @@ enum
   PROP_URI,
   PROP_FILENAME,
   PROP_STATE,
-  PROP_ACTOR
+  PROP_ACTOR,
+  PROP_ETAG
 };
 
 typedef struct _ChamplainTilePrivate ChamplainTilePrivate;
@@ -59,6 +60,9 @@ struct _ChamplainTilePrivate {
   ChamplainState state;
   gchar *filename;
   ClutterActor *actor;
+
+  GTimeVal *modified_time;
+  gchar* etag;
 };
 
 static void
@@ -94,6 +98,9 @@ champlain_tile_get_property (GObject *object,
       case PROP_ACTOR:
         g_value_set_object (value, champlain_tile_get_actor (self));
         break;
+      case PROP_ETAG:
+        g_value_set_string (value, champlain_tile_get_etag (self));
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -132,6 +139,9 @@ champlain_tile_set_property (GObject *object,
       case PROP_ACTOR:
         champlain_tile_set_actor (self, g_value_get_object (value));
         break;
+      case PROP_ETAG:
+        champlain_tile_set_etag (self, g_value_get_string (value));
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -244,6 +254,15 @@ champlain_tile_class_init (ChamplainTileClass *klass)
         "The tile's actor",
         CLUTTER_TYPE_ACTOR,
         G_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+      PROP_ETAG,
+      g_param_spec_string ("etag",
+        "Entity Tag",
+        "The entity tag of the tile",
+        NULL,
+        G_PARAM_READWRITE));
+
 }
 
 static void
@@ -258,6 +277,7 @@ champlain_tile_init (ChamplainTile *self)
   priv->size = 0;
   priv->uri = g_strdup ("");
   priv->filename = g_strdup ("");
+  priv->etag = NULL;
 
   /* Can't call champlain_tile_set_actor (self, NULL); because the assertion will fail */
   priv->actor = NULL;
@@ -456,3 +476,60 @@ champlain_tile_set_actor (ChamplainTile *self, ClutterActor *actor)
   priv->actor = g_object_ref (actor);
   g_object_notify (G_OBJECT (self), "actor");
 }
+
+const GTimeVal *
+champlain_tile_get_modified_time (ChamplainTile *self)
+{
+  g_return_val_if_fail (CHAMPLAIN_TILE(self), NULL);
+
+  ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+  return priv->modified_time;
+}
+
+void
+champlain_tile_set_modified_time (ChamplainTile *self,
+    GTimeVal *time)
+{
+  g_return_if_fail (CHAMPLAIN_TILE(self));
+  g_return_if_fail (time != NULL);
+
+  ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+  priv->modified_time = time;
+}
+
+char *
+champlain_tile_get_modified_time_string (ChamplainTile *self)
+{
+  g_return_val_if_fail(CHAMPLAIN_TILE(self), NULL);
+  ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+  struct tm *other_time = gmtime (&priv->modified_time->tv_sec);
+  char value [100];
+
+  strftime (value, 100, "%a, %d %b %Y %T %Z", other_time);
+
+  return g_strdup (value);
+}
+
+const char *
+champlain_tile_get_etag (ChamplainTile *self)
+{
+  g_return_val_if_fail(CHAMPLAIN_TILE(self), "");
+  ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+  return priv->etag;
+}
+
+void
+champlain_tile_set_etag (ChamplainTile *self,
+    const gchar *etag)
+{
+  g_return_if_fail(CHAMPLAIN_TILE(self));
+
+  ChamplainTilePrivate *priv = GET_PRIVATE (self);
+
+  priv->etag = g_strdup (etag);
+  g_object_notify (G_OBJECT (self), "etag");
+}
index 1100b695e39cc0856d670900ded929ec07fb91e7..63bb639af7c04126bba70bd5fcf4c24ba6ab5020 100644 (file)
@@ -64,6 +64,9 @@ ChamplainState champlain_tile_get_state (ChamplainTile *self);
 G_CONST_RETURN gchar * champlain_tile_get_uri (ChamplainTile *self);
 G_CONST_RETURN gchar * champlain_tile_get_filename (ChamplainTile *self);
 ClutterActor * champlain_tile_get_actor (ChamplainTile *self);
+const GTimeVal * champlain_tile_get_modified_time (ChamplainTile *self);
+char * champlain_tile_get_modified_time_string (ChamplainTile *self);
+const char * champlain_tile_get_etag (ChamplainTile *self);
 
 void champlain_tile_set_x (ChamplainTile *self, gint x);
 void champlain_tile_set_y (ChamplainTile *self, gint y);
@@ -73,6 +76,8 @@ void champlain_tile_set_state (ChamplainTile *self, ChamplainState state);
 void champlain_tile_set_uri (ChamplainTile *self, const gchar *uri);
 void champlain_tile_set_filename (ChamplainTile *self, const gchar *filename);
 void champlain_tile_set_actor (ChamplainTile *self, ClutterActor* actor);
+void champlain_tile_set_etag (ChamplainTile *self, const gchar *etag);
+void champlain_tile_set_modified_time (ChamplainTile *self, GTimeVal *time);
 
 G_END_DECLS