[libgdata] [core] Move all GTimeVal parsing into a single function



commit 5c64f9db5a20fd4967a8e2190c9a719a1e04360e
Author: Philip Withnall <philip tecnocode co uk>
Date:   Wed Mar 24 21:44:27 2010 +0000

    [core] Move all GTimeVal parsing into a single function

 gdata/gdata-entry.c                                |   75 +++++---------------
 gdata/gdata-feed.c                                 |   19 +-----
 gdata/gdata-parser.c                               |   69 ++++++++++++++++++
 gdata/gdata-parser.h                               |    7 ++-
 gdata/gdata-private.h                              |    1 +
 gdata/services/calendar/gdata-calendar-calendar.c  |   29 +++-----
 gdata/services/calendar/gdata-calendar-event.c     |   43 +++++-------
 gdata/services/contacts/gdata-contacts-contact.c   |   55 ++++++---------
 gdata/services/documents/gdata-documents-entry.c   |   44 ++++++------
 gdata/services/documents/gdata-documents-folder.c  |    8 ++-
 .../documents/gdata-documents-presentation.c       |    8 ++-
 .../documents/gdata-documents-spreadsheet.c        |    8 ++-
 gdata/services/documents/gdata-documents-text.c    |    8 ++-
 gdata/services/picasaweb/gdata-picasaweb-album.c   |   26 +++----
 gdata/services/picasaweb/gdata-picasaweb-file.c    |   26 +++----
 gdata/services/youtube/gdata-youtube-group.c       |   18 +----
 16 files changed, 216 insertions(+), 228 deletions(-)
---
diff --git a/gdata/gdata-entry.c b/gdata/gdata-entry.c
index 98ec252..e5ec364 100644
--- a/gdata/gdata-entry.c
+++ b/gdata/gdata-entry.c
@@ -373,73 +373,32 @@ static gboolean
 parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
 {
 	gboolean success;
-	GDataEntry *self;
+	GDataEntryPrivate *priv = GDATA_ENTRY (parsable)->priv;
 
-	g_return_val_if_fail (GDATA_IS_ENTRY (parsable), FALSE);
-	g_return_val_if_fail (doc != NULL, FALSE);
-	g_return_val_if_fail (node != NULL, FALSE);
-
-	self = GDATA_ENTRY (parsable);
-
-	if (xmlStrcmp (node->name, (xmlChar*) "title") == 0) {
-		/* atom:title */
-		xmlChar *title = xmlNodeListGetString (doc, node->children, TRUE);
-
-		/* Title can be empty */
-		self->priv->title = (title != NULL) ? (gchar*) title : g_strdup ("");
-	} else if (xmlStrcmp (node->name, (xmlChar*) "id") == 0) {
-		/* atom:id */
-		g_free (self->priv->id);
-		self->priv->id = (gchar*) xmlNodeListGetString (doc, node->children, TRUE);
-	} else if (xmlStrcmp (node->name, (xmlChar*) "updated") == 0) {
-		/* atom:updated */
-		xmlChar *updated;
-
-		updated = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) updated, &(self->priv->updated)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) updated, error);
-			xmlFree (updated);
-			return FALSE;
-		}
-		xmlFree (updated);
-	} else if (xmlStrcmp (node->name, (xmlChar*) "published") == 0) {
-		/* atom:published */
-		xmlChar *published;
-
-		published = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) published, &(self->priv->published)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) published, error);
-			xmlFree (published);
-			return FALSE;
-		}
-		xmlFree (published);
-	} else if (gdata_parser_object_from_element_setter (node, "category", P_REQUIRED, GDATA_TYPE_CATEGORY,
-	                                                    gdata_entry_add_category, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "link", P_REQUIRED, GDATA_TYPE_LINK,
-	                                                    gdata_entry_add_link, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "author", P_REQUIRED, GDATA_TYPE_AUTHOR,
-	                                                    gdata_entry_add_author, self, &success, error) == TRUE) {
+	if (gdata_parser_string_from_element (node, "title", P_DEFAULT, &(priv->title), &success, error) == TRUE ||
+	    gdata_parser_string_from_element (node, "id", P_REQUIRED | P_NON_EMPTY | P_NO_DUPES, &(priv->id), &success, error) == TRUE ||
+	    gdata_parser_string_from_element (node, "summary", P_NONE, &(priv->summary), &success, error) == TRUE ||
+	    gdata_parser_string_from_element (node, "rights", P_NONE, &(priv->rights), &success, error) == TRUE ||
+	    gdata_parser_time_val_from_element (node, "updated", P_REQUIRED | P_NO_DUPES, &(priv->updated), &success, error) == TRUE ||
+	    gdata_parser_time_val_from_element (node, "published", P_REQUIRED | P_NO_DUPES, &(priv->published), &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "category", P_REQUIRED, GDATA_TYPE_CATEGORY,
+	                                             gdata_entry_add_category, parsable, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "link", P_REQUIRED, GDATA_TYPE_LINK,
+	                                             gdata_entry_add_link, parsable, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "author", P_REQUIRED, GDATA_TYPE_AUTHOR,
+	                                             gdata_entry_add_author, parsable, &success, error) == TRUE) {
 		return success;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "content") == 0) {
 		/* atom:content */
 		xmlChar *content = xmlNodeListGetString (doc, node->children, TRUE);
 		if (content == NULL)
 			content = xmlGetProp (node, (xmlChar*) "src");
-		self->priv->content = (gchar*) content;
-	} else if (xmlStrcmp (node->name, (xmlChar*) "summary") == 0) {
-		/* atom:summary */
-		self->priv->summary = (gchar*) xmlNodeListGetString (doc, node->children, TRUE);
-	} else if (xmlStrcmp (node->name, (xmlChar*) "rights") == 0) {
-		/* atom:rights */
-		self->priv->rights = (gchar*) xmlNodeListGetString (doc, node->children, TRUE);
-	} else if (GDATA_PARSABLE_CLASS (gdata_entry_parent_class)->parse_xml (parsable, doc, node, user_data, error) == FALSE) {
-		/* Error! */
-		return FALSE;
+		priv->content = (gchar*) content;
+
+		return TRUE;
 	}
 
