[libgdata] Bug 613551 — Add gContact namespace
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgdata] Bug 613551 — Add gContact namespace
- Date: Thu, 25 Mar 2010 23:05:56 +0000 (UTC)
commit aa23ec1cdcaaa5511a5ba713bebdbe0f101bf180
Author: Philip Withnall <philip tecnocode co uk>
Date: Thu Mar 25 00:43:09 2010 +0000
Bug 613551 â?? Add gContact namespace
Add support for various elements from the gContact namespace, and implement
them in #GDataContactsContact. There is now support for contact:
* nicknames
* birthdays
* jots (notes about a contact)
* relations (with other people, not necessarily family)
* websites
* events (anniversaries, important dates, etc.)
* calendar links
Helps: bgo#613551
Makefile.am | 22 +-
docs/reference/gdata-docs.xml | 9 +
docs/reference/gdata-sections.txt | 176 ++++++
gdata/gcontact/gdata-gcontact-calendar.c | 490 ++++++++++++++++
gdata/gcontact/gdata-gcontact-calendar.h | 119 ++++
gdata/gcontact/gdata-gcontact-event.c | 446 +++++++++++++++
gdata/gcontact/gdata-gcontact-event.h | 102 ++++
gdata/gcontact/gdata-gcontact-jot.c | 308 ++++++++++
gdata/gcontact/gdata-gcontact-jot.h | 135 +++++
gdata/gcontact/gdata-gcontact-relation.c | 406 +++++++++++++
gdata/gcontact/gdata-gcontact-relation.h | 246 ++++++++
gdata/gcontact/gdata-gcontact-website.c | 469 +++++++++++++++
gdata/gcontact/gdata-gcontact-website.h | 167 ++++++
gdata/gdata-parser.c | 25 +
gdata/gdata-parser.h | 1 +
gdata/gdata.h | 7 +
gdata/gdata.symbols | 65 +++
gdata/services/contacts/gdata-contacts-contact.c | 665 ++++++++++++++++++++--
gdata/services/contacts/gdata-contacts-contact.h | 37 ++-
gdata/tests/contacts.c | 317 ++++++++++-
gdata/tests/general.c | 432 ++++++++++++++
21 files changed, 4591 insertions(+), 53 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d9ad164..01db9e6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -188,6 +188,14 @@ gdatagdinclude_HEADERS = \
gdata/gd/gdata-gd-who.h \
gdata/gd/gdata-gd-name.h
+gdatagcontactincludedir = $(gdataincludedir)/gcontact
+gdatagcontactinclude_HEADERS = \
+ gdata/gcontact/gdata-gcontact-calendar.h \
+ gdata/gcontact/gdata-gcontact-event.h \
+ gdata/gcontact/gdata-gcontact-jot.h \
+ gdata/gcontact/gdata-gcontact-relation.h \
+ gdata/gcontact/gdata-gcontact-website.h
+
gdatamediaincludedir = $(gdataincludedir)/media
gdata_media_headers = \
gdata/media/gdata-media-category.h \
@@ -289,6 +297,12 @@ gdata_libgdata_la_SOURCES = \
gdata/gd/gdata-gd-who.c \
gdata/gd/gdata-gd-name.c \
\
+ gdata/gcontact/gdata-gcontact-calendar.c \
+ gdata/gcontact/gdata-gcontact-event.c \
+ gdata/gcontact/gdata-gcontact-jot.c \
+ gdata/gcontact/gdata-gcontact-relation.c \
+ gdata/gcontact/gdata-gcontact-website.c \
+ \
gdata/georss/gdata-georss-where.c \
\
gdata/media/gdata-media-category.c \
@@ -375,10 +389,10 @@ gdata/GData-$(GDATA_API_VERSION).gir: Makefile gdata/libgdata.la
--library=gdata/libgdata.la $(_gir_libtool) --output $@ \
-I$(top_srcdir) -I$(top_srcdir)/gdata \
$(addprefix $(srcdir)/,$(gdatainclude_HEADERS) $(gdataatominclude_HEADERS) \
- $(gdatagdinclude_HEADERS) $(gdatamediainclude_HEADERS) \
- $(gdatacalendarinclude_HEADERS) $(gdatacontactsinclude_HEADERS) \
- $(gdatadocumentsinclude_HEADERS) $(gdatapicasawebinclude_HEADERS) \
- $(gdatayoutubeinclude_HEADERS)))
+ $(gdatagdinclude_HEADERS) $(gdatagcontactinclude_HEADERS) \
+ $(gdatamediainclude_HEADERS) $(gdatacalendarinclude_HEADERS) \
+ $(gdatacontactsinclude_HEADERS) $(gdatadocumentsinclude_HEADERS) \
+ $(gdatapicasawebinclude_HEADERS) $(gdatayoutubeinclude_HEADERS)))
BUILT_GIRSOURCES = gdata/GData-$(GDATA_API_VERSION).gir
girdir = $(datadir)/gir-1.0
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index 023ec4c..8dddbe4 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -67,6 +67,15 @@
</chapter>
<chapter>
+ <title>GContact API</title>
+ <xi:include href="xml/gdata-gcontact-calendar.xml"/>
+ <xi:include href="xml/gdata-gcontact-event.xml"/>
+ <xi:include href="xml/gdata-gcontact-jot.xml"/>
+ <xi:include href="xml/gdata-gcontact-relation.xml"/>
+ <xi:include href="xml/gdata-gcontact-website.xml"/>
+ </chapter>
+
+ <chapter>
<title>Media RSS API</title>
<xi:include href="xml/gdata-media-category.xml"/>
<xi:include href="xml/gdata-media-content.xml"/>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 28dbcb3..0feadd0 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -318,6 +318,10 @@ GDataContactsContactClass
gdata_contacts_contact_new
gdata_contacts_contact_get_name
gdata_contacts_contact_set_name
+gdata_contacts_contact_get_nickname
+gdata_contacts_contact_set_nickname
+gdata_contacts_contact_get_birthday
+gdata_contacts_contact_set_birthday
gdata_contacts_contact_get_email_addresses
gdata_contacts_contact_get_primary_email_address
gdata_contacts_contact_add_email_address
@@ -338,6 +342,23 @@ gdata_contacts_contact_get_organizations
gdata_contacts_contact_get_primary_organization
gdata_contacts_contact_add_organization
gdata_contacts_contact_remove_all_organizations
+gdata_contacts_contact_add_jot
+gdata_contacts_contact_get_jots
+gdata_contacts_contact_remove_all_jots
+gdata_contacts_contact_add_relation
+gdata_contacts_contact_get_relations
+gdata_contacts_contact_remove_all_relations
+gdata_contacts_contact_add_website
+gdata_contacts_contact_get_websites
+gdata_contacts_contact_get_primary_website
+gdata_contacts_contact_remove_all_websites
+gdata_contacts_contact_add_event
+gdata_contacts_contact_get_events
+gdata_contacts_contact_remove_all_events
+gdata_contacts_contact_add_calendar
+gdata_contacts_contact_get_calendars
+gdata_contacts_contact_get_primary_calendar
+gdata_contacts_contact_remove_all_calendars
gdata_contacts_contact_get_groups
gdata_contacts_contact_add_group
gdata_contacts_contact_remove_group
@@ -1651,3 +1672,158 @@ GDATA_TYPE_GD_NAME
<SUBSECTION Private>
GDataGDNamePrivate
</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-jot</FILE>
+<TITLE>GDataGContactJot</TITLE>
+GDATA_GCONTACT_JOT_HOME
+GDATA_GCONTACT_JOT_KEYWORDS
+GDATA_GCONTACT_JOT_OTHER
+GDATA_GCONTACT_JOT_USER
+GDATA_GCONTACT_JOT_WORK
+GDataGContactJot
+GDataGContactJotClass
+gdata_gcontact_jot_new
+gdata_gcontact_jot_get_content
+gdata_gcontact_jot_set_content
+gdata_gcontact_jot_get_relation_type
+gdata_gcontact_jot_set_relation_type
+<SUBSECTION Standard>
+gdata_gcontact_jot_get_type
+GDATA_GCONTACT_JOT
+GDATA_GCONTACT_JOT_CLASS
+GDATA_GCONTACT_JOT_GET_CLASS
+GDATA_IS_GCONTACT_JOT
+GDATA_IS_GCONTACT_JOT_CLASS
+GDATA_TYPE_GCONTACT_JOT
+<SUBSECTION Private>
+GDataGContactJotPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-relation</FILE>
+<TITLE>GDataGContactRelation</TITLE>
+GDATA_GCONTACT_RELATION_ASSISTANT
+GDATA_GCONTACT_RELATION_BROTHER
+GDATA_GCONTACT_RELATION_CHILD
+GDATA_GCONTACT_RELATION_DOMESTIC_PARTNER
+GDATA_GCONTACT_RELATION_FATHER
+GDATA_GCONTACT_RELATION_FRIEND
+GDATA_GCONTACT_RELATION_MANAGER
+GDATA_GCONTACT_RELATION_MOTHER
+GDATA_GCONTACT_RELATION_PARENT
+GDATA_GCONTACT_RELATION_PARTNER
+GDATA_GCONTACT_RELATION_REFERRER
+GDATA_GCONTACT_RELATION_RELATIVE
+GDATA_GCONTACT_RELATION_SISTER
+GDATA_GCONTACT_RELATION_SPOUSE
+GDataGContactRelation
+GDataGContactRelationClass
+gdata_gcontact_relation_new
+gdata_gcontact_relation_get_name
+gdata_gcontact_relation_set_name
+gdata_gcontact_relation_get_relation_type
+gdata_gcontact_relation_set_relation_type
+gdata_gcontact_relation_get_label
+gdata_gcontact_relation_set_label
+<SUBSECTION Standard>
+gdata_gcontact_relation_get_type
+GDATA_GCONTACT_RELATION
+GDATA_GCONTACT_RELATION_CLASS
+GDATA_GCONTACT_RELATION_GET_CLASS
+GDATA_IS_GCONTACT_RELATION
+GDATA_IS_GCONTACT_RELATION_CLASS
+GDATA_TYPE_GCONTACT_RELATION
+<SUBSECTION Private>
+GDataGContactRelationPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-website</FILE>
+<TITLE>GDataGContactWebsite</TITLE>
+GDATA_GCONTACT_WEBSITE_HOME_PAGE
+GDATA_GCONTACT_WEBSITE_BLOG
+GDATA_GCONTACT_WEBSITE_PROFILE
+GDATA_GCONTACT_WEBSITE_HOME
+GDATA_GCONTACT_WEBSITE_WORK
+GDATA_GCONTACT_WEBSITE_OTHER
+GDATA_GCONTACT_WEBSITE_FTP
+GDataGContactWebsite
+GDataGContactWebsiteClass
+gdata_gcontact_website_new
+gdata_gcontact_website_compare
+gdata_gcontact_website_get_uri
+gdata_gcontact_website_set_uri
+gdata_gcontact_website_get_relation_type
+gdata_gcontact_website_set_relation_type
+gdata_gcontact_website_get_label
+gdata_gcontact_website_set_label
+gdata_gcontact_website_is_primary
+gdata_gcontact_website_set_is_primary
+<SUBSECTION Standard>
+gdata_gcontact_website_get_type
+GDATA_GCONTACT_WEBSITE
+GDATA_GCONTACT_WEBSITE_CLASS
+GDATA_GCONTACT_WEBSITE_GET_CLASS
+GDATA_IS_GCONTACT_WEBSITE
+GDATA_IS_GCONTACT_WEBSITE_CLASS
+GDATA_TYPE_GCONTACT_WEBSITE
+<SUBSECTION Private>
+GDataGContactWebsitePrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-event</FILE>
+<TITLE>GDataGContactEvent</TITLE>
+GDATA_GCONTACT_EVENT_ANNIVERSARY
+GDATA_GCONTACT_EVENT_OTHER
+GDataGContactEvent
+GDataGContactEventClass
+gdata_gcontact_event_new
+gdata_gcontact_event_get_date
+gdata_gcontact_event_set_date
+gdata_gcontact_event_get_relation_type
+gdata_gcontact_event_set_relation_type
+gdata_gcontact_event_get_label
+gdata_gcontact_event_set_label
+<SUBSECTION Standard>
+gdata_gcontact_event_get_type
+GDATA_GCONTACT_EVENT
+GDATA_GCONTACT_EVENT_CLASS
+GDATA_GCONTACT_EVENT_GET_CLASS
+GDATA_IS_GCONTACT_EVENT
+GDATA_IS_GCONTACT_EVENT_CLASS
+GDATA_TYPE_GCONTACT_EVENT
+<SUBSECTION Private>
+GDataGContactEventPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-calendar</FILE>
+<TITLE>GDataGContactCalendar</TITLE>
+GDATA_GCONTACT_CALENDAR_HOME
+GDATA_GCONTACT_CALENDAR_WORK
+GDATA_GCONTACT_CALENDAR_FREE_BUSY
+GDataGContactCalendar
+GDataGContactCalendarClass
+gdata_gcontact_calendar_new
+gdata_gcontact_calendar_compare
+gdata_gcontact_calendar_get_uri
+gdata_gcontact_calendar_set_uri
+gdata_gcontact_calendar_get_relation_type
+gdata_gcontact_calendar_set_relation_type
+gdata_gcontact_calendar_get_label
+gdata_gcontact_calendar_set_label
+gdata_gcontact_calendar_is_primary
+gdata_gcontact_calendar_set_is_primary
+<SUBSECTION Standard>
+gdata_gcontact_calendar_get_type
+GDATA_GCONTACT_CALENDAR
+GDATA_GCONTACT_CALENDAR_CLASS
+GDATA_GCONTACT_CALENDAR_GET_CLASS
+GDATA_IS_GCONTACT_CALENDAR
+GDATA_IS_GCONTACT_CALENDAR_CLASS
+GDATA_TYPE_GCONTACT_CALENDAR
+<SUBSECTION Private>
+GDataGContactCalendarPrivate
+</SECTION>
diff --git a/gdata/gcontact/gdata-gcontact-calendar.c b/gdata/gcontact/gdata-gcontact-calendar.c
new file mode 100644
index 0000000..4e84c44
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-calendar.c
@@ -0,0 +1,490 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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-gcontact-calendar
+ * @short_description: gContact calendar element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-calendar.h
+ *
+ * #GDataGContactCalendar represents a "calendarLink" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-calendar.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_calendar_finalize (GObject *object);
+static void gdata_gcontact_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactCalendarPrivate {
+ gchar *uri;
+ gchar *relation_type;
+ gchar *label;
+ gboolean is_primary;
+};
+
+enum {
+ PROP_URI = 1,
+ PROP_RELATION_TYPE,
+ PROP_LABEL,
+ PROP_IS_PRIMARY
+};
+
+G_DEFINE_TYPE (GDataGContactCalendar, gdata_gcontact_calendar, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_calendar_class_init (GDataGContactCalendarClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataGContactCalendarPrivate));
+
+ gobject_class->get_property = gdata_gcontact_calendar_get_property;
+ gobject_class->set_property = gdata_gcontact_calendar_set_property;
+ gobject_class->finalize = gdata_gcontact_calendar_finalize;
+
+ parsable_class->pre_parse_xml = pre_parse_xml;
+ parsable_class->pre_get_xml = pre_get_xml;
+ parsable_class->get_namespaces = get_namespaces;
+ parsable_class->element_name = "calendarLink";
+ parsable_class->element_namespace = "gContact";
+
+ /**
+ * GDataGContactCalendar:uri:
+ *
+ * The URI of the calendar.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_URI,
+ g_param_spec_string ("uri",
+ "URI", "The URI of the calendar.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactCalendar:relation-type:
+ *
+ * A programmatic value that identifies the type of calendar. It is mutually exclusive with #GDataGContactCalendar:label.
+ * Examples are %GDATA_GCONTACT_CALENDAR_HOME or %GDATA_GCONTACT_CALENDAR_FREE_BUSY.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+ g_param_spec_string ("relation-type",
+ "Relation type", "A programmatic value that identifies the type of calendar.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactCalendar:label:
+ *
+ * A free-form string that identifies the type of calendar. It is mutually exclusive with #GDataGContactCalendar:relation-type.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_LABEL,
+ g_param_spec_string ("label",
+ "Label", "A free-form string that identifies the type of calendar.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactCalendar:is-primary:
+ *
+ * Indicates which calendar out of a group is primary.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_IS_PRIMARY,
+ g_param_spec_boolean ("is-primary",
+ "Primary?", "Indicates which calendar out of a group is primary.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_calendar_init (GDataGContactCalendar *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendarPrivate);
+}
+
+static void
+gdata_gcontact_calendar_finalize (GObject *object)
+{
+ GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (object)->priv;
+
+ g_free (priv->uri);
+ g_free (priv->relation_type);
+ g_free (priv->label);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_gcontact_calendar_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (object)->priv;
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, priv->uri);
+ break;
+ case PROP_RELATION_TYPE:
+ g_value_set_string (value, priv->relation_type);
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ case PROP_IS_PRIMARY:
+ g_value_set_boolean (value, priv->is_primary);
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_gcontact_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataGContactCalendar *self = GDATA_GCONTACT_CALENDAR (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ gdata_gcontact_calendar_set_uri (self, g_value_get_string (value));
+ break;
+ case PROP_RELATION_TYPE:
+ gdata_gcontact_calendar_set_relation_type (self, g_value_get_string (value));
+ break;
+ case PROP_LABEL:
+ gdata_gcontact_calendar_set_label (self, g_value_get_string (value));
+ break;
+ case PROP_IS_PRIMARY:
+ gdata_gcontact_calendar_set_is_primary (self, g_value_get_boolean (value));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+ xmlChar *uri, *rel, *label;
+ gboolean primary_bool;
+ GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (parsable)->priv;
+
+ /* Is it the primary calendar? */
+ if (gdata_parser_boolean_from_property (root_node, "primary", &primary_bool, 0, error) == FALSE)
+ return FALSE;
+
+ uri = xmlGetProp (root_node, (xmlChar*) "href");
+ if (uri == NULL || *uri == '\0') {
+ xmlFree (uri);
+ return gdata_parser_error_required_property_missing (root_node, "href", error);
+ }
+
+ rel = xmlGetProp (root_node, (xmlChar*) "rel");
+ label = xmlGetProp (root_node, (xmlChar*) "label");
+ if ((rel == NULL || *rel == '\0') && (label == NULL || *label == '\0')) {
+ xmlFree (uri);
+ xmlFree (rel);
+ xmlFree (label);
+ return gdata_parser_error_required_property_missing (root_node, "rel", error);
+ } else if (rel != NULL && label != NULL) {
+ /* Can't have both set at once */
+ xmlFree (uri);
+ xmlFree (rel);
+ xmlFree (label);
+ return gdata_parser_error_mutexed_properties (root_node, "rel", "label", error);
+ }
+
+ priv->uri = (gchar*) uri;
+ priv->relation_type = (gchar*) rel;
+ priv->label = (gchar*) label;
+ priv->is_primary = primary_bool;
+
+ return TRUE;
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (parsable)->priv;
+
+ gdata_parser_string_append_escaped (xml_string, " href='", priv->uri, "'");
+
+ if (priv->relation_type != NULL)
+ g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+ else if (priv->label != NULL)
+ gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+ else
+ g_assert_not_reached ();
+
+ if (priv->is_primary == TRUE)
+ g_string_append (xml_string, " primary='true'");
+ else
+ g_string_append (xml_string, " primary='false'");
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+ g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008");
+}
+
+/**
+ * gdata_gcontact_calendar_new:
+ * @uri: the URI of the calendar
+ * @relation_type: the type of calendar, or %NULL
+ * @label: a free-form label for the calendar, or %NULL
+ * @is_primary: %TRUE if this calendar is its owner's primary calendar, %FALSE otherwise
+ *
+ * Creates a new #GDataGContactCalendar. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">gContact specification</ulink>.
+ *
+ * Exactly one of @relation_type and @label should be provided; the other must be %NULL.
+ *
+ * Return value: a new #GDataGContactCalendar; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactCalendar *
+gdata_gcontact_calendar_new (const gchar *uri, const gchar *relation_type, const gchar *label, gboolean is_primary)
+{
+ g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+ g_return_val_if_fail ((relation_type != NULL && *relation_type != '\0' && label == NULL) ||
+ (relation_type == NULL && label != NULL && *label != '\0'), NULL);
+
+ return g_object_new (GDATA_TYPE_GCONTACT_CALENDAR, "uri", uri, "relation-type", relation_type, "label", label, "is-primary", is_primary, NULL);
+}
+
+/**
+ * gdata_gcontact_calendar_compare:
+ * @a: a #GDataGContactCalendar, or %NULL
+ * @b: another #GDataGContactCalendar, or %NULL
+ *
+ * Compares the two calendars in a strcmp() fashion. %NULL values are handled gracefully, with
+ * %0 returned if both @a and @b are %NULL, %-1 if @a is %NULL and %1 if @b is %NULL.
+ *
+ * The comparison of non-%NULL values is done on the basis of the @uri, @relation_type and @label properties of the #GDataGContactCalendar<!-- -->s.
+ *
+ * Return value: %0 if @a equals @b, %-1 or %1 as appropriate otherwise
+ *
+ * Since: 0.7.0
+ **/
+gint
+gdata_gcontact_calendar_compare (const GDataGContactCalendar *a, const GDataGContactCalendar *b)
+{
+ if (a == NULL && b != NULL)
+ return -1;
+ else if (a != NULL && b == NULL)
+ return 1;
+
+ if (a == b)
+ return 0;
+
+ if (g_strcmp0 (a->priv->uri, b->priv->uri) == 0 &&
+ g_strcmp0 (a->priv->relation_type, b->priv->relation_type) == 0 &&
+ g_strcmp0 (a->priv->label, b->priv->label) == 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * gdata_gcontact_calendar_get_uri:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:uri property.
+ *
+ * Return value: the calendar's URI
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_calendar_get_uri (GDataGContactCalendar *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), NULL);
+ return self->priv->uri;
+}
+
+/**
+ * gdata_gcontact_calendar_set_uri:
+ * @self: a #GDataGContactCalendar
+ * @uri: the new URI for the calendar
+ *
+ * Sets the #GDataGContactCalendar:uri property to @uri.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_uri (GDataGContactCalendar *self, const gchar *uri)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+ g_return_if_fail (uri != NULL && *uri != '\0');
+
+ g_free (self->priv->uri);
+ self->priv->uri = g_strdup (uri);
+ g_object_notify (G_OBJECT (self), "uri");
+}
+
+/**
+ * gdata_gcontact_calendar_get_relation_type:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:relation-type property.
+ *
+ * Return value: the type of the relation, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_calendar_get_relation_type (GDataGContactCalendar *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), NULL);
+ return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_calendar_set_relation_type:
+ * @self: a #GDataGContactCalendar
+ * @relation_type: the new type for the calendar, or %NULL
+ *
+ * Sets the #GDataGContactCalendar:relation-type property to @relation_type,
+ * such as %GDATA_GCONTACT_CALENDAR_HOME or %GDATA_GCONTACT_CALENDAR_FREE_BUSY.
+ *
+ * If @relation_type is %NULL, the relation type will be unset. When the #GDataGContactCalendar is used in a query, however,
+ * exactly one of #GDataGContactCalendar:relation-type and #GDataGContactCalendar:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_relation_type (GDataGContactCalendar *self, const gchar *relation_type)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+ g_return_if_fail (relation_type == NULL || *relation_type != '\0');
+
+ g_free (self->priv->relation_type);
+ self->priv->relation_type = g_strdup (relation_type);
+ g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_calendar_get_label:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:label property.
+ *
+ * Return value: a free-form label for the calendar, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_calendar_get_label (GDataGContactCalendar *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), NULL);
+ return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_calendar_set_label:
+ * @self: a #GDataGContactCalendar
+ * @label: the new free-form label for the calendar, or %NULL
+ *
+ * Sets the #GDataGContactCalendar:label property to @label.
+ *
+ * If @label is %NULL, the label will be unset. When the #GDataGContactCalendar is used in a query, however,
+ * exactly one of #GDataGContactCalendar:relation-type and #GDataGContactCalendar:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_label (GDataGContactCalendar *self, const gchar *label)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+ g_return_if_fail (label == NULL || *label != '\0');
+
+ g_free (self->priv->label);
+ self->priv->label = g_strdup (label);
+ g_object_notify (G_OBJECT (self), "label");
+}
+
+/**
+ * gdata_gcontact_calendar_is_primary:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:is-primary property.
+ *
+ * Return value: %TRUE if this is the contact's primary calendar, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ **/
+gboolean
+gdata_gcontact_calendar_is_primary (GDataGContactCalendar *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), FALSE);
+ return self->priv->is_primary;
+}
+
+/**
+ * gdata_gcontact_calendar_set_is_primary:
+ * @self: a #GDataGContactCalendar
+ * @is_primary: %TRUE if this is the contact's primary calendar, %FALSE otherwise
+ *
+ * Sets the #GDataGContactCalendar:is-primary property to @is_primary.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_is_primary (GDataGContactCalendar *self, gboolean is_primary)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+
+ self->priv->is_primary = is_primary;
+ g_object_notify (G_OBJECT (self), "is-primary");
+}
diff --git a/gdata/gcontact/gdata-gcontact-calendar.h b/gdata/gcontact/gdata-gcontact-calendar.h
new file mode 100644
index 0000000..cfc6427
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-calendar.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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_GCONTACT_CALENDAR_H
+#define GDATA_GCONTACT_CALENDAR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_CALENDAR_WORK:
+ *
+ * The relation type URI for a contact's work calendar.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_CALENDAR_WORK "work"
+
+/**
+ * GDATA_GCONTACT_CALENDAR_HOME:
+ *
+ * The relation type URI for a contact's home calendar.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_CALENDAR_HOME "home"
+
+/**
+ * GDATA_GCONTACT_CALENDAR_FREE_BUSY:
+ *
+ * The relation type URI for a contact's free/busy calendar.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_CALENDAR_FREE_BUSY "free-busy"
+
+#define GDATA_TYPE_GCONTACT_CALENDAR (gdata_gcontact_calendar_get_type ())
+#define GDATA_GCONTACT_CALENDAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendar))
+#define GDATA_GCONTACT_CALENDAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendarClass))
+#define GDATA_IS_GCONTACT_CALENDAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_CALENDAR))
+#define GDATA_IS_GCONTACT_CALENDAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_CALENDAR))
+#define GDATA_GCONTACT_CALENDAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendarClass))
+
+typedef struct _GDataGContactCalendarPrivate GDataGContactCalendarPrivate;
+
+/**
+ * GDataGContactCalendar:
+ *
+ * All the fields in the #GDataGContactCalendar structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ GDataParsable parent;
+ GDataGContactCalendarPrivate *priv;
+} GDataGContactCalendar;
+
+/**
+ * GDataGContactCalendarClass:
+ *
+ * All the fields in the #GDataGContactCalendarClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ /*< private >*/
+ GDataParsableClass parent;
+} GDataGContactCalendarClass;
+
+GType gdata_gcontact_calendar_get_type (void) G_GNUC_CONST;
+
+GDataGContactCalendar *gdata_gcontact_calendar_new (const gchar *uri, const gchar *relation_type,
+ const gchar *label, gboolean is_primary) G_GNUC_WARN_UNUSED_RESULT;
+gint gdata_gcontact_calendar_compare (const GDataGContactCalendar *a, const GDataGContactCalendar *b);
+
+const gchar *gdata_gcontact_calendar_get_uri (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_uri (GDataGContactCalendar *self, const gchar *uri);
+
+const gchar *gdata_gcontact_calendar_get_relation_type (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_relation_type (GDataGContactCalendar *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_calendar_get_label (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_label (GDataGContactCalendar *self, const gchar *label);
+
+gboolean gdata_gcontact_calendar_is_primary (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_is_primary (GDataGContactCalendar *self, gboolean is_primary);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_CALENDAR_H */
diff --git a/gdata/gcontact/gdata-gcontact-event.c b/gdata/gcontact/gdata-gcontact-event.c
new file mode 100644
index 0000000..0d9ae02
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-event.c
@@ -0,0 +1,446 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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-gcontact-event
+ * @short_description: gContact event element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-event.h
+ *
+ * #GDataGContactEvent represents a "event" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <string.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-event.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_event_finalize (GObject *object);
+static void gdata_gcontact_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean post_parse_xml (GDataParsable *parsable, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactEventPrivate {
+ GDate date;
+ gchar *relation_type;
+ gchar *label;
+};
+
+enum {
+ PROP_DATE = 1,
+ PROP_RELATION_TYPE,
+ PROP_LABEL
+};
+
+G_DEFINE_TYPE (GDataGContactEvent, gdata_gcontact_event, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_event_class_init (GDataGContactEventClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataGContactEventPrivate));
+
+ gobject_class->get_property = gdata_gcontact_event_get_property;
+ gobject_class->set_property = gdata_gcontact_event_set_property;
+ gobject_class->finalize = gdata_gcontact_event_finalize;
+
+ parsable_class->pre_parse_xml = pre_parse_xml;
+ parsable_class->parse_xml = parse_xml;
+ parsable_class->post_parse_xml = post_parse_xml;
+ parsable_class->pre_get_xml = pre_get_xml;
+ parsable_class->get_xml = get_xml;
+ parsable_class->get_namespaces = get_namespaces;
+ parsable_class->element_name = "event";
+ parsable_class->element_namespace = "gContact";
+
+ /**
+ * GDataGContactEvent:date:
+ *
+ * The date of the event.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">GContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_DATE,
+ g_param_spec_boxed ("date",
+ "Date", "The date of the event.",
+ G_TYPE_DATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactEvent:relation-type:
+ *
+ * A programmatic value that identifies the type of event. It is mutually exclusive with #GDataGContactEvent:label.
+ * Examples are %GDATA_GCONTACT_EVENT_ANNIVERSARY or %GDATA_GCONTACT_EVENT_OTHER.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+ g_param_spec_string ("relation-type",
+ "Relation type", "A programmatic value that identifies the type of website.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactEvent:label:
+ *
+ * A simple string value used to name this event. It is mutually exclusive with #GDataGContactEvent:relation-type.
+ * It allows UIs to display a label such as "Wedding anniversary".
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_LABEL,
+ g_param_spec_string ("label",
+ "Label", "A simple string value used to name this event.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_event_init (GDataGContactEvent *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_EVENT, GDataGContactEventPrivate);
+
+ /* Clear the date to an invalid but sane value */
+ g_date_clear (&(self->priv->date), 1);
+}
+
+static void
+gdata_gcontact_event_finalize (GObject *object)
+{
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (object)->priv;
+
+ g_free (priv->relation_type);
+ g_free (priv->label);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_gcontact_event_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (object)->priv;
+
+ switch (property_id) {
+ case PROP_DATE:
+ g_value_set_boxed (value, &(priv->date));
+ break;
+ case PROP_RELATION_TYPE:
+ g_value_set_string (value, priv->relation_type);
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_gcontact_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataGContactEvent *self = GDATA_GCONTACT_EVENT (object);
+
+ switch (property_id) {
+ case PROP_DATE:
+ gdata_gcontact_event_set_date (self, g_value_get_boxed (value));
+ break;
+ case PROP_RELATION_TYPE:
+ gdata_gcontact_event_set_relation_type (self, g_value_get_string (value));
+ break;
+ case PROP_LABEL:
+ gdata_gcontact_event_set_label (self, g_value_get_string (value));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+ xmlChar *rel, *label;
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+ rel = xmlGetProp (root_node, (xmlChar*) "rel");
+ label = xmlGetProp (root_node, (xmlChar*) "label");
+ if ((rel == NULL || *rel == '\0') && (label == NULL || *label == '\0')) {
+ xmlFree (rel);
+ xmlFree (label);
+ return gdata_parser_error_required_property_missing (root_node, "rel", error);
+ } else if (rel != NULL && label != NULL) {
+ /* Can't have both set at once */
+ xmlFree (rel);
+ xmlFree (label);
+ return gdata_parser_error_mutexed_properties (root_node, "rel", "label", error);
+ }
+
+ priv->relation_type = (gchar*) rel;
+ priv->label = (gchar*) label;
+
+ return TRUE;
+}
+
+static gboolean
+parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+{
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+ if (gdata_parser_is_namespace (node, "http://schemas.google.com/g/2005") == TRUE && xmlStrcmp (node->name, (xmlChar*) "when") == 0) {
+ xmlChar *start_time;
+ guint year, month, day;
+
+ /* gd:when; note we don't use GDataGDWhen here because gContact:event only uses a limited subset
+ * of gd:when (i.e. only the startTime property in date format) */
+ if (g_date_valid (&(priv->date)) == TRUE)
+ return gdata_parser_error_duplicate_element (node, error);
+
+ start_time = xmlGetProp (node, (xmlChar*) "startTime");
+ if (start_time == NULL)
+ return gdata_parser_error_required_property_missing (node, "startTime", error);
+
+ /* Try parsing the date format: YYYY-MM-DD */
+ if (strlen ((char*) start_time) == 10 &&
+ sscanf ((char*) start_time, "%4u-%2u-%2u", &year, &month, &day) == 3 && g_date_valid_dmy (day, month, year) == TRUE) {
+ /* Store the values in the GDate */
+ g_date_set_dmy (&(priv->date), day, month, year);
+ xmlFree (start_time);
+ } else {
+ /* Parsing failed */
+ gdata_parser_error_not_iso8601_format (node, (gchar*) start_time, error);
+ xmlFree (start_time);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return GDATA_PARSABLE_CLASS (gdata_gcontact_event_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+}
+
+static gboolean
+post_parse_xml (GDataParsable *parsable, gpointer user_data, GError **error)
+{
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+ /* Check for missing required elements */
+ if (g_date_valid (&(priv->date)) == FALSE)
+ return gdata_parser_error_required_element_missing ("gd:when", "gContact:event", error);
+
+ return TRUE;
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+ if (priv->relation_type != NULL)
+ g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+ else if (priv->label != NULL)
+ gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+ else
+ g_assert_not_reached ();
+}
+
+static void
+get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+ g_string_append_printf (xml_string, "<gd:when startTime='%04u-%02u-%02u'/>",
+ g_date_get_year (&(priv->date)),
+ g_date_get_month (&(priv->date)),
+ g_date_get_day (&(priv->date)));
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+ g_hash_table_insert (namespaces, (gchar*) "gd", (gchar*) "http://schemas.google.com/g/2005");
+ g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008");
+}
+
+/**
+ * gdata_gcontact_event_new:
+ * @date: the date of the event
+ * @relation_type: the relationship between the event and its owner, or %NULL
+ * @label: a human-readable label for the event, or %NULL
+ *
+ * Creates a new #GDataGContactEvent. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">gContact specification</ulink>.
+ *
+ * Exactly one of @relation_type and @label should be provided; the other must be %NULL.
+ *
+ * Return value: a new #GDataGContactEvent; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactEvent *
+gdata_gcontact_event_new (const GDate *date, const gchar *relation_type, const gchar *label)
+{
+ g_return_val_if_fail (date != NULL && g_date_valid (date) == TRUE, NULL);
+ g_return_val_if_fail ((relation_type != NULL && *relation_type != '\0' && label == NULL) ||
+ (relation_type == NULL && label != NULL && *label != '\0'), NULL);
+
+ return g_object_new (GDATA_TYPE_GCONTACT_EVENT, "date", date, "relation-type", relation_type, "label", label, NULL);
+}
+
+/**
+ * gdata_gcontact_event_get_date:
+ * @self: a #GDataGContactEvent
+ * @date: return location for the date of the event
+ *
+ * Gets the #GDataGContactEvent:date property.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_get_date (GDataGContactEvent *self, GDate *date)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+ g_return_if_fail (date != NULL);
+ *date = self->priv->date;
+}
+
+/**
+ * gdata_gcontact_event_set_date:
+ * @self: a #GDataGContactEvent
+ * @date: the new date for the event
+ *
+ * Sets the #GDataGContactEvent:date property to @date.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_set_date (GDataGContactEvent *self, const GDate *date)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+ g_return_if_fail (date != NULL && g_date_valid (date) == TRUE);
+
+ self->priv->date = *date;
+ g_object_notify (G_OBJECT (self), "date");
+}
+
+/**
+ * gdata_gcontact_event_get_relation_type:
+ * @self: a #GDataGContactEvent
+ *
+ * Gets the #GDataGContactEvent:relation-type property.
+ *
+ * Return value: the event's relation type, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_event_get_relation_type (GDataGContactEvent *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_EVENT (self), NULL);
+ return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_event_set_relation_type:
+ * @self: a #GDataGContactEvent
+ * @relation_type: the new relation type for the event, or %NULL
+ *
+ * Sets the #GDataGContactEvent:relation-type property to @relation_type
+ * such as %GDATA_GCONTACT_EVENT_ANNIVERSARY or %GDATA_GCONTACT_EVENT_OTHER.
+ *
+ * If @relation_type is %NULL, the relation type will be unset. When the #GDataGContactEvent is used in a query, however,
+ * exactly one of #GDataGContactEvent:relation-type and #GDataGContactEvent:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_set_relation_type (GDataGContactEvent *self, const gchar *relation_type)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+ g_return_if_fail (relation_type == NULL || *relation_type != '\0');
+
+ g_free (self->priv->relation_type);
+ self->priv->relation_type = g_strdup (relation_type);
+ g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_event_get_label:
+ * @self: a #GDataGContactEvent
+ *
+ * Gets the #GDataGContactEvent:label property.
+ *
+ * Return value: the event's label, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_event_get_label (GDataGContactEvent *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_EVENT (self), NULL);
+ return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_event_set_label:
+ * @self: a #GDataGContactEvent
+ * @label: the new label for the event, or %NULL
+ *
+ * Sets the #GDataGContactEvent:label property to @label.
+ *
+ * If @label is %NULL, the label will be unset. When the #GDataGContactEvent is used in a query, however,
+ * exactly one of #GDataGContactEvent:relation-type and #GDataGContactEvent:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_set_label (GDataGContactEvent *self, const gchar *label)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+ g_return_if_fail (label == NULL || *label != '\0');
+
+ g_free (self->priv->label);
+ self->priv->label = g_strdup (label);
+ g_object_notify (G_OBJECT (self), "label");
+}
diff --git a/gdata/gcontact/gdata-gcontact-event.h b/gdata/gcontact/gdata-gcontact-event.h
new file mode 100644
index 0000000..6a9caf0
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-event.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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_GCONTACT_EVENT_H
+#define GDATA_GCONTACT_EVENT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_EVENT_ANNIVERSARY:
+ *
+ * The relation type URI for an anniversary event.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_EVENT_ANNIVERSARY "anniversary"
+
+/**
+ * GDATA_GCONTACT_EVENT_OTHER:
+ *
+ * The relation type URI for a miscellaneous event.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_EVENT_OTHER "other"
+
+#define GDATA_TYPE_GCONTACT_EVENT (gdata_gcontact_event_get_type ())
+#define GDATA_GCONTACT_EVENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_EVENT, GDataGContactEvent))
+#define GDATA_GCONTACT_EVENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_EVENT, GDataGContactEventClass))
+#define GDATA_IS_GCONTACT_EVENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_EVENT))
+#define GDATA_IS_GCONTACT_EVENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_EVENT))
+#define GDATA_GCONTACT_EVENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_EVENT, GDataGContactEventClass))
+
+typedef struct _GDataGContactEventPrivate GDataGContactEventPrivate;
+
+/**
+ * GDataGContactEvent:
+ *
+ * All the fields in the #GDataGContactEvent structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ GDataParsable parent;
+ GDataGContactEventPrivate *priv;
+} GDataGContactEvent;
+
+/**
+ * GDataGContactEventClass:
+ *
+ * All the fields in the #GDataGContactEventClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ /*< private >*/
+ GDataParsableClass parent;
+} GDataGContactEventClass;
+
+GType gdata_gcontact_event_get_type (void) G_GNUC_CONST;
+
+GDataGContactEvent *gdata_gcontact_event_new (const GDate *date, const gchar *relation_type, const gchar *label) G_GNUC_WARN_UNUSED_RESULT;
+
+void gdata_gcontact_event_get_date (GDataGContactEvent *self, GDate *date);
+void gdata_gcontact_event_set_date (GDataGContactEvent *self, const GDate *date);
+
+const gchar *gdata_gcontact_event_get_relation_type (GDataGContactEvent *self);
+void gdata_gcontact_event_set_relation_type (GDataGContactEvent *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_event_get_label (GDataGContactEvent *self);
+void gdata_gcontact_event_set_label (GDataGContactEvent *self, const gchar *label);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_EVENT_H */
diff --git a/gdata/gcontact/gdata-gcontact-jot.c b/gdata/gcontact/gdata-gcontact-jot.c
new file mode 100644
index 0000000..075856f
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-jot.c
@@ -0,0 +1,308 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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-gcontact-jot
+ * @short_description: gContact jot element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-jot.h
+ *
+ * #GDataGContactJot represents a "jot" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-jot.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_jot_finalize (GObject *object);
+static void gdata_gcontact_jot_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_jot_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactJotPrivate {
+ gchar *content;
+ gchar *relation_type;
+};
+
+enum {
+ PROP_CONTENT = 1,
+ PROP_RELATION_TYPE
+};
+
+G_DEFINE_TYPE (GDataGContactJot, gdata_gcontact_jot, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_jot_class_init (GDataGContactJotClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataGContactJotPrivate));
+
+ gobject_class->get_property = gdata_gcontact_jot_get_property;
+ gobject_class->set_property = gdata_gcontact_jot_set_property;
+ gobject_class->finalize = gdata_gcontact_jot_finalize;
+
+ parsable_class->pre_parse_xml = pre_parse_xml;
+ parsable_class->parse_xml = parse_xml;
+ parsable_class->pre_get_xml = pre_get_xml;
+ parsable_class->get_xml = get_xml;
+ parsable_class->get_namespaces = get_namespaces;
+ parsable_class->element_name = "jot";
+ parsable_class->element_namespace = "gContact";
+
+ /**
+ * GDataGContactJot:content:
+ *
+ * The content of the jot.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_CONTENT,
+ g_param_spec_string ("content",
+ "Content", "The content of the jot.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactJot:relation-type:
+ *
+ * A programmatic value that identifies the type of jot. Examples are %GDATA_GCONTACT_JOT_HOME or %GDATA_GCONTACT_JOT_OTHER.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+ g_param_spec_string ("relation-type",
+ "Relation type", "A programmatic value that identifies the type of jot.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_jot_init (GDataGContactJot *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_JOT, GDataGContactJotPrivate);
+}
+
+static void
+gdata_gcontact_jot_finalize (GObject *object)
+{
+ GDataGContactJotPrivate *priv = GDATA_GCONTACT_JOT (object)->priv;
+
+ g_free (priv->content);
+ g_free (priv->relation_type);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_gcontact_jot_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_jot_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ GDataGContactJotPrivate *priv = GDATA_GCONTACT_JOT (object)->priv;
+
+ switch (property_id) {
+ case PROP_CONTENT:
+ g_value_set_string (value, priv->content);
+ break;
+ case PROP_RELATION_TYPE:
+ g_value_set_string (value, priv->relation_type);
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_gcontact_jot_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataGContactJot *self = GDATA_GCONTACT_JOT (object);
+
+ switch (property_id) {
+ case PROP_CONTENT:
+ gdata_gcontact_jot_set_content (self, g_value_get_string (value));
+ break;
+ case PROP_RELATION_TYPE:
+ gdata_gcontact_jot_set_relation_type (self, g_value_get_string (value));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+ xmlChar *rel;
+ GDataGContactJotPrivate *priv = GDATA_GCONTACT_JOT (parsable)->priv;
+
+ rel = xmlGetProp (root_node, (xmlChar*) "rel");
+ if (rel == NULL || *rel == '\0') {
+ xmlFree (rel);
+ return gdata_parser_error_required_property_missing (root_node, "rel", error);
+ }
+
+ priv->content = (gchar*) xmlNodeListGetString (doc, root_node->children, TRUE);
+ priv->relation_type = (gchar*) rel;
+
+ return TRUE;
+}
+
+static gboolean
+parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+{
+ /* Textual content's handled in pre_parse_xml */
+ if (node->type != XML_ELEMENT_NODE)
+ return TRUE;
+
+ return GDATA_PARSABLE_CLASS (gdata_gcontact_jot_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ g_string_append_printf (xml_string, " rel='%s'", GDATA_GCONTACT_JOT (parsable)->priv->relation_type);
+}
+
+static void
+get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ gdata_parser_string_append_escaped (xml_string, NULL, GDATA_GCONTACT_JOT (parsable)->priv->content, NULL);
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+ g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008");
+}
+
+/**
+ * gdata_gcontact_jot_new:
+ * @content: the content of the jot
+ * @relation_type: the relationship between the jot and its owner
+ *
+ * Creates a new #GDataGContactJot. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">gContact specification</ulink>.
+ *
+ * Return value: a new #GDataGContactJot; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactJot *
+gdata_gcontact_jot_new (const gchar *content, const gchar *relation_type)
+{
+ g_return_val_if_fail (content != NULL, NULL);
+ g_return_val_if_fail (relation_type != NULL && *relation_type != '\0', NULL);
+ return g_object_new (GDATA_TYPE_GCONTACT_JOT, "content", content, "relation-type", relation_type, NULL);
+}
+
+/**
+ * gdata_gcontact_jot_get_content:
+ * @self: a #GDataGContactJot
+ *
+ * Gets the #GDataGContactJot:content property.
+ *
+ * Return value: the jot's content
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_jot_get_content (GDataGContactJot *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_JOT (self), NULL);
+ return self->priv->content;
+}
+
+/**
+ * gdata_gcontact_jot_set_content:
+ * @self: a #GDataGContactJot
+ * @content: the new content
+ *
+ * Sets the #GDataGContactJot:content property to @content.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_jot_set_content (GDataGContactJot *self, const gchar *content)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_JOT (self));
+ g_return_if_fail (content != NULL);
+
+ g_free (self->priv->content);
+ self->priv->content = g_strdup (content);
+ g_object_notify (G_OBJECT (self), "content");
+}
+
+/**
+ * gdata_gcontact_jot_get_relation_type:
+ * @self: a #GDataGContactJot
+ *
+ * Gets the #GDataGContactJot:relation-type property.
+ *
+ * Return value: the jot's relation type
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_jot_get_relation_type (GDataGContactJot *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_JOT (self), NULL);
+ return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_jot_set_relation_type:
+ * @self: a #GDataGContactJot
+ * @relation_type: the new relation type for the jot, or %NULL
+ *
+ * Sets the #GDataGContactJot:relation-type property to @relation_type
+ * such as %GDATA_GCONTACT_JOT_HOME or %GDATA_GCONTACT_JOT_OTHER.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_jot_set_relation_type (GDataGContactJot *self, const gchar *relation_type)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_JOT (self));
+ g_return_if_fail (relation_type != NULL && *relation_type != '\0');
+
+ g_free (self->priv->relation_type);
+ self->priv->relation_type = g_strdup (relation_type);
+ g_object_notify (G_OBJECT (self), "relation-type");
+}
diff --git a/gdata/gcontact/gdata-gcontact-jot.h b/gdata/gcontact/gdata-gcontact-jot.h
new file mode 100644
index 0000000..b4e4217
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-jot.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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_GCONTACT_JOT_H
+#define GDATA_GCONTACT_JOT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_JOT_HOME:
+ *
+ * The relation type URI for a jot about a contact's home.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_HOME "home"
+
+/**
+ * GDATA_GCONTACT_JOT_WORK:
+ *
+ * The relation type URI for a jot about a contact's work.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_WORK "work"
+
+/**
+ * GDATA_GCONTACT_JOT_OTHER:
+ *
+ * The relation type URI for a jot about an other facet of a contact.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_OTHER "other"
+
+/**
+ * GDATA_GCONTACT_JOT_KEYWORDS:
+ *
+ * The relation type URI for a jot with keywords about a contact.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_KEYWORDS "keywords"
+
+/**
+ * GDATA_GCONTACT_JOT_USER:
+ *
+ * The relation type URI for a jot about the relationship between a contact and the user.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_USER "user"
+
+#define GDATA_TYPE_GCONTACT_JOT (gdata_gcontact_jot_get_type ())
+#define GDATA_GCONTACT_JOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_JOT, GDataGContactJot))
+#define GDATA_GCONTACT_JOT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_JOT, GDataGContactJotClass))
+#define GDATA_IS_GCONTACT_JOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_JOT))
+#define GDATA_IS_GCONTACT_JOT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_JOT))
+#define GDATA_GCONTACT_JOT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_JOT, GDataGContactJotClass))
+
+typedef struct _GDataGContactJotPrivate GDataGContactJotPrivate;
+
+/**
+ * GDataGContactJot:
+ *
+ * All the fields in the #GDataGContactJot structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ GDataParsable parent;
+ GDataGContactJotPrivate *priv;
+} GDataGContactJot;
+
+/**
+ * GDataGContactJotClass:
+ *
+ * All the fields in the #GDataGContactJotClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ /*< private >*/
+ GDataParsableClass parent;
+} GDataGContactJotClass;
+
+GType gdata_gcontact_jot_get_type (void) G_GNUC_CONST;
+
+GDataGContactJot *gdata_gcontact_jot_new (const gchar *content, const gchar *relation_type) G_GNUC_WARN_UNUSED_RESULT;
+
+const gchar *gdata_gcontact_jot_get_content (GDataGContactJot *self);
+void gdata_gcontact_jot_set_content (GDataGContactJot *self, const gchar *content);
+
+const gchar *gdata_gcontact_jot_get_relation_type (GDataGContactJot *self);
+void gdata_gcontact_jot_set_relation_type (GDataGContactJot *self, const gchar *relation_type);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_JOT_H */
diff --git a/gdata/gcontact/gdata-gcontact-relation.c b/gdata/gcontact/gdata-gcontact-relation.c
new file mode 100644
index 0000000..024e63f
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-relation.c
@@ -0,0 +1,406 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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-gcontact-relation
+ * @short_description: gContact relation element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-relation.h
+ *
+ * #GDataGContactRelation represents a "relation" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-relation.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_relation_finalize (GObject *object);
+static void gdata_gcontact_relation_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_relation_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactRelationPrivate {
+ gchar *name;
+ gchar *relation_type;
+ gchar *label;
+};
+
+enum {
+ PROP_NAME = 1,
+ PROP_RELATION_TYPE,
+ PROP_LABEL
+};
+
+G_DEFINE_TYPE (GDataGContactRelation, gdata_gcontact_relation, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_relation_class_init (GDataGContactRelationClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataGContactRelationPrivate));
+
+ gobject_class->get_property = gdata_gcontact_relation_get_property;
+ gobject_class->set_property = gdata_gcontact_relation_set_property;
+ gobject_class->finalize = gdata_gcontact_relation_finalize;
+
+ parsable_class->pre_parse_xml = pre_parse_xml;
+ parsable_class->parse_xml = parse_xml;
+ parsable_class->pre_get_xml = pre_get_xml;
+ parsable_class->get_xml = get_xml;
+ parsable_class->get_namespaces = get_namespaces;
+ parsable_class->element_name = "relation";
+ parsable_class->element_namespace = "gContact";
+
+ /**
+ * GDataGContactRelation:name:
+ *
+ * The name of the relation. It need not be a full name, and there need not be a contact representing the name.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_NAME,
+ g_param_spec_string ("name",
+ "Name", "The name of the relation.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactRelation:relation-type:
+ *
+ * A programmatic value that identifies the type of relation. It is mutually exclusive with #GDataGContactRelation:label.
+ * Examples are %GDATA_GCONTACT_RELATION_MANAGER or %GDATA_GCONTACT_RELATION_CHILD.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+ g_param_spec_string ("relation-type",
+ "Relation type", "A programmatic value that identifies the type of relation.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactRelation:label:
+ *
+ * A free-form string that identifies the type of relation. It is mutually exclusive with #GDataGContactRelation:relation-type.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_LABEL,
+ g_param_spec_string ("label",
+ "Label", "A free-form string that identifies the type of relation.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_relation_init (GDataGContactRelation *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelationPrivate);
+}
+
+static void
+gdata_gcontact_relation_finalize (GObject *object)
+{
+ GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (object)->priv;
+
+ g_free (priv->name);
+ g_free (priv->relation_type);
+ g_free (priv->label);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_gcontact_relation_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_relation_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (object)->priv;
+
+ switch (property_id) {
+ case PROP_NAME:
+ g_value_set_string (value, priv->name);
+ break;
+ case PROP_RELATION_TYPE:
+ g_value_set_string (value, priv->relation_type);
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_gcontact_relation_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataGContactRelation *self = GDATA_GCONTACT_RELATION (object);
+
+ switch (property_id) {
+ case PROP_NAME:
+ gdata_gcontact_relation_set_name (self, g_value_get_string (value));
+ break;
+ case PROP_RELATION_TYPE:
+ gdata_gcontact_relation_set_relation_type (self, g_value_get_string (value));
+ break;
+ case PROP_LABEL:
+ gdata_gcontact_relation_set_label (self, g_value_get_string (value));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+ xmlChar *rel, *label, *name;
+ GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (parsable)->priv;
+
+ rel = xmlGetProp (root_node, (xmlChar*) "rel");
+ label = xmlGetProp (root_node, (xmlChar*) "label");
+ if ((rel == NULL || *rel == '\0') && (label == NULL || *label == '\0')) {
+ xmlFree (rel);
+ xmlFree (label);
+ return gdata_parser_error_required_property_missing (root_node, "rel", error);
+ } else if (rel != NULL && label != NULL) {
+ /* Can't have both set at once */
+ xmlFree (rel);
+ xmlFree (label);
+ return gdata_parser_error_mutexed_properties (root_node, "rel", "label", error);
+ }
+
+ /* Get the name */
+ name = xmlNodeListGetString (doc, root_node->children, TRUE);
+ if (name == NULL || *name == '\0') {
+ xmlFree (rel);
+ xmlFree (label);
+ xmlFree (name);
+ return gdata_parser_error_required_content_missing (root_node, error);
+ }
+
+ priv->name = (gchar*) name;
+ priv->relation_type = (gchar*) rel;
+ priv->label = (gchar*) label;
+
+ return TRUE;
+}
+
+static gboolean
+parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+{
+ /* Textual content's handled in pre_parse_xml */
+ if (node->type != XML_ELEMENT_NODE)
+ return TRUE;
+
+ return GDATA_PARSABLE_CLASS (gdata_gcontact_relation_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (parsable)->priv;
+
+ if (priv->relation_type != NULL)
+ g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+ else if (priv->label != NULL)
+ gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+ else
+ g_assert_not_reached ();
+}
+
+static void
+get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ gdata_parser_string_append_escaped (xml_string, NULL, GDATA_GCONTACT_RELATION (parsable)->priv->name, NULL);
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+ g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008");
+}
+
+/**
+ * gdata_gcontact_relation_new:
+ * @name: the name of the relation
+ * @relation_type: the type of relation, or %NULL
+ * @label: a free-form label for the type of relation, or %NULL
+ *
+ * Creates a new #GDataGContactRelation. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">gContact specification</ulink>.
+ *
+ * Exactly one of @relation_type and @label should be provided; the other must be %NULL.
+ *
+ * Return value: a new #GDataGContactRelation; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactRelation *
+gdata_gcontact_relation_new (const gchar *name, const gchar *relation_type, const gchar *label)
+{
+ g_return_val_if_fail (name != NULL && *name != '\0', NULL);
+ g_return_val_if_fail ((relation_type != NULL && *relation_type != '\0' && label == NULL) ||
+ (relation_type == NULL && label != NULL && *label != '\0'), NULL);
+
+ return g_object_new (GDATA_TYPE_GCONTACT_RELATION, "name", name, "relation-type", relation_type, "label", label, NULL);
+}
+
+/**
+ * gdata_gcontact_relation_get_name:
+ * @self: a #GDataGContactRelation
+ *
+ * Gets the #GDataGContactRelation:name property.
+ *
+ * Return value: the relation's name
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_relation_get_name (GDataGContactRelation *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_RELATION (self), NULL);
+ return self->priv->name;
+}
+
+/**
+ * gdata_gcontact_relation_set_name:
+ * @self: a #GDataGContactRelation
+ * @name: the new name for the relation
+ *
+ * Sets the #GDataGContactRelation:name property to @name.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_relation_set_name (GDataGContactRelation *self, const gchar *name)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_RELATION (self));
+ g_return_if_fail (name != NULL && *name != '\0');
+
+ g_free (self->priv->name);
+ self->priv->name = g_strdup (name);
+ g_object_notify (G_OBJECT (self), "name");
+}
+
+/**
+ * gdata_gcontact_relation_get_relation_type:
+ * @self: a #GDataGContactRelation
+ *
+ * Gets the #GDataGContactRelation:relation-type property.
+ *
+ * Return value: the type of the relation, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_relation_get_relation_type (GDataGContactRelation *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_RELATION (self), NULL);
+ return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_relation_set_relation_type:
+ * @self: a #GDataGContactRelation
+ * @relation_type: the new type for the relation, or %NULL
+ *
+ * Sets the #GDataGContactRelation:relation-type property to @relation_type,
+ * such as %GDATA_GCONTACT_RELATION_MANAGER or %GDATA_GCONTACT_RELATION_CHILD.
+ *
+ * If @relation_type is %NULL, the relation type will be unset. When the #GDataGContactRelation is used in a query, however,
+ * exactly one of #GDataGContactRelation:relation-type and #GDataGContactRelation:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_relation_set_relation_type (GDataGContactRelation *self, const gchar *relation_type)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_RELATION (self));
+ g_return_if_fail (relation_type == NULL || *relation_type != '\0');
+
+ g_free (self->priv->relation_type);
+ self->priv->relation_type = g_strdup (relation_type);
+ g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_relation_get_label:
+ * @self: a #GDataGContactRelation
+ *
+ * Gets the #GDataGContactRelation:label property.
+ *
+ * Return value: a free-form label for the type of the relation, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_relation_get_label (GDataGContactRelation *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_RELATION (self), NULL);
+ return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_relation_set_label:
+ * @self: a #GDataGContactRelation
+ * @label: the new free-form type for the relation, or %NULL
+ *
+ * Sets the #GDataGContactRelation:label property to @label.
+ *
+ * If @label is %NULL, the label will be unset. When the #GDataGContactRelation is used in a query, however,
+ * exactly one of #GDataGContactRelation:relation-type and #GDataGContactRelation:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_relation_set_label (GDataGContactRelation *self, const gchar *label)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_RELATION (self));
+ g_return_if_fail (label == NULL || *label != '\0');
+
+ g_free (self->priv->label);
+ self->priv->label = g_strdup (label);
+ g_object_notify (G_OBJECT (self), "label");
+}
diff --git a/gdata/gcontact/gdata-gcontact-relation.h b/gdata/gcontact/gdata-gcontact-relation.h
new file mode 100644
index 0000000..efcd004
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-relation.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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_GCONTACT_RELATION_H
+#define GDATA_GCONTACT_RELATION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_RELATION_ASSISTANT:
+ *
+ * The relation type URI for a contact's assistant.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_ASSISTANT "assistant"
+
+/**
+ * GDATA_GCONTACT_RELATION_BROTHER:
+ *
+ * The relation type URI for a contact's brother.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_BROTHER "brother"
+
+/**
+ * GDATA_GCONTACT_RELATION_CHILD:
+ *
+ * The relation type URI for a contact's child.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_CHILD "child"
+
+/**
+ * GDATA_GCONTACT_RELATION_DOMESTIC_PARTNER:
+ *
+ * The relation type URI for a contact's domestic partner.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_DOMESTIC_PARTNER "domestic-partner"
+
+/**
+ * GDATA_GCONTACT_RELATION_FATHER:
+ *
+ * The relation type URI for a contact's father.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_FATHER "father"
+
+/**
+ * GDATA_GCONTACT_RELATION_FRIEND:
+ *
+ * The relation type URI for a contact's friend.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_FRIEND "friend"
+
+/**
+ * GDATA_GCONTACT_RELATION_MANAGER:
+ *
+ * The relation type URI for a contact's manager.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_MANAGER "manager"
+
+/**
+ * GDATA_GCONTACT_RELATION_MOTHER:
+ *
+ * The relation type URI for a contact's mother.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_MOTHER "mother"
+
+/**
+ * GDATA_GCONTACT_RELATION_PARENT:
+ *
+ * The relation type URI for a contact's parent.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_PARENT "parent"
+
+/**
+ * GDATA_GCONTACT_RELATION_PARTNER:
+ *
+ * The relation type URI for a contact's business partner.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_PARTNER "partner"
+
+/**
+ * GDATA_GCONTACT_RELATION_REFERRER:
+ *
+ * The relation type URI for a contact's referrer.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_REFERRER "referred-by"
+
+/**
+ * GDATA_GCONTACT_RELATION_RELATIVE:
+ *
+ * The relation type URI for a contact's (general) family relative.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_RELATIVE "relative"
+
+/**
+ * GDATA_GCONTACT_RELATION_SISTER:
+ *
+ * The relation type URI for a contact's sister.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_SISTER "sister"
+
+/**
+ * GDATA_GCONTACT_RELATION_SPOUSE:
+ *
+ * The relation type URI for a contact's spouse.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_SPOUSE "spouse"
+
+#define GDATA_TYPE_GCONTACT_RELATION (gdata_gcontact_relation_get_type ())
+#define GDATA_GCONTACT_RELATION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelation))
+#define GDATA_GCONTACT_RELATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelationClass))
+#define GDATA_IS_GCONTACT_RELATION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_RELATION))
+#define GDATA_IS_GCONTACT_RELATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_RELATION))
+#define GDATA_GCONTACT_RELATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelationClass))
+
+typedef struct _GDataGContactRelationPrivate GDataGContactRelationPrivate;
+
+/**
+ * GDataGContactRelation:
+ *
+ * All the fields in the #GDataGContactRelation structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ GDataParsable parent;
+ GDataGContactRelationPrivate *priv;
+} GDataGContactRelation;
+
+/**
+ * GDataGContactRelationClass:
+ *
+ * All the fields in the #GDataGContactRelationClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ /*< private >*/
+ GDataParsableClass parent;
+} GDataGContactRelationClass;
+
+GType gdata_gcontact_relation_get_type (void) G_GNUC_CONST;
+
+GDataGContactRelation *gdata_gcontact_relation_new (const gchar *name, const gchar *relation_type, const gchar *label) G_GNUC_WARN_UNUSED_RESULT;
+
+const gchar *gdata_gcontact_relation_get_name (GDataGContactRelation *self);
+void gdata_gcontact_relation_set_name (GDataGContactRelation *self, const gchar *name);
+
+const gchar *gdata_gcontact_relation_get_relation_type (GDataGContactRelation *self);
+void gdata_gcontact_relation_set_relation_type (GDataGContactRelation *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_relation_get_label (GDataGContactRelation *self);
+void gdata_gcontact_relation_set_label (GDataGContactRelation *self, const gchar *label);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_RELATION_H */
diff --git a/gdata/gcontact/gdata-gcontact-website.c b/gdata/gcontact/gdata-gcontact-website.c
new file mode 100644
index 0000000..b9182f2
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-website.c
@@ -0,0 +1,469 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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-gcontact-website
+ * @short_description: gContact website element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-website.h
+ *
+ * #GDataGContactWebsite represents a "website" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-website.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_website_finalize (GObject *object);
+static void gdata_gcontact_website_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_website_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactWebsitePrivate {
+ gchar *uri;
+ gchar *relation_type;
+ gchar *label;
+ gboolean is_primary;
+};
+
+enum {
+ PROP_URI = 1,
+ PROP_RELATION_TYPE,
+ PROP_LABEL,
+ PROP_IS_PRIMARY
+};
+
+G_DEFINE_TYPE (GDataGContactWebsite, gdata_gcontact_website, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_website_class_init (GDataGContactWebsiteClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataGContactWebsitePrivate));
+
+ gobject_class->get_property = gdata_gcontact_website_get_property;
+ gobject_class->set_property = gdata_gcontact_website_set_property;
+ gobject_class->finalize = gdata_gcontact_website_finalize;
+
+ parsable_class->pre_parse_xml = pre_parse_xml;
+ parsable_class->pre_get_xml = pre_get_xml;
+ parsable_class->get_namespaces = get_namespaces;
+ parsable_class->element_name = "website";
+ parsable_class->element_namespace = "gContact";
+
+ /**
+ * GDataGContactWebsite:uri:
+ *
+ * The URI of the website.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_URI,
+ g_param_spec_string ("uri",
+ "URI", "The URI of the website.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactWebsite:relation-type:
+ *
+ * A programmatic value that identifies the type of website. Examples are %GDATA_GCONTACT_WEBSITE_HOME_PAGE or %GDATA_GCONTACT_WEBSITE_FTP.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+ g_param_spec_string ("relation-type",
+ "Relation type", "A programmatic value that identifies the type of website.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactWebsite:label:
+ *
+ * A simple string value used to name this website. It allows UIs to display a label such as "Work", "Travel blog", "Personal blog", etc.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_LABEL,
+ g_param_spec_string ("label",
+ "Label", "A simple string value used to name this website.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataGContactWebsite:is-primary:
+ *
+ * Indicates which website out of a group is primary.
+ *
+ * For more information, see the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_IS_PRIMARY,
+ g_param_spec_boolean ("is-primary",
+ "Primary?", "Indicates which website out of a group is primary.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_website_init (GDataGContactWebsite *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsitePrivate);
+}
+
+static void
+gdata_gcontact_website_finalize (GObject *object)
+{
+ GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (object)->priv;
+
+ g_free (priv->uri);
+ g_free (priv->relation_type);
+ g_free (priv->label);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_gcontact_website_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_website_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (object)->priv;
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string (value, priv->uri);
+ break;
+ case PROP_RELATION_TYPE:
+ g_value_set_string (value, priv->relation_type);
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ case PROP_IS_PRIMARY:
+ g_value_set_boolean (value, priv->is_primary);
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_gcontact_website_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataGContactWebsite *self = GDATA_GCONTACT_WEBSITE (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ gdata_gcontact_website_set_uri (self, g_value_get_string (value));
+ break;
+ case PROP_RELATION_TYPE:
+ gdata_gcontact_website_set_relation_type (self, g_value_get_string (value));
+ break;
+ case PROP_LABEL:
+ gdata_gcontact_website_set_label (self, g_value_get_string (value));
+ break;
+ case PROP_IS_PRIMARY:
+ gdata_gcontact_website_set_is_primary (self, g_value_get_boolean (value));
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+ xmlChar *uri, *rel;
+ gboolean primary_bool;
+ GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (parsable)->priv;
+
+ /* Is it the primary website? */
+ if (gdata_parser_boolean_from_property (root_node, "primary", &primary_bool, 0, error) == FALSE)
+ return FALSE;
+
+ uri = xmlGetProp (root_node, (xmlChar*) "href");
+ if (uri == NULL || *uri == '\0') {
+ xmlFree (uri);
+ return gdata_parser_error_required_property_missing (root_node, "href", error);
+ }
+
+ rel = xmlGetProp (root_node, (xmlChar*) "rel");
+ if (rel == NULL || *rel == '\0') {
+ xmlFree (uri);
+ xmlFree (rel);
+ return gdata_parser_error_required_property_missing (root_node, "rel", error);
+ }
+
+ priv->uri = (gchar*) uri;
+ priv->relation_type = (gchar*) rel;
+ priv->label = (gchar*) xmlGetProp (root_node, (xmlChar*) "label");
+ priv->is_primary = primary_bool;
+
+ return TRUE;
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+ GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (parsable)->priv;
+
+ gdata_parser_string_append_escaped (xml_string, " href='", priv->uri, "'");
+ g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+
+ if (priv->label != NULL)
+ gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+
+ if (priv->is_primary == TRUE)
+ g_string_append (xml_string, " primary='true'");
+ else
+ g_string_append (xml_string, " primary='false'");
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+ g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008");
+}
+
+/**
+ * gdata_gcontact_website_new:
+ * @uri: the website URI
+ * @relation_type: the relationship between the website and its owner
+ * @label: a human-readable label for the website, or %NULL
+ * @is_primary: %TRUE if this website is its owner's primary website, %FALSE otherwise
+ *
+ * Creates a new #GDataGContactWebsite. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">gContact specification</ulink>.
+ *
+ * Return value: a new #GDataGContactWebsite; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactWebsite *
+gdata_gcontact_website_new (const gchar *uri, const gchar *relation_type, const gchar *label, gboolean is_primary)
+{
+ g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+ g_return_val_if_fail (relation_type != NULL && *relation_type != '\0', NULL);
+ return g_object_new (GDATA_TYPE_GCONTACT_WEBSITE, "uri", uri, "relation-type", relation_type, "label", label, "is-primary", is_primary, NULL);
+}
+
+/**
+ * gdata_gcontact_website_compare:
+ * @a: a #GDataGContactWebsite, or %NULL
+ * @b: another #GDataGContactWebsite, or %NULL
+ *
+ * Compares the two websites in a strcmp() fashion. %NULL values are handled gracefully, with
+ * %0 returned if both @a and @b are %NULL, %-1 if @a is %NULL and %1 if @b is %NULL.
+ *
+ * The comparison of non-%NULL values is done on the basis of the @uri, @relation_type and @label properties of the #GDataGContactWebsite<!-- -->s.
+ *
+ * Return value: %0 if @a equals @b, %-1 or %1 as appropriate otherwise
+ *
+ * Since: 0.7.0
+ **/
+gint
+gdata_gcontact_website_compare (const GDataGContactWebsite *a, const GDataGContactWebsite *b)
+{
+ if (a == NULL && b != NULL)
+ return -1;
+ else if (a != NULL && b == NULL)
+ return 1;
+
+ if (a == b)
+ return 0;
+
+ if (g_strcmp0 (a->priv->uri, b->priv->uri) == 0 &&
+ g_strcmp0 (a->priv->relation_type, b->priv->relation_type) == 0 &&
+ g_strcmp0 (a->priv->label, b->priv->label) == 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * gdata_gcontact_website_get_uri:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:uri property.
+ *
+ * Return value: the URI of the website
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_website_get_uri (GDataGContactWebsite *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), NULL);
+ return self->priv->uri;
+}
+
+/**
+ * gdata_gcontact_website_set_uri:
+ * @self: a #GDataGContactWebsite
+ * @uri: the new website URI
+ *
+ * Sets the #GDataGContactWebsite:uri property to @uri.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_uri (GDataGContactWebsite *self, const gchar *uri)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+ g_return_if_fail (uri != NULL && *uri != '\0');
+
+ g_free (self->priv->uri);
+ self->priv->uri = g_strdup (uri);
+ g_object_notify (G_OBJECT (self), "uri");
+}
+
+/**
+ * gdata_gcontact_website_get_relation_type:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:relation-type property.
+ *
+ * Return value: the website's relation type
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_website_get_relation_type (GDataGContactWebsite *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), NULL);
+ return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_website_set_relation_type:
+ * @self: a #GDataGContactWebsite
+ * @relation_type: the new relation type for the website
+ *
+ * Sets the #GDataGContactWebsite:relation-type property to @relation_type
+ * such as %GDATA_GCONTACT_WEBSITE_HOME_PAGE or %GDATA_GCONTACT_WEBSITE_FTP.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_relation_type (GDataGContactWebsite *self, const gchar *relation_type)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+ g_return_if_fail (relation_type != NULL && *relation_type != '\0');
+
+ g_free (self->priv->relation_type);
+ self->priv->relation_type = g_strdup (relation_type);
+ g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_website_get_label:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:label property.
+ *
+ * Return value: the website's label, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_website_get_label (GDataGContactWebsite *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), NULL);
+ return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_website_set_label:
+ * @self: a #GDataGContactWebsite
+ * @label: the new label for the website, or %NULL
+ *
+ * Sets the #GDataGContactWebsite:label property to @label.
+ *
+ * Set @label to %NULL to unset the property in the website.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_label (GDataGContactWebsite *self, const gchar *label)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+
+ g_free (self->priv->label);
+ self->priv->label = g_strdup (label);
+ g_object_notify (G_OBJECT (self), "label");
+}
+
+/**
+ * gdata_gcontact_website_is_primary:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:is-primary property.
+ *
+ * Return value: %TRUE if this is the primary website, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ **/
+gboolean
+gdata_gcontact_website_is_primary (GDataGContactWebsite *self)
+{
+ g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), FALSE);
+ return self->priv->is_primary;
+}
+
+/**
+ * gdata_gcontact_website_set_is_primary:
+ * @self: a #GDataGContactWebsite
+ * @is_primary: %TRUE if this is the primary website, %FALSE otherwise
+ *
+ * Sets the #GDataGContactWebsite:is-primary property to @is_primary.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_is_primary (GDataGContactWebsite *self, gboolean is_primary)
+{
+ g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+
+ self->priv->is_primary = is_primary;
+ g_object_notify (G_OBJECT (self), "is-primary");
+}
diff --git a/gdata/gcontact/gdata-gcontact-website.h b/gdata/gcontact/gdata-gcontact-website.h
new file mode 100644
index 0000000..da0d62f
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-website.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * 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_GCONTACT_WEBSITE_H
+#define GDATA_GCONTACT_WEBSITE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_WEBSITE_HOME_PAGE:
+ *
+ * The relation type URI for a contact's home page.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_HOME_PAGE "home-page"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_BLOG:
+ *
+ * The relation type URI for a contact's blog.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_BLOG "blog"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_PROFILE:
+ *
+ * The relation type URI for a contact's online profile.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_PROFILE "profile"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_HOME:
+ *
+ * The relation type URI for a contact's home website.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_HOME "home"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_WORK:
+ *
+ * The relation type URI for a contact's work website.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_WORK "work"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_OTHER:
+ *
+ * The relation type URI for a miscellaneous website of the contact.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_OTHER "other"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_FTP:
+ *
+ * The relation type URI for a contact's FTP site.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite">
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_FTP "ftp"
+
+#define GDATA_TYPE_GCONTACT_WEBSITE (gdata_gcontact_website_get_type ())
+#define GDATA_GCONTACT_WEBSITE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsite))
+#define GDATA_GCONTACT_WEBSITE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsiteClass))
+#define GDATA_IS_GCONTACT_WEBSITE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_WEBSITE))
+#define GDATA_IS_GCONTACT_WEBSITE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_WEBSITE))
+#define GDATA_GCONTACT_WEBSITE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsiteClass))
+
+typedef struct _GDataGContactWebsitePrivate GDataGContactWebsitePrivate;
+
+/**
+ * GDataGContactWebsite:
+ *
+ * All the fields in the #GDataGContactWebsite structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ GDataParsable parent;
+ GDataGContactWebsitePrivate *priv;
+} GDataGContactWebsite;
+
+/**
+ * GDataGContactWebsiteClass:
+ *
+ * All the fields in the #GDataGContactWebsiteClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+ /*< private >*/
+ GDataParsableClass parent;
+} GDataGContactWebsiteClass;
+
+GType gdata_gcontact_website_get_type (void) G_GNUC_CONST;
+
+GDataGContactWebsite *gdata_gcontact_website_new (const gchar *uri, const gchar *relation_type,
+ const gchar *label, gboolean is_primary) G_GNUC_WARN_UNUSED_RESULT;
+gint gdata_gcontact_website_compare (const GDataGContactWebsite *a, const GDataGContactWebsite *b);
+
+const gchar *gdata_gcontact_website_get_uri (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_uri (GDataGContactWebsite *self, const gchar *uri);
+
+const gchar *gdata_gcontact_website_get_relation_type (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_relation_type (GDataGContactWebsite *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_website_get_label (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_label (GDataGContactWebsite *self, const gchar *label);
+
+gboolean gdata_gcontact_website_is_primary (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_is_primary (GDataGContactWebsite *self, gboolean is_primary);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_WEBSITE_H */
diff --git a/gdata/gdata-parser.c b/gdata/gdata-parser.c
index b580076..e59a517 100644
--- a/gdata/gdata-parser.c
+++ b/gdata/gdata-parser.c
@@ -145,6 +145,31 @@ gdata_parser_error_required_property_missing (xmlNode *element, const gchar *pro
}
gboolean
+gdata_parser_error_mutexed_properties (xmlNode *element, const gchar *property1_name, const gchar *property2_name, GError **error)
+{
+ gchar *property1_string, *property2_string, *element_string;
+
+ property1_string = g_strdup_printf ("@%s", property1_name);
+ property2_string = g_strdup_printf ("@%s", property2_name);
+ element_string = print_element (element);
+
+ g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
+ /* Translators: the first two parameters are the names of XML properties of an XML element given in the third
+ * parameter (including the angle brackets ("<" and ">")).
+ *
+ * For example:
+ * Values were present for properties @rel and @label of a <entry/gContact:relation> element when only one of the
+ * two is allowed. */
+ _("Values were present for properties %s and %s of a %s element when only one of the two is allowed."),
+ property1_string, property2_string, element_string);
+ g_free (property1_string);
+ g_free (property2_string);
+ g_free (element_string);
+
+ return FALSE;
+}
+
+gboolean
gdata_parser_error_required_element_missing (const gchar *element_name, const gchar *parent_element_name, GError **error)
{
/* NOTE: This can't take an xmlNode, since such a node wouldn't exist. */
diff --git a/gdata/gdata-parser.h b/gdata/gdata-parser.h
index 198846b..1e123d6 100644
--- a/gdata/gdata-parser.h
+++ b/gdata/gdata-parser.h
@@ -31,6 +31,7 @@ gboolean gdata_parser_error_not_iso8601_format (xmlNode *element, const gchar *a
gboolean gdata_parser_error_unknown_property_value (xmlNode *element, const gchar *property_name, const gchar *actual_value, GError **error);
gboolean gdata_parser_error_unknown_content (xmlNode *element, const gchar *actual_content, GError **error);
gboolean gdata_parser_error_required_property_missing (xmlNode *element, const gchar *property_name, GError **error);
+gboolean gdata_parser_error_mutexed_properties (xmlNode *element, const gchar *property1_name, const gchar *property2_name, GError **error);
gboolean gdata_parser_error_required_element_missing (const gchar *element_name, const gchar *parent_element_name, GError **error);
gboolean gdata_parser_error_duplicate_element (xmlNode *element, GError **error);
diff --git a/gdata/gdata.h b/gdata/gdata.h
index c51c0a4..1351d3c 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -53,6 +53,13 @@
#include <gdata/gd/gdata-gd-where.h>
#include <gdata/gd/gdata-gd-who.h>
+/* GContact */
+#include <gdata/gcontact/gdata-gcontact-calendar.h>
+#include <gdata/gcontact/gdata-gcontact-event.h>
+#include <gdata/gcontact/gdata-gcontact-jot.h>
+#include <gdata/gcontact/gdata-gcontact-relation.h>
+#include <gdata/gcontact/gdata-gcontact-website.h>
+
/* Media RSS */
#include <gdata/media/gdata-media-category.h>
#include <gdata/media/gdata-media-content.h>
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 1e8d531..682188a 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -720,3 +720,68 @@ gdata_gd_name_get_full_name
gdata_gd_name_set_full_name
gdata_youtube_action_get_type
gdata_youtube_permission_get_type
+gdata_gcontact_jot_get_type
+gdata_gcontact_jot_new
+gdata_gcontact_jot_get_content
+gdata_gcontact_jot_set_content
+gdata_gcontact_jot_get_relation_type
+gdata_gcontact_jot_set_relation_type
+gdata_contacts_contact_add_jot
+gdata_contacts_contact_get_jots
+gdata_contacts_contact_remove_all_jots
+gdata_contacts_contact_get_nickname
+gdata_contacts_contact_set_nickname
+gdata_contacts_contact_get_birthday
+gdata_contacts_contact_set_birthday
+gdata_gcontact_relation_get_type
+gdata_gcontact_relation_new
+gdata_gcontact_relation_get_name
+gdata_gcontact_relation_set_name
+gdata_gcontact_relation_get_relation_type
+gdata_gcontact_relation_set_relation_type
+gdata_gcontact_relation_get_label
+gdata_gcontact_relation_set_label
+gdata_contacts_contact_add_relation
+gdata_contacts_contact_get_relations
+gdata_contacts_contact_remove_all_relations
+gdata_gcontact_website_get_type
+gdata_gcontact_website_new
+gdata_gcontact_website_compare
+gdata_gcontact_website_get_uri
+gdata_gcontact_website_set_uri
+gdata_gcontact_website_get_relation_type
+gdata_gcontact_website_set_relation_type
+gdata_gcontact_website_get_label
+gdata_gcontact_website_set_label
+gdata_gcontact_website_is_primary
+gdata_gcontact_website_set_is_primary
+gdata_contacts_contact_add_website
+gdata_contacts_contact_get_websites
+gdata_contacts_contact_get_primary_website
+gdata_contacts_contact_remove_all_websites
+gdata_gcontact_event_get_type
+gdata_gcontact_event_new
+gdata_gcontact_event_get_date
+gdata_gcontact_event_set_date
+gdata_gcontact_event_get_relation_type
+gdata_gcontact_event_set_relation_type
+gdata_gcontact_event_get_label
+gdata_gcontact_event_set_label
+gdata_contacts_contact_add_event
+gdata_contacts_contact_get_events
+gdata_contacts_contact_remove_all_events
+gdata_gcontact_calendar_get_type
+gdata_gcontact_calendar_new
+gdata_gcontact_calendar_compare
+gdata_gcontact_calendar_get_uri
+gdata_gcontact_calendar_set_uri
+gdata_gcontact_calendar_get_relation_type
+gdata_gcontact_calendar_set_relation_type
+gdata_gcontact_calendar_get_label
+gdata_gcontact_calendar_set_label
+gdata_gcontact_calendar_is_primary
+gdata_gcontact_calendar_set_is_primary
+gdata_contacts_contact_add_calendar
+gdata_contacts_contact_get_calendars
+gdata_contacts_contact_get_primary_calendar
+gdata_contacts_contact_remove_all_calendars
diff --git a/gdata/services/contacts/gdata-contacts-contact.c b/gdata/services/contacts/gdata-contacts-contact.c
index fda55f2..631b316 100644
--- a/gdata/services/contacts/gdata-contacts-contact.c
+++ b/gdata/services/contacts/gdata-contacts-contact.c
@@ -68,13 +68,24 @@ struct _GDataContactsContactPrivate {
GHashTable *groups;
gboolean deleted;
gchar *photo_etag;
+ GList *jots; /* GDataGContactJot */
+ gchar *nickname;
+ GDate birthday;
+ gboolean birthday_has_year; /* contacts can choose to just give the month and day of their birth */
+ GList *relations; /* GDataGContactRelation */
+ GList *websites; /* GDataGContactWebsite */
+ GList *events; /* GDataGContactEvent */
+ GList *calendars; /* GDataGContactCalendar */
};
enum {
PROP_EDITED = 1,
PROP_DELETED,
PROP_HAS_PHOTO,
- PROP_NAME
+ PROP_NAME,
+ PROP_NICKNAME,
+ PROP_BIRTHDAY,
+ PROP_BIRTHDAY_HAS_YEAR
};
G_DEFINE_TYPE (GDataContactsContact, gdata_contacts_contact, GDATA_TYPE_ENTRY)
@@ -150,6 +161,45 @@ gdata_contacts_contact_class_init (GDataContactsContactClass *klass)
"Name", "The contact's name in a structured representation.",
GDATA_TYPE_GD_NAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataContactsContact:nickname:
+ *
+ * The contact's chosen nickname.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_NICKNAME,
+ g_param_spec_string ("nickname",
+ "Nickname", "The contact's chosen nickname.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataContactsContact:birthday:
+ *
+ * The contact's birthday.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_BIRTHDAY,
+ g_param_spec_boxed ("birthday",
+ "Birthday", "The contact's birthday.",
+ G_TYPE_DATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GDataContactsContact:birthday-has-year:
+ *
+ * Whether the contact's birthday includes the year of their birth.
+ *
+ * Since: 0.7.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_BIRTHDAY_HAS_YEAR,
+ g_param_spec_boolean ("birthday-has-year",
+ "Birthday has year?", "Whether the contact's birthday includes the year of their birth.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void notify_full_name_cb (GObject *gobject, GParamSpec *pspec, GDataContactsContact *self);
@@ -188,6 +238,9 @@ gdata_contacts_contact_init (GDataContactsContact *self)
g_signal_connect (self, "notify::title", (GCallback) notify_title_cb, self);
g_signal_connect (self->priv->name, "notify::full-name", (GCallback) notify_full_name_cb, self);
+ /* Initialise the contact's birthday to a sane but invalid date */
+ g_date_clear (&(self->priv->birthday), 1);
+
/* Add the "contact" kind category */
category = gdata_category_new ("http://schemas.google.com/contact/2008#contact", "http://schemas.google.com/g/2005#kind", NULL);
gdata_entry_add_category (GDATA_ENTRY (self), category);
@@ -208,6 +261,11 @@ gdata_contacts_contact_dispose (GObject *object)
gdata_contacts_contact_remove_all_im_addresses (self);
gdata_contacts_contact_remove_all_postal_addresses (self);
gdata_contacts_contact_remove_all_phone_numbers (self);
+ gdata_contacts_contact_remove_all_jots (self);
+ gdata_contacts_contact_remove_all_relations (self);
+ gdata_contacts_contact_remove_all_websites (self);
+ gdata_contacts_contact_remove_all_events (self);
+ gdata_contacts_contact_remove_all_calendars (self);
/* Chain up to the parent class */
G_OBJECT_CLASS (gdata_contacts_contact_parent_class)->dispose (object);
@@ -221,6 +279,7 @@ gdata_contacts_contact_finalize (GObject *object)
g_hash_table_destroy (priv->extended_properties);
g_hash_table_destroy (priv->groups);
g_free (priv->photo_etag);
+ g_free (priv->nickname);
/* Chain up to the parent class */
G_OBJECT_CLASS (gdata_contacts_contact_parent_class)->finalize (object);
@@ -244,6 +303,15 @@ gdata_contacts_contact_get_property (GObject *object, guint property_id, GValue
case PROP_NAME:
g_value_set_object (value, priv->name);
break;
+ case PROP_NICKNAME:
+ g_value_set_string (value, priv->nickname);
+ break;
+ case PROP_BIRTHDAY:
+ g_value_set_boxed (value, &(priv->birthday));
+ break;
+ case PROP_BIRTHDAY_HAS_YEAR:
+ g_value_set_boolean (value, priv->birthday_has_year);
+ break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -260,6 +328,15 @@ gdata_contacts_contact_set_property (GObject *object, guint property_id, const G
case PROP_NAME:
gdata_contacts_contact_set_name (self, g_value_get_object (value));
break;
+ case PROP_NICKNAME:
+ gdata_contacts_contact_set_nickname (self, g_value_get_string (value));
+ break;
+ case PROP_BIRTHDAY:
+ gdata_contacts_contact_set_birthday (self, g_value_get_boxed (value), self->priv->birthday_has_year);
+ break;
+ case PROP_BIRTHDAY_HAS_YEAR:
+ gdata_contacts_contact_set_birthday (self, &(self->priv->birthday), g_value_get_boolean (value));
+ break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -267,28 +344,6 @@ gdata_contacts_contact_set_property (GObject *object, guint property_id, const G
}
}
-/**
- * gdata_contacts_contact_new:
- * @id: the contact's ID, or %NULL
- *
- * Creates a new #GDataContactsContact with the given ID and default properties.
- *
- * Return value: a new #GDataContactsContact; unref with g_object_unref()
- *
- * Since: 0.2.0
- **/
-GDataContactsContact *
-gdata_contacts_contact_new (const gchar *id)
-{
- GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (g_object_new (GDATA_TYPE_CONTACTS_CONTACT, "id", id, NULL));
-
- /* Set the edited property to the current time (creation time). We don't do this in *_init() since that would cause
- * setting it from parse_xml() to fail (duplicate element). */
- g_get_current_time (&(contact->priv->edited));
-
- return contact;
-}
-
static gboolean
parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
{
@@ -344,22 +399,64 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
} else {
return GDATA_PARSABLE_CLASS (gdata_contacts_contact_parent_class)->parse_xml (parsable, doc, node, user_data, error);
}
- } else if (gdata_parser_is_namespace (node, "http://schemas.google.com/contact/2008") == TRUE &&
- xmlStrcmp (node->name, (xmlChar*) "groupMembershipInfo") == 0) {
- /* gContact:groupMembershipInfo */
- xmlChar *href;
- gboolean deleted_bool;
-
- href = xmlGetProp (node, (xmlChar*) "href");
- if (href == NULL)
- return gdata_parser_error_required_property_missing (node, "href", error);
-
- /* Has it been deleted? */
- if (gdata_parser_boolean_from_property (node, "deleted", &deleted_bool, 0, error) == FALSE)
- return FALSE;
-
- /* Insert it into the hash table */
- g_hash_table_insert (self->priv->groups, (gchar*) href, GUINT_TO_POINTER (deleted_bool));
+ } else if (gdata_parser_is_namespace (node, "http://schemas.google.com/contact/2008") == TRUE) {
+ if (gdata_parser_object_from_element_setter (node, "jot", P_REQUIRED, GDATA_TYPE_GCONTACT_JOT,
+ gdata_contacts_contact_add_jot, self, &success, error) == TRUE ||
+ gdata_parser_object_from_element_setter (node, "relation", P_REQUIRED, GDATA_TYPE_GCONTACT_RELATION,
+ gdata_contacts_contact_add_relation, self, &success, error) == TRUE ||
+ gdata_parser_object_from_element_setter (node, "event", P_REQUIRED, GDATA_TYPE_GCONTACT_EVENT,
+ gdata_contacts_contact_add_event, self, &success, error) == TRUE ||
+ gdata_parser_object_from_element_setter (node, "website", P_REQUIRED, GDATA_TYPE_GCONTACT_WEBSITE,
+ gdata_contacts_contact_add_website, self, &success, error) == TRUE ||
+ gdata_parser_object_from_element_setter (node, "calendarLink", P_REQUIRED, GDATA_TYPE_GCONTACT_CALENDAR,
+ gdata_contacts_contact_add_calendar, self, &success, error) == TRUE ||
+ gdata_parser_string_from_element (node, "nickname", P_REQUIRED | P_NO_DUPES, &(self->priv->nickname), &success, error) == TRUE) {
+ return success;
+ } else if (xmlStrcmp (node->name, (xmlChar*) "groupMembershipInfo") == 0) {
+ /* gContact:groupMembershipInfo */
+ xmlChar *href;
+ gboolean deleted_bool;
+
+ href = xmlGetProp (node, (xmlChar*) "href");
+ if (href == NULL)
+ return gdata_parser_error_required_property_missing (node, "href", error);
+
+ /* Has it been deleted? */
+ if (gdata_parser_boolean_from_property (node, "deleted", &deleted_bool, 0, error) == FALSE)
+ return FALSE;
+
+ /* Insert it into the hash table */
+ g_hash_table_insert (self->priv->groups, (gchar*) href, GUINT_TO_POINTER (deleted_bool));
+ } else if (xmlStrcmp (node->name, (xmlChar*) "birthday") == 0) {
+ /* gContact:birthday */
+ xmlChar *birthday;
+ guint length = 0, year = 666, month, day;
+
+ if (g_date_valid (&(self->priv->birthday)) == TRUE)
+ return gdata_parser_error_duplicate_element (node, error);
+
+ birthday = xmlGetProp (node, (xmlChar*) "when");
+ if (birthday == NULL)
+ return gdata_parser_error_required_property_missing (node, "when", error);
+ length = strlen ((char*) birthday);
+
+ /* Try parsing the two possible formats: YYYY-MM-DD and --MM-DD */
+ if (((length == 10 && sscanf ((char*) birthday, "%4u-%2u-%2u", &year, &month, &day) == 3) ||
+ (length == 7 && sscanf ((char*) birthday, "--%2u-%2u", &month, &day) == 2)) &&
+ g_date_valid_dmy (day, month, year) == TRUE) {
+ /* Store the values in the GDate */
+ g_date_set_dmy (&(self->priv->birthday), day, month, year);
+ self->priv->birthday_has_year = (length == 10) ? TRUE : FALSE;
+ xmlFree (birthday);
+ } else {
+ /* Parsing failed */
+ gdata_parser_error_not_iso8601_format (node, (gchar*) birthday, error);
+ xmlFree (birthday);
+ return FALSE;
+ }
+ } else {
+ return GDATA_PARSABLE_CLASS (gdata_contacts_contact_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+ }
} else {
/* If we haven't yet found a photo, check to see if it's a photo <link> element */
if (self->priv->photo_etag == NULL && xmlStrcmp (node->name, (xmlChar*) "link") == 0) {
@@ -416,6 +513,11 @@ get_xml (GDataParsable *parsable, GString *xml_string)
get_child_xml (priv->phone_numbers, xml_string);
get_child_xml (priv->postal_addresses, xml_string);
get_child_xml (priv->organizations, xml_string);
+ get_child_xml (priv->jots, xml_string);
+ get_child_xml (priv->relations, xml_string);
+ get_child_xml (priv->websites, xml_string);
+ get_child_xml (priv->events, xml_string);
+ get_child_xml (priv->calendars, xml_string);
/* Extended properties */
g_hash_table_foreach (priv->extended_properties, (GHFunc) get_extended_property_xml_cb, xml_string);
@@ -423,6 +525,24 @@ get_xml (GDataParsable *parsable, GString *xml_string)
/* Group membership info */
g_hash_table_foreach (priv->groups, (GHFunc) get_group_xml_cb, xml_string);
+ /* gContact:nickname */
+ if (priv->nickname != NULL)
+ gdata_parser_string_append_escaped (xml_string, "<gContact:nickname>", priv->nickname, "</gContact:nickname>");
+
+ /* gContact:birthday */
+ if (g_date_valid (&(priv->birthday)) == TRUE) {
+ if (priv->birthday_has_year == TRUE) {
+ g_string_append_printf (xml_string, "<gContact:birthday when='%04u-%02u-%02u'/>",
+ g_date_get_year (&(priv->birthday)),
+ g_date_get_month (&(priv->birthday)),
+ g_date_get_day (&(priv->birthday)));
+ } else {
+ g_string_append_printf (xml_string, "<gContact:birthday when='--%02u-%02u'/>",
+ g_date_get_month (&(priv->birthday)),
+ g_date_get_day (&(priv->birthday)));
+ }
+ }
+
/* TODO:
* - Finish supporting all tags
* - Check things are escaped (or not) as appropriate
@@ -441,6 +561,28 @@ get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
}
/**
+ * gdata_contacts_contact_new:
+ * @id: the contact's ID, or %NULL
+ *
+ * Creates a new #GDataContactsContact with the given ID and default properties.
+ *
+ * Return value: a new #GDataContactsContact; unref with g_object_unref()
+ *
+ * Since: 0.2.0
+ **/
+GDataContactsContact *
+gdata_contacts_contact_new (const gchar *id)
+{
+ GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (g_object_new (GDATA_TYPE_CONTACTS_CONTACT, "id", id, NULL));
+
+ /* Set the edited property to the current time (creation time). We don't do this in *_init() since that would cause
+ * setting it from parse_xml() to fail (duplicate element). */
+ g_get_current_time (&(contact->priv->edited));
+
+ return contact;
+}
+
+/**
* gdata_contacts_contact_get_edited:
* @self: a #GDataContactsContact
* @edited: a #GTimeVal
@@ -502,6 +644,100 @@ gdata_contacts_contact_set_name (GDataContactsContact *self, GDataGDName *name)
}
/**
+ * gdata_contacts_contact_get_nickname:
+ * @self: a #GDataContactsContact
+ *
+ * Gets the #GDataContactsContact:nickname property.
+ *
+ * Return value: the contact's nickname, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_contacts_contact_get_nickname (GDataContactsContact *self)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+ return self->priv->nickname;
+}
+
+/**
+ * gdata_contacts_contact_set_nickname:
+ * @self: a #GDataContactsContact
+ * @nickname: the new nickname, or %NULL
+ *
+ * Sets the #GDataContactsContact:nickname property to @nickname.
+ *
+ * If @nickname is %NULL, the contact's nickname will be removed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_set_nickname (GDataContactsContact *self, const gchar *nickname)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+ g_free (self->priv->nickname);
+ self->priv->nickname = g_strdup (nickname);
+ g_object_notify (G_OBJECT (self), "nickname");
+}
+
+/**
+ * gdata_contacts_contact_get_birthday:
+ * @self: a #GDataContactsContact
+ * @birthday: return location for the birthday, or %NULL
+ *
+ * Gets the #GDataContactsContact:birthday and #GDataContactsContact:birthday-has-year properties. If @birthday is non-%NULL,
+ * #GDataContactsContact:birthday is returned in it. The function returns the value of #GDataContactsContact:birthday-has-year,
+ * which specifies whether the year in @birthday is meaningful. Contacts may not have the year of their birth set, in which
+ * case, the function would return %FALSE, and the year in @birthday should be ignored.
+ *
+ * Return value: whether the contact's birthday has the year set
+ *
+ * Since: 0.7.0
+ **/
+gboolean
+gdata_contacts_contact_get_birthday (GDataContactsContact *self, GDate *birthday)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), FALSE);
+
+ if (birthday != NULL)
+ *birthday = self->priv->birthday;
+ return self->priv->birthday_has_year;
+}
+
+/**
+ * gdata_contacts_contact_set_birthday:
+ * @self: a #GDataContactsContact
+ * @birthday: the new birthday, or %NULL
+ * @birthday_has_year: %TRUE if @birthday's year is relevant, %FALSE otherwise
+ *
+ * Sets the #GDataContactsContact:birthday property to @birthday and the #GDataContactsContact:birthday-has-year property to @birthday_has_year.
+ * See gdata_contacts_contact_get_birthday() for an explanation of the interation between these two properties.
+ *
+ * If @birthday is %NULL, the contact's birthday will be removed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_set_birthday (GDataContactsContact *self, GDate *birthday, gboolean birthday_has_year)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+ g_return_if_fail (birthday == NULL || g_date_valid (birthday));
+
+ if (birthday != NULL)
+ self->priv->birthday = *birthday;
+ else
+ g_date_clear (&(self->priv->birthday), 1);
+
+ self->priv->birthday_has_year = birthday_has_year;
+
+ g_object_freeze_notify (G_OBJECT (self));
+ g_object_notify (G_OBJECT (self), "birthday");
+ g_object_notify (G_OBJECT (self), "birthday-has-year");
+ g_object_thaw_notify (G_OBJECT (self));
+}
+
+/**
* gdata_contacts_contact_add_email_address:
* @self: a #GDataContactsContact
* @email_address: a #GDataGDEmailAddress to add
@@ -947,6 +1183,357 @@ gdata_contacts_contact_remove_all_organizations (GDataContactsContact *self)
}
/**
+ * gdata_contacts_contact_add_jot:
+ * @self: a #GDataContactsContact
+ * @jot: a #GDataGContactJot to add
+ *
+ * Adds a jot to the contact's list of jots and increments its reference count.
+ *
+ * Duplicate jots will be added to the list, and multiple jots with the same relation type can be added to a single contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_jot (GDataContactsContact *self, GDataGContactJot *jot)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+ g_return_if_fail (GDATA_IS_GCONTACT_JOT (jot));
+
+ self->priv->jots = g_list_append (self->priv->jots, g_object_ref (jot));
+}
+
+/**
+ * gdata_contacts_contact_get_jots:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the jots attached to the contact.
+ *
+ * Return value: a #GList of #GDataGContactJot<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_jots (GDataContactsContact *self)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+ return self->priv->jots;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_jots:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all jots from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_jots (GDataContactsContact *self)
+{
+ GDataContactsContactPrivate *priv = self->priv;
+
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+ if (priv->jots != NULL) {
+ g_list_foreach (priv->jots, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->jots);
+ }
+ priv->jots = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_relation:
+ * @self: a #GDataContactsContact
+ * @relation: a #GDataGContactRelation to add
+ *
+ * Adds a relation to the contact's list of relations and increments its reference count.
+ *
+ * Duplicate relations will be added to the list, and multiple relations with the same relation type can be added to a single contact.
+ * Though it may not make sense for some relation types to be repeated, adding them is allowed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_relation (GDataContactsContact *self, GDataGContactRelation *relation)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+ g_return_if_fail (GDATA_IS_GCONTACT_RELATION (relation));
+
+ self->priv->relations = g_list_append (self->priv->relations, g_object_ref (relation));
+}
+
+/**
+ * gdata_contacts_contact_get_relations:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the relations of the contact.
+ *
+ * Return value: a #GList of #GDataGContactRelation<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_relations (GDataContactsContact *self)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+ return self->priv->relations;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_relations:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all relations from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_relations (GDataContactsContact *self)
+{
+ GDataContactsContactPrivate *priv = self->priv;
+
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+ if (priv->relations != NULL) {
+ g_list_foreach (priv->relations, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->relations);
+ }
+ priv->relations = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_website:
+ * @self: a #GDataContactsContact
+ * @website: a #GDataGContactWebsite to add
+ *
+ * Adds a website to the contact's list of websites and increments its reference count.
+ *
+ * Duplicate websites will not be added to the list, though the same URI may appear in several #GDataGContactWebsite<!-- -->s with different
+ * relation types or labels.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_website (GDataContactsContact *self, GDataGContactWebsite *website)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+ g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (website));
+
+ if (g_list_find_custom (self->priv->websites, website, (GCompareFunc) gdata_gcontact_website_compare) == NULL)
+ self->priv->websites = g_list_append (self->priv->websites, g_object_ref (website));
+}
+
+/**
+ * gdata_contacts_contact_get_websites:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the websites of the contact.
+ *
+ * Return value: a #GList of #GDataGContactWebsite<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_websites (GDataContactsContact *self)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+ return self->priv->websites;
+}
+
+/**
+ * gdata_contacts_contact_get_primary_website:
+ * @self: a #GDataContactsContact
+ *
+ * Gets the contact's primary website, if one exists.
+ *
+ * Return value: a #GDataGContactWebsite, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactWebsite *
+gdata_contacts_contact_get_primary_website (GDataContactsContact *self)
+{
+ GList *i;
+
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+
+ for (i = self->priv->websites; i != NULL; i = i->next) {
+ if (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (i->data)) == TRUE)
+ return GDATA_GCONTACT_WEBSITE (i->data);
+ }
+
+ return NULL;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_websites:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all websites from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_websites (GDataContactsContact *self)
+{
+ GDataContactsContactPrivate *priv = self->priv;
+
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+ if (priv->websites != NULL) {
+ g_list_foreach (priv->websites, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->websites);
+ }
+ priv->websites = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_event:
+ * @self: a #GDataContactsContact
+ * @event: a #GDataGContactEvent to add
+ *
+ * Adds an event to the contact's list of events and increments its reference count.
+ *
+ * Duplicate events will be added to the list, and multiple events with the same event type can be added to a single contact.
+ * Though it may not make sense for some event types to be repeated, adding them is allowed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_event (GDataContactsContact *self, GDataGContactEvent *event)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+ g_return_if_fail (GDATA_IS_GCONTACT_EVENT (event));
+
+ self->priv->events = g_list_append (self->priv->events, g_object_ref (event));
+}
+
+/**
+ * gdata_contacts_contact_get_events:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the events of the contact.
+ *
+ * Return value: a #GList of #GDataGContactEvent<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_events (GDataContactsContact *self)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+ return self->priv->events;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_events:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all events from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_events (GDataContactsContact *self)
+{
+ GDataContactsContactPrivate *priv = self->priv;
+
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+ if (priv->events != NULL) {
+ g_list_foreach (priv->events, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->events);
+ }
+ priv->events = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_calendar:
+ * @self: a #GDataContactsContact
+ * @calendar: a #GDataGContactCalendar to add
+ *
+ * Adds a calendar to the contact's list of calendars and increments its reference count.
+ *
+ * Duplicate calendars will not be added to the list, though the same URI may appear in several #GDataGContactCalendar<!-- -->s with different
+ * relation types or labels.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_calendar (GDataContactsContact *self, GDataGContactCalendar *calendar)
+{
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+ g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (calendar));
+
+ if (g_list_find_custom (self->priv->calendars, calendar, (GCompareFunc) gdata_gcontact_calendar_compare) == NULL)
+ self->priv->calendars = g_list_append (self->priv->calendars, g_object_ref (calendar));
+}
+
+/**
+ * gdata_contacts_contact_get_calendars:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the calendars of the contact.
+ *
+ * Return value: a #GList of #GDataGContactCalendar<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_calendars (GDataContactsContact *self)
+{
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+ return self->priv->calendars;
+}
+
+/**
+ * gdata_contacts_contact_get_primary_calendar:
+ * @self: a #GDataContactsContact
+ *
+ * Gets the contact's primary calendar, if one exists.
+ *
+ * Return value: a #GDataGContactCalendar, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactCalendar *
+gdata_contacts_contact_get_primary_calendar (GDataContactsContact *self)
+{
+ GList *i;
+
+ g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+
+ for (i = self->priv->calendars; i != NULL; i = i->next) {
+ if (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (i->data)) == TRUE)
+ return GDATA_GCONTACT_CALENDAR (i->data);
+ }
+
+ return NULL;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_calendars:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all calendars from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_calendars (GDataContactsContact *self)
+{
+ GDataContactsContactPrivate *priv = self->priv;
+
+ g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+ if (priv->calendars != NULL) {
+ g_list_foreach (priv->calendars, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->calendars);
+ }
+ priv->calendars = NULL;
+}
+
+/**
* gdata_contacts_contact_get_extended_property:
* @self: a #GDataContactsContact
* @name: the property name; an arbitrary, unique string
diff --git a/gdata/services/contacts/gdata-contacts-contact.h b/gdata/services/contacts/gdata-contacts-contact.h
index 8c384c0..03227ec 100644
--- a/gdata/services/contacts/gdata-contacts-contact.h
+++ b/gdata/services/contacts/gdata-contacts-contact.h
@@ -30,6 +30,11 @@
#include <gdata/gd/gdata-gd-organization.h>
#include <gdata/gd/gdata-gd-phone-number.h>
#include <gdata/gd/gdata-gd-postal-address.h>
+#include <gdata/gcontact/gdata-gcontact-calendar.h>
+#include <gdata/gcontact/gdata-gcontact-event.h>
+#include <gdata/gcontact/gdata-gcontact-jot.h>
+#include <gdata/gcontact/gdata-gcontact-relation.h>
+#include <gdata/gcontact/gdata-gcontact-website.h>
G_BEGIN_DECLS
@@ -76,6 +81,12 @@ gboolean gdata_contacts_contact_is_deleted (GDataContactsContact *self);
GDataGDName *gdata_contacts_contact_get_name (GDataContactsContact *self);
void gdata_contacts_contact_set_name (GDataContactsContact *self, GDataGDName *name);
+const gchar *gdata_contacts_contact_get_nickname (GDataContactsContact *self);
+void gdata_contacts_contact_set_nickname (GDataContactsContact *self, const gchar *nickname);
+
+gboolean gdata_contacts_contact_get_birthday (GDataContactsContact *self, GDate *birthday);
+void gdata_contacts_contact_set_birthday (GDataContactsContact *self, GDate *birthday, gboolean birthday_has_year);
+
void gdata_contacts_contact_add_email_address (GDataContactsContact *self, GDataGDEmailAddress *email_address);
GList *gdata_contacts_contact_get_email_addresses (GDataContactsContact *self);
GDataGDEmailAddress *gdata_contacts_contact_get_primary_email_address (GDataContactsContact *self);
@@ -101,6 +112,28 @@ GList *gdata_contacts_contact_get_organizations (GDataContactsContact *self);
GDataGDOrganization *gdata_contacts_contact_get_primary_organization (GDataContactsContact *self);
void gdata_contacts_contact_remove_all_organizations (GDataContactsContact *self);
+void gdata_contacts_contact_add_jot (GDataContactsContact *self, GDataGContactJot *jot);
+GList *gdata_contacts_contact_get_jots (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_jots (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_relation (GDataContactsContact *self, GDataGContactRelation *relation);
+GList *gdata_contacts_contact_get_relations (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_relations (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_website (GDataContactsContact *self, GDataGContactWebsite *website);
+GList *gdata_contacts_contact_get_websites (GDataContactsContact *self);
+GDataGContactWebsite *gdata_contacts_contact_get_primary_website (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_websites (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_event (GDataContactsContact *self, GDataGContactEvent *event);
+GList *gdata_contacts_contact_get_events (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_events (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_calendar (GDataContactsContact *self, GDataGContactCalendar *calendar);
+GList *gdata_contacts_contact_get_calendars (GDataContactsContact *self);
+GDataGContactCalendar *gdata_contacts_contact_get_primary_calendar (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_calendars (GDataContactsContact *self);
+
const gchar *gdata_contacts_contact_get_extended_property (GDataContactsContact *self, const gchar *name);
GHashTable *gdata_contacts_contact_get_extended_properties (GDataContactsContact *self);
gboolean gdata_contacts_contact_set_extended_property (GDataContactsContact *self, const gchar *name, const gchar *value);
@@ -114,9 +147,9 @@ GList *gdata_contacts_contact_get_groups (GDataContactsContact *self) G_GNUC_WAR
gboolean gdata_contacts_contact_has_photo (GDataContactsContact *self);
gchar *gdata_contacts_contact_get_photo (GDataContactsContact *self, GDataContactsService *service, gsize *length, gchar **content_type,
- GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+ GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
gboolean gdata_contacts_contact_set_photo (GDataContactsContact *self, GDataService *service, const gchar *data, gsize length,
- GCancellable *cancellable, GError **error);
+ GCancellable *cancellable, GError **error);
G_END_DECLS
diff --git a/gdata/tests/contacts.c b/gdata/tests/contacts.c
index 0b4e6d3..cae7bab 100644
--- a/gdata/tests/contacts.c
+++ b/gdata/tests/contacts.c
@@ -153,17 +153,23 @@ static void
test_insert_simple (gconstpointer service)
{
GDataContactsContact *contact, *new_contact;
- GDataCategory *category;
GDataGDName *name, *name2;
GDataGDEmailAddress *email_address1, *email_address2;
GDataGDPhoneNumber *phone_number1, *phone_number2;
GDataGDIMAddress *im_address;
+ GDataGDOrganization *org;
GDataGDPostalAddress *postal_address;
- gchar *xml;
+ GDataGContactJot *jot;
+ GDataGContactRelation *relation;
+ GDataGContactWebsite *website;
+ GDataGContactEvent *event;
+ GDataGContactCalendar *calendar;
+ gchar *xml, *nickname;
GList *list;
+ GDate date, *date2;
GHashTable *properties;
GTimeVal *edited, creation_time;
- gboolean deleted, has_photo;
+ gboolean deleted, has_photo, birthday_has_year;
GError *error = NULL;
contact = gdata_contacts_contact_new (NULL);
@@ -186,32 +192,61 @@ test_insert_simple (gconstpointer service)
g_object_unref (name2);
g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (contact)), ==, "John Smith");
+ gdata_contacts_contact_set_nickname (contact, "Big J");
+ g_date_set_dmy (&date, 1, 1, 1900);
+ gdata_contacts_contact_set_birthday (contact, &date, FALSE);
gdata_entry_set_content (GDATA_ENTRY (contact), "Notes");
- /* TODO: Have it add this category automatically? Same for GDataCalendarEvent */
- category = gdata_category_new ("http://schemas.google.com/contact/2008#contact", "http://schemas.google.com/g/2005#kind", NULL);
- gdata_entry_add_category (GDATA_ENTRY (contact), category);
- g_object_unref (category);
+
email_address1 = gdata_gd_email_address_new ("liz gmail com", "http://schemas.google.com/g/2005#work", NULL, FALSE);
gdata_contacts_contact_add_email_address (contact, email_address1);
g_object_unref (email_address1);
+
email_address2 = gdata_gd_email_address_new ("liz example org", "http://schemas.google.com/g/2005#home", NULL, FALSE);
gdata_contacts_contact_add_email_address (contact, email_address2);
g_object_unref (email_address2);
+
phone_number1 = gdata_gd_phone_number_new ("(206)555-1212", "http://schemas.google.com/g/2005#work", NULL, NULL, TRUE);
gdata_contacts_contact_add_phone_number (contact, phone_number1);
g_object_unref (phone_number1);
+
phone_number2 = gdata_gd_phone_number_new ("(206)555-1213", "http://schemas.google.com/g/2005#home", NULL, NULL, FALSE);
gdata_contacts_contact_add_phone_number (contact, phone_number2);
g_object_unref (phone_number2);
+
im_address = gdata_gd_im_address_new ("liz gmail com", "http://schemas.google.com/g/2005#GOOGLE_TALK", "http://schemas.google.com/g/2005#home",
NULL, FALSE);
gdata_contacts_contact_add_im_address (contact, im_address);
g_object_unref (im_address);
+
postal_address = gdata_gd_postal_address_new ("http://schemas.google.com/g/2005#work", NULL, TRUE);
gdata_gd_postal_address_set_street (postal_address, "1600 Amphitheatre Pkwy Mountain View");
gdata_contacts_contact_add_postal_address (contact, postal_address);
g_object_unref (postal_address);
+ org = gdata_gd_organization_new ("OrgCorp", "President", "http://schemas.google.com/g/2005#work", NULL, FALSE);
+ gdata_contacts_contact_add_organization (contact, org);
+ g_object_unref (org);
+
+ jot = gdata_gcontact_jot_new ("This is a jot.", GDATA_GCONTACT_JOT_OTHER);
+ gdata_contacts_contact_add_jot (contact, jot);
+ g_object_unref (jot);
+
+ relation = gdata_gcontact_relation_new ("Brian Haddock", GDATA_GCONTACT_RELATION_FRIEND, NULL);
+ gdata_contacts_contact_add_relation (contact, relation);
+ g_object_unref (relation);
+
+ website = gdata_gcontact_website_new ("http://example.com/", GDATA_GCONTACT_WEBSITE_PROFILE, NULL, TRUE);
+ gdata_contacts_contact_add_website (contact, website);
+ g_object_unref (website);
+
+ event = gdata_gcontact_event_new (&date, GDATA_GCONTACT_EVENT_ANNIVERSARY, NULL);
+ gdata_contacts_contact_add_event (contact, event);
+ g_object_unref (event);
+
+ calendar = gdata_gcontact_calendar_new ("http://calendar.example.com/", GDATA_GCONTACT_CALENDAR_HOME, NULL, TRUE);
+ gdata_contacts_contact_add_calendar (contact, calendar);
+ g_object_unref (calendar);
+
/* Add some extended properties */
g_assert (gdata_contacts_contact_set_extended_property (contact, "TITLE", NULL) == TRUE);
g_assert (gdata_contacts_contact_set_extended_property (contact, "ROLE", "") == TRUE);
@@ -223,6 +258,9 @@ test_insert_simple (gconstpointer service)
"deleted", &deleted,
"has-photo", &has_photo,
"name", &name,
+ "nickname", &nickname,
+ "birthday", &date2,
+ "birthday-has-year", &birthday_has_year,
NULL);
g_assert_cmpint (edited->tv_sec, ==, creation_time.tv_sec);
@@ -230,8 +268,15 @@ test_insert_simple (gconstpointer service)
g_assert (deleted == FALSE);
g_assert (has_photo == FALSE);
g_assert (name2 == name);
+ g_assert_cmpstr (nickname, ==, "Big J");
+ g_assert (g_date_valid (date2) == TRUE);
+ g_assert_cmpuint (g_date_get_month (date2), ==, 1);
+ g_assert_cmpuint (g_date_get_day (date2), ==, 1);
+ g_assert (birthday_has_year == FALSE);
g_object_unref (name2);
+ g_free (date2);
+ g_free (nickname);
/* Check the XML */
xml = gdata_parsable_get_xml (GDATA_PARSABLE (contact));
@@ -257,7 +302,18 @@ test_insert_simple (gconstpointer service)
"<gd:structuredPostalAddress rel='http://schemas.google.com/g/2005#work' primary='true'>"
"<gd:street>1600 Amphitheatre Pkwy Mountain View</gd:street>"
"</gd:structuredPostalAddress>"
+ "<gd:organization rel='http://schemas.google.com/g/2005#work' primary='false'>"
+ "<gd:orgName>OrgCorp</gd:orgName>"
+ "<gd:orgTitle>President</gd:orgTitle>"
+ "</gd:organization>"
+ "<gContact:jot rel='other'>This is a jot.</gContact:jot>"
+ "<gContact:relation rel='friend'>Brian Haddock</gContact:relation>"
+ "<gContact:website href='http://example.com/' rel='profile' primary='true'/>"
+ "<gContact:event rel='anniversary'><gd:when startTime='1900-01-01'/></gContact:event>"
+ "<gContact:calendarLink href='http://calendar.example.com/' rel='home' primary='true'/>"
"<gd:extendedProperty name='CALURI'>http://example.com/</gd:extendedProperty>"
+ "<gContact:nickname>Big J</gContact:nickname>"
+ "<gContact:birthday when='--01-01'/>"
"</entry>");
g_free (xml);
@@ -272,6 +328,13 @@ test_insert_simple (gconstpointer service)
gdata_contacts_contact_get_edited (new_contact, &creation_time);
g_assert_cmpint (creation_time.tv_sec, >=, edited->tv_sec);
+ /* Nickname and birthday */
+ g_assert_cmpstr (gdata_contacts_contact_get_nickname (new_contact), ==, "Big J");
+ g_assert (gdata_contacts_contact_get_birthday (new_contact, &date) == FALSE);
+ g_assert (g_date_valid (&date) == TRUE);
+ g_assert_cmpuint (g_date_get_month (&date), ==, 1);
+ g_assert_cmpuint (g_date_get_day (&date), ==, 1);
+
/* E-mail addresses */
list = gdata_contacts_contact_get_email_addresses (new_contact);
g_assert_cmpuint (g_list_length (list), ==, 2);
@@ -302,10 +365,40 @@ test_insert_simple (gconstpointer service)
/* Organizations */
list = gdata_contacts_contact_get_organizations (new_contact);
- g_assert (list == NULL);
+ g_assert_cmpuint (g_list_length (list), ==, 1);
+ g_assert (GDATA_IS_GD_ORGANIZATION (list->data));
g_assert (gdata_contacts_contact_get_primary_organization (new_contact) == NULL);
+ /* Jots */
+ list = gdata_contacts_contact_get_jots (new_contact);
+ g_assert_cmpuint (g_list_length (list), ==, 1);
+ g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+
+ /* Relations */
+ list = gdata_contacts_contact_get_relations (new_contact);
+ g_assert_cmpuint (g_list_length (list), ==, 1);
+ g_assert (GDATA_IS_GCONTACT_RELATION (list->data));
+
+ /* Websites */
+ list = gdata_contacts_contact_get_websites (new_contact);
+ g_assert_cmpuint (g_list_length (list), ==, 1);
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (gdata_contacts_contact_get_primary_website (new_contact)));
+
+ /* Events */
+ list = gdata_contacts_contact_get_events (new_contact);
+ g_assert_cmpuint (g_list_length (list), ==, 1);
+ g_assert (GDATA_IS_GCONTACT_EVENT (list->data));
+
+ /* Calendars */
+ list = gdata_contacts_contact_get_calendars (new_contact);
+ g_assert_cmpuint (g_list_length (list), ==, 1);
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (gdata_contacts_contact_get_primary_calendar (new_contact)));
+
/* Extended properties */
g_assert_cmpstr (gdata_contacts_contact_get_extended_property (new_contact, "CALURI"), ==, "http://example.com/");
g_assert (gdata_contacts_contact_get_extended_property (new_contact, "non-existent") == NULL);
@@ -323,6 +416,44 @@ test_insert_simple (gconstpointer service)
/* TODO: check entries and feed properties */
+ /* Try removing some things from the new contact and ensure it works */
+ gdata_contacts_contact_remove_all_email_addresses (new_contact);
+ g_assert (gdata_contacts_contact_get_email_addresses (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_email_address (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_im_addresses (new_contact);
+ g_assert (gdata_contacts_contact_get_im_addresses (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_im_address (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_phone_numbers (new_contact);
+ g_assert (gdata_contacts_contact_get_phone_numbers (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_phone_number (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_postal_addresses (new_contact);
+ g_assert (gdata_contacts_contact_get_postal_addresses (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_postal_address (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_organizations (new_contact);
+ g_assert (gdata_contacts_contact_get_organizations (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_organization (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_jots (new_contact);
+ g_assert (gdata_contacts_contact_get_jots (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_relations (new_contact);
+ g_assert (gdata_contacts_contact_get_relations (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_websites (new_contact);
+ g_assert (gdata_contacts_contact_get_websites (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_website (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_events (new_contact);
+ g_assert (gdata_contacts_contact_get_events (new_contact) == NULL);
+
+ gdata_contacts_contact_remove_all_calendars (new_contact);
+ g_assert (gdata_contacts_contact_get_calendars (new_contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_calendar (new_contact) == NULL);
+
g_free (edited);
g_object_unref (contact);
g_object_unref (new_contact);
@@ -442,6 +573,7 @@ static void
test_parser_minimal (gconstpointer service)
{
GDataContactsContact *contact;
+ GDate birthday;
GError *error = NULL;
g_test_bug ("580330");
@@ -471,6 +603,17 @@ test_parser_minimal (gconstpointer service)
/* TODO: Check the other properties */
+ g_assert (gdata_contacts_contact_get_nickname (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_birthday (contact, &birthday) == FALSE);
+ g_assert (g_date_valid (&birthday) == FALSE);
+ g_assert (gdata_contacts_contact_get_jots (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_relations (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_websites (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_website (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_events (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_calendars (contact) == NULL);
+ g_assert (gdata_contacts_contact_get_primary_calendar (contact) == NULL);
+
g_object_unref (contact);
}
@@ -478,6 +621,8 @@ static void
test_parser_normal (gconstpointer service)
{
GDataContactsContact *contact;
+ GDate date;
+ GList *list;
GError *error = NULL;
contact = GDATA_CONTACTS_CONTACT (gdata_parsable_new_from_xml (GDATA_TYPE_CONTACTS_CONTACT,
@@ -500,6 +645,21 @@ test_parser_normal (gconstpointer service)
"deleted='true'/>"
"<gContact:groupMembershipInfo href='http://www.google.com/feeds/contacts/groups/jo%40gmail.com/base/1234b'/>"
"<gd:deleted/>"
+ "<gContact:nickname>Agent Smith</gContact:nickname>"
+ "<gContact:birthday when='2010-12-03'/>"
+ "<gContact:jot rel='home'>Moved house on 2010-02-14 to the North Pole.</gContact:jot>"
+ "<gContact:jot rel='user'>Owes me ten pounds.</gContact:jot>"
+ "<gContact:jot rel='other'></gContact:jot>" /* Empty on purpose */
+ "<gContact:relation rel='father'>Darth Vader</gContact:relation>"
+ "<gContact:relation label='Favourite singer'>Rob Halford</gContact:relation>"
+ "<gContact:website href='http://example.com' rel='home-page' label='Home tab #1' primary='true'/>"
+ "<gContact:website href='http://example.com' rel='work'/>"
+ "<gContact:website href='http://bar.com' rel='profile' primary='false'/>"
+ "<gContact:event rel='anniversary'><gd:when startTime='2010-03-04'/></gContact:event>"
+ "<gContact:event label='Foobar'><gd:when startTime='1900-01-01'/></gContact:event>"
+ "<gContact:calendarLink href='http://example.com/' rel='free-busy' primary='true'/>"
+ "<gContact:calendarLink href='http://example.com/' label='Gig list' primary='false'/>"
+ "<gContact:calendarLink href='http://foo.com/calendar' rel='home'/>"
"</entry>", -1, &error));
g_assert_no_error (error);
g_assert (GDATA_IS_CONTACTS_CONTACT (contact));
@@ -508,6 +668,123 @@ test_parser_normal (gconstpointer service)
/* TODO: Check the other properties */
+ g_assert_cmpstr (gdata_contacts_contact_get_nickname (contact), ==, "Agent Smith");
+
+ /* Birthday */
+ g_assert (gdata_contacts_contact_get_birthday (contact, &date) == TRUE);
+ g_assert (g_date_valid (&date) == TRUE);
+ g_assert_cmpuint (g_date_get_year (&date), ==, 2010);
+ g_assert_cmpuint (g_date_get_month (&date), ==, 12);
+ g_assert_cmpuint (g_date_get_day (&date), ==, 3);
+
+ /* Jots */
+ list = gdata_contacts_contact_get_jots (contact);
+ g_assert_cmpuint (g_list_length (list), ==, 3);
+
+ g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+ g_assert_cmpstr (gdata_gcontact_jot_get_content (GDATA_GCONTACT_JOT (list->data)), ==, "Moved house on 2010-02-14 to the North Pole.");
+ g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (GDATA_GCONTACT_JOT (list->data)), ==, GDATA_GCONTACT_JOT_HOME);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+ g_assert_cmpstr (gdata_gcontact_jot_get_content (GDATA_GCONTACT_JOT (list->data)), ==, "Owes me ten pounds.");
+ g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (GDATA_GCONTACT_JOT (list->data)), ==, GDATA_GCONTACT_JOT_USER);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+ g_assert (gdata_gcontact_jot_get_content (GDATA_GCONTACT_JOT (list->data)) == NULL);
+ g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (GDATA_GCONTACT_JOT (list->data)), ==, GDATA_GCONTACT_JOT_OTHER);
+
+ /* Relations */
+ list = gdata_contacts_contact_get_relations (contact);
+ g_assert_cmpuint (g_list_length (list), ==, 2);
+
+ g_assert (GDATA_IS_GCONTACT_RELATION (list->data));
+ g_assert_cmpstr (gdata_gcontact_relation_get_name (GDATA_GCONTACT_RELATION (list->data)), ==, "Darth Vader");
+ g_assert_cmpstr (gdata_gcontact_relation_get_relation_type (GDATA_GCONTACT_RELATION (list->data)), ==, GDATA_GCONTACT_RELATION_FATHER);
+ g_assert (gdata_gcontact_relation_get_label (GDATA_GCONTACT_RELATION (list->data)) == NULL);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_RELATION (list->data));
+ g_assert_cmpstr (gdata_gcontact_relation_get_name (GDATA_GCONTACT_RELATION (list->data)), ==, "Rob Halford");
+ g_assert (gdata_gcontact_relation_get_relation_type (GDATA_GCONTACT_RELATION (list->data)) == NULL);
+ g_assert_cmpstr (gdata_gcontact_relation_get_label (GDATA_GCONTACT_RELATION (list->data)), ==, "Favourite singer");
+
+ /* Websites */
+ list = gdata_contacts_contact_get_websites (contact);
+ g_assert_cmpuint (g_list_length (list), ==, 3);
+
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+ g_assert_cmpstr (gdata_gcontact_website_get_uri (GDATA_GCONTACT_WEBSITE (list->data)), ==, "http://example.com");
+ g_assert_cmpstr (gdata_gcontact_website_get_relation_type (GDATA_GCONTACT_WEBSITE (list->data)), ==, GDATA_GCONTACT_WEBSITE_HOME_PAGE);
+ g_assert_cmpstr (gdata_gcontact_website_get_label (GDATA_GCONTACT_WEBSITE (list->data)), ==, "Home tab #1");
+ g_assert (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (list->data)) == TRUE);
+
+ g_assert (gdata_contacts_contact_get_primary_website (contact) == list->data);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+ g_assert_cmpstr (gdata_gcontact_website_get_uri (GDATA_GCONTACT_WEBSITE (list->data)), ==, "http://example.com");
+ g_assert_cmpstr (gdata_gcontact_website_get_relation_type (GDATA_GCONTACT_WEBSITE (list->data)), ==, GDATA_GCONTACT_WEBSITE_WORK);
+ g_assert (gdata_gcontact_website_get_label (GDATA_GCONTACT_WEBSITE (list->data)) == NULL);
+ g_assert (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (list->data)) == FALSE);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+ g_assert_cmpstr (gdata_gcontact_website_get_uri (GDATA_GCONTACT_WEBSITE (list->data)), ==, "http://bar.com");
+ g_assert_cmpstr (gdata_gcontact_website_get_relation_type (GDATA_GCONTACT_WEBSITE (list->data)), ==, GDATA_GCONTACT_WEBSITE_PROFILE);
+ g_assert (gdata_gcontact_website_get_label (GDATA_GCONTACT_WEBSITE (list->data)) == NULL);
+ g_assert (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (list->data)) == FALSE);
+
+ /* Events */
+ list = gdata_contacts_contact_get_events (contact);
+ g_assert_cmpuint (g_list_length (list), ==, 2);
+
+ g_assert (GDATA_IS_GCONTACT_EVENT (list->data));
+ gdata_gcontact_event_get_date (GDATA_GCONTACT_EVENT (list->data), &date);
+ g_assert (g_date_valid (&date) == TRUE);
+ g_assert_cmpuint (g_date_get_year (&date), ==, 2010);
+ g_assert_cmpuint (g_date_get_month (&date), ==, 3);
+ g_assert_cmpuint (g_date_get_day (&date), ==, 4);
+ g_assert_cmpstr (gdata_gcontact_event_get_relation_type (GDATA_GCONTACT_EVENT (list->data)), ==, GDATA_GCONTACT_EVENT_ANNIVERSARY);
+ g_assert (gdata_gcontact_event_get_label (GDATA_GCONTACT_EVENT (list->data)) == NULL);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_EVENT (list->data));
+ gdata_gcontact_event_get_date (GDATA_GCONTACT_EVENT (list->data), &date);
+ g_assert (g_date_valid (&date) == TRUE);
+ g_assert_cmpuint (g_date_get_year (&date), ==, 1900);
+ g_assert_cmpuint (g_date_get_month (&date), ==, 1);
+ g_assert_cmpuint (g_date_get_day (&date), ==, 1);
+ g_assert (gdata_gcontact_event_get_relation_type (GDATA_GCONTACT_EVENT (list->data)) == NULL);
+ g_assert_cmpstr (gdata_gcontact_event_get_label (GDATA_GCONTACT_EVENT (list->data)), ==, "Foobar");
+
+ /* Calendars */
+ list = gdata_contacts_contact_get_calendars (contact);
+ g_assert_cmpuint (g_list_length (list), ==, 3);
+
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+ g_assert_cmpstr (gdata_gcontact_calendar_get_uri (GDATA_GCONTACT_CALENDAR (list->data)), ==, "http://example.com/");
+ g_assert_cmpstr (gdata_gcontact_calendar_get_relation_type (GDATA_GCONTACT_CALENDAR (list->data)), ==, GDATA_GCONTACT_CALENDAR_FREE_BUSY);
+ g_assert (gdata_gcontact_calendar_get_label (GDATA_GCONTACT_CALENDAR (list->data)) == NULL);
+ g_assert (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (list->data)) == TRUE);
+
+ g_assert (gdata_contacts_contact_get_primary_calendar (contact) == list->data);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+ g_assert_cmpstr (gdata_gcontact_calendar_get_uri (GDATA_GCONTACT_CALENDAR (list->data)), ==, "http://example.com/");
+ g_assert (gdata_gcontact_calendar_get_relation_type (GDATA_GCONTACT_CALENDAR (list->data)) == NULL);
+ g_assert_cmpstr (gdata_gcontact_calendar_get_label (GDATA_GCONTACT_CALENDAR (list->data)), ==, "Gig list");
+ g_assert (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (list->data)) == FALSE);
+
+ list = list->next;
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+ g_assert_cmpstr (gdata_gcontact_calendar_get_uri (GDATA_GCONTACT_CALENDAR (list->data)), ==, "http://foo.com/calendar");
+ g_assert_cmpstr (gdata_gcontact_calendar_get_relation_type (GDATA_GCONTACT_CALENDAR (list->data)), ==, GDATA_GCONTACT_CALENDAR_HOME);
+ g_assert (gdata_gcontact_calendar_get_label (GDATA_GCONTACT_CALENDAR (list->data)) == NULL);
+ g_assert (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (list->data)) == FALSE);
+
g_object_unref (contact);
}
@@ -556,6 +833,30 @@ test_parser_error_handling (gconstpointer service)
TEST_XML_ERROR_HANDLING ("<gContact:groupMembershipInfo/>");
TEST_XML_ERROR_HANDLING ("<gContact:groupMembershipInfo href='http://foobar.com/base/1234b' deleted='maybe'/>");
+ /* gContact:nickname */
+ TEST_XML_ERROR_HANDLING ("<gContact:nickname/>"); /* missing content */
+ TEST_XML_ERROR_HANDLING ("<gContact:nickname>Nickname 1</gContact:nickname><gContact:nickname>Duplicate!</gContact:nickname>"); /* duplicate */
+
+ /* gContact:birthday */
+ TEST_XML_ERROR_HANDLING ("<gContact:birthday/>"); /* missing "when" attribute */
+ TEST_XML_ERROR_HANDLING ("<gContact:birthday when='foobar'/>"); /* invalid date */
+ TEST_XML_ERROR_HANDLING ("<gContact:birthday when='2000-01-01'/><gContact:birthday when='--01-01'/>"); /* duplicate */
+
+ /* gContact:jot */
+ TEST_XML_ERROR_HANDLING ("<gContact:jot/>");
+
+ /* gContact:relation */
+ TEST_XML_ERROR_HANDLING ("<gContact:relation/>");
+
+ /* gContact:website */
+ TEST_XML_ERROR_HANDLING ("<gContact:website/>");
+
+ /* gContact:event */
+ TEST_XML_ERROR_HANDLING ("<gContact:event/>");
+
+ /* gContact:calendar */
+ TEST_XML_ERROR_HANDLING ("<gContact:calendarLink/>");
+
#undef TEST_XML_ERROR_HANDLING
}
diff --git a/gdata/tests/general.c b/gdata/tests/general.c
index c8b1621..5ed1e86 100644
--- a/gdata/tests/general.c
+++ b/gdata/tests/general.c
@@ -2334,6 +2334,427 @@ test_media_thumbnail (void)
g_object_unref (thumbnail);
}
+static void
+test_gcontact_calendar (void)
+{
+ GDataGContactCalendar *calendar, *calendar2;
+ gchar *xml;
+ GError *error = NULL;
+
+ calendar = GDATA_GCONTACT_CALENDAR (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_CALENDAR,
+ "<gContact:calendarLink xmlns:gContact='http://schemas.google.com/contact/2008' rel='work' primary='true' "
+ "href='http://calendar.com/'/>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (calendar));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_calendar_get_uri (calendar), ==, "http://calendar.com/");
+ g_assert_cmpstr (gdata_gcontact_calendar_get_relation_type (calendar), ==, GDATA_GCONTACT_CALENDAR_WORK);
+ g_assert (gdata_gcontact_calendar_get_label (calendar) == NULL);
+ g_assert (gdata_gcontact_calendar_is_primary (calendar) == TRUE);
+
+ /* Compare it against another identical calendar */
+ calendar2 = gdata_gcontact_calendar_new ("http://calendar.com/", GDATA_GCONTACT_CALENDAR_WORK, NULL, TRUE);
+ g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, calendar2), ==, 0);
+
+ /* â?¦and a different one */
+ gdata_gcontact_calendar_set_uri (calendar2, "http://calendar.somewhereelse.com/");
+ g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, calendar2), !=, 0);
+ g_object_unref (calendar2);
+
+ /* More comparisons */
+ g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, NULL), ==, 1);
+ g_assert_cmpint (gdata_gcontact_calendar_compare (NULL, calendar), ==, -1);
+ g_assert_cmpint (gdata_gcontact_calendar_compare (NULL, NULL), ==, 0);
+ g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, calendar), ==, 0);
+
+ /* Check the outputted XML is the same */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (calendar));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:calendarLink xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+ "href='http://calendar.com/' rel='work' primary='true'/>");
+ g_free (xml);
+ g_object_unref (calendar);
+
+ /* Now parse a calendar with less information available */
+ calendar = GDATA_GCONTACT_CALENDAR (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_CALENDAR,
+ "<gContact:calendarLink xmlns:gContact='http://schemas.google.com/contact/2008' href='http://example.com/' label='<a>'/>",
+ -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_CALENDAR (calendar));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_calendar_get_uri (calendar), ==, "http://example.com/");
+ g_assert (gdata_gcontact_calendar_get_relation_type (calendar) == NULL);
+ g_assert_cmpstr (gdata_gcontact_calendar_get_label (calendar), ==, "<a>");
+ g_assert (gdata_gcontact_calendar_is_primary (calendar) == FALSE);
+
+ /* Check the outputted XML is still OK */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (calendar));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:calendarLink xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+ "href='http://example.com/' label='<a>' primary='false'/>");
+ g_free (xml);
+ g_object_unref (calendar);
+}
+
+static void
+test_gcontact_calendar_error_handling (void)
+{
+ GDataGContactCalendar *calendar;
+ GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) calendar = GDATA_GCONTACT_CALENDAR (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_CALENDAR,\
+ "<gContact:calendarLink xmlns:gContact='http://schemas.google.com/contact/2008' "\
+ x\
+ "/>", -1, &error));\
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+ g_assert (calendar == NULL);\
+ g_clear_error (&error)
+
+ TEST_XML_ERROR_HANDLING ("rel='work'"); /* no href */
+ TEST_XML_ERROR_HANDLING ("rel='work' href=''"); /* empty href */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/'"); /* no rel or label */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel=''"); /* empty rel */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/' label=''"); /* empty label */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel='other' label='Other'"); /* rel and label */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel='home' primary='not a boolean'"); /* invalid primary */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_event (void)
+{
+ GDataGContactEvent *event, *event2;
+ GDate date;
+ gchar *xml;
+ GError *error = NULL;
+
+ event = GDATA_GCONTACT_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_EVENT,
+ "<gContact:event xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' rel='other'>"
+ "<gd:when startTime='2004-03-12'/>"
+ "</gContact:event>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_EVENT (event));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ gdata_gcontact_event_get_date (event, &date);
+ g_assert (g_date_valid (&date) == TRUE);
+ g_assert_cmpuint (g_date_get_year (&date), ==, 2004);
+ g_assert_cmpuint (g_date_get_month (&date), ==, 3);
+ g_assert_cmpuint (g_date_get_day (&date), ==, 12);
+ g_assert_cmpstr (gdata_gcontact_event_get_relation_type (event), ==, GDATA_GCONTACT_EVENT_OTHER);
+ g_assert (gdata_gcontact_event_get_label (event) == NULL);
+
+ /* Try creating another event from scratch */
+ event2 = gdata_gcontact_event_new (&date, GDATA_GCONTACT_EVENT_OTHER, NULL);
+ g_assert (GDATA_IS_GCONTACT_EVENT (event2));
+ g_object_unref (event2);
+
+ /* Check the outputted XML is the same */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (event));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:event xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005' "
+ "xmlns:gContact='http://schemas.google.com/contact/2008' rel='other'>"
+ "<gd:when startTime='2004-03-12'/>"
+ "</gContact:event>");
+ g_free (xml);
+ g_object_unref (event);
+
+ /* Now parse an event with different information available */
+ event = GDATA_GCONTACT_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_EVENT,
+ "<gContact:event xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' "
+ "label='<a>'>"
+ "<gd:when startTime='2000-01-01'/>"
+ "</gContact:event>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_EVENT (event));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ gdata_gcontact_event_get_date (event, &date);
+ g_assert (g_date_valid (&date) == TRUE);
+ g_assert_cmpuint (g_date_get_year (&date), ==, 2000);
+ g_assert_cmpuint (g_date_get_month (&date), ==, 1);
+ g_assert_cmpuint (g_date_get_day (&date), ==, 1);
+ g_assert (gdata_gcontact_event_get_relation_type (event) == NULL);
+ g_assert_cmpstr (gdata_gcontact_event_get_label (event), ==, "<a>");
+
+ /* Check the outputted XML is still OK */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (event));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:event xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005' "
+ "xmlns:gContact='http://schemas.google.com/contact/2008' label='<a>'>"
+ "<gd:when startTime='2000-01-01'/>"
+ "</gContact:event>");
+ g_free (xml);
+ g_object_unref (event);
+}
+
+static void
+test_gcontact_event_error_handling (void)
+{
+ GDataGContactEvent *event;
+ GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) event = GDATA_GCONTACT_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_EVENT,\
+ "<gContact:event xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' "\
+ x\
+ "</gContact:event>", -1, &error));\
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+ g_assert (event == NULL);\
+ g_clear_error (&error)
+
+ TEST_XML_ERROR_HANDLING ("rel='anniversary'>"); /* no gd:when */
+ TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when/>"); /* no startTime */
+ TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when startTime='foobar'/>"); /* invalid startTime */
+ TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when startTime='2001-12-41'/>");
+ TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when startTime='2010-03-25T22:01Z'/>");
+ TEST_XML_ERROR_HANDLING ("><gd:when startTime='2000-01-01'/>"); /* no rel or label */
+ TEST_XML_ERROR_HANDLING ("rel=''><gd:when startTime='2000-01-01'/>"); /* empty rel */
+ TEST_XML_ERROR_HANDLING ("label=''><gd:when startTime='2000-01-01'/>"); /* empty label */
+ TEST_XML_ERROR_HANDLING ("rel='other' label='Other'><gd:when startTime='2000-01-01'/>"); /* rel and label */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_jot (void)
+{
+ GDataGContactJot *jot, *jot2;
+ gchar *xml;
+ GError *error = NULL;
+
+ jot = GDATA_GCONTACT_JOT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_JOT,
+ "<gContact:jot xmlns:gContact='http://schemas.google.com/contact/2008' rel='user'>They like <angles>.</gContact:jot>",
+ -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_JOT (jot));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (jot), ==, GDATA_GCONTACT_JOT_USER);
+ g_assert_cmpstr (gdata_gcontact_jot_get_content (jot), ==, "They like <angles>.");
+
+ /* Try creating another jot from scratch */
+ jot2 = gdata_gcontact_jot_new ("friend,local,oss", GDATA_GCONTACT_JOT_KEYWORDS);
+ g_assert (GDATA_IS_GCONTACT_JOT (jot2));
+ g_object_unref (jot2);
+
+ /* Check the outputted XML is the same */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (jot));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:jot xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' rel='user'>"
+ "They like <angles>.</gContact:jot>");
+ g_free (xml);
+ g_object_unref (jot);
+
+ /* Now parse a jot with different information available */
+ jot = GDATA_GCONTACT_JOT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_JOT,
+ "<gContact:jot xmlns:gContact='http://schemas.google.com/contact/2008' rel='other'/>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_JOT (jot));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (jot), ==, GDATA_GCONTACT_JOT_OTHER);
+ g_assert (gdata_gcontact_jot_get_content (jot) == NULL);
+
+ /* Check the outputted XML is still OK */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (jot));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:jot xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' rel='other'/>");
+ g_free (xml);
+ g_object_unref (jot);
+}
+
+static void
+test_gcontact_jot_error_handling (void)
+{
+ GDataGContactJot *jot;
+ GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) jot = GDATA_GCONTACT_JOT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_JOT,\
+ "<gContact:jot xmlns:gContact='http://schemas.google.com/contact/2008' "\
+ x\
+ "</gContact:jot>", -1, &error));\
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+ g_assert (jot == NULL);\
+ g_clear_error (&error)
+
+ TEST_XML_ERROR_HANDLING (">Content"); /* no rel */
+ TEST_XML_ERROR_HANDLING ("rel=''>Content"); /* empty rel */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_relation (void)
+{
+ GDataGContactRelation *relation, *relation2;
+ gchar *xml;
+ GError *error = NULL;
+
+ relation = GDATA_GCONTACT_RELATION (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_RELATION,
+ "<gContact:relation xmlns:gContact='http://schemas.google.com/contact/2008' rel='child'>Fred</gContact:relation>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_RELATION (relation));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_relation_get_name (relation), ==, "Fred");
+ g_assert_cmpstr (gdata_gcontact_relation_get_relation_type (relation), ==, GDATA_GCONTACT_RELATION_CHILD);
+ g_assert (gdata_gcontact_relation_get_label (relation) == NULL);
+
+ /* Try creating another relation from scratch */
+ relation2 = gdata_gcontact_relation_new ("Brian", GDATA_GCONTACT_RELATION_RELATIVE, NULL);
+ g_assert (GDATA_IS_GCONTACT_RELATION (relation2));
+ g_object_unref (relation2);
+
+ /* Check the outputted XML is the same */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (relation));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:relation xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' rel='child'>"
+ "Fred</gContact:relation>");
+ g_free (xml);
+ g_object_unref (relation);
+
+ /* Now parse a relation with different information available */
+ relation = GDATA_GCONTACT_RELATION (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_RELATION,
+ "<gContact:relation xmlns:gContact='http://schemas.google.com/contact/2008' label='<a>'>Sid</gContact:relation>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_RELATION (relation));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_relation_get_name (relation), ==, "Sid");
+ g_assert (gdata_gcontact_relation_get_relation_type (relation) == NULL);
+ g_assert_cmpstr (gdata_gcontact_relation_get_label (relation), ==, "<a>");
+
+ /* Check the outputted XML is still OK */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (relation));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:relation xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+ "label='<a>'>Sid</gContact:relation>");
+ g_free (xml);
+ g_object_unref (relation);
+}
+
+static void
+test_gcontact_relation_error_handling (void)
+{
+ GDataGContactRelation *relation;
+ GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) relation = GDATA_GCONTACT_RELATION (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_RELATION,\
+ "<gContact:relation xmlns:gContact='http://schemas.google.com/contact/2008' "\
+ x\
+ "</gContact:relation>", -1, &error));\
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+ g_assert (relation == NULL);\
+ g_clear_error (&error)
+
+ TEST_XML_ERROR_HANDLING ("rel='spouse'>"); /* no name */
+ TEST_XML_ERROR_HANDLING (">Brian"); /* no rel or label */
+ TEST_XML_ERROR_HANDLING ("rel=''>Brian"); /* empty rel */
+ TEST_XML_ERROR_HANDLING ("label=''>Brian"); /* empty label */
+ TEST_XML_ERROR_HANDLING ("rel='sister' label='Older sister'>Brian"); /* rel and label */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_website (void)
+{
+ GDataGContactWebsite *website, *website2;
+ gchar *xml;
+ GError *error = NULL;
+
+ website = GDATA_GCONTACT_WEBSITE (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_WEBSITE,
+ "<gContact:website xmlns:gContact='http://schemas.google.com/contact/2008' href='http://example.com/' rel='work' primary='true' "
+ "label='<Markup> blog'/>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (website));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_website_get_uri (website), ==, "http://example.com/");
+ g_assert_cmpstr (gdata_gcontact_website_get_relation_type (website), ==, GDATA_GCONTACT_WEBSITE_WORK);
+ g_assert_cmpstr (gdata_gcontact_website_get_label (website), ==, "<Markup> blog");
+ g_assert (gdata_gcontact_website_is_primary (website) == TRUE);
+
+ /* Compare it against another identical website */
+ website2 = gdata_gcontact_website_new ("http://example.com/", GDATA_GCONTACT_WEBSITE_WORK, "<Markup> blog", TRUE);
+ g_assert_cmpint (gdata_gcontact_website_compare (website, website2), ==, 0);
+
+ /* â?¦and a different one */
+ gdata_gcontact_website_set_uri (website2, "http://somewhereelse.com/");
+ g_assert_cmpint (gdata_gcontact_website_compare (website, website2), !=, 0);
+ g_object_unref (website2);
+
+ /* More comparisons */
+ g_assert_cmpint (gdata_gcontact_website_compare (website, NULL), ==, 1);
+ g_assert_cmpint (gdata_gcontact_website_compare (NULL, website), ==, -1);
+ g_assert_cmpint (gdata_gcontact_website_compare (NULL, NULL), ==, 0);
+ g_assert_cmpint (gdata_gcontact_website_compare (website, website), ==, 0);
+
+ /* Check the outputted XML is the same */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (website));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:website xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+ "href='http://example.com/' rel='work' label='<Markup> blog' primary='true'/>");
+ g_free (xml);
+ g_object_unref (website);
+
+ /* Now parse a website with less information available */
+ website = GDATA_GCONTACT_WEBSITE (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_WEBSITE,
+ "<gContact:website xmlns:gContact='http://schemas.google.com/contact/2008' href='http://test.com/' rel='ftp'/>", -1, &error));
+ g_assert_no_error (error);
+ g_assert (GDATA_IS_GCONTACT_WEBSITE (website));
+ g_clear_error (&error);
+
+ /* Check the properties */
+ g_assert_cmpstr (gdata_gcontact_website_get_uri (website), ==, "http://test.com/");
+ g_assert_cmpstr (gdata_gcontact_website_get_relation_type (website), ==, GDATA_GCONTACT_WEBSITE_FTP);
+ g_assert (gdata_gcontact_website_get_label (website) == NULL);
+ g_assert (gdata_gcontact_website_is_primary (website) == FALSE);
+
+ /* Check the outputted XML is still OK */
+ xml = gdata_parsable_get_xml (GDATA_PARSABLE (website));
+ g_assert_cmpstr (xml, ==,
+ "<gContact:website xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+ "href='http://test.com/' rel='ftp' primary='false'/>");
+ g_free (xml);
+ g_object_unref (website);
+}
+
+static void
+test_gcontact_website_error_handling (void)
+{
+ GDataGContactWebsite *website;
+ GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) website = GDATA_GCONTACT_WEBSITE (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_WEBSITE,\
+ "<gContact:website xmlns:gContact='http://schemas.google.com/contact/2008' "\
+ x\
+ "/>", -1, &error));\
+ g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+ g_assert (website == NULL);\
+ g_clear_error (&error)
+
+ TEST_XML_ERROR_HANDLING ("rel='work'"); /* no href */
+ TEST_XML_ERROR_HANDLING ("rel='work' href=''"); /* empty href */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/'"); /* no rel */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel=''"); /* empty rel */
+ TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel='profile' primary='not a boolean'"); /* invalid primary */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
int
main (int argc, char *argv[])
{
@@ -2380,5 +2801,16 @@ main (int argc, char *argv[])
/*g_test_add_data_func ("/media/thumbnail/parse_time", "", test_media_thumbnail_parse_time);
g_test_add_data_func ("/media/thumbnail/parse_time", "de_DE", test_media_thumbnail_parse_time);*/
+ g_test_add_func ("/gcontact/calendar", test_gcontact_calendar);
+ g_test_add_func ("/gcontact/calendar/error_handling", test_gcontact_calendar_error_handling);
+ g_test_add_func ("/gcontact/event", test_gcontact_event);
+ g_test_add_func ("/gcontact/event/error_handling", test_gcontact_event_error_handling);
+ g_test_add_func ("/gcontact/jot", test_gcontact_jot);
+ g_test_add_func ("/gcontact/jot/error_handling", test_gcontact_jot_error_handling);
+ g_test_add_func ("/gcontact/relation", test_gcontact_relation);
+ g_test_add_func ("/gcontact/relation/error_handling", test_gcontact_relation_error_handling);
+ g_test_add_func ("/gcontact/website", test_gcontact_website);
+ g_test_add_func ("/gcontact/website/error_handling", test_gcontact_website_error_handling);
+
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]