[libgdata] freebase: Add Topic query API
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgdata] freebase: Add Topic query API
- Date: Tue, 27 May 2014 00:46:58 +0000 (UTC)
commit f4635b25e2a5809f111f232c9f81d6b0a650dfcf
Author: Carlos Garnacho <carlosg gnome org>
Date: Mon Mar 17 02:34:31 2014 +0100
freebase: Add Topic query API
With this API, structured data can be obtained about any Freebase
ID, including localized text and references to images.
https://bugzilla.gnome.org/show_bug.cgi?id=726486
Makefile.am | 4 +
docs/reference/gdata-docs.xml | 2 +
docs/reference/gdata-sections.txt | 70 ++
gdata/gdata.h | 2 +
gdata/gdata.symbols | 34 +
gdata/services/freebase/gdata-freebase-service.c | 62 ++
gdata/services/freebase/gdata-freebase-service.h | 7 +
.../services/freebase/gdata-freebase-topic-query.c | 338 +++++++
.../services/freebase/gdata-freebase-topic-query.h | 75 ++
.../freebase/gdata-freebase-topic-result.c | 1019 ++++++++++++++++++++
.../freebase/gdata-freebase-topic-result.h | 126 +++
11 files changed, 1739 insertions(+), 0 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index e255abc..b2b685d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -309,6 +309,8 @@ gdatafreebaseincludedir = $(gdataincludedir)/services/freebase
gdata_freebase_headers = \
gdata/services/freebase/gdata-freebase-service.h \
gdata/services/freebase/gdata-freebase-result.h \
+ gdata/services/freebase/gdata-freebase-topic-query.h \
+ gdata/services/freebase/gdata-freebase-topic-result.h \
gdata/services/freebase/gdata-freebase-query.h
gdatafreebaseinclude_HEADERS = \
$(gdata_freebase_headers)
@@ -424,6 +426,8 @@ gdata_sources = \
\
gdata/services/freebase/gdata-freebase-service.c \
gdata/services/freebase/gdata-freebase-result.c \
+ gdata/services/freebase/gdata-freebase-topic-query.c \
+ gdata/services/freebase/gdata-freebase-topic-result.c \
gdata/services/freebase/gdata-freebase-query.c
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index fb1edd3..262ec28 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -189,6 +189,8 @@
<xi:include href="xml/gdata-freebase-service.xml"/>
<xi:include href="xml/gdata-freebase-query.xml"/>
<xi:include href="xml/gdata-freebase-result.xml"/>
+ <xi:include href="xml/gdata-freebase-topic-query.xml"/>
+ <xi:include href="xml/gdata-freebase-topic-result.xml"/>
</chapter>
</part>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 0c452aa..4cc4604 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -2575,6 +2575,8 @@ gdata_freebase_service_new
gdata_freebase_service_get_primary_authorization_domain
gdata_freebase_service_query
gdata_freebase_service_query_async
+gdata_freebase_service_get_topic
+gdata_freebase_service_get_topic_async
<SUBSECTION Standard>
gdata_freebase_service_get_type
GDATA_FREEBASE_SERVICE
@@ -2624,3 +2626,71 @@ GDATA_TYPE_FREEBASE_RESULT
<SUBSECTION Private>
GDataFreebaseResultPrivate
</SECTION>
+
+<SECTION>
+<FILE>gdata-freebase-topic-query</FILE>
+<TITLE>GDataFreebaseTopicQuery</TITLE>
+GDataFreebaseTopicQuery
+GDataFreebaseTopicQueryClass
+gdata_freebase_topic_query_new
+gdata_freebase_topic_query_set_filter
+gdata_freebase_topic_query_get_filter
+gdata_freebase_topic_query_set_language
+gdata_freebase_topic_query_get_language
+<SUBSECTION Standard>
+gdata_freebase_topic_query_get_type
+GDATA_FREEBASE_TOPIC_QUERY
+GDATA_FREEBASE_TOPIC_QUERY_CLASS
+GDATA_FREEBASE_TOPIC_QUERY_GET_CLASS
+GDATA_IS_FREEBASE_TOPIC_QUERY
+GDATA_IS_FREEBASE_TOPIC_QUERY_CLASS
+GDATA_TYPE_FREEBASE_TOPIC_QUERY
+<SUBSECTION Private>
+GDataFreebaseTopicQueryPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-freebase-topic-result</FILE>
+<TITLE>GDataFreebaseTopicResult</TITLE>
+GDataFreebaseTopicResult
+GDataFreebaseTopicResultClass
+gdata_freebase_topic_result_new
+gdata_freebase_topic_result_dup_object
+GDataFreebaseTopicObject
+gdata_freebase_topic_object_ref
+gdata_freebase_topic_object_unref
+gdata_freebase_topic_object_list_properties
+gdata_freebase_topic_object_get_id
+gdata_freebase_topic_object_get_property_count
+gdata_freebase_topic_object_get_property_hits
+gdata_freebase_topic_object_get_property_value
+GDataFreebaseTopicValue
+gdata_freebase_topic_value_ref
+gdata_freebase_topic_value_unref
+gdata_freebase_topic_value_get_property
+gdata_freebase_topic_value_get_text
+gdata_freebase_topic_value_get_language
+gdata_freebase_topic_value_get_creator
+gdata_freebase_topic_value_get_timestamp
+gdata_freebase_topic_value_get_value_type
+gdata_freebase_topic_value_copy_value
+gdata_freebase_topic_value_get_int
+gdata_freebase_topic_value_get_double
+gdata_freebase_topic_value_get_string
+gdata_freebase_topic_value_get_object
+gdata_freebase_topic_value_is_image
+<SUBSECTION Standard>
+gdata_freebase_topic_result_get_type
+gdata_freebase_topic_object_get_type
+gdata_freebase_topic_value_get_type
+GDATA_FREEBASE_TOPIC_RESULT
+GDATA_FREEBASE_TOPIC_RESULT_CLASS
+GDATA_FREEBASE_TOPIC_RESULT_GET_CLASS
+GDATA_IS_FREEBASE_TOPIC_RESULT
+GDATA_IS_FREEBASE_TOPIC_RESULT_CLASS
+GDATA_TYPE_FREEBASE_TOPIC_RESULT
+GDATA_TYPE_FREEBASE_TOPIC_OBJECT
+GDATA_TYPE_FREEBASE_TOPIC_VALUE
+<SUBSECTION Private>
+GDataFreebaseTopicResultPrivate
+</SECTION>
diff --git a/gdata/gdata.h b/gdata/gdata.h
index 7c43d4c..579ecad 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -147,5 +147,7 @@
#include <gdata/services/freebase/gdata-freebase-service.h>
#include <gdata/services/freebase/gdata-freebase-query.h>
#include <gdata/services/freebase/gdata-freebase-result.h>
+#include <gdata/services/freebase/gdata-freebase-topic-query.h>
+#include <gdata/services/freebase/gdata-freebase-topic-result.h>
#endif /* !GDATA_H */
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index b996686..b149eb6 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -1024,9 +1024,43 @@ gdata_freebase_service_get_type
gdata_freebase_service_new
gdata_freebase_service_query
gdata_freebase_service_query_async
+gdata_freebase_service_get_topic
+gdata_freebase_service_get_topic_async
gdata_freebase_query_get_type
gdata_freebase_query_new
gdata_freebase_query_new_from_variant
+gdata_freebase_topic_query_get_type
+gdata_freebase_topic_query_new
+gdata_freebase_topic_query_set_filter
+gdata_freebase_topic_query_get_filter
+gdata_freebase_topic_query_set_language
+gdata_freebase_topic_query_get_language
+gdata_freebase_topic_object_get_type
+gdata_freebase_topic_object_ref
+gdata_freebase_topic_object_unref
+gdata_freebase_topic_object_list_properties
+gdata_freebase_topic_object_get_id
+gdata_freebase_topic_object_get_property_count
+gdata_freebase_topic_object_get_property_hits
+gdata_freebase_topic_object_get_property_value
+gdata_freebase_topic_value_get_type
+gdata_freebase_topic_value_ref
+gdata_freebase_topic_value_unref
+gdata_freebase_topic_value_get_property
+gdata_freebase_topic_value_get_text
+gdata_freebase_topic_value_get_language
+gdata_freebase_topic_value_get_creator
+gdata_freebase_topic_value_get_timestamp
+gdata_freebase_topic_value_get_value_type
+gdata_freebase_topic_value_copy_value
+gdata_freebase_topic_value_get_int
+gdata_freebase_topic_value_get_double
+gdata_freebase_topic_value_get_string
+gdata_freebase_topic_value_get_object
+gdata_freebase_topic_value_is_image
+gdata_freebase_topic_result_get_type
+gdata_freebase_topic_result_new
+gdata_freebase_topic_result_dup_object
gdata_freebase_result_error_get_type
gdata_freebase_result_error_quark
gdata_freebase_result_get_type
diff --git a/gdata/services/freebase/gdata-freebase-service.c
b/gdata/services/freebase/gdata-freebase-service.c
index f4006db..96e1a1e 100644
--- a/gdata/services/freebase/gdata-freebase-service.c
+++ b/gdata/services/freebase/gdata-freebase-service.c
@@ -253,3 +253,65 @@ gdata_freebase_service_query_async (GDataFreebaseService *self, GDataFreebaseQue
gdata_service_query_single_entry_async (GDATA_SERVICE (self), get_freebase_authorization_domain (),
"mqlread",
GDATA_QUERY (query), GDATA_TYPE_FREEBASE_RESULT, cancellable,
callback, user_data);
}
+
+/**
+ * gdata_freebase_service_get_topic:
+ * @self: a #GDataFreebaseService
+ * @query: a #GDataFreebaseTopicQuery containing the topic ID
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: (allow-none): a #GError, or %NULL
+ *
+ * Queries information about a topic, identified through a Freebase ID. You can find out more about topic
queries in the
+ * <ulink type="http" url="https://developers.google.com/freebase/v1/topic-response">online
documentation</ulink>.
+ *
+ * Return value: (transfer full): a #GDataFreebaseTopicResult containing information about the topic; unref
with g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataFreebaseTopicResult *
+gdata_freebase_service_get_topic (GDataFreebaseService *self, GDataFreebaseTopicQuery *query, GCancellable
*cancellable, GError **error)
+{
+ GDataEntry *entry;
+
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SERVICE (self), NULL);
+ g_return_val_if_fail (GDATA_IS_FREEBASE_TOPIC_QUERY (query), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ entry = gdata_service_query_single_entry (GDATA_SERVICE (self), get_freebase_authorization_domain (),
"topic",
+ GDATA_QUERY (query), GDATA_TYPE_FREEBASE_TOPIC_RESULT,
cancellable, error);
+ if (entry == NULL)
+ return NULL;
+
+ return GDATA_FREEBASE_TOPIC_RESULT (entry);
+}
+
+/**
+ * gdata_freebase_service_get_topic_async:
+ * @self: a #GDataFreebaseService
+ * @query: a #GDataFreebaseQuery with the MQL query
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when authentication is finished
+ * @user_data: (closure): data to pass to the @callback function
+ *
+ * Queries information about a topic, identified through a Freebase ID. @self and @query are all reffed when
this
+ * function is called, so can safely be unreffed after this function returns. When the query is replied, or
fails,
+ * @callback will be executed, and the result can be obtained through
gdata_service_query_single_entry_finish().
+ *
+ * For more details, see gdata_freebase_service_get_topic(), which is the synchronous version of
+ * this function.
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_freebase_service_get_topic_async (GDataFreebaseService *self, GDataFreebaseTopicQuery *query,
+ GCancellable *cancellable, GAsyncReadyCallback callback, gpointer
user_data)
+{
+ g_return_if_fail (GDATA_IS_FREEBASE_SERVICE (self));
+ g_return_if_fail (GDATA_IS_FREEBASE_TOPIC_QUERY (query));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (callback != NULL);
+
+ gdata_service_query_single_entry_async (GDATA_SERVICE (self), get_freebase_authorization_domain (),
"topic",
+ GDATA_QUERY (query), GDATA_TYPE_FREEBASE_RESULT, cancellable,
callback, user_data);
+}
diff --git a/gdata/services/freebase/gdata-freebase-service.h
b/gdata/services/freebase/gdata-freebase-service.h
index 1ccce64..af553bb 100644
--- a/gdata/services/freebase/gdata-freebase-service.h
+++ b/gdata/services/freebase/gdata-freebase-service.h
@@ -28,6 +28,8 @@
#include <gdata/gdata-download-stream.h>
#include "gdata-freebase-query.h"
#include "gdata-freebase-result.h"
+#include "gdata-freebase-topic-query.h"
+#include "gdata-freebase-topic-result.h"
G_BEGIN_DECLS
@@ -76,6 +78,11 @@ GDataFreebaseResult *gdata_freebase_service_query (GDataFreebaseService *self, G
void gdata_freebase_service_query_async (GDataFreebaseService *self, GDataFreebaseQuery *query,
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer
user_data);
+GDataFreebaseTopicResult *gdata_freebase_service_get_topic (GDataFreebaseService *self,
GDataFreebaseTopicQuery *query,
+ GCancellable *cancellable, GError **error)
G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+void gdata_freebase_service_get_topic_async (GDataFreebaseService *self, GDataFreebaseTopicQuery *query,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
gpointer user_data);
+
G_END_DECLS
#endif /* !GDATA_FREEBASE_SERVICE_H */
diff --git a/gdata/services/freebase/gdata-freebase-topic-query.c
b/gdata/services/freebase/gdata-freebase-topic-query.c
new file mode 100644
index 0000000..1fddd1d
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-topic-query.c
@@ -0,0 +1,338 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg gnome org>
+ *
+ * GData Client 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.
+ *
+ * GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-freebase-topic-query
+ * @short_description: GData Freebase topic query object
+ * @stability: Unstable
+ * @include: gdata/services/freebase/gdata-freebase-topic-query.h
+ *
+ * #GDataFreebaseTopicQuery represents a Freebase topic query. The topic query happens on a single Freebase
ID,
+ * given in gdata_freebase_topic_query_new(), the reply returns all known information in Freebase for that
given ID.
+ * For more documentation and examples, see the <ulink type="http"
url="https://developers.google.com/freebase/v1/topic-response">
+ * Topic response API documentation</ulink>
+ *
+ * This implementation of #GDataQuery respects the gdata_query_set_max_results() and
gdata_query_set_updated_max() calls.
+ *
+ * For more details of Google Freebase API, see the <ulink type="http"
url="https://developers.google.com/freebase/v1/">
+ * online documentation</ulink>.
+ *
+ * Since: UNRELEASED
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <json-glib/json-glib.h>
+
+#include "gdata-freebase-topic-query.h"
+#include "gdata-query.h"
+#include "gdata-parser.h"
+
+static void gdata_freebase_topic_query_finalize (GObject *self);
+static void gdata_freebase_topic_query_set_property (GObject *self, guint prop_id, const GValue *value,
GParamSpec *pspec);
+static void gdata_freebase_topic_query_get_property (GObject *self, guint prop_id, GValue *value, GParamSpec
*pspec);
+static void get_query_uri (GDataQuery *self, const gchar *feed_uri, GString *query_uri, gboolean
*params_started);
+
+struct _GDataFreebaseTopicQueryPrivate {
+ gchar *lang;
+ gchar **filter;
+};
+
+enum {
+ PROP_LANGUAGE = 1,
+ PROP_FILTER
+};
+
+G_DEFINE_TYPE (GDataFreebaseTopicQuery, gdata_freebase_topic_query, GDATA_TYPE_QUERY)
+
+static void
+gdata_freebase_topic_query_class_init (GDataFreebaseTopicQueryClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataQueryClass *query_class = GDATA_QUERY_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataFreebaseTopicQueryPrivate));
+
+ gobject_class->finalize = gdata_freebase_topic_query_finalize;
+ gobject_class->set_property = gdata_freebase_topic_query_set_property;
+ gobject_class->get_property = gdata_freebase_topic_query_get_property;
+
+ query_class->get_query_uri = get_query_uri;
+
+ /**
+ * GDataFreebaseTopicQuery:language:
+ *
+ * Language used for topic values in the result, in ISO-639-1 format.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_LANGUAGE,
+ g_param_spec_string ("language",
+ "Language used for results",
+ "Language in ISO-639-1 format.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataFreebaseTopicQuery:filter:
+ *
+ * Array of properties (eg. "/common/topic/description", or "/computer/software/first_released"), or
property
+ * domains (eg. "/common/topic", or "/computer") to be used as filter.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_FILTER,
+ g_param_spec_boxed ("filter",
+ "Filter",
+ "Property domain to be used as filter",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_freebase_topic_query_init (GDataFreebaseTopicQuery *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_FREEBASE_TOPIC_QUERY,
GDataFreebaseTopicQueryPrivate);
+}
+
+static void
+gdata_freebase_topic_query_finalize (GObject *self)
+{
+ GDataFreebaseTopicQueryPrivate *priv = GDATA_FREEBASE_TOPIC_QUERY (self)->priv;
+
+ g_free (priv->lang);
+ g_free (priv->filter);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_freebase_topic_query_parent_class)->finalize (self);
+}
+
+static void
+gdata_freebase_topic_query_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec
*pspec)
+{
+ GDataFreebaseTopicQuery *query = GDATA_FREEBASE_TOPIC_QUERY (self);
+
+ switch (prop_id) {
+ case PROP_LANGUAGE:
+ gdata_freebase_topic_query_set_language (query, g_value_get_string (value));
+ break;
+ case PROP_FILTER:
+ gdata_freebase_topic_query_set_filter (query, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_freebase_topic_query_get_property (GObject *self, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GDataFreebaseTopicQueryPrivate *priv = GDATA_FREEBASE_TOPIC_QUERY (self)->priv;
+
+ switch (prop_id) {
+ case PROP_LANGUAGE:
+ g_value_set_string (value, priv->lang);
+ break;
+ case PROP_FILTER:
+ g_value_set_boxed (value, priv->filter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_query_uri (GDataQuery *self, const gchar *feed_uri, GString *query_uri, gboolean *params_started)
+{
+ GDataFreebaseTopicQueryPrivate *priv = GDATA_FREEBASE_TOPIC_QUERY (self)->priv;
+ const gchar *lang = NULL;
+ gint64 updated_max;
+ guint limit;
+
+ g_string_append (query_uri, gdata_query_get_q (self));
+
+#define APPEND_SEP g_string_append_c (query_uri, (*params_started == FALSE) ? '?' : '&'); *params_started =
TRUE;
+
+ if (priv->lang != NULL) {
+ lang = priv->lang;
+ } else {
+ const gchar * const *user_languages;
+ guint i;
+
+ user_languages = g_get_language_names ();
+
+ /* Pick the first user language */
+ for (i = 0; user_languages[i] != NULL; i++) {
+ if (strlen (user_languages[i]) == 2) {
+ lang = user_languages[i];
+ break;
+ }
+ }
+ }
+
+ APPEND_SEP;
+ g_string_append (query_uri, "lang=");
+ g_string_append (query_uri, lang);
+
+ if (priv->filter) {
+ guint i;
+
+ for (i = 0; priv->filter[i] != NULL; i++) {
+ APPEND_SEP;
+ g_string_append (query_uri, "filter=");
+ g_string_append (query_uri, priv->filter[i]);
+ }
+ }
+
+ updated_max = gdata_query_get_updated_max (self);
+
+ if (updated_max > -1) {
+ APPEND_SEP;
+ g_string_append_printf (query_uri, "dateline=%" G_GINT64_FORMAT, updated_max);
+ }
+
+ limit = gdata_query_get_max_results (self);
+
+ if (limit > 0) {
+ APPEND_SEP;
+ g_string_append_printf (query_uri, "limit=%d", limit);
+ }
+
+ /* We don't chain up with parent class get_query_uri because it uses
+ * GData protocol parameters and they aren't compatible with newest API family
+ */
+#undef APPEND_SEP
+}
+
+/**
+ * gdata_freebase_topic_query_new:
+ * @id: a Freebase ID or MID
+ *
+ * Creates a new #GDataFreebaseTopicQuery for the given Freebase ID. Those can be
+ * obtained programmatically through gdata_freebase_search_result_item_get_id() or
+ * embedded in the result of a gdata_freebase_service_query() call.
+ *
+ * Return value: (transfer full): a new #GDataFreebaseTopicQuery
+ *
+ * Since: UNRELEASED
+ */
+GDataFreebaseTopicQuery *
+gdata_freebase_topic_query_new (const gchar *id)
+{
+ g_return_val_if_fail (id != NULL, NULL);
+ return g_object_new (GDATA_TYPE_FREEBASE_TOPIC_QUERY, "q", id, NULL);
+}
+
+/**
+ * gdata_freebase_topic_query_set_language:
+ * @self: a #GDataFreebaseTopicQuery
+ * @lang: (allow-none): language used on the topic query, in ISO-639-1 format, or %NULL to unset the language
+ *
+ * Sets the language used in the topic query. If unset,
+ * the locale preferences will be respected.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_topic_query_set_language (GDataFreebaseTopicQuery *self,
+ const gchar *lang)
+{
+ GDataFreebaseTopicQueryPrivate *priv;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_TOPIC_QUERY (self));
+ g_return_if_fail (lang == NULL || strlen (lang) == 2);
+
+ priv = self->priv;
+
+ if (g_strcmp0 (priv->lang, lang) == 0)
+ return;
+
+ g_free (priv->lang);
+ priv->lang = g_strdup (lang);
+ g_object_notify (G_OBJECT (self), "language");
+}
+
+/**
+ * gdata_freebase_topic_query_get_language:
+ * @self: a #GDataFreebaseTopicQuery
+ *
+ * Gets the language set on the topic query, or %NULL if unset.
+ *
+ * Return value: (allow-none): The language used on the query.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_query_get_language (GDataFreebaseTopicQuery *self)
+{
+ g_return_val_if_fail (GDATA_IS_FREEBASE_TOPIC_QUERY (self), NULL);
+
+ return self->priv->lang;
+}
+
+/**
+ * gdata_freebase_topic_query_set_filter:
+ * @self: a #GDataFreebaseTopicQuery
+ * @filter: (array zero-terminated=1) (allow-none): %NULL-terminated array of filter strings, or %NULL to
unset
+ *
+ * Sets a filter on the properties to be returned by the #GDataFreebaseTopicQuery, a filter string usually
contains either
+ * a specific property (eg. "/common/topic/description", or "/computer/software/first_released"), or a
property domain
+ * (eg. "/common/topic", or "/computer"), all properties pertaining to the domain will be returned through
the
+ * #GDataFreebaseTopicResult in the latter case. Other special strings can be passed as filter strings,
those are documented
+ * in the <ulink type="http" url="https://developers.google.com/freebase/v1/topic-overview#filter">Topic API
overview</ulink>
+ *
+ * If multiple filter strings are set, the result will contain all information necessary to satisfy each of
those individually.
+ * If no filter is set, the "commons" special value will be implicitly assumed, which returns a reasonably
complete data set.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_topic_query_set_filter (GDataFreebaseTopicQuery *self, const gchar * const *filter)
+{
+ GDataFreebaseTopicQueryPrivate *priv;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_TOPIC_QUERY (self));
+ priv = self->priv;
+
+ g_strfreev (priv->filter);
+ priv->filter = g_strdupv ((gchar **) filter);
+ g_object_notify (G_OBJECT (self), "filter");
+}
+
+/**
+ * gdata_freebase_topic_query_get_filter:
+ * @self: a #GDataFreebaseTopicQuery
+ *
+ * Gets the filter set on the topic query, or %NULL if unset.
+ *
+ * Return value: (array zero-terminated=1) (transfer none) (allow-none): The filter used on the query.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar * const *
+gdata_freebase_topic_query_get_filter (GDataFreebaseTopicQuery *self)
+{
+ g_return_val_if_fail (GDATA_IS_FREEBASE_TOPIC_QUERY (self), NULL);
+
+ return (const gchar * const *) self->priv->filter;
+}
diff --git a/gdata/services/freebase/gdata-freebase-topic-query.h
b/gdata/services/freebase/gdata-freebase-topic-query.h
new file mode 100644
index 0000000..1f1053a
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-topic-query.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg gnome org>
+ *
+ * GData Client 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.
+ *
+ * GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_FREEBASE_TOPIC_QUERY_H
+#define GDATA_FREEBASE_TOPIC_QUERY_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-query.h>
+#include <gdata/gdata-types.h>
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_FREEBASE_TOPIC_QUERY (gdata_freebase_topic_query_get_type ())
+#define GDATA_FREEBASE_TOPIC_QUERY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o),
GDATA_TYPE_FREEBASE_TOPIC_QUERY, GDataFreebaseTopicQuery))
+#define GDATA_FREEBASE_TOPIC_QUERY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k),
GDATA_TYPE_FREEBASE_TOPIC_QUERY, GDataFreebaseTopicQueryClass))
+#define GDATA_IS_FREEBASE_TOPIC_QUERY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o),
GDATA_TYPE_FREEBASE_TOPIC_QUERY))
+#define GDATA_IS_FREEBASE_TOPIC_QUERY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k),
GDATA_TYPE_FREEBASE_TOPIC_QUERY))
+#define GDATA_FREEBASE_TOPIC_QUERY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),
GDATA_TYPE_FREEBASE_TOPIC_QUERY, GDataFreebaseTopicQueryClass))
+
+typedef struct _GDataFreebaseTopicQueryPrivate GDataFreebaseTopicQueryPrivate;
+
+/**
+ * GDataFreebaseTopicQuery:
+ *
+ * All the fields in the #GDataFreebaseTopicQuery structure are private and should never be accessed
directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ GDataQuery parent;
+ GDataFreebaseTopicQueryPrivate *priv;
+} GDataFreebaseTopicQuery;
+
+/**
+ * GDataFreebaseTopicQueryClass:
+ *
+ * All the fields in the #GDataFreebaseTopicQueryClass structure are private and should never be accessed
directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ /*< private >*/
+ GDataQueryClass parent;
+} GDataFreebaseTopicQueryClass;
+
+GType gdata_freebase_topic_query_get_type (void) G_GNUC_CONST;
+
+GDataFreebaseTopicQuery *gdata_freebase_topic_query_new (const gchar *id) G_GNUC_WARN_UNUSED_RESULT
G_GNUC_MALLOC;
+void gdata_freebase_topic_query_set_language (GDataFreebaseTopicQuery *self, const gchar *lang);
+const gchar *gdata_freebase_topic_query_get_language (GDataFreebaseTopicQuery *self);
+
+void gdata_freebase_topic_query_set_filter (GDataFreebaseTopicQuery *self, const gchar * const *filter);
+const gchar * const *gdata_freebase_topic_query_get_filter (GDataFreebaseTopicQuery *self);
+
+G_END_DECLS
+
+#endif /* !GDATA_FREEBASE_TOPIC_QUERY_H */
diff --git a/gdata/services/freebase/gdata-freebase-topic-result.c
b/gdata/services/freebase/gdata-freebase-topic-result.c
new file mode 100644
index 0000000..5357bbb
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-topic-result.c
@@ -0,0 +1,1019 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg gnome org>
+ *
+ * GData Client 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.
+ *
+ * GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-freebase-topic-result
+ * @short_description: GData Freebase topic result object
+ * @stability: Unstable
+ * @include: gdata/services/freebase/gdata-freebase-topic-result.h
+ *
+ * #GDataFreebaseTopicResult is a subclass of #GDataFreebaseResult that contains all or a subset of the
information
+ * contained in Freebase about the Freebase ID given to the #GDataFreebaseTopicQuery.
+ *
+ * For more details of Google Freebase API, see the <ulink type="http"
url="https://developers.google.com/freebase/v1/">
+ * online documentation</ulink>.
+ *
+ * Since: UNRELEASED
+ */
+
+typedef struct _GDataFreebaseTopicValueArray GDataFreebaseTopicValueArray;
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#include "gdata-freebase-topic-result.h"
+#include "gdata-download-stream.h"
+#include "gdata-private.h"
+#include "gdata-types.h"
+
+typedef enum {
+ TYPE_NONE,
+ TYPE_BOOL,
+ TYPE_INT,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_DATETIME,
+ TYPE_COMPOUND,
+ TYPE_OBJECT,
+ TYPE_KEY,
+ TYPE_URI
+} TopicValueType;
+
+/* Wraps a compound object, either the main object returned by the result, or a
+ * complex object within the result values (events are at least composed of
+ * location and time at least, for example).
+ */
+struct _GDataFreebaseTopicObject {
+ gchar *id;
+ GHashTable *values; /* Hashtable of property->GDataFreebaseTopicValueArray */
+ volatile gint ref_count;
+};
+
+/* Wraps an array of values, single-valued properties will contain an array with a single value here */
+struct _GDataFreebaseTopicValueArray {
+ TopicValueType type;
+ GPtrArray *values;
+ guint64 hits; /* Total number of hits, as opposed to values->len */
+};
+
+/* Wraps a single value in the topic result, may be either simple (numbers, strings, Freebase IDs...),
+ * or nested compound types (contained by a GDataFreebaseTopicObject, which is what the value would
+ * contain in that case).
+ */
+struct _GDataFreebaseTopicValue {
+ gchar *property;
+ gchar *text;
+ gchar *lang;
+ gchar *creator;
+ gint64 timestamp;
+ GValue value;
+ gint ref_count;
+};
+
+struct _GDataFreebaseTopicResultPrivate {
+ GDataFreebaseTopicObject *object;
+};
+
+static void gdata_freebase_topic_result_finalize (GObject *self);
+static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
+static GDataFreebaseTopicObject *object_new (const gchar *id);
+static void value_free (GDataFreebaseTopicValue *object);
+static gboolean reader_get_properties (JsonReader *reader, GDataFreebaseTopicObject *object, GError **error);
+
+G_DEFINE_BOXED_TYPE (GDataFreebaseTopicObject, gdata_freebase_topic_object, gdata_freebase_topic_object_ref,
gdata_freebase_topic_object_unref)
+G_DEFINE_BOXED_TYPE (GDataFreebaseTopicValue, gdata_freebase_topic_value, gdata_freebase_topic_value_ref,
gdata_freebase_topic_value_unref)
+G_DEFINE_TYPE (GDataFreebaseTopicResult, gdata_freebase_topic_result, GDATA_TYPE_FREEBASE_RESULT)
+
+static void
+gdata_freebase_topic_result_class_init (GDataFreebaseTopicResultClass *klass)
+{
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataFreebaseTopicResultPrivate));
+
+ gobject_class->finalize = gdata_freebase_topic_result_finalize;
+ parsable_class->parse_json = parse_json;
+}
+
+static void
+gdata_freebase_topic_result_init (GDataFreebaseTopicResult *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_FREEBASE_TOPIC_RESULT,
GDataFreebaseTopicResultPrivate);
+}
+
+static void
+gdata_freebase_topic_result_finalize (GObject *self)
+{
+ GDataFreebaseTopicResultPrivate *priv = GDATA_FREEBASE_TOPIC_RESULT (self)->priv;
+
+ gdata_freebase_topic_object_unref (priv->object);
+
+ G_OBJECT_CLASS (gdata_freebase_topic_result_parent_class)->finalize (self);
+}
+
+static GDataFreebaseTopicValueArray *
+value_array_new (TopicValueType type, guint64 hits)
+{
+ GDataFreebaseTopicValueArray *array;
+
+ array = g_slice_new0 (GDataFreebaseTopicValueArray);
+ array->values = g_ptr_array_new_with_free_func ((GDestroyNotify) gdata_freebase_topic_value_unref);
+ array->type = type;
+ array->hits = hits;
+
+ return array;
+}
+
+/* Takes ownership on @value */
+static void
+value_array_add (GDataFreebaseTopicValueArray *array, GDataFreebaseTopicValue *value)
+{
+ g_ptr_array_add (array->values, value);
+}
+
+static void
+value_array_free (GDataFreebaseTopicValueArray *array)
+{
+ g_ptr_array_unref (array->values);
+ g_slice_free (GDataFreebaseTopicValueArray, array);
+}
+
+static guint64
+reader_get_item_count (JsonReader *reader)
+{
+ gint64 count;
+
+ json_reader_read_member (reader, "count");
+ count = json_reader_get_int_value (reader);
+ json_reader_end_member (reader);
+
+ return (guint64) count;
+}
+
+static guint
+reader_get_value_type (JsonReader *reader, const gchar *property, GError **error)
+{
+ TopicValueType type = TYPE_NONE;
+ const GError *reader_error;
+ const gchar *valuestr;
+
+ json_reader_read_member (reader, "valuetype");
+ valuestr = json_reader_get_string_value (reader);
+
+ reader_error = json_reader_get_error (reader);
+
+ if (reader_error != NULL) {
+ if (error != NULL && *error == NULL)
+ *error = g_error_copy (reader_error);
+ } else {
+ if (strcmp (valuestr, "key") == 0)
+ type = TYPE_KEY;
+ else if (strcmp (valuestr, "uri") == 0)
+ type = TYPE_URI;
+ else if (strcmp (valuestr, "compound") == 0)
+ type = TYPE_COMPOUND;
+ else if (strcmp (valuestr, "object") == 0)
+ type = TYPE_OBJECT;
+ else if (strcmp (valuestr, "float") == 0)
+ type = TYPE_DOUBLE;
+ else if (strcmp (valuestr, "string") == 0)
+ type = TYPE_STRING;
+ else if (strcmp (valuestr, "int") == 0)
+ type = TYPE_INT;
+ else if (strcmp (valuestr, "bool") == 0)
+ type = TYPE_BOOL;
+ else if (strcmp (valuestr, "datetime") == 0)
+ type = TYPE_DATETIME;
+ else
+ gdata_parser_error_required_json_content_missing (reader, error);
+ }
+
+ json_reader_end_member (reader);
+ return type;
+}
+
+static void
+value_free (GDataFreebaseTopicValue *value)
+{
+ if (G_IS_VALUE (&value->value))
+ g_value_unset (&value->value);
+ g_free (value->text);
+ g_free (value->lang);
+ g_free (value->creator);
+ g_free (value->property);
+ g_slice_free (GDataFreebaseTopicValue, value);
+}
+
+/* Parsing functions to create GDataFreebaseTopicValues, and arrays of those */
+static gchar *
+reader_dup_member_string (JsonReader *reader, const gchar *member, GError **error)
+{
+ const GError *reader_error;
+ gchar *str;
+
+ if (error != NULL && *error != NULL)
+ return NULL;
+
+ json_reader_read_member (reader, member);
+ str = g_strdup (json_reader_get_string_value (reader));
+ reader_error = json_reader_get_error (reader);
+
+ if (reader_error != NULL) {
+ g_free (str);
+ str = NULL;
+
+ if (error != NULL)
+ *error = g_error_copy (reader_error);
+ }
+
+ json_reader_end_member (reader);
+
+ return str;
+}
+
+static gint64
+reader_parse_timestamp (JsonReader *reader, const gchar *member, GError **error)
+{
+ const GError *reader_error;
+ const gchar *date_str;
+ gint64 timestamp = -1;
+
+ if (error != NULL && *error != NULL)
+ return -1;
+
+ json_reader_read_member (reader, member);
+ date_str = json_reader_get_string_value (reader);
+ reader_error = json_reader_get_error (reader);
+
+ if (reader_error != NULL) {
+ if (error != NULL)
+ *error = g_error_copy (reader_error);
+ } else if (date_str) {
+ if (!gdata_parser_int64_from_iso8601 (date_str, ×tamp))
+ timestamp = -1;
+ }
+
+ json_reader_end_member (reader);
+
+ return timestamp;
+}
+
+static gboolean
+reader_fill_simple_gvalue (JsonReader *reader, TopicValueType type, GValue *value)
+{
+ gboolean retval = TRUE;
+ gint64 datetime;
+
+ json_reader_read_member (reader, "value");
+
+ if (json_reader_get_error (reader) != NULL) {
+ json_reader_end_member (reader);
+ return FALSE;
+ }
+
+ switch (type) {
+ case TYPE_BOOL:
+ case TYPE_INT:
+ case TYPE_DOUBLE:
+ case TYPE_STRING:
+ json_node_get_value (json_reader_get_value (reader), value);
+ break;
+ case TYPE_DATETIME:
+ if (gdata_parser_int64_from_iso8601 (json_reader_get_string_value (reader), &datetime) ||
+ gdata_parser_int64_from_date (json_reader_get_string_value (reader), &datetime)) {
+ g_value_init (value, G_TYPE_INT64);
+ g_value_set_int64 (value, datetime);
+ } else {
+ retval = FALSE;
+ }
+
+ break;
+ case TYPE_NONE:
+ case TYPE_COMPOUND:
+ case TYPE_OBJECT:
+ case TYPE_KEY:
+ case TYPE_URI:
+ default:
+ retval = FALSE;
+ }
+
+ json_reader_end_member (reader);
+ return retval;
+}
+
+static GDataFreebaseTopicObject *
+reader_create_object (JsonReader *reader, TopicValueType type)
+{
+ GDataFreebaseTopicObject *object;
+
+ if (type != TYPE_OBJECT && type != TYPE_COMPOUND)
+ return NULL;
+
+ json_reader_read_member (reader, "id");
+
+ if (json_reader_get_error (reader) != NULL) {
+ json_reader_end_member (reader);
+ return NULL;
+ }
+
+ object = object_new (json_reader_get_string_value (reader));
+ json_reader_end_member (reader);
+
+ return object;
+}
+
+static gboolean
+reader_fill_object_gvalue (JsonReader *reader, TopicValueType type, GValue *value)
+{
+ GDataFreebaseTopicObject *object;
+
+ if (type != TYPE_OBJECT && type != TYPE_COMPOUND)
+ return FALSE;
+
+ object = reader_create_object (reader, type);
+
+ if (object != NULL) {
+ g_value_init (value, GDATA_TYPE_FREEBASE_TOPIC_OBJECT);
+ g_value_take_boxed (value, object);
+ }
+
+ return (object != NULL);
+}
+
+static gboolean
+reader_fill_compound_gvalue (JsonReader *reader, TopicValueType type, GValue *value, GError **error)
+{
+ GDataFreebaseTopicObject *object;
+
+ if (type != TYPE_COMPOUND)
+ return FALSE;
+
+ object = reader_create_object (reader, type);
+
+ if (object != NULL)
+ return FALSE;
+
+ json_reader_read_member (reader, "property");
+
+ if (json_reader_get_error (reader) != NULL) {
+ json_reader_end_member (reader);
+ gdata_freebase_topic_object_unref (object);
+ return FALSE;
+ }
+
+ reader_get_properties (reader, object, error);
+ json_reader_end_member (reader);
+
+ g_value_init (value, GDATA_TYPE_FREEBASE_TOPIC_OBJECT);
+ g_value_take_boxed (value, object);
+ return TRUE;
+}
+
+static GDataFreebaseTopicValue *
+reader_create_value (JsonReader *reader, const gchar *property, TopicValueType type, GError **error)
+{
+ GDataFreebaseTopicValue *value;
+
+ value = g_slice_new0 (GDataFreebaseTopicValue);
+
+ value->ref_count = 1;
+ value->property = g_strdup (property);
+ value->text = reader_dup_member_string (reader, "text", error);
+ value->lang = reader_dup_member_string (reader, "lang", error);
+
+ /* Not all parsed nodes are meant to contain creator/timestamp tags,
+ * do not pass error to those, so parsing continues.
+ */
+ value->creator = reader_dup_member_string (reader, "creator", NULL);
+ value->timestamp = reader_parse_timestamp (reader, "timestamp", NULL);
+
+ if (reader_fill_simple_gvalue (reader, type, &value->value) ||
+ reader_fill_object_gvalue (reader, type, &value->value) ||
+ reader_fill_compound_gvalue (reader, type, &value->value, error))
+ return value;
+
+ value_free (value);
+ return NULL;
+}
+
+static GDataFreebaseTopicValueArray *
+reader_create_value_array (JsonReader *reader, const gchar *property, GError **error)
+{
+ GDataFreebaseTopicValueArray *array;
+ GDataFreebaseTopicValue *value;
+ TopicValueType type;
+ guint64 count, i;
+
+ count = reader_get_item_count (reader);
+
+ if (count <= 0)
+ return NULL;
+
+ type = reader_get_value_type (reader, property, error);
+
+ if (type == TYPE_NONE || type == TYPE_URI || type == TYPE_KEY)
+ return NULL;
+
+ array = value_array_new (type, count);
+
+ json_reader_read_member (reader, "values");
+ count = json_reader_count_elements (reader);
+
+ for (i = 0; i < count; i++) {
+ json_reader_read_element (reader, i);
+ value = reader_create_value (reader, property, type, error);
+ json_reader_end_element (reader);
+
+ if (value != NULL)
+ value_array_add (array, value);
+ }
+
+ json_reader_end_member (reader);
+
+ return array;
+}
+
+static GDataFreebaseTopicObject *
+object_new (const gchar *id)
+{
+ GDataFreebaseTopicObject *object;
+
+ object = g_slice_new0 (GDataFreebaseTopicObject);
+ object->id = g_strdup (id);
+ object->ref_count = 1;
+ object->values = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) value_array_free);
+ return object;
+}
+
+/* Takes ownership on @array */
+static void
+object_add_value (GDataFreebaseTopicObject *object, const gchar *property, GDataFreebaseTopicValueArray
*array)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (property != NULL);
+ g_return_if_fail (array != NULL);
+ g_hash_table_replace (object->values, g_strdup (property), array);
+}
+
+static gboolean
+reader_get_properties (JsonReader *reader, GDataFreebaseTopicObject *object, GError **error)
+{
+ GDataFreebaseTopicValueArray *array;
+ gboolean retval = TRUE;
+ gint count, i;
+
+ count = json_reader_count_members (reader);
+
+ for (i = 0; i < count; i++) {
+ GError *inner_error = NULL;
+ const gchar *name;
+ gchar *property;
+
+ json_reader_read_element (reader, i);
+ property = g_strdup (json_reader_get_member_name (reader));
+ name = property;
+
+ /* Reverse properties start with !, display those as
+ * regular properties, and skip that char
+ */
+ if (name[0] == '!')
+ name++;
+
+ /* All Freebase properties and IDs start with '/' */
+ if (name[0] != '/')
+ continue;
+
+ /* Parse the value for this property, possibly with nested contents */
+ array = reader_create_value_array (reader, name, &inner_error);
+ json_reader_end_element (reader);
+
+ if (inner_error != NULL) {
+ g_propagate_error (error, inner_error);
+ retval = FALSE;
+ break;
+ } else if (array != NULL) {
+ /* Takes ownership of array */
+ object_add_value (object, name, array);
+ }
+
+ g_free (property);
+ }
+
+ return retval;
+}
+
+static gboolean
+parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
+{
+ GDataFreebaseTopicResultPrivate *priv = GDATA_FREEBASE_TOPIC_RESULT (parsable)->priv;
+ const gchar *member_name;
+
+ GDATA_PARSABLE_CLASS (gdata_freebase_topic_result_parent_class)->parse_json (parsable, reader,
user_data, error);
+
+ member_name = json_reader_get_member_name (reader);
+
+ if (member_name == NULL)
+ return FALSE;
+
+ if (strcmp (member_name, "id") == 0) {
+ /* We only expect one member containing information */
+ g_assert (priv->object == NULL);
+ priv->object = object_new (json_reader_get_string_value (reader));
+ } else if (strcmp (member_name, "property") == 0) {
+ reader_get_properties (reader, priv->object, error);
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gdata_freebase_topic_result_new:
+ *
+ * Creates a new #GDataFreebaseTopicResult with the given ID and default properties.
+ *
+ * Return value: (transfer full): a new #GDataFreebaseTopicResult; unref with g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataFreebaseTopicResult *
+gdata_freebase_topic_result_new (void)
+{
+ return g_object_new (GDATA_TYPE_FREEBASE_TOPIC_RESULT, NULL);
+}
+
+/**
+ * gdata_freebase_topic_result_dup_object:
+ * @self: a #GDataFreebaseTopicResult
+ *
+ * Returns a reference to the root #GDataFreebaseTopicObject containing the
+ * topic query results.
+ *
+ * Returns: (transfer full): A new reference on the result object, unref with
+ * gdata_freebase_topic_object_unref()
+ *
+ * Since: UNRELEASED
+ **/
+GDataFreebaseTopicObject *
+gdata_freebase_topic_result_dup_object (GDataFreebaseTopicResult *self)
+{
+ g_return_val_if_fail (GDATA_IS_FREEBASE_TOPIC_RESULT (self), NULL);
+
+ return gdata_freebase_topic_object_ref (self->priv->object);
+}
+
+/**
+ * gdata_freebase_topic_object_ref:
+ * @object: a #GDataFreebaseTopicObject
+ *
+ * Creates and returns a new reference on @object.
+ *
+ * Returns: (transfer full): @object, with an extra reference.
+ *
+ * Since: UNRELEASED
+ **/
+GDataFreebaseTopicObject *
+gdata_freebase_topic_object_ref (GDataFreebaseTopicObject *object)
+{
+ g_return_val_if_fail (object != NULL, NULL);
+
+ g_atomic_int_inc (&object->ref_count);
+ return object;
+}
+
+/**
+ * gdata_freebase_topic_object_unref:
+ * @object: (transfer full): a #GDataFreebaseTopicResult
+ *
+ * Removes a reference from @object. If the reference count drops to 0,
+ * the object is freed.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_topic_object_unref (GDataFreebaseTopicObject *object)
+{
+ g_return_if_fail (object != NULL);
+
+ if (g_atomic_int_dec_and_test (&object->ref_count)) {
+ g_hash_table_unref (object->values);
+ g_free (object->id);
+ g_slice_free (GDataFreebaseTopicObject, object);
+ }
+}
+
+/**
+ * gdata_freebase_topic_object_list_properties:
+ * @object: a #GDataFreebaseTopicObject
+ *
+ * Returns the list of Freebase properties described by @object.
+ *
+ * Returns: (transfer container) (element-type gchar*): An array of property names, free with
g_ptr_array_unref().
+ *
+ * Since: UNRELEASED
+ **/
+GPtrArray *
+gdata_freebase_topic_object_list_properties (const GDataFreebaseTopicObject *object)
+{
+ GPtrArray *properties;
+ GHashTableIter iter;
+ gchar *property;
+
+ g_return_val_if_fail (object != NULL, NULL);
+
+ properties = g_ptr_array_new ();
+ g_hash_table_iter_init (&iter, object->values);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &property, NULL))
+ g_ptr_array_add (properties, property);
+
+ return properties;
+}
+
+/**
+ * gdata_freebase_topic_object_get_property_count:
+ * @object: a #GDataFreebaseTopicObject
+ * @property: a property name contained in @object
+ *
+ * Returns the number of values that @object holds for the given @property. If @object
+ * contains no information about @property, 0 is returned.
+ *
+ * Returns: The number of values contained for @property
+ *
+ * Since: UNRELEASED
+ **/
+guint64
+gdata_freebase_topic_object_get_property_count (const GDataFreebaseTopicObject *object, const gchar
*property)
+{
+ GDataFreebaseTopicValueArray *array;
+
+ g_return_val_if_fail (object != NULL, 0);
+ g_return_val_if_fail (property != NULL, 0);
+ array = g_hash_table_lookup (object->values, property);
+
+ if (array == NULL)
+ return 0;
+
+ return array->values->len;
+}
+
+/**
+ * gdata_freebase_topic_object_get_property_hits:
+ * @object: a #GDataFreebaseTopicObject
+ * @property: a property name contained in @object
+ *
+ * Returns the total number of hits that the Freebase database stores
+ * for this object, this number either equals or is greater than
+ * gdata_freebase_topic_object_get_property_count(), the query limit
+ * can be controlled through gdata_query_set_max_results() on the topic
+ * query.
+ *
+ * If @object contains no information about @property, 0 is returned.
+ *
+ * Returns: the total number of hits for this property
+ *
+ * Since: UNRELEASED
+ **/
+guint64
+gdata_freebase_topic_object_get_property_hits (const GDataFreebaseTopicObject *object, const gchar *property)
+{
+ GDataFreebaseTopicValueArray *array;
+
+ g_return_val_if_fail (object != NULL, 0);
+ g_return_val_if_fail (property != NULL, 0);
+ array = g_hash_table_lookup (object->values, property);
+
+ if (array == NULL)
+ return 0;
+
+ return array->hits;
+}
+
+/**
+ * gdata_freebase_topic_object_get_property_value:
+ * @object: a #GDataFreebaseTopicObject
+ * @property: a property name contained in @object
+ * @item: item number to retrieve from @property
+ *
+ * Gets the value that @object stores for this @property/@item pair, as a generic
+ * #GDataFreebaseTopicValue. If @object contains no information about @property,
+ * or @item is outside the [0..gdata_freebase_topic_object_get_property_count() - 1]
+ * range, %NULL is returned.
+ *
+ * Returns: (allow-none) (transfer none): the value for this property/item
+ *
+ * Since: UNRELEASED
+ **/
+GDataFreebaseTopicValue *
+gdata_freebase_topic_object_get_property_value (const GDataFreebaseTopicObject *object, const gchar
*property, gint64 item)
+{
+ GDataFreebaseTopicValueArray *array;
+
+ g_return_val_if_fail (object != NULL, NULL);
+ g_return_val_if_fail (property != NULL, NULL);
+ array = g_hash_table_lookup (object->values, property);
+
+ if (array == NULL)
+ return NULL;
+
+ if (item < 0 || item >= array->values->len)
+ return NULL;
+
+ return g_ptr_array_index (array->values, item);
+}
+
+/**
+ * gdata_freebase_topic_object_get_id:
+ * @object: a #GDataFreebaseTopicObject
+ *
+ * Gets the Freebase ID for this specific object.
+ *
+ * Returns: (transfer none): the Freebase ID of this object
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_object_get_id (const GDataFreebaseTopicObject *object)
+{
+ g_return_val_if_fail (object != NULL, NULL);
+ return object->id;
+}
+
+/**
+ * gdata_freebase_topic_value_ref:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Creates and returns a new reference on @value.
+ *
+ * Returns: (transfer full): @value, with an extra reference.
+ *
+ * Since: UNRELEASED
+ **/
+GDataFreebaseTopicValue *
+gdata_freebase_topic_value_ref (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+
+ g_atomic_int_inc (&value->ref_count);
+ return value;
+}
+
+/**
+ * gdata_freebase_topic_value_unref:
+ * @value: (transfer full): a #GDataFreebaseTopicValue
+ *
+ * Removes a reference from @value. If the reference count drops to 0,
+ * the object is freed.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_topic_value_unref (GDataFreebaseTopicValue *value)
+{
+ g_return_if_fail (value != NULL);
+
+ if (g_atomic_int_dec_and_test (&value->ref_count))
+ value_free (value);
+}
+
+/**
+ * gdata_freebase_topic_value_get_property:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns the property name that this value describes
+ *
+ * Returns: the property name of @value
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_value_get_property (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ return value->property;
+}
+
+/**
+ * gdata_freebase_topic_value_get_text:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns a textual representation of this value, this is either
+ * the value contained transformed to a string, or a concatenation
+ * of subvalues for compound types.
+ *
+ * Returns: a textual representation of @value
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_value_get_text (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ return value->text;
+}
+
+/**
+ * gdata_freebase_topic_value_get_language:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns the language used in the content of @value
+ *
+ * Returns: the language @value is written in
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_value_get_language (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ return value->lang;
+}
+
+/**
+ * gdata_freebase_topic_value_get_creator:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns the Freebase ID of the user that created this value.
+ *
+ * Returns: the creator of this value, as a Freebase ID
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_value_get_creator (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ return value->creator;
+}
+
+/**
+ * gdata_freebase_topic_value_get_timestamp:
+ * @value: #GDataFreebaseTopicValue
+ *
+ * Returns the time at which this value was created in the Freebase database.
+ * It's a UNIX timestamp in seconds since the epoch. If @value has no timestamp,
+ * -1 will be returned.
+ *
+ * Returns: The creation time of @value, or -1
+ *
+ * Since: UNRELEASED
+ **/
+gint64
+gdata_freebase_topic_value_get_timestamp (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, 0);
+ return value->timestamp;
+}
+
+/**
+ * gdata_freebase_topic_value_get_value_type:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns the #GType of the real value held in @value.
+ *
+ * Returns: the #GType of the contained value
+ *
+ * Since: UNRELEASED
+ **/
+GType
+gdata_freebase_topic_value_get_value_type (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, G_TYPE_INVALID);
+ return G_VALUE_TYPE (&value->value);
+}
+
+/**
+ * gdata_freebase_topic_value_copy_value:
+ * @value: a #GDataFreebaseTopicValue
+ * @gvalue: (out caller-allocates) (transfer full): an empty #GValue
+ *
+ * Copies in @gvalue the value held in @value. the #GValue must be later freed through g_value_unset()
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_topic_value_copy_value (GDataFreebaseTopicValue *value, GValue *gvalue)
+{
+ g_return_if_fail (value != NULL);
+
+ g_value_copy (&value->value, gvalue);
+}
+
+/**
+ * gdata_freebase_topic_value_get_int:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns a #gint64 value held in @value. It is only valid to call this if the #GType is a %G_TYPE_INT64
+ *
+ * Returns: the #gint64 value
+ *
+ * Since: UNRELEASED
+ **/
+gint64
+gdata_freebase_topic_value_get_int (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, 0);
+ g_return_val_if_fail (G_VALUE_HOLDS_INT64 (&value->value), 0);
+
+ return g_value_get_int64 (&value->value);
+}
+
+/**
+ * gdata_freebase_topic_value_get_double:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns a #gdouble value held in @value. It is only valid to call this if the #GType is a %G_TYPE_DOUBLE
+ *
+ * Returns: the #gdouble value
+ *
+ * Since: UNRELEASED
+ **/
+gdouble
+gdata_freebase_topic_value_get_double (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, 0);
+ g_return_val_if_fail (G_VALUE_HOLDS_DOUBLE (&value->value), 0);
+
+ return g_value_get_double (&value->value);
+}
+
+/**
+ * gdata_freebase_topic_value_get_string:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns a string value held in @value. It is only valid to call this if the #GType is a %G_TYPE_STRING
+ *
+ * Returns: the string value
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_topic_value_get_string (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (G_VALUE_HOLDS_STRING (&value->value), NULL);
+
+ return g_value_get_string (&value->value);
+}
+
+/**
+ * gdata_freebase_topic_value_get_object:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns a compound/complex object held in @value. It is only valid to call this if the #GType is a
+ * %GDATA_TYPE_FREEBASE_TOPIC_OBJECT.
+ *
+ * Returns: (transfer none): the compound value as a #GDataFreebaseTopicObject
+ *
+ * Since: UNRELEASED
+ **/
+const GDataFreebaseTopicObject *
+gdata_freebase_topic_value_get_object (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (G_VALUE_HOLDS (&value->value, GDATA_TYPE_FREEBASE_TOPIC_OBJECT), NULL);
+
+ return g_value_get_boxed (&value->value);
+}
+
+/**
+ * gdata_freebase_topic_value_is_image:
+ * @value: a #GDataFreebaseTopicValue
+ *
+ * Returns true if @value holds a freebase image object, on such values it
+ * will be valid to call gdata_freebase_service_get_image() to get a stream
+ * to the image itself.
+ *
+ * Returns: Whether @value holds a Freebase image object
+ *
+ * Since: UNRELEASED
+ **/
+gboolean
+gdata_freebase_topic_value_is_image (GDataFreebaseTopicValue *value)
+{
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ return (strcmp (value->property, "/common/topic/image") == 0);
+}
diff --git a/gdata/services/freebase/gdata-freebase-topic-result.h
b/gdata/services/freebase/gdata-freebase-topic-result.h
new file mode 100644
index 0000000..7355d24
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-topic-result.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg gnome org>
+ *
+ * GData Client 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.
+ *
+ * GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_FREEBASE_TOPIC_RESULT_H
+#define GDATA_FREEBASE_TOPIC_RESULT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-types.h>
+#include "gdata-freebase-result.h"
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_FREEBASE_TOPIC_OBJECT (gdata_freebase_topic_object_get_type ())
+#define GDATA_TYPE_FREEBASE_TOPIC_VALUE (gdata_freebase_topic_value_get_type ())
+#define GDATA_TYPE_FREEBASE_TOPIC_RESULT (gdata_freebase_topic_result_get_type ())
+#define GDATA_FREEBASE_TOPIC_RESULT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o),
GDATA_TYPE_FREEBASE_TOPIC_RESULT, GDataFreebaseTopicResult))
+#define GDATA_FREEBASE_TOPIC_RESULT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k),
GDATA_TYPE_FREEBASE_TOPIC_RESULT, GDataFreebaseTopicResultClass))
+#define GDATA_IS_FREEBASE_TOPIC_RESULT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o),
GDATA_TYPE_FREEBASE_TOPIC_RESULT))
+#define GDATA_IS_FREEBASE_TOPIC_RESULT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k),
GDATA_TYPE_FREEBASE_TOPIC_RESULT))
+#define GDATA_FREEBASE_TOPIC_RESULT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),
GDATA_TYPE_FREEBASE_TOPIC_RESULT, GDataFreebaseTopicResultClass))
+
+typedef struct _GDataFreebaseTopicResultPrivate GDataFreebaseTopicResultPrivate;
+
+/**
+ * GDataFreebaseTopicObject:
+ *
+ * Opaque struct containing a Freebase topic object. This object may contain one or more
+ * #GDataFreebaseTopicValue structs, which may in turn contain nested #GDataFreebaseTopicObject
+ * structs to express complex data.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct _GDataFreebaseTopicObject GDataFreebaseTopicObject;
+
+/**
+ * GDataFreebaseTopicValue:
+ *
+ * Opaque struct containing a value of a Freebase topic object. This struct may contain a simple
+ * value (integers, doubles, strings...) or complex values, expressed through a #GDataFreebaseTopicObject.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct _GDataFreebaseTopicValue GDataFreebaseTopicValue;
+
+/**
+ * GDataFreebaseTopicResult:
+ *
+ * All the fields in the #GDataFreebaseTopicResult structure are private and should never be accessed
directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ GDataFreebaseResult parent;
+ GDataFreebaseTopicResultPrivate *priv;
+} GDataFreebaseTopicResult;
+
+/**
+ * GDataFreebaseTopicResultClass:
+ *
+ * All the fields in the #GDataFreebaseTopicResultClass structure are private and should never be accessed
directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ /*< private >*/
+ GDataFreebaseResultClass parent;
+} GDataFreebaseTopicResultClass;
+
+GType gdata_freebase_topic_object_get_type (void) G_GNUC_CONST;
+GType gdata_freebase_topic_value_get_type (void) G_GNUC_CONST;
+GType gdata_freebase_topic_result_get_type (void) G_GNUC_CONST;
+
+GDataFreebaseTopicResult *gdata_freebase_topic_result_new (void) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+
+GDataFreebaseTopicObject *gdata_freebase_topic_result_dup_object (GDataFreebaseTopicResult *self);
+
+GDataFreebaseTopicObject *gdata_freebase_topic_object_ref (GDataFreebaseTopicObject *object);
+void gdata_freebase_topic_object_unref (GDataFreebaseTopicObject *object);
+
+GPtrArray *gdata_freebase_topic_object_list_properties (const GDataFreebaseTopicObject *object);
+
+const gchar *gdata_freebase_topic_object_get_id (const GDataFreebaseTopicObject *object);
+guint64 gdata_freebase_topic_object_get_property_count (const GDataFreebaseTopicObject *object, const gchar
*property);
+guint64 gdata_freebase_topic_object_get_property_hits (const GDataFreebaseTopicObject *object, const gchar
*property);
+GDataFreebaseTopicValue *gdata_freebase_topic_object_get_property_value (const GDataFreebaseTopicObject
*object, const gchar *property, gint64 item);
+
+GDataFreebaseTopicValue *gdata_freebase_topic_value_ref (GDataFreebaseTopicValue *value);
+void gdata_freebase_topic_value_unref (GDataFreebaseTopicValue *value);
+
+const gchar *gdata_freebase_topic_value_get_property (GDataFreebaseTopicValue *value);
+
+const gchar *gdata_freebase_topic_value_get_text (GDataFreebaseTopicValue *value);
+const gchar *gdata_freebase_topic_value_get_language (GDataFreebaseTopicValue *value);
+const gchar *gdata_freebase_topic_value_get_creator (GDataFreebaseTopicValue *value);
+gint64 gdata_freebase_topic_value_get_timestamp (GDataFreebaseTopicValue *value);
+GType gdata_freebase_topic_value_get_value_type (GDataFreebaseTopicValue *value);
+void gdata_freebase_topic_value_copy_value (GDataFreebaseTopicValue *value, GValue *gvalue);
+
+gint64 gdata_freebase_topic_value_get_int (GDataFreebaseTopicValue *value);
+gdouble gdata_freebase_topic_value_get_double (GDataFreebaseTopicValue *value);
+const gchar *gdata_freebase_topic_value_get_string (GDataFreebaseTopicValue *value);
+const GDataFreebaseTopicObject *gdata_freebase_topic_value_get_object (GDataFreebaseTopicValue *value);
+
+gboolean gdata_freebase_topic_value_is_image (GDataFreebaseTopicValue *value);
+
+G_END_DECLS
+
+#endif /* !GDATA_FREEBASE_TOPIC_RESULT_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]