-	return TRUE;
+	return GDATA_PARSABLE_CLASS (gdata_entry_parent_class)->parse_xml (parsable, doc, node, user_data, error);
 }
 
 static gboolean
diff --git a/gdata/gdata-feed.c b/gdata/gdata-feed.c
index e0740c1..65f91cf 100644
--- a/gdata/gdata-feed.c
+++ b/gdata/gdata-feed.c
@@ -440,25 +440,10 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 	           gdata_parser_object_from_element_setter (node, "author", P_REQUIRED, GDATA_TYPE_AUTHOR,
 	                                                    _gdata_feed_add_author, self, &success, error) == TRUE ||
 	           gdata_parser_object_from_element (node, "generator", P_REQUIRED | P_NO_DUPES, GDATA_TYPE_GENERATOR,
-	                                             &(self->priv->generator), &success, error) == TRUE) {
+	                                             &(self->priv->generator), &success, error) == TRUE ||
+	           gdata_parser_time_val_from_element (node, "updated", P_REQUIRED | P_NO_DUPES, &(self->priv->updated), &success, error) == TRUE) {
 		return success;
 	/*TODO for atom:id: xmlStrcmp (node->ns->href, (xmlChar*) "http://www.w3.org/2005/Atom";) == 0) {*/
-	} else if (xmlStrcmp (node->name, (xmlChar*) "updated") == 0) {
-		/* atom:updated */
-		xmlChar *updated_string;
-
-		/* Duplicate checking */
-		if (self->priv->updated.tv_sec != 0 || self->priv->updated.tv_usec != 0)
-			return gdata_parser_error_duplicate_element (node, error);
-
-		/* Parse the string */
-		updated_string = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) updated_string, &(self->priv->updated)) == FALSE) {
-			gdata_parser_error_not_iso8601_format (node, (gchar*) updated_string, error);
-			xmlFree (updated_string);
-			return FALSE;
-		}
-		xmlFree (updated_string);
 	} else if (xmlStrcmp (node->name, (xmlChar*) "totalResults") == 0) {
 		/* openSearch:totalResults */
 		xmlChar *total_results_string;
diff --git a/gdata/gdata-parser.c b/gdata/gdata-parser.c
index 039d44e..3cb9cc4 100644
--- a/gdata/gdata-parser.c
+++ b/gdata/gdata-parser.c
@@ -300,6 +300,8 @@ gdata_parser_string_from_element (xmlNode *element, const gchar *element_name, G
 		xmlFree (text);
 		*success = gdata_parser_error_required_content_missing (element, error);
 		return TRUE;
+	} else if (options & P_DEFAULT && (text == NULL || *text == '\0')) {
+		text = (xmlChar*) g_strdup ("");
 	}
 
 	/* Success! */
@@ -311,6 +313,73 @@ gdata_parser_string_from_element (xmlNode *element, const gchar *element_name, G
 }
 
 /*
+ * gdata_parser_time_val_from_element:
+ * @element: the element to check against
+ * @element_name: the name of the element to parse
+ * @options: a bitwise combination of parsing options from #GDataParserOptions, or %P_NONE
+ * @output: the return location for the parsed time value
+ * @success: the return location for a value which is %TRUE if the time val was parsed successfully, %FALSE if an error was encountered,
+ * and undefined if @element didn't match @element_name
+ * @error: a #GError, or %NULL
+ *
+ * Gets the time value of @element if its name is @element_name, subject to various checks specified by @options. It expects the text content
+ * of @element to be a date or time value in ISO 8601 format.
+ *
+ * If @element doesn't match @element_name, %FALSE will be returned, @error will be unset and @success will be unset.
+ *
+ * If @element matches @element_name but one of the checks specified by @options fails, %TRUE will be returned, @error will be set to a
+ * %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error and @success will be set to %FALSE.
+ *
+ * If @element matches @element_name and all of the checks specified by @options pass, %TRUE will be returned, @error will be unset and
+ * @success will be set to %TRUE.
+ *
+ * The reason for returning the success of the parsing in @success is so that calls to gdata_parser_time_val_from_element() can be chained
+ * together in a large "or" statement based on their return values, for the purposes of determining whether any of the calls matched
+ * a given @element. If any of the calls to gdata_parser_time_val_from_element() return %TRUE, the value of @success can be examined.
+ *
+ * Return value: %TRUE if @element matched @element_name, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ */
+gboolean
+gdata_parser_time_val_from_element (xmlNode *element, const gchar *element_name, GDataParserOptions options,
+                                    GTimeVal *output, gboolean *success, GError **error)
+{
+	xmlChar *text;
+
+	/* Check it's the right element */
+	if (xmlStrcmp (element->name, (xmlChar*) element_name) != 0)
+		return FALSE;
+
+	/* Check if the output time val has already been set */
+	if (options & P_NO_DUPES && (output->tv_sec != 0 || output->tv_usec != 0)) {
+		*success = gdata_parser_error_duplicate_element (element, error);
+		return TRUE;
+	}
+
+	/* Get the string and check it for NULLness */
+	text = xmlNodeListGetString (element->doc, element->children, TRUE);
+	if (options & P_REQUIRED && text == NULL) {
+		xmlFree (text);
+		*success = gdata_parser_error_required_content_missing (element, error);
+		return TRUE;
+	}
+
+	/* Attempt to parse the string as a GTimeVal */
+	if (g_time_val_from_iso8601 ((gchar*) text, output) == FALSE) {
+		*success = gdata_parser_error_not_iso8601_format (element, (gchar*) text, error);
+		xmlFree (text);
+		return TRUE;
+	}
+
+	/* Success! */
+	xmlFree (text);
+	*success = TRUE;
+
+	return TRUE;
+}
+
+/*
  * gdata_parser_object_from_element_setter:
  * @element: the element to check against
  * @element_name: the name of the element to parse
diff --git a/gdata/gdata-parser.h b/gdata/gdata-parser.h
index c8d9692..7938eca 100644
--- a/gdata/gdata-parser.h
+++ b/gdata/gdata-parser.h
@@ -44,6 +44,8 @@ gchar *gdata_parser_date_from_time_val (const GTimeVal *_time) G_GNUC_WARN_UNUSE
  * @P_REQUIRED: the element content must not be %NULL if the element exists
  * @P_NON_EMPTY: the element content must not be empty (i.e. a zero-length string) if the element exists;
  * this only applies to gdata_parser_string_from_element()
+ * @P_DEFAULT: if the element content is %NULL or empty, return an empty value instead of erroring (this is mutually exclusive with %P_REQUIRED
+ * and %P_NON_EMPTY)
  *
  * Parsing options to be passed in a bitwise fashion to gdata_parser_string_from_element() or gdata_parser_object_from_element().
  * Their names aren't namespaced as they aren't public, and brevity is important, since they're used frequently in the parsing code.
@@ -54,7 +56,8 @@ typedef enum {
 	P_NONE = 0,
 	P_NO_DUPES = 1 << 0,
 	P_REQUIRED = 1 << 1,
-	P_NON_EMPTY = 1 << 2
+	P_NON_EMPTY = 1 << 2,
+	P_DEFAULT = 1 << 3
 } GDataParserOptions;
 
 typedef void (*GDataParserSetterFunc) (GDataParsable *parent_parsable, GDataParsable *parsable);
@@ -62,6 +65,8 @@ typedef void (*GDataParserSetterFunc) (GDataParsable *parent_parsable, GDataPars
 gboolean gdata_parser_boolean_from_property (xmlNode *element, const gchar *property_name, gboolean *output, gint default_output, GError **error);
 gboolean gdata_parser_string_from_element (xmlNode *element, const gchar *element_name, GDataParserOptions options,
                                            gchar **output, gboolean *success, GError **error);
+gboolean gdata_parser_time_val_from_element (xmlNode *element, const gchar *element_name, GDataParserOptions options,
+                                             GTimeVal *output, gboolean *success, GError **error);
 gboolean gdata_parser_object_from_element_setter (xmlNode *element, const gchar *element_name, GDataParserOptions options, GType object_type,
                                                   gpointer /* GDataParserSetterFunc */ _setter, gpointer /* GDataParsable * */ _parent_parsable,
                                                   gboolean *success, GError **error);
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index 68a4b7f..3fbf3dd 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -80,6 +80,7 @@ void _gdata_feed_call_progress_callback (GDataFeed *self, gpointer user_data, GD
 GFile *_gdata_documents_entry_download_document (GDataDocumentsEntry *self, GDataService *service, gchar **content_type, const gchar *download_uri,
 						 GFile *destination_directory, const gchar *file_extension, gboolean replace_file_if_exists,
 						 GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+void _gdata_documents_entry_init_edited (GDataDocumentsEntry *self);
 
 #include "gdata/services/documents/gdata-documents-service.h"
 
diff --git a/gdata/services/calendar/gdata-calendar-calendar.c b/gdata/services/calendar/gdata-calendar-calendar.c
index df92a2e..265e6a8 100644
--- a/gdata/services/calendar/gdata-calendar-calendar.c
+++ b/gdata/services/calendar/gdata-calendar-calendar.c
@@ -276,19 +276,20 @@ gdata_calendar_calendar_set_property (GObject *object, guint property_id, const
 GDataCalendarCalendar *
 gdata_calendar_calendar_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_CALENDAR_CALENDAR, "id", id, NULL);
+	GDataCalendarCalendar *calendar = GDATA_CALENDAR_CALENDAR (g_object_new (GDATA_TYPE_CALENDAR_CALENDAR, "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 (&(calendar->priv->edited));
+
+	return calendar;
 }
 
 static gboolean
 parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
 {
-	GDataCalendarCalendar *self;
-
-	g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (parsable), FALSE);
-	g_return_val_if_fail (doc != NULL, FALSE);
-	g_return_val_if_fail (node != NULL, FALSE);
-
-	self = GDATA_CALENDAR_CALENDAR (parsable);
+	gboolean success;
+	GDataCalendarCalendar *self = GDATA_CALENDAR_CALENDAR (parsable);
 
 	if (xmlStrcmp (node->name, (xmlChar*) "timezone") == 0) {
 		/* gCal:timezone */
@@ -340,16 +341,8 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 		self->priv->access_level = (gchar*) xmlGetProp (node, (xmlChar*) "value");
 		if (self->priv->access_level == NULL)
 			return gdata_parser_error_required_property_missing (node, "value", error);
-	} else if (xmlStrcmp (node->name, (xmlChar*) "edited") == 0) {
-		/* app:edited */
-		xmlChar *edited = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) edited, &(self->priv->edited)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) edited, error);
-			xmlFree (edited);
-			return FALSE;
-		}
-		xmlFree (edited);
+	} else if (gdata_parser_time_val_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE) {
+		return success;
 	} else if (GDATA_PARSABLE_CLASS (gdata_calendar_calendar_parent_class)->parse_xml (parsable, doc, node, user_data, error) == FALSE) {
 		/* Error! */
 		return FALSE;
diff --git a/gdata/services/calendar/gdata-calendar-event.c b/gdata/services/calendar/gdata-calendar-event.c
index 4c38582..8a73a9b 100644
--- a/gdata/services/calendar/gdata-calendar-event.c
+++ b/gdata/services/calendar/gdata-calendar-event.c
@@ -441,31 +441,29 @@ gdata_calendar_event_set_property (GObject *object, guint property_id, const GVa
 GDataCalendarEvent *
 gdata_calendar_event_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_CALENDAR_EVENT, "id", id, NULL);
+	GDataCalendarEvent *event = GDATA_CALENDAR_EVENT (g_object_new (GDATA_TYPE_CALENDAR_EVENT, "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 (&(event->priv->edited));
+
+	return event;
 }
 
 static gboolean
 parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
 {
 	gboolean success;
-	GDataCalendarEvent *self;
-
-	g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (parsable), FALSE);
-	g_return_val_if_fail (doc != NULL, FALSE);
-	g_return_val_if_fail (node != NULL, FALSE);
-
-	self = GDATA_CALENDAR_EVENT (parsable);
-
-	if (xmlStrcmp (node->name, (xmlChar*) "edited") == 0) {
-		/* app:edited */
-		xmlChar *edited = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) edited, &(self->priv->edited)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) edited, error);
-			xmlFree (edited);
-			return FALSE;
-		}
-		xmlFree (edited);
+	GDataCalendarEvent *self = GDATA_CALENDAR_EVENT (parsable);
+
+	if (gdata_parser_time_val_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "when", P_REQUIRED, GDATA_TYPE_GD_WHEN,
+	                                             gdata_calendar_event_add_time, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "who", P_REQUIRED, GDATA_TYPE_GD_WHO,
+	                                             gdata_calendar_event_add_person, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "where", P_REQUIRED, GDATA_TYPE_GD_WHERE,
+	                                             gdata_calendar_event_add_place, self, &success, error) == TRUE) {
+		return success;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "comments") == 0) {
 		/* gd:comments */
 		xmlChar *rel, *href, *count_hint;
@@ -529,13 +527,6 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 		xmlFree (value);
 
 		gdata_calendar_event_set_sequence (self, value_uint);
-	} else if (gdata_parser_object_from_element_setter (node, "when", P_REQUIRED, GDATA_TYPE_GD_WHEN,
-	                                                    gdata_calendar_event_add_time, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "who", P_REQUIRED, GDATA_TYPE_GD_WHO,
-	                                                    gdata_calendar_event_add_person, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "where", P_REQUIRED, GDATA_TYPE_GD_WHERE,
-	                                                    gdata_calendar_event_add_place, self, &success, error) == TRUE) {
-		return success;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "guestsCanModify") == 0) {
 		/* gCal:guestsCanModify */
 		gboolean guests_can_modify;
diff --git a/gdata/services/contacts/gdata-contacts-contact.c b/gdata/services/contacts/gdata-contacts-contact.c
index 7e71a98..c14cbd4 100644
--- a/gdata/services/contacts/gdata-contacts-contact.c
+++ b/gdata/services/contacts/gdata-contacts-contact.c
@@ -189,9 +189,6 @@ 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);
 
