[libgdata] core: Support pagination using page tokens from JSON



commit 38b934a9ef9dab89692d7253dcb332f4ce46f8f0
Author: Philip Withnall <philip tecnocode co uk>
Date:   Thu Dec 8 14:51:00 2016 +0000

    core: Support pagination using page tokens from JSON
    
    Based on a patch by Milan Crha <mcrha redhat com>.
    
    This reworks how pagination is implemented so that multiple pagination
    mechanisms are supported explicitly, making the code a lot clearer. A
    lot of the new services use pageToken parameters, which we did not
    previously support — so this fixes support for pagination in the Google
    Tasks service, for example.
    
    This also means that we can drop the hacky pagination support from
    GDataDocumentsService.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=775813

 docs/reference/gdata-sections.txt                  |    1 +
 gdata/gdata-core.symbols                           |    1 +
 gdata/gdata-feed.c                                 |   47 +++++-
 gdata/gdata-feed.h                                 |    1 +
 gdata/gdata-private.h                              |   12 +-
 gdata/gdata-query.c                                |  201 ++++++++++++++------
 gdata/gdata-service.c                              |   10 +
 gdata/services/calendar/gdata-calendar-query.c     |    4 +
 gdata/services/contacts/gdata-contacts-query.c     |    5 +
 gdata/services/documents/gdata-documents-query.c   |    4 +
 gdata/services/documents/gdata-documents-service.c |   59 ------
 gdata/services/freebase/gdata-freebase-query.c     |    5 +
 gdata/services/picasaweb/gdata-picasaweb-query.c   |    5 +
 gdata/services/tasks/gdata-tasks-query.c           |    4 +
 gdata/services/youtube/gdata-youtube-query.c       |    5 +
 15 files changed, 245 insertions(+), 119 deletions(-)
---
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 441d67d..04b5e47 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -126,6 +126,7 @@ gdata_feed_get_rights
 gdata_feed_get_start_index
 gdata_feed_get_total_results
 gdata_feed_get_items_per_page
+gdata_feed_get_next_page_token
 <SUBSECTION Standard>
 GDATA_FEED
 GDATA_FEED_CLASS
diff --git a/gdata/gdata-core.symbols b/gdata/gdata-core.symbols
index 7f5da8a..e8c4377 100644
--- a/gdata/gdata-core.symbols
+++ b/gdata/gdata-core.symbols
@@ -45,6 +45,7 @@ gdata_feed_get_rights
 gdata_feed_get_items_per_page
 gdata_feed_get_start_index
 gdata_feed_get_total_results
+gdata_feed_get_next_page_token
 gdata_service_get_type
 gdata_service_error_quark
 gdata_service_query
