summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2014-12-12 23:18:05 +0000
committerPhilip Withnall <philip@tecnocode.co.uk>2014-12-12 23:22:21 +0000
commitb449554962090aed5500a6fd10cd04a7bb27c6d7 (patch)
tree29719ab57b4a1ffa30b190051e048b9079fa70e1
parent18edbc4e90f582bc67b08f6158c97666ebd8acaf (diff)
downloadlibgdata-b449554962090aed5500a6fd10cd04a7bb27c6d7.tar.gz
core: Explicitly support a final page in GDataQuery pagination
The pagination model previously used by GDataQuery was to assume that the server would return an empty GDataFeed once the final page was passed by calling gdata_query_next_page(). Unfortunately, the Google Documents servers don’t do that — instead, they provide a next page URI in each GDataFeed, and don’t provide one on the final page. GDataQuery did not support this kind of explicit knowledge about the current page being the final one. Add support for it with a delightful set of hacky internal functions. Return a dummy empty feed from GDataService if passed a GDataQuery which is flagged as having reached the end of its pagination. https://bugzilla.gnome.org/show_bug.cgi?id=741345
-rw-r--r--gdata/gdata-private.h3
-rw-r--r--gdata/gdata-query.c45
-rw-r--r--gdata/gdata-service.c15
3 files changed, 61 insertions, 2 deletions
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index f470561b..8ad24876 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -72,7 +72,10 @@ G_GNUC_INTERNAL void _gdata_service_secure_strfree (GDataSecureString str);
#include "gdata-query.h"
G_GNUC_INTERNAL void _gdata_query_set_next_uri (GDataQuery *self, const gchar *next_uri);
+G_GNUC_INTERNAL void _gdata_query_set_next_uri_end (GDataQuery *self);
+G_GNUC_INTERNAL gboolean _gdata_query_is_finished (GDataQuery *self);
G_GNUC_INTERNAL void _gdata_query_set_previous_uri (GDataQuery *self, const gchar *previous_uri);
+G_GNUC_INTERNAL void _gdata_query_set_previous_uri_end (GDataQuery *self);
#include "gdata-parsable.h"
G_GNUC_INTERNAL GDataParsable *_gdata_parsable_new_from_xml (GType parsable_type, const gchar *xml, gint length, gpointer user_data,
diff --git a/gdata/gdata-query.c b/gdata/gdata-query.c
index bf42fe2b..a2a7cd8b 100644
--- a/gdata/gdata-query.c
+++ b/gdata/gdata-query.c
@@ -66,6 +66,18 @@ struct _GDataQueryPrivate {
gboolean is_strict;
guint max_results;
+ /* Pagination management. Supports three states:
+ * 1. (next_uri == NULL && !use_next_uri):
+ * Implement pagination by incrementing #GDataQuery:start-index
+ * internally with each call to gdata_query_next_page(). Stop
+ * when the returned #GDataFeed is empty.
+ * 2a. (next_uri != NULL && use_next_uri):
+ * Implement pagination with an explicit URI for the next page,
+ * which will be used when gdata_query_next_page() is called.
+ * 2b. (next_uri == NULL && use_next_uri):
+ * End of pagination using known URIs; return an empty
+ * #GDataFeed when gdata_query_next_page() is called.
+ */
gchar *next_uri;
gchar *previous_uri;
gboolean use_next_uri;
@@ -967,6 +979,25 @@ _gdata_query_set_next_uri (GDataQuery *self, const gchar *next_uri)
}
void
+_gdata_query_set_next_uri_end (GDataQuery *self)
+{
+ g_return_if_fail (GDATA_IS_QUERY (self));
+
+ g_free (self->priv->next_uri);
+ self->priv->next_uri = NULL;
+ self->priv->use_next_uri = TRUE;
+ self->priv->use_previous_uri = FALSE;
+}
+
+gboolean
+_gdata_query_is_finished (GDataQuery *self)
+{
+ g_return_val_if_fail (GDATA_IS_QUERY (self), FALSE);
+
+ return (self->priv->next_uri == NULL && self->priv->use_next_uri);
+}
+
+void
_gdata_query_set_previous_uri (GDataQuery *self, const gchar *previous_uri)
{
g_return_if_fail (GDATA_IS_QUERY (self));
@@ -976,6 +1007,17 @@ _gdata_query_set_previous_uri (GDataQuery *self, const gchar *previous_uri)
self->priv->use_previous_uri = FALSE;
}
+void
+_gdata_query_set_previous_uri_end (GDataQuery *self)
+{
+ g_return_if_fail (GDATA_IS_QUERY (self));
+
+ g_free (self->priv->previous_uri);
+ self->priv->previous_uri = NULL;
+ self->priv->use_next_uri = TRUE;
+ self->priv->use_previous_uri = FALSE;
+}
+
/**
* gdata_query_next_page:
* @self: a #GDataQuery
@@ -1029,7 +1071,8 @@ gdata_query_previous_page (GDataQuery *self)
if (priv->previous_uri != NULL) {
priv->use_previous_uri = TRUE;
priv->use_next_uri = FALSE;
- } else if (priv->start_index <= priv->max_results) {
+ } else if (priv->start_index <= priv->max_results ||
+ (priv->previous_uri == NULL && priv->use_previous_uri)) {
return FALSE;
} else {
priv->start_index -= priv->max_results;
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index ad4c1bc4..300824b2 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -963,14 +963,27 @@ __gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, con
SoupMessage *message;
GDataFeed *feed;
+ klass = GDATA_SERVICE_GET_CLASS (self);
+
+ /* Are we off the end of the final page? */
+ if (query != NULL && _gdata_query_is_finished (query)) {
+ GTimeVal updated;
+
+ /* Build an empty dummy feed to signify the end of the list. */
+ g_get_current_time (&updated);
+ return _gdata_feed_new (klass->feed_type, "Empty feed", "feed1",
+ updated.tv_sec);
+ }
+
+ /* Send the request. */
message = _gdata_service_query (self, domain, feed_uri, query, cancellable, error);
if (message == NULL)
return NULL;
g_assert (message->response_body->data != NULL);
- klass = GDATA_SERVICE_GET_CLASS (self);
g_assert (klass->parse_feed != NULL);
+ /* Parse the response. */
feed = klass->parse_feed (self, domain, query, entry_type,
message, cancellable, progress_callback,
progress_user_data, error);