-	/* Set the edited property to the current time (creation time) */
-	g_get_current_time (&(self->priv->edited));
-
 	/* 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);
@@ -284,43 +281,33 @@ gdata_contacts_contact_set_property (GObject *object, guint property_id, const G
 GDataContactsContact *
 gdata_contacts_contact_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_CONTACTS_CONTACT, "id", id, NULL);
+	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)
 {
 	gboolean success;
-	GDataContactsContact *self;
-
-	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (parsable), FALSE);
-	g_return_val_if_fail (doc != NULL, FALSE);
-	g_return_val_if_fail (node != NULL, FALSE);
-
-	self = GDATA_CONTACTS_CONTACT (parsable);
-
-	if (xmlStrcmp (node->name, (xmlChar*) "edited") == 0) {
-		/* app:edited */
-		/* TODO: Should be in GDataEntry? */
-		xmlChar *edited = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) edited, &(self->priv->edited)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) edited, error);
-			g_free (edited);
-			return FALSE;
-		}
-		g_free (edited);
-	} else if (gdata_parser_object_from_element_setter (node, "email", P_REQUIRED, GDATA_TYPE_GD_EMAIL_ADDRESS,
-	                                                    gdata_contacts_contact_add_email_address, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "im", P_REQUIRED, GDATA_TYPE_GD_IM_ADDRESS,
-	                                                    gdata_contacts_contact_add_im_address, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "phoneNumber", P_REQUIRED, GDATA_TYPE_GD_PHONE_NUMBER,
-	                                                    gdata_contacts_contact_add_phone_number, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "structuredPostalAddress", P_REQUIRED, GDATA_TYPE_GD_POSTAL_ADDRESS,
-	                                                    gdata_contacts_contact_add_postal_address, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element_setter (node, "organization", P_REQUIRED, GDATA_TYPE_GD_ORGANIZATION,
-	                                                    gdata_contacts_contact_add_organization, self, &success, error) == TRUE ||
-	           gdata_parser_object_from_element (node, "name", P_REQUIRED, GDATA_TYPE_GD_NAME, &(self->priv->name), &success, error) == TRUE) {
+	GDataContactsContact *self = GDATA_CONTACTS_CONTACT (parsable);
+
+	if (gdata_parser_time_val_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "email", P_REQUIRED, GDATA_TYPE_GD_EMAIL_ADDRESS,
+	                                             gdata_contacts_contact_add_email_address, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "im", P_REQUIRED, GDATA_TYPE_GD_IM_ADDRESS,
+	                                             gdata_contacts_contact_add_im_address, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "phoneNumber", P_REQUIRED, GDATA_TYPE_GD_PHONE_NUMBER,
+	                                             gdata_contacts_contact_add_phone_number, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "structuredPostalAddress", P_REQUIRED, GDATA_TYPE_GD_POSTAL_ADDRESS,
+	                                             gdata_contacts_contact_add_postal_address, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "organization", P_REQUIRED, GDATA_TYPE_GD_ORGANIZATION,
+	                                             gdata_contacts_contact_add_organization, self, &success, error) == TRUE ||
+	    gdata_parser_object_from_element (node, "name", P_REQUIRED, GDATA_TYPE_GD_NAME, &(self->priv->name), &success, error) == TRUE) {
 		return success;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "extendedProperty") == 0) {
 		/* gd:extendedProperty */
diff --git a/gdata/services/documents/gdata-documents-entry.c b/gdata/services/documents/gdata-documents-entry.c
index e31467f..5833ca5 100644
--- a/gdata/services/documents/gdata-documents-entry.c
+++ b/gdata/services/documents/gdata-documents-entry.c
@@ -181,8 +181,22 @@ static void
 gdata_documents_entry_init (GDataDocumentsEntry *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_DOCUMENTS_ENTRY, GDataDocumentsEntryPrivate);
+}
 