diff --git a/gdata/gdata-feed.c b/gdata/gdata-feed.c
index 14fe8a5..9085103 100644
--- a/gdata/gdata-feed.c
+++ b/gdata/gdata-feed.c
@@ -78,6 +78,7 @@ struct _GDataFeedPrivate {
        guint start_index;
        guint total_results;
        gchar *rights;
+       gchar *next_page_token;
 };
 
 enum {
@@ -92,7 +93,8 @@ enum {
        PROP_ITEMS_PER_PAGE,
        PROP_START_INDEX,
        PROP_TOTAL_RESULTS,
-       PROP_RIGHTS
+       PROP_RIGHTS,
+       PROP_NEXT_PAGE_TOKEN,
 };
 
 G_DEFINE_TYPE (GDataFeed, gdata_feed, GDATA_TYPE_PARSABLE)
@@ -296,6 +298,22 @@ gdata_feed_class_init (GDataFeedClass *klass)
                                                            "Total results", "The total number of results in 
the feed.",
                                                            0, 1000000, 0,
                                                            G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+       /**
+        * GDataFeed:next-page-token:
+        *
+        * The next page token for feeds. Pass this to
+        * gdata_query_set_page_token() to advance to the next page when
+        * querying APIs which use page tokens rather than page numbers or
+        * offsets.
+        *
+        * Since: UNRELEASED
+        */
+       g_object_class_install_property (gobject_class, PROP_NEXT_PAGE_TOKEN,
+                                        g_param_spec_string ("next-page-token",
+                                                             "Next page token", "The next page token for 
feeds.",
+                                                             NULL,
+                                                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -354,6 +372,7 @@ gdata_feed_finalize (GObject *object)
        g_free (priv->logo);
        g_free (priv->icon);
        g_free (priv->rights);
+       g_free (priv->next_page_token);
 
        /* Chain up to the parent class */
        G_OBJECT_CLASS (gdata_feed_parent_class)->finalize (object);
@@ -401,6 +420,9 @@ gdata_feed_get_property (GObject *object, guint property_id, GValue *value, GPar
                case PROP_TOTAL_RESULTS:
                        g_value_set_uint (value, priv->total_results);
                        break;
+               case PROP_NEXT_PAGE_TOKEN:
+                       g_value_set_string (value, priv->next_page_token);
+                       break;
                default:
                        /* We don't have any other property... */
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -643,6 +665,8 @@ parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GEr
                /* Ignore. */
        } else if (g_strcmp0 (json_reader_get_member_name (reader), "etag") == 0) {
                GDATA_FEED (parsable)->priv->etag = g_strdup (json_reader_get_string_value (reader));
+       } else if (g_strcmp0 (json_reader_get_member_name (reader), "nextPageToken") == 0) {
+               GDATA_FEED (parsable)->priv->next_page_token = g_strdup (json_reader_get_string_value 
(reader));
        } else {
                return GDATA_PARSABLE_CLASS (gdata_feed_parent_class)->parse_json (parsable, reader, 
user_data, error);
        }
@@ -1072,6 +1096,27 @@ gdata_feed_get_total_results (GDataFeed *self)
        return self->priv->total_results;
 }
 
+/**
+ * gdata_feed_get_next_page_token:
+ * @self: a #GDataFeed
+ *
+ * Returns the next page token for a query result, or %NULL if not set.
+ * This is #GDataFeed:next-page-token. The page token might not be set if there
+ * is no next page, or if this service does not use token based paging (for
+ * example, if it uses page number or offset based paging instead). Most more
+ * recent services use token based paging.
+ *
+ * Return value: (nullable): the next page token
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_feed_get_next_page_token (GDataFeed *self)
+{
+       g_return_val_if_fail (GDATA_IS_FEED (self), NULL);
+       return self->priv->next_page_token;
+}
+
 void
 _gdata_feed_add_entry (GDataFeed *self, GDataEntry *entry)
 {
diff --git a/gdata/gdata-feed.h b/gdata/gdata-feed.h
index 6a1e050..7bd510b 100644
--- a/gdata/gdata-feed.h
+++ b/gdata/gdata-feed.h
@@ -90,6 +90,7 @@ guint gdata_feed_get_items_per_page (GDataFeed *self) G_GNUC_PURE;
 guint gdata_feed_get_start_index (GDataFeed *self) G_GNUC_PURE;
 guint gdata_feed_get_total_results (GDataFeed *self) G_GNUC_PURE;
 const gchar *gdata_feed_get_icon (GDataFeed *self) G_GNUC_PURE;
+const gchar *gdata_feed_get_next_page_token (GDataFeed *self) G_GNUC_PURE;
 
 G_END_DECLS
 
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index ecb4534..4d65bdd 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -71,13 +71,21 @@ G_GNUC_INTERNAL GDataSecureString _gdata_service_secure_strndup (const gchar *st
 G_GNUC_INTERNAL void _gdata_service_secure_strfree (GDataSecureString str);
 
 #include "gdata-query.h"
+typedef enum {
+       GDATA_QUERY_PAGINATION_INDEXED,
+       GDATA_QUERY_PAGINATION_URIS,
+       GDATA_QUERY_PAGINATION_TOKENS,
+} GDataQueryPaginationType;
+
 G_GNUC_INTERNAL void _gdata_query_add_q_internal (GDataQuery *self, const gchar *q);
 G_GNUC_INTERNAL void _gdata_query_clear_q_internal (GDataQuery *self);
+G_GNUC_INTERNAL void _gdata_query_clear_pagination (GDataQuery *self);
+G_GNUC_INTERNAL void _gdata_query_set_pagination_type (GDataQuery               *self,
+                                                       GDataQueryPaginationType  type);
+G_GNUC_INTERNAL void _gdata_query_set_next_page_token (GDataQuery  *self, const gchar *next_page_token);
 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 e1f5446..ebddeac 100644
--- a/gdata/gdata-query.c
+++ b/gdata/gdata-query.c
@@ -68,22 +68,37 @@ 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.
+       /* Pagination management. The type of pagination is set as
+        * pagination_type, and should be set in the init() vfunc implementation
+        * of any class derived from GDataQuery. It defaults to
+        * %GDATA_QUERY_PAGINATION_INDEXED, which most subclasses will not want.
+        *
+        * The next_uri, previous_uri or next_page_token are set by
+        * #GDataService if a query returns a new #GDataFeed containing them. If
+        * the user then calls next_page() or previous_page(), use_next_page or
+        * use_previous_page are set as appopriate, and the next call to
+        * get_uri() will return a URI for the next or previous page. This might
+        * be next_uri, previous_uri, or a constructed URI which appends the
+        * next_page_token.
+        *
+        * Note that %GDATA_QUERY_PAGINATION_TOKENS does not support returning
+        * to the previous page.
+        *
+        * It is not invalid to have use_next_page set and to not have a
+        * next_uri for %GDATA_QUERY_PAGINATION_URIS; or to not have a
+        * next_page_token for %GDATA_QUERY_PAGINATION_TOKENS: this signifies
+        * that the current set of results are the last page. There are no
+        * further pages. Similarly for use_previous_page and a %NULL
+        * previous_page.
         */
+       GDataQueryPaginationType pagination_type;
+
        gchar *next_uri;
        gchar *previous_uri;
-       gboolean use_next_uri;
-       gboolean use_previous_uri;
+       gchar *next_page_token;
+
+       gboolean use_next_page;
+       gboolean use_previous_page;
 
        gchar *etag;
 };
@@ -295,6 +310,8 @@ gdata_query_init (GDataQuery *self)
        self->priv->updated_max = -1;
        self->priv->published_min = -1;
        self->priv->published_max = -1;
+
+       _gdata_query_set_pagination_type (self, GDATA_QUERY_PAGINATION_INDEXED);
 }
 
 static void
