diff options
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/application-service-lru-file.c | 473 | ||||
| -rw-r--r-- | src/application-service-lru-file.h | 59 | 
3 files changed, 0 insertions, 534 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index 620527c..a7cfc0b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,8 +44,6 @@ indicator_application_service_SOURCES = \  	application-service.c \  	application-service-appstore.h \  	application-service-appstore.c \ -	application-service-lru-file.h \ -	application-service-lru-file.c \  	application-service-marshal.c \  	application-service-watcher.h \  	application-service-watcher.c \ diff --git a/src/application-service-lru-file.c b/src/application-service-lru-file.c deleted file mode 100644 index c69a20f..0000000 --- a/src/application-service-lru-file.c +++ /dev/null @@ -1,473 +0,0 @@ -/* -This object manages the database of when the entires were touched -and loved.  And writes that out to disk occationally as well. - -Copyright 2010 Canonical Ltd. - -Authors: -    Ted Gould <ted@canonical.com> - -This program is free software: you can redistribute it and/or modify it  -under the terms of the GNU General Public License version 3, as published  -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but  -WITHOUT ANY WARRANTY; without even the implied warranties of  -MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include <gio/gio.h> -#include <json-glib/json-glib.h> -#include "application-service-lru-file.h" - -#define ENTRY_CATEGORY   "category" -#define ENTRY_FIRST_TIME "first-time" -#define ENTRY_LAST_TIME  "last-time" -#define ENTRY_VERSION    "version" - -#define CONFIG_DIR       ("indicators" G_DIR_SEPARATOR_S "application") -#define CONFIG_FILE      "lru-file.json" - -typedef struct _AppLruFilePrivate AppLruFilePrivate; -struct _AppLruFilePrivate { -	GHashTable * apps; -	gboolean dirty; -	guint timer; -	gchar * filename; -}; - -typedef struct _AppData AppData; -struct _AppData { -	gchar * category; -	GTimeVal last_touched; -	GTimeVal first_touched; -}; - -#define APP_LRU_FILE_GET_PRIVATE(o) \ -		(G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_LRU_FILE_TYPE, AppLruFilePrivate)) - -static void app_lru_file_class_init (AppLruFileClass *klass); -static void app_lru_file_init       (AppLruFile *self); -static void app_lru_file_dispose    (GObject *object); -static void app_lru_file_finalize   (GObject *object); -static void app_data_free           (gpointer data); -static void get_dirty (AppLruFile * lrufile); -static gboolean load_from_file      (gpointer data); -static void load_file_object_cb (JsonObject * obj, const gchar * key, JsonNode * value, gpointer data); -static void clean_off_hash_cb (gpointer key, gpointer value, gpointer data); -static void clean_off_write_end_cb (GObject * obj, GAsyncResult * res, gpointer data); - -G_DEFINE_TYPE (AppLruFile, app_lru_file, G_TYPE_OBJECT); - -/* Set up the class variable stuff */ -static void -app_lru_file_class_init (AppLruFileClass *klass) -{ -	GObjectClass *object_class = G_OBJECT_CLASS (klass); - -	g_type_class_add_private (klass, sizeof (AppLruFilePrivate)); - -	object_class->dispose = app_lru_file_dispose; -	object_class->finalize = app_lru_file_finalize; - -	return; -} - -/* Set all the data of the per-instance variables */ -static void -app_lru_file_init (AppLruFile *self) -{ -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(self); - -	/* Default values */ -	priv->apps = NULL; -	priv->dirty = FALSE; -	priv->timer = 0; -	priv->filename = NULL; - -	/* Now let's build some stuff */ -	priv->apps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, app_data_free); -	priv->filename = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, CONFIG_FILE, NULL); - -	/* No reason to delay other stuff for this, we'll -	   merge any values that get touched. */ -	g_idle_add(load_from_file, self); - -	return; -} - -/* Get rid of objects and other big things */ -static void -app_lru_file_dispose (GObject *object) -{ -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(object); - -	if (priv->timer != 0) { -		g_source_remove(priv->timer); -		priv->timer = 0; -	} - -	if (priv->apps != NULL) { -		/* Cleans up the keys and entries itself */ -		g_hash_table_destroy(priv->apps); -		priv->apps = NULL; -	} - -	G_OBJECT_CLASS (app_lru_file_parent_class)->dispose (object); -	return; -} - -/* Deallocate memory */ -static void -app_lru_file_finalize (GObject *object) -{ -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(object); -	 -	if (priv->filename != NULL) { -		g_free(priv->filename); -		priv->filename = NULL; -	} - -	G_OBJECT_CLASS (app_lru_file_parent_class)->finalize (object); -	return; -} - -/* Takes an AppData structure and free's the -   memory from it. */ -static void -app_data_free (gpointer data) -{ -	AppData * appdata = (AppData *)data; - -	if (appdata->category != NULL) { -		g_free(appdata->category); -	} - -	g_free(appdata); - -	return; -} - -/* Loads all of the data out of a json file */ -static gboolean -load_from_file (gpointer data) -{ -	AppLruFile * lrufile = (AppLruFile *)data; -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); - -	if (!g_file_test(priv->filename, G_FILE_TEST_EXISTS)) { -		return FALSE; -	} - -	JsonParser * parser = json_parser_new(); - -	if (!json_parser_load_from_file(parser, priv->filename, NULL)) { -		g_warning("Unable to parse JSON file '%s'", priv->filename); -		g_object_unref(parser); -		return FALSE; -	} - -	JsonNode * root = json_parser_get_root(parser); -	JsonObject * rootobj = json_node_get_object(root); -	if (rootobj == NULL) { -		g_warning("Malformed LRU file.  The root node is not an object."); -		g_object_unref(parser); -		return FALSE; -	} - -	json_object_foreach_member(rootobj, load_file_object_cb, lrufile); - -	g_object_unref(parser); -	return FALSE; -} - -/* Looks at the various things that we need on a node, makes -   sure that we have them, and then copies them into the -   application hash table of love. */ -static void -load_file_object_cb (JsonObject * rootobj, const gchar * key, JsonNode * value, gpointer data) -{ -	AppLruFile * lrufile = (AppLruFile *)data; -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); - -	/* We're not looking at this today. */ -	if (!g_strcmp0(key, ENTRY_VERSION)) { -		return; -	} - -	JsonObject * obj = json_node_get_object(value); -	if (obj == NULL) { -		g_warning("Data for node '%s' is not an object.", key); -		return; -	} - -	const gchar * obj_category = json_object_get_string_member(obj, ENTRY_CATEGORY); -	const gchar * obj_first    = json_object_get_string_member(obj, ENTRY_FIRST_TIME); -	const gchar * obj_last     = json_object_get_string_member(obj, ENTRY_LAST_TIME); - -	if (obj_category == NULL || obj_first == NULL || obj_last == NULL) { -		g_warning("Node '%s' is missing data.  Got: ('%s', '%s', '%s')", key, obj_category, obj_first, obj_last); -		get_dirty(lrufile); -		return; -	} - -	/* Check to see how old this entry is.  If it hasn't been -	   used in the last year, we remove the cruft. */ -	GTimeVal currenttime; -	g_get_current_time(¤ttime); -	GDate * currentdate = g_date_new(); -	g_date_set_time_val(currentdate, ¤ttime); - -	GTimeVal lasttouch; -	g_time_val_from_iso8601(obj_last, &lasttouch); -	GDate * lastdate = g_date_new(); -	g_date_set_time_val(lastdate, &lasttouch); - -	gint spread = g_date_days_between(lastdate, currentdate); - -	g_date_free(currentdate); -	g_date_free(lastdate); - -	if (spread > 365) { -		g_debug("Removing node '%s' as it's %d days old.", key, spread); -		get_dirty(lrufile); -		return; -	} - -	/* See if we already have one of these.  It's a little bit -	   unlikely, but since we're async, we need to check */ -	gpointer datapntr = g_hash_table_lookup(priv->apps, key); -	if (datapntr == NULL) { -		/* Build a new node */ -		AppData * appdata = g_new0(AppData, 1); -		appdata->category = g_strdup(obj_category); -		g_time_val_from_iso8601(obj_first, &appdata->first_touched); -		g_time_val_from_iso8601(obj_last, &appdata->last_touched); - -		g_hash_table_insert(priv->apps, g_strdup(key), appdata); -	} else { -		/* Merge nodes */ -		AppData * appdata = (AppData *)datapntr; -		GTimeVal temptime; -		g_time_val_from_iso8601(obj_first, &temptime); - -		if (temptime.tv_sec < appdata->first_touched.tv_sec) { -			g_time_val_from_iso8601(obj_first, &appdata->first_touched); -		} -	} - -	return; -} - -/* Write out our cache to a file so that we can unmark the dirty -   bit and be happy. */ -static gboolean -clean_off (gpointer data) -{ -	AppLruFile * lrufile = (AppLruFile *)data; -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); -	priv->timer = 0; - -	GError * error = NULL; -	 -	/* Check to see if our directory exists.  Build it if not. */ -	gchar * dirname = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, NULL); -	if (!g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { -		GFile * dirfile = g_file_new_for_path(dirname); -		g_file_make_directory_with_parents(dirfile, NULL, NULL); -		g_object_unref(dirfile); -	} -	g_free(dirname); - -	GFile * file = g_file_new_for_path(priv->filename); -	GFileOutputStream * ostream = g_file_replace(file, -	                                             NULL, /* etag */ -	                                             TRUE, /* backup */ -	                                             G_FILE_CREATE_NONE, /* flags */ -	                                             NULL, /* cancelable */ -	                                             &error); -	if (error != NULL) { -		g_warning("Unable to open a file to store LRU file: %s", error->message); -		return FALSE; -	} - -	/* This is how the file will start */ -	GString * filestring = g_string_new("{\n  \"" ENTRY_VERSION "\": 1"); - -	/* Put the middle in. */ -	g_hash_table_foreach (priv->apps, clean_off_hash_cb, filestring); - -	/* And then tack on the end. */ -	g_string_append(filestring, "\n}\n"); -	gchar * filedata = g_string_free(filestring, FALSE); -	g_output_stream_write_async(G_OUTPUT_STREAM(ostream), -	                            filedata, -	                            strlen(filedata), -	                            G_PRIORITY_DEFAULT_IDLE, -	                            NULL, -	                            clean_off_write_end_cb, -	                            filedata); - -	return FALSE; /* drop the timer */ -} - -/* Looks at every value in the applications hash table and -   turns it into a string for writing out. */ -static void -clean_off_hash_cb (gpointer key, gpointer value, gpointer data) -{ -	/* Mega-cast */ -	gchar * id = (gchar *)key; -	AppData * appdata = (AppData *)value; -	GString * string = (GString *)data; - -	gchar * firsttime = g_time_val_to_iso8601(&appdata->first_touched); -	gchar * lasttime = g_time_val_to_iso8601(&appdata->last_touched); - -	g_string_append_printf(string, ",\n  \"%s\": { \"" ENTRY_FIRST_TIME "\": \"%s\", \"" ENTRY_LAST_TIME "\": \"%s\", \"" ENTRY_CATEGORY "\": \"%s\"}", id, firsttime, lasttime, appdata->category); - -	g_free(lasttime); -	g_free(firsttime); - -	return; -} - -/* Very much like clean_off_write_cb except that it is the -   last actor on this Output Stream so it closes it. */ -static void -clean_off_write_end_cb (GObject * obj, GAsyncResult * res, gpointer data) -{ -	g_free(data); - -	GError * error = NULL; -	g_output_stream_close(G_OUTPUT_STREAM(obj), NULL, &error); - -	if (error != NULL) { -		g_warning("Unable to close LRU File: %s", error->message); -		g_error_free(error); -	} - -	return; -} - -/* Sets the dirty bit if not already set and makes sure that -   we have a timer to fix that at some point. */ -static void -get_dirty (AppLruFile * lrufile) -{ -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); - -	priv->dirty = TRUE; - -	if (priv->timer == 0) { -		priv->timer = g_timeout_add_seconds(60, clean_off, lrufile); -	} - -	return; -} - -/* API */ - -/* Simple helper to create a new object */ -AppLruFile * -app_lru_file_new (void) -{ -	return APP_LRU_FILE(g_object_new(APP_LRU_FILE_TYPE, NULL)); -} - -/* This updates the timestamp for a particular -   entry in the database.  It also queues up a  -   write out of the database.  But that'll happen -   later. */ -void -app_lru_file_touch (AppLruFile * lrufile, const gchar * id, const gchar * category) -{ -	g_return_if_fail(IS_APP_LRU_FILE(lrufile)); -	g_return_if_fail(id != NULL && id[0] != '\0'); -	g_return_if_fail(category != NULL && category[0] != '\0'); - -	AppData * appdata = NULL; -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); -	gpointer data = g_hash_table_lookup(priv->apps, id); - -	if (data == NULL) { -		/* Oh, we don't have one, let's build it and put it -		   into the hash table ourselves */ -		appdata = g_new0(AppData, 1); - -		appdata->category = g_strdup(category); -		g_get_current_time(&(appdata->first_touched)); -		/* NOTE: last touched set below */ - -		g_hash_table_insert(priv->apps, g_strdup(id), appdata); -	} else { -		/* Boring, we've got this one already */ -		appdata = (AppData *)data; -	} - -	/* Touch it and mark the DB as dirty */ -	g_get_current_time(&(appdata->last_touched)); -	get_dirty(lrufile); -	return; -} - -/* Used to sort or compare different applications. */ -gint -app_lru_file_sort (AppLruFile * lrufile, const gchar * id_a, const gchar * id_b) -{ -	g_return_val_if_fail(IS_APP_LRU_FILE(lrufile), -1); - -	/* Let's first look to see if the IDs are the same, this -	   really shouldn't happen, but it'll be confusing if we -	   don't get it out of the way to start. */ -	if (g_strcmp0(id_a, id_b) == 0) { -		return 0; -	} - -	AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile); - -	/* Now make sure we have app data for both of these.  If -	   not we'll assume that the one without is newer? */ -	gpointer data_a = g_hash_table_lookup(priv->apps, id_a); -	if (data_a == NULL) { -		return -1; -	} - -	gpointer data_b = g_hash_table_lookup(priv->apps, id_b); -	if (data_b == NULL) { -		return 1; -	} - -	/* Ugly casting */ -	AppData * appdata_a = (AppData *)data_a; -	AppData * appdata_b = (AppData *)data_b; - -	/* Look at categories, we'll put the categories in alpha -	   order if they're not the same. */ -	gint catcompare = g_strcmp0(appdata_a->category, appdata_b->category); -	if (catcompare != 0) { -		return catcompare; -	} - -	/* Now we're looking at the first time that these two were -	   seen in this account.  Only using seconds.  I mean, seriously. */ -	if (appdata_a->first_touched.tv_sec < appdata_b->first_touched.tv_sec) { -		return -1; -	} -	if (appdata_b->first_touched.tv_sec < appdata_a->first_touched.tv_sec) { -		return 1; -	} - -	/* Eh, this seems roughly impossible.  But if we have to choose, -	   I like A better. */ -	return 1; -} diff --git a/src/application-service-lru-file.h b/src/application-service-lru-file.h deleted file mode 100644 index 7c92c82..0000000 --- a/src/application-service-lru-file.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -This object manages the database of when the entires were touched -and loved.  And writes that out to disk occationally as well. - -Copyright 2010 Canonical Ltd. - -Authors: -    Ted Gould <ted@canonical.com> - -This program is free software: you can redistribute it and/or modify it  -under the terms of the GNU General Public License version 3, as published  -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but  -WITHOUT ANY WARRANTY; without even the implied warranties of  -MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef __APP_LRU_FILE_H__ -#define __APP_LRU_FILE_H__ - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define APP_LRU_FILE_TYPE            (app_lru_file_get_type ()) -#define APP_LRU_FILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_LRU_FILE_TYPE, AppLruFile)) -#define APP_LRU_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), APP_LRU_FILE_TYPE, AppLruFileClass)) -#define IS_APP_LRU_FILE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_LRU_FILE_TYPE)) -#define IS_APP_LRU_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_LRU_FILE_TYPE)) -#define APP_LRU_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_LRU_FILE_TYPE, AppLruFileClass)) - -typedef struct _AppLruFile      AppLruFile; -typedef struct _AppLruFileClass AppLruFileClass; - -struct _AppLruFileClass { -	GObjectClass parent_class; - -}; - -struct _AppLruFile { -	GObject parent; - -}; - -GType app_lru_file_get_type (void); - -AppLruFile * app_lru_file_new (void); -void app_lru_file_touch (AppLruFile * lrufile, const gchar * id, const gchar * category); -gint app_lru_file_sort  (AppLruFile * lrufile, const gchar * id_a, const gchar * id_b); - -G_END_DECLS - -#endif | 