-	/* Initialise the edited properties to the current time */
+/*
+ * _gdata_documents_entry_init_edited:
+ * @self: a #GDataDocumentsEntry
+ *
+ * Initialises the #GDataDocumentsEntry:edited property of @self to the current time. This is designed
+ * to be called from the public constructors of classes which derive #GDataDocumentsEntry; it can't be
+ * put in the init function of #GDataDocumentsEntry, as it would then be called even for entries parsed
+ * from XML from the server, which would break duplicate element detection for the app:edited element.
+ *
+ * Since: 0.7.0
+ */
+void
+_gdata_documents_entry_init_edited (GDataDocumentsEntry *self)
+{
 	g_get_current_time (&(self->priv->edited));
 }
 
@@ -204,22 +218,13 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 	gboolean success;
 	GDataDocumentsEntry *self = GDATA_DOCUMENTS_ENTRY (parsable);
 
-	if (xmlStrcmp (node->name, (xmlChar*) "edited") == 0) {
-		xmlChar *edited = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) edited, &(self->priv->edited)) == FALSE) {
-			gdata_parser_error_not_iso8601_format (node, (gchar*) edited, error);
-			xmlFree (edited);
-			return FALSE;
-		}
-		xmlFree (edited);
-	} else if (xmlStrcmp (node->name, (xmlChar*) "lastViewed") == 0) {
-		xmlChar *last_viewed = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) last_viewed, &(self->priv->last_viewed)) == FALSE) {
-			gdata_parser_error_not_iso8601_format (node, (gchar*) last_viewed, error);
-			xmlFree (last_viewed);
-			return FALSE;
-		}
-		xmlFree (last_viewed);
+	if (gdata_parser_time_val_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE ||
+	    gdata_parser_time_val_from_element (node, "lastViewed", P_REQUIRED | P_NO_DUPES, &(self->priv->last_viewed), &success, error) == TRUE ||
+	    gdata_parser_object_from_element_setter (node, "feedLink", P_REQUIRED, GDATA_TYPE_LINK,
+	                                             gdata_entry_add_link, self,  &success, error) == TRUE ||
+	    gdata_parser_object_from_element (node, "lastModifiedBy", P_REQUIRED, GDATA_TYPE_AUTHOR,
+	                                      &(self->priv->last_modified_by), &success, error) == TRUE) {
+		return success;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "writersCanInvite") ==  0) {
 		if (gdata_parser_boolean_from_property (node, "value", &(self->priv->writers_can_invite), -1, error) == FALSE)
 			return FALSE;
@@ -250,11 +255,6 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 
 		self->priv->document_id = g_strdup (document_id_parts[1]);
 		g_strfreev (document_id_parts);
-	} else if (gdata_parser_object_from_element_setter (node, "feedLink", P_REQUIRED, GDATA_TYPE_LINK,
-	                                                    gdata_entry_add_link, self,  &success, error) == TRUE ||
-	           gdata_parser_object_from_element (node, "lastModifiedBy", P_REQUIRED, GDATA_TYPE_AUTHOR,
-	                                             &(self->priv->last_modified_by), &success, error) == TRUE) {
-		return success;
 	} else if (GDATA_PARSABLE_CLASS (gdata_documents_entry_parent_class)->parse_xml (parsable, doc, node, user_data, error) == FALSE) {
 		/* Error! */
 		return FALSE;
diff --git a/gdata/services/documents/gdata-documents-folder.c b/gdata/services/documents/gdata-documents-folder.c
index 3a87bd9..377faf7 100644
--- a/gdata/services/documents/gdata-documents-folder.c
+++ b/gdata/services/documents/gdata-documents-folder.c
@@ -68,7 +68,13 @@ gdata_documents_folder_class_init (GDataDocumentsFolderClass *klass)
 GDataDocumentsFolder *
 gdata_documents_folder_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_DOCUMENTS_FOLDER, "id", id, NULL);