@@ -309,6 +326,7 @@ gdata_query_finalize (GObject *object)
        g_free (priv->next_uri);
        g_free (priv->previous_uri);
        g_free (priv->etag);
+       g_free (priv->next_page_token);
 
        /* Chain up to the parent class */
        G_OBJECT_CLASS (gdata_query_parent_class)->finalize (object);
@@ -490,6 +508,13 @@ get_query_uri (GDataQuery *self, const gchar *feed_uri, GString *query_uri, gboo
                APPEND_SEP
                g_string_append_printf (query_uri, "max-results=%u", priv->max_results);
        }
+
+       if (priv->pagination_type == GDATA_QUERY_PAGINATION_TOKENS && priv->use_next_page &&
+           priv->next_page_token != NULL && *priv->next_page_token != '\0') {
+               APPEND_SEP
+               g_string_append (query_uri, "pageToken=");
+               g_string_append_uri_escaped (query_uri, priv->next_page_token, NULL, FALSE);
+       }
 }
 
 /**
@@ -550,10 +575,12 @@ gdata_query_get_query_uri (GDataQuery *self, const gchar *feed_uri)
        g_return_val_if_fail (feed_uri != NULL, NULL);
 
        /* Check to see if we're paginating first */
-       if (self->priv->use_next_uri == TRUE)
-               return g_strdup (self->priv->next_uri);
-       if (self->priv->use_previous_uri == TRUE)
-               return g_strdup (self->priv->previous_uri);
+       if (self->priv->pagination_type == GDATA_QUERY_PAGINATION_URIS) {
+               if (self->priv->use_next_page)
+                       return g_strdup (self->priv->next_uri);
+               if (self->priv->use_previous_page)
+                       return g_strdup (self->priv->previous_uri);
+       }
 
        klass = GDATA_QUERY_GET_CLASS (self);
        g_assert (klass->get_query_uri != NULL);
@@ -1014,24 +1041,60 @@ gdata_query_set_etag (GDataQuery *self, const gchar *etag)
 }
 
 void
