[libgdata] core: Explicitly support a final page in GDataQuery pagination



commit b449554962090aed5500a6fd10cd04a7bb27c6d7
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Dec 12 23:18:05 2014 +0000

    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

 gdata/gdata-private.h |    3 +++
 gdata/gdata-query.c   |   45 ++++++++++++++++++++++++++++++++++++++++++++-
 gdata/gdata-service.c |   15 ++++++++++++++-
 3 files changed, 61 insertions(+), 2 deletions(-)
---
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index f470561..8ad2487 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 bf42fe2..a2a7cd8 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 ad4c1bc..300824b 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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]