+	GDataDocumentsFolder *folder = GDATA_DOCUMENTS_FOLDER (g_object_new (GDATA_TYPE_DOCUMENTS_FOLDER, "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). */
+	_gdata_documents_entry_init_edited (GDATA_DOCUMENTS_ENTRY (folder));
+
+	return folder;
 }
 
 static void
diff --git a/gdata/services/documents/gdata-documents-presentation.c b/gdata/services/documents/gdata-documents-presentation.c
index 2239578..a8b85cd 100644
--- a/gdata/services/documents/gdata-documents-presentation.c
+++ b/gdata/services/documents/gdata-documents-presentation.c
@@ -96,7 +96,13 @@ get_xml (GDataParsable *parsable, GString *xml_string)
 GDataDocumentsPresentation *
 gdata_documents_presentation_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_DOCUMENTS_PRESENTATION, "id", id, NULL);
+	GDataDocumentsPresentation *presentation = GDATA_DOCUMENTS_PRESENTATION (g_object_new (GDATA_TYPE_DOCUMENTS_PRESENTATION, "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). */
+	_gdata_documents_entry_init_edited (GDATA_DOCUMENTS_ENTRY (presentation));
+
+	return presentation;
 }
 
 /**
diff --git a/gdata/services/documents/gdata-documents-spreadsheet.c b/gdata/services/documents/gdata-documents-spreadsheet.c
index b44434d..6458608 100644
--- a/gdata/services/documents/gdata-documents-spreadsheet.c
+++ b/gdata/services/documents/gdata-documents-spreadsheet.c
@@ -96,7 +96,13 @@ get_xml (GDataParsable *parsable, GString *xml_string)
 GDataDocumentsSpreadsheet *
 gdata_documents_spreadsheet_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_DOCUMENTS_SPREADSHEET, "id", id, NULL);
+	GDataDocumentsSpreadsheet *spreadsheet = GDATA_DOCUMENTS_SPREADSHEET (g_object_new (GDATA_TYPE_DOCUMENTS_SPREADSHEET, "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). */
+	_gdata_documents_entry_init_edited (GDATA_DOCUMENTS_ENTRY (spreadsheet));
+
+	return spreadsheet;
 }
 
 /**
diff --git a/gdata/services/documents/gdata-documents-text.c b/gdata/services/documents/gdata-documents-text.c
index da42d2f..1c82753 100644
--- a/gdata/services/documents/gdata-documents-text.c
+++ b/gdata/services/documents/gdata-documents-text.c
@@ -98,7 +98,13 @@ get_xml (GDataParsable *parsable, GString *xml_string)
 GDataDocumentsText *
 gdata_documents_text_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_DOCUMENTS_TEXT, "id", id, NULL);
+	GDataDocumentsText *text = GDATA_DOCUMENTS_TEXT (g_object_new (GDATA_TYPE_DOCUMENTS_TEXT, "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). */
+	_gdata_documents_entry_init_edited (GDATA_DOCUMENTS_ENTRY (text));
+
+	return text;
 }
 
 /**
diff --git a/gdata/services/picasaweb/gdata-picasaweb-album.c b/gdata/services/picasaweb/gdata-picasaweb-album.c
index 96341bf..a06d64f 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-album.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-album.c
@@ -407,10 +407,6 @@ gdata_picasaweb_album_init (GDataPicasaWebAlbum *self)
 	self->priv->georss_where = g_object_new (GDATA_TYPE_GEORSS_WHERE, NULL);
 	self->priv->visibility = GDATA_PICASAWEB_PRIVATE;
 
-	/* Initialise the timestamp and edited properties to the current time (bgo#599140) */
-	g_get_current_time (&(self->priv->timestamp));
-	g_get_current_time (&(self->priv->edited));
-
 	/* Connect to the notify::title signal from GDataEntry so our media:group title can be kept in sync
 	 * (the title of an album is duplicated in atom:title and media:group/media:title) */
 	g_signal_connect (GDATA_ENTRY (self), "notify::title", G_CALLBACK (notify_title_cb), NULL);