-_gdata_query_set_next_uri (GDataQuery *self, const gchar *next_uri)
+_gdata_query_clear_pagination (GDataQuery *self)
 {
        g_return_if_fail (GDATA_IS_QUERY (self));
-       g_free (self->priv->next_uri);
-       self->priv->next_uri = g_strdup (next_uri);
-       self->priv->use_next_uri = FALSE;
-       self->priv->use_previous_uri = FALSE;
+
+       switch (self->priv->pagination_type) {
+       case GDATA_QUERY_PAGINATION_INDEXED:
+               /* Nothing to do here: indexes can always be incremented. */
+               break;
+       case GDATA_QUERY_PAGINATION_URIS:
+               g_clear_pointer (&self->priv->next_uri, g_free);
+               g_clear_pointer (&self->priv->previous_uri, g_free);
+               break;
+       case GDATA_QUERY_PAGINATION_TOKENS:
+               g_clear_pointer (&self->priv->next_page_token, g_free);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       self->priv->use_next_page = FALSE;
+       self->priv->use_previous_page = FALSE;
 }
 
 void
-_gdata_query_set_next_uri_end (GDataQuery *self)
+_gdata_query_set_pagination_type (GDataQuery               *self,
+                                  GDataQueryPaginationType  type)
+{
+       g_debug ("%s: Pagination type set to %u", G_STRFUNC, type);
+
+       _gdata_query_clear_pagination (self);
+       self->priv->pagination_type = type;
+}
+
+void
+_gdata_query_set_next_page_token (GDataQuery  *self,
+                                  const gchar *next_page_token)
 {
        g_return_if_fail (GDATA_IS_QUERY (self));
+       g_return_if_fail (self->priv->pagination_type ==
+                         GDATA_QUERY_PAGINATION_TOKENS);
+
+       g_free (self->priv->next_page_token);
+       self->priv->next_page_token = g_strdup (next_page_token);
+}
+
+void
+_gdata_query_set_next_uri (GDataQuery *self, const gchar *next_uri)
+{
+       g_return_if_fail (GDATA_IS_QUERY (self));
+       g_return_if_fail (self->priv->pagination_type ==
+                         GDATA_QUERY_PAGINATION_URIS);
 
        g_free (self->priv->next_uri);
-       self->priv->next_uri = NULL;
-       self->priv->use_next_uri = TRUE;
-       self->priv->use_previous_uri = FALSE;
+       self->priv->next_uri = g_strdup (next_uri);
 }
 
 gboolean
@@ -1039,28 +1102,27 @@ _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);
+       switch (self->priv->pagination_type) {
+       case GDATA_QUERY_PAGINATION_INDEXED:
+               return FALSE;
+       case GDATA_QUERY_PAGINATION_URIS:
+               return (self->priv->next_uri == NULL && self->priv->use_next_page);
+       case GDATA_QUERY_PAGINATION_TOKENS:
+               return (self->priv->next_page_token == NULL && self->priv->use_next_page);
+       default:
+               g_assert_not_reached ();
+       }
 }
 
 void
 _gdata_query_set_previous_uri (GDataQuery *self, const gchar *previous_uri)
 {
        g_return_if_fail (GDATA_IS_QUERY (self));
-       g_free (self->priv->previous_uri);
-       self->priv->previous_uri = g_strdup (previous_uri);
-       self->priv->use_next_uri = FALSE;
-       self->priv->use_previous_uri = FALSE;
-}
-
-void
-_gdata_query_set_previous_uri_end (GDataQuery *self)
-{
-       g_return_if_fail (GDATA_IS_QUERY (self));
+       g_return_if_fail (self->priv->pagination_type ==
+                         GDATA_QUERY_PAGINATION_URIS);
 
        g_free (self->priv->previous_uri);
-       self->priv->previous_uri = NULL;
-       self->priv->use_next_uri = TRUE;
-       self->priv->use_previous_uri = FALSE;
+       self->priv->previous_uri = g_strdup (previous_uri);
 }
 
 /**
@@ -1082,13 +1144,19 @@ gdata_query_next_page (GDataQuery *self)
 
        g_return_if_fail (GDATA_IS_QUERY (self));
 
-       if (priv->next_uri != NULL) {
-               priv->use_next_uri = TRUE;
-               priv->use_previous_uri = FALSE;
-       } else {
+       switch (self->priv->pagination_type) {
+       case GDATA_QUERY_PAGINATION_INDEXED:
                if (priv->start_index == 0)
                        priv->start_index++;
                priv->start_index += priv->max_results;
+               break;
+       case GDATA_QUERY_PAGINATION_URIS:
+       case GDATA_QUERY_PAGINATION_TOKENS:
+               priv->use_next_page = TRUE;
+               priv->use_previous_page = FALSE;
+               break;
+       default:
+               g_assert_not_reached ();
        }
 
        /* Our current ETag will no longer be relevant */