@@ -558,18 +554,9 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 	                                      &(self->priv->georss_where), &success, error) == TRUE ||
 	    gdata_parser_string_from_element (node, "user", P_REQUIRED | P_NON_EMPTY, &(self->priv->user), &success, error) == TRUE ||
 	    gdata_parser_string_from_element (node, "nickname", P_REQUIRED | P_NON_EMPTY, &(self->priv->nickname), &success, error) == TRUE ||
-	    gdata_parser_string_from_element (node, "location", P_NONE, &(self->priv->location), &success, error) == TRUE) {
+	    gdata_parser_string_from_element (node, "location", P_NONE, &(self->priv->location), &success, error) == TRUE ||
+	    gdata_parser_time_val_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE) {
 		return success;
-	} else if (xmlStrcmp (node->name, (xmlChar*) "edited") == 0) {
-		/* app:edited */
-		xmlChar *edited = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) edited, &(self->priv->edited)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) edited, error);
-			xmlFree (edited);
-			return FALSE;
-		}
-		xmlFree (edited);
 	} else if (xmlStrcmp (node->name, (xmlChar*) "access") == 0) {
 		/* gphoto:access */
 		xmlChar *access = xmlNodeListGetString (doc, node->children, TRUE);
@@ -736,7 +723,14 @@ get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
 GDataPicasaWebAlbum *
 gdata_picasaweb_album_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_PICASAWEB_ALBUM, "id", id, NULL);
+	GDataPicasaWebAlbum *album = GDATA_PICASAWEB_ALBUM (g_object_new (GDATA_TYPE_PICASAWEB_ALBUM, "id", id, NULL));
+
+	/* Set the edited and timestamp properties to the current time (creation time). bgo#599140
+	 * We don't do this in *_init() since that would cause setting it from parse_xml() to fail (duplicate element). */
+	g_get_current_time (&(album->priv->timestamp));
+	g_get_current_time (&(album->priv->edited));
+
+	return album;
 }
 
 /**
diff --git a/gdata/services/picasaweb/gdata-picasaweb-file.c b/gdata/services/picasaweb/gdata-picasaweb-file.c
index 9cea5a1..851551a 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-file.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-file.c
@@ -592,10 +592,6 @@ gdata_picasaweb_file_init (GDataPicasaWebFile *self)
 	self->priv->georss_where = g_object_new (GDATA_TYPE_GEORSS_WHERE, NULL);
 	self->priv->is_commenting_enabled = TRUE;
 
-	/* Initialise the timestamp and edited properties to the current time (bgo#599140) */
-	g_get_current_time (&(self->priv->timestamp));
-	g_get_current_time (&(self->priv->edited));
-
 	/* We need to keep atom:title (the canonical title for the file) in sync with media:group/media:title */
 	g_signal_connect (self, "notify::title", G_CALLBACK (notify_title_cb), NULL);
 	/* atom:summary (the canonical summary/caption for the file) in sync with media:group/media:description */