@@ -1110,23 +1178,42 @@ gboolean
 gdata_query_previous_page (GDataQuery *self)
 {
        GDataQueryPrivate *priv = self->priv;
+       gboolean retval;
 
        g_return_val_if_fail (GDATA_IS_QUERY (self), FALSE);
 
-       if (priv->previous_uri != NULL) {
-               priv->use_previous_uri = TRUE;
-               priv->use_next_uri = FALSE;
-       } 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;
-               if (priv->start_index == 1)
-                       priv->start_index--;
+       switch (self->priv->pagination_type) {
+       case GDATA_QUERY_PAGINATION_INDEXED:
+               if (priv->start_index <= priv->max_results) {
+                       retval = FALSE;
+               } else {
+                       priv->start_index -= priv->max_results;
+                       if (priv->start_index == 1)
+                               priv->start_index--;
+                       retval = TRUE;
+               }
+               break;
+       case GDATA_QUERY_PAGINATION_URIS:
+               if (priv->previous_uri != NULL) {
+                       priv->use_next_page = FALSE;
+                       priv->use_previous_page = TRUE;
+                       retval = TRUE;
+               } else {
+                       retval = FALSE;
+               }
+               break;
+       case GDATA_QUERY_PAGINATION_TOKENS:
+               /* There are no previous page tokens, unfortunately. */
+               retval = FALSE;
+               break;
+       default:
+               g_assert_not_reached ();
        }
 
-       /* Our current ETag will no longer be relevant */
-       gdata_query_set_etag (self, NULL);
+       if (retval) {
+               /* Our current ETag will no longer be relevant */
+               gdata_query_set_etag (self, NULL);
+       }
 
-       return TRUE;
+       return retval;
 }
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index 1e6feeb..dfc4623 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -1035,13 +1035,23 @@ real_parse_feed (GDataService *self,
        /* Update the query with the next and previous URIs from the feed */
        if (query != NULL && feed != NULL) {
                GDataLink *_link;
+               const gchar *token;
 
+               _gdata_query_clear_pagination (query);
+
+               /* Atom-style next and previous page links. */
                _link = gdata_feed_look_up_link (feed, "http://www.iana.org/assignments/relation/next";);
                if (_link != NULL)
                        _gdata_query_set_next_uri (query, gdata_link_get_uri (_link));
                _link = gdata_feed_look_up_link (feed, "http://www.iana.org/assignments/relation/previous";);
                if (_link != NULL)
                        _gdata_query_set_previous_uri (query, gdata_link_get_uri (_link));
+
+               /* JSON-style next page token. (There is no previous page
+                * token.) */
+               token = gdata_feed_get_next_page_token (feed);
+               if (token != NULL)
+                       _gdata_query_set_next_page_token (query, token);
        }
 
        return feed;
diff --git a/gdata/services/calendar/gdata-calendar-query.c b/gdata/services/calendar/gdata-calendar-query.c
index 89597ae..ea16dc8 100644
--- a/gdata/services/calendar/gdata-calendar-query.c
+++ b/gdata/services/calendar/gdata-calendar-query.c
@@ -85,6 +85,7 @@
 #include "gdata-calendar-query.h"
 #include "gdata-query.h"
 #include "gdata-parser.h"
+#include "gdata-private.h"
 
 static void gdata_calendar_query_finalize (GObject *object);
 static void gdata_calendar_query_get_property (GObject *object, guint property_id, GValue *value, GParamSpec 
*pspec);
@@ -287,6 +288,9 @@ gdata_calendar_query_init (GDataCalendarQuery *self)
        self->priv->recurrence_expansion_end = -1;
        self->priv->start_min = -1;
        self->priv->start_max = -1;
+
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_TOKENS);
 }
 
 static void
diff --git a/gdata/services/contacts/gdata-contacts-query.c b/gdata/services/contacts/gdata-contacts-query.c
index 23f40bb..69c8b0b 100644
--- a/gdata/services/contacts/gdata-contacts-query.c
+++ b/gdata/services/contacts/gdata-contacts-query.c
@@ -84,6 +84,7 @@
 
 #include "gdata-contacts-query.h"
 #include "gdata-query.h"