@@ -808,18 +804,9 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 	    gdata_parser_string_from_element (node, "imageVersion", P_NONE, &(self->priv->version), &success, error) == TRUE ||
 	    gdata_parser_string_from_element (node, "albumid", P_NONE, &(self->priv->album_id), &success, error) == TRUE ||
 	    gdata_parser_string_from_element (node, "client", P_NONE, &(self->priv->client), &success, error) == TRUE ||
-	    gdata_parser_string_from_element (node, "checksum", P_NONE, &(self->priv->client), &success, error) == TRUE) {
+	    gdata_parser_string_from_element (node, "checksum", P_NONE, &(self->priv->client), &success, error) == TRUE ||
+	    gdata_parser_time_val_from_element (node, "edited", P_REQUIRED | P_NO_DUPES, &(self->priv->edited), &success, error) == TRUE) {
 		return success;
-	} else if (xmlStrcmp (node->name, (xmlChar*) "edited") == 0) {
-		/* app:edited */
-		xmlChar *edited = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) edited, &(self->priv->edited)) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) edited, error);
-			xmlFree (edited);
-			return FALSE;
-		}
-		xmlFree (edited);
 	} else if (xmlStrcmp (node->name, (xmlChar*) "position") == 0) {
 		/* gphoto:position */
 		xmlChar *position_str = xmlNodeListGetString (doc, node->children, TRUE);
@@ -971,7 +958,14 @@ get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
 GDataPicasaWebFile *
 gdata_picasaweb_file_new (const gchar *id)
 {
-	return g_object_new (GDATA_TYPE_PICASAWEB_FILE, "id", id, NULL);
+	GDataPicasaWebFile *file = GDATA_PICASAWEB_FILE (g_object_new (GDATA_TYPE_PICASAWEB_FILE, "id", id, NULL));
+
+	/* Set the edited and timestamp properties to the current time (creation time). bgo#599140
+	 * We don't do this in *_init() since that would cause setting it from parse_xml() to fail (duplicate element). */
+	g_get_current_time (&(file->priv->timestamp));
+	g_get_current_time (&(file->priv->edited));
+
+	return file;
 }
 
 /**
diff --git a/gdata/services/youtube/gdata-youtube-group.c b/gdata/services/youtube/gdata-youtube-group.c
index 208195a..1b431a7 100644
--- a/gdata/services/youtube/gdata-youtube-group.c
+++ b/gdata/services/youtube/gdata-youtube-group.c
@@ -95,7 +95,8 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 
 	if (gdata_parser_object_from_element_setter (node, "content", P_REQUIRED, GDATA_TYPE_YOUTUBE_CONTENT,
 	                                             _gdata_media_group_add_content, self, &success, error) == TRUE ||
-	    gdata_parser_string_from_element (node, "videoid", P_NO_DUPES, &(self->priv->video_id), &success, error) == TRUE) {
+	    gdata_parser_string_from_element (node, "videoid", P_NO_DUPES, &(self->priv->video_id), &success, error) == TRUE ||
+	    gdata_parser_time_val_from_element (node, "uploaded", P_REQUIRED | P_NO_DUPES, &(self->priv->uploaded), &success, error) == TRUE) {
 		return success;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "credit") == 0) {
 		/* media:credit */
@@ -121,21 +122,6 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 	} else if (xmlStrcmp (node->name, (xmlChar*) "private") == 0) {
 		/* yt:private */
 		gdata_youtube_group_set_is_private (self, TRUE);
-	} else if (xmlStrcmp (node->name, (xmlChar*) "uploaded") == 0) {
-		/* yt:uploaded */
-		xmlChar *uploaded;
-		GTimeVal uploaded_timeval;
-
-		uploaded = xmlNodeListGetString (doc, node->children, TRUE);
-		if (g_time_val_from_iso8601 ((gchar*) uploaded, &uploaded_timeval) == FALSE) {
-			/* Error */
-			gdata_parser_error_not_iso8601_format (node, (gchar*) uploaded, error);
-			xmlFree (uploaded);
-			return FALSE;
-		}
-		xmlFree (uploaded);
-
-		self->priv->uploaded = uploaded_timeval;
 	} else if (xmlStrcmp (node->name, (xmlChar*) "aspectRatio") == 0) {
 		/* yt:aspectRatio */
 		xmlChar *aspect_ratio = xmlNodeGetContent (node);



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