+#include "gdata-private.h"
 
 static void gdata_contacts_query_finalize (GObject *object);
 static void gdata_contacts_query_get_property (GObject *object, guint property_id, GValue *value, GParamSpec 
*pspec);
@@ -180,6 +181,10 @@ static void
 gdata_contacts_query_init (GDataContactsQuery *self)
 {
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_CONTACTS_QUERY, GDataContactsQueryPrivate);
+
+       /* 
https://developers.google.com/google-apps/contacts/v3/reference#contacts-query-parameters-reference */
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_INDEXED);
 }
 
 static void
diff --git a/gdata/services/documents/gdata-documents-query.c 
b/gdata/services/documents/gdata-documents-query.c
index 634e7a2..6070c1b 100644
--- a/gdata/services/documents/gdata-documents-query.c
+++ b/gdata/services/documents/gdata-documents-query.c
@@ -208,6 +208,10 @@ static void
 gdata_documents_query_init (GDataDocumentsQuery *self)
 {
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_DOCUMENTS_QUERY, 
GDataDocumentsQueryPrivate);
+
+       /* https://developers.google.com/drive/v3/reference/files/list#q */
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_TOKENS);
 }
 
 static void
diff --git a/gdata/services/documents/gdata-documents-service.c 
b/gdata/services/documents/gdata-documents-service.c
index 0de0aec..07c4062 100644
--- a/gdata/services/documents/gdata-documents-service.c
+++ b/gdata/services/documents/gdata-documents-service.c
@@ -270,17 +270,6 @@ gdata_documents_service_error_quark (void)
 
 static void append_query_headers (GDataService *self, GDataAuthorizationDomain *domain, SoupMessage 
*message);
 static GList *get_authorization_domains (void);
-static GDataFeed *
-parse_feed (GDataService *self,
-            GDataAuthorizationDomain *domain,
-            GDataQuery *query,
-            GType entry_type,
-            SoupMessage *message,
-            GCancellable *cancellable,
-            GDataQueryProgressCallback progress_callback,
-            gpointer progress_user_data,
-            GError **error);
-
 static gchar *_get_upload_uri_for_query_and_folder (GDataDocumentsUploadQuery *query,
                                                     GDataDocumentsFolder *folder) G_GNUC_WARN_UNUSED_RESULT 
G_GNUC_MALLOC;
 
@@ -296,7 +285,6 @@ gdata_documents_service_class_init (GDataDocumentsServiceClass *klass)
 
        service_class->append_query_headers = append_query_headers;
        service_class->get_authorization_domains = get_authorization_domains;
-       service_class->parse_feed = parse_feed;
 
        service_class->api_version = "3";
 }
@@ -361,53 +349,6 @@ get_authorization_domains (void)
        return authorization_domains;
 }
 
-static GDataFeed *
-parse_feed (GDataService *self,
-            GDataAuthorizationDomain *domain,
-            GDataQuery *query,
-            GType entry_type,
-            SoupMessage *message,
-            GCancellable *cancellable,
-            GDataQueryProgressCallback progress_callback,
-            gpointer progress_user_data,
-            GError **error)
-{
-       GDataServiceClass *klass;  /* unowned */
-       GDataFeed *feed = NULL;  /* owned */
-
-       klass = GDATA_SERVICE_CLASS (gdata_documents_service_parent_class);
-
-       /* Parse the feed. */
-       feed = klass->parse_feed (self, domain, query, entry_type, message,
-                                 cancellable, progress_callback,
-                                 progress_user_data, error);
-
-       /* Update the query with the next and previous URIs from the feed. If
-        * they are not present, we are on the first or final page of the
-        * feed. (This behaviour is specific to Google Docs.) */
-       if (query != NULL && feed != NULL) {
-               GDataLink *_link;
-
-               _link = gdata_feed_look_up_link (feed, "http://www.iana.org/assignments/relation/next";);
-
-               if (_link != NULL) {
-                       _gdata_query_set_next_uri (query, gdata_link_get_uri (_link));
-               } else {
-                       _gdata_query_set_next_uri_end (query);
-               }
-
-               _link = gdata_feed_look_up_link (feed, "http://www.iana.org/assignments/relation/previous";);
-
-               if (_link != NULL) {
-                       _gdata_query_set_previous_uri (query, gdata_link_get_uri (_link));
-               } else {
-                       _gdata_query_set_previous_uri_end (query);
-               }
-       }
-
-       return feed;
-}
-
 /**
  * gdata_documents_service_new:
  * @authorizer: (allow-none): a #GDataAuthorizer to authorize the service's requests, or %NULL
diff --git a/gdata/services/freebase/gdata-freebase-query.c b/gdata/services/freebase/gdata-freebase-query.c
index 8e66de1..19f0b10 100644
--- a/gdata/services/freebase/gdata-freebase-query.c
+++ b/gdata/services/freebase/gdata-freebase-query.c
@@ -42,6 +42,7 @@
 #include "gdata-freebase-query.h"
 #include "gdata-query.h"
 #include "gdata-parser.h"
+#include "gdata-private.h"
 
 static void gdata_freebase_query_finalize (GObject *self);
 static void gdata_freebase_query_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec 
*pspec);
@@ -94,6 +95,10 @@ static void
 gdata_freebase_query_init (GDataFreebaseQuery *self)
 {
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_FREEBASE_QUERY, GDataFreebaseQueryPrivate);
+
+       /* https://developers.google.com/freebase/v1/search#cursor */
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_INDEXED);
 }
 
 static void
diff --git a/gdata/services/picasaweb/gdata-picasaweb-query.c 
b/gdata/services/picasaweb/gdata-picasaweb-query.c
index d459748..5785157 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-query.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-query.c
@@ -41,6 +41,7 @@
 #include "gdata-picasaweb-query.h"
 #include "gdata-query.h"
 #include "gdata-picasaweb-enums.h"
+#include "gdata-private.h"
 
 static void gdata_picasaweb_query_finalize (GObject *object);
 static void gdata_picasaweb_query_get_property (GObject *object, guint property_id, GValue *value, 
GParamSpec *pspec);
@@ -166,6 +167,10 @@ static void
 gdata_picasaweb_query_init (GDataPicasaWebQuery *self)
 {
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_PICASAWEB_QUERY, 
GDataPicasaWebQueryPrivate);
+
+       /* https://developers.google.com/picasa-web/docs/3.0/reference#Parameters */
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_INDEXED);
 }
 
 static void
diff --git a/gdata/services/tasks/gdata-tasks-query.c b/gdata/services/tasks/gdata-tasks-query.c
index 9e6dbd9..f566605 100644
--- a/gdata/services/tasks/gdata-tasks-query.c
+++ b/gdata/services/tasks/gdata-tasks-query.c
@@ -40,6 +40,7 @@
 #include "gdata-tasks-query.h"
 #include "gdata-query.h"
 #include "gdata-parser.h"
+#include "gdata-private.h"
 
 static void gdata_tasks_query_finalize (GObject *object);
 static void gdata_tasks_query_get_property (GObject *object, guint property_id, GValue *value, GParamSpec 
*pspec);
@@ -185,6 +186,9 @@ gdata_tasks_query_init (GDataTasksQuery *self)
        self->priv->completed_max = -1;
        self->priv->due_min = -1;
        self->priv->due_max = -1;
+
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_TOKENS);
 }
 
 static void
diff --git a/gdata/services/youtube/gdata-youtube-query.c b/gdata/services/youtube/gdata-youtube-query.c
index b15d01e..99ae996 100644
--- a/gdata/services/youtube/gdata-youtube-query.c
+++ b/gdata/services/youtube/gdata-youtube-query.c
@@ -45,6 +45,7 @@
 #include "gdata-youtube-query.h"
 #include "gdata-query.h"
 #include "gdata-youtube-content.h"
+#include "gdata-private.h"
 
 static void gdata_youtube_query_finalize (GObject *object);
 static void gdata_youtube_query_get_property (GObject *object, guint property_id, GValue *value, GParamSpec 
*pspec);
@@ -354,6 +355,10 @@ gdata_youtube_query_init (GDataYouTubeQuery *self)
 
        self->priv->latitude = G_MAXDOUBLE;
        self->priv->longitude = G_MAXDOUBLE;
+
+       /* https://developers.google.com/youtube/v3/docs/search/list#pageToken */
+       _gdata_query_set_pagination_type (GDATA_QUERY (self),
+                                         GDATA_QUERY_PAGINATION_TOKENS);
 }
 
 static void


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