/* sml_data_sync_change_item.c
 *
 * Copyright (C) 2009 Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 */

#include "sml_data_sync_change_item_internals.h"
#include "sml_data_sync_enum_types.h"
#include "../sml_error_internals.h"
#include <string.h>

G_DEFINE_TYPE (SmlDataSyncChangeItem, sml_data_sync_change_item, G_TYPE_OBJECT)

enum
{
	PROP_0,
	PROP_TYPE,
	PROP_LOCATION,
	PROP_DATA,
	PROP_PLANNED_SIZE,
	PROP_CONTENT_TYPE,
	PROP_MISSING_DATA,
	PROP_DATA_STORE,
	PROP_USERDATA
};

struct _SmlDataSyncChangeItemPrivate
{
	SmlChangeType         type;
	SmlLocation*          loc;
	gchar*                data;
	guint64               planned_size;
	guint64               removed_chars;
	gchar*                content_type;
	gboolean              missing_data;
	SmlDataSyncDataStoreSession* data_store;
	void*                 userdata;
};

static void
sml_data_sync_change_item_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
	switch (property_id) {
	case PROP_TYPE:
		g_value_set_enum (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->type);
		break;
	case PROP_LOCATION:
		g_value_set_object (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->loc);
		break;
	case PROP_DATA:
		g_value_set_string (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->data);
		break;
	case PROP_PLANNED_SIZE:
		g_value_set_uint64 (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->planned_size);
		break;
	case PROP_CONTENT_TYPE:
		g_value_set_string (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->content_type);
		break;
	case PROP_MISSING_DATA:
		g_value_set_boolean (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->missing_data);
		break;
	case PROP_DATA_STORE:
		g_value_set_pointer (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->data_store);
		break;
	case PROP_USERDATA:
		g_value_set_pointer (value, SML_DATA_SYNC_CHANGE_ITEM (object)->priv->userdata);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_data_sync_change_item_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
	switch (property_id) {
	case PROP_TYPE:
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->type = g_value_get_enum (value);
		break;
	case PROP_LOCATION:
        	if (SML_DATA_SYNC_CHANGE_ITEM (object)->priv->loc)
        		g_object_unref (SML_DATA_SYNC_CHANGE_ITEM (object)->priv->loc);
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->loc = SML_LOCATION (value);
		g_object_ref(SML_DATA_SYNC_CHANGE_ITEM (object)->priv->loc);
		break;
	case PROP_DATA:
		g_free (SML_DATA_SYNC_CHANGE_ITEM (object)->priv->data);
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->data = g_strdup (g_value_get_string (value));
		break;
	case PROP_PLANNED_SIZE:
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->planned_size = g_value_get_uint64 (value);
		break;
	case PROP_CONTENT_TYPE:
		g_free (SML_DATA_SYNC_CHANGE_ITEM (object)->priv->content_type);
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->content_type = g_strdup (g_value_get_string (value));
		break;
	case PROP_MISSING_DATA:
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->missing_data = g_value_get_boolean (value);
		break;
	case PROP_DATA_STORE:
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->data_store = (SmlDataSyncDataStoreSession *) g_value_get_pointer(value);
		break;
	case PROP_USERDATA:
		SML_DATA_SYNC_CHANGE_ITEM (object)->priv->userdata = (void *) g_value_get_pointer(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_data_sync_change_item_finalize (GObject *object)
{
	SmlDataSyncChangeItem *self = (SmlDataSyncChangeItem *) object;
	if (self->priv->loc)
		g_object_unref(self->priv->loc);
	g_free(self->priv->data);
	g_free(self->priv->content_type);
	/* all pointers must be NULL */
	self->priv->loc = NULL;
	self->priv->data = NULL;
	self->priv->planned_size = 0;
	self->priv->removed_chars = 0;
	self->priv->content_type = NULL;
	self->priv->data_store = NULL;
	self->priv->userdata = NULL;
	G_OBJECT_CLASS (sml_data_sync_change_item_parent_class)->finalize (object);
}

static void
sml_data_sync_change_item_class_init (SmlDataSyncChangeItemClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	g_type_class_add_private (klass, sizeof (SmlDataSyncChangeItemPrivate));

	object_class->get_property = sml_data_sync_change_item_get_property;
	object_class->set_property = sml_data_sync_change_item_set_property;
	object_class->finalize     = sml_data_sync_change_item_finalize;

	/**
	 * SmlDataSyncChangeItem:type:
	 *
	 * The This is the the kind of change. property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_TYPE,
	                                 g_param_spec_enum ("type",
	                                                    "type",
	                                                    "This is the kind of change.",
	                                                    SML_DATA_SYNC_TYPE_CHANGE_TYPE,
	                                                    SML_CHANGE_UNKNOWN,
	                                                    G_PARAM_READWRITE));
	/**
	 * SmlDataSyncChangeItem:location:
	 *
	 * The location property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_LOCATION,
	                                 g_param_spec_object ("location",
	                                                      "location",
	                                                      "location",
	                                                      G_TYPE_OBJECT,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncChangeItem:data:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_DATA,
	                                 g_param_spec_string ("data",
	                                                      "data",
	                                                      "",
	                                                      NULL,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncChangeItem:planned_size:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_PLANNED_SIZE,
	                                 g_param_spec_uint64 ("planned_size",
	                                                      "planned size",
	                                                      "",
	                                                      0,
	                                                      G_MAXUINT64,
	                                                      0,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncChangeItem:content_type:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_CONTENT_TYPE,
	                                 g_param_spec_string ("content_type",
	                                                      "content-type",
	                                                      "",
	                                                      NULL,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncChangeItem:missing_data:
	 *
	 * The MoreData flag property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_MISSING_DATA,
	                                 g_param_spec_boolean ("missing_data",
	                                                       "MoreData flag",
	                                                       "MoreData flag",
	                                                       FALSE,
	                                                       G_PARAM_READWRITE));
	/**
	 * SmlDataSyncChangeItem:data_store:
	 *
	 * The data store property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_DATA_STORE,
	                                 g_param_spec_pointer ("data_store",
	                                                       "data store",
	                                                       "the used data store",
	                                                       G_PARAM_READWRITE));

}

static void
sml_data_sync_change_item_init (SmlDataSyncChangeItem *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
	                                          SML_TYPE_DATA_SYNC_CHANGE_ITEM,
	                                          SmlDataSyncChangeItemPrivate);
}

/**
 * sml_data_sync_change_item_new:
 *
 * Creates a new instance of #SmlDataSyncChangeItem.
 *
 * Return value: the newly created #SmlDataSyncChangeItem instance
 */
SmlDataSyncChangeItem*
sml_data_sync_change_item_new (void)
{
	return g_object_new (SML_TYPE_DATA_SYNC_CHANGE_ITEM, NULL);
}

/**
 * sml_data_sync_change_item_get_location:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the location property.
 *
 * Return value: 
 */
SmlLocation*
sml_data_sync_change_item_get_location (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), NULL);
	return self->priv->loc;
}

/**
 * sml_data_sync_change_item_set_location:
 * @self: A #SmlDataSyncChangeItem
 * @loc:
 *
 * Sets the location property.
 */
gboolean
sml_data_sync_change_item_set_location (SmlDataSyncChangeItem *self,
                              SmlLocation* loc,
                              GError **error)
{
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (!loc || sml_location_get_uri(loc), FALSE, error, SML_ERROR_GENERIC, "The location must have an URI.");

	if (self->priv->loc)
		g_object_unref (self->priv->loc);
	self->priv->loc = loc;
	if (loc)
		g_object_ref (loc);
	return TRUE;
}

/**
 * sml_data_sync_change_item_get_data:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the data property.
 *
 * Return value: 
 */
G_CONST_RETURN gchar*
sml_data_sync_change_item_get_data (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), NULL);
	return self->priv->data;
}

/**
 * sml_data_sync_change_item_set_data:
 * @self: A #SmlDataSyncChangeItem
 * @data:
 * @size:
 *
 * Sets the data property.
 */
gboolean
sml_data_sync_change_item_set_data (SmlDataSyncChangeItem *self,
                          const gchar* data,
                          gsize size,
                          GError **error)
{
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (data != NULL, FALSE, error, SML_ERROR_GENERIC, "The data must be different from NULL.");
	sml_return_val_error_if_fail (strlen(data), FALSE, error, SML_ERROR_GENERIC, "The data must not be the empty word.");

	/* if the size is not set then we copy the whole string */

	if (!size)
		size = strlen(data);

	g_free (self->priv->data);
	self->priv->data = g_strndup(data, size);

	/* Carriage Return and Newlines must be normalized
	 * because XML / WBXML conversion fails otherwise.
	 * This is an expat issue inside of libwbxml.
	 */

	gsize i = 0;
	for (; i < strlen(self->priv->data); i++)
	{
		if (self->priv->data[i] == '\r')
		{
			if (i+1 < strlen(self->priv->data) &&
			    self->priv->data[i+1] == '\n')
			{
				/* \r\n => \n */
				self->priv->data[i] = '\0';
				gchar *help = g_strjoin(NULL, self->priv->data, self->priv->data + i + 1, NULL);
				g_free(self->priv->data);
				self->priv->data = help;
				self->priv->removed_chars++;
			} else {
				/* \r => \n */
				self->priv->data[i] = '\n';
			}
		}
	}

	sml_return_val_error_if_fail (self->priv->data, FALSE, error, SML_ERROR_GENERIC, "Cannot copy the data - out of memory.");

	return TRUE;
}

/**
 * sml_data_sync_change_item_get_binary_data:
 * @self: A #SmlDataSyncChangeItem
 * @data: A pointer of a #gchar pointer
 * @size: A pointer of #gsize
 *
 * Gets the data property.
 *
 * Return value: 
 */
gboolean
sml_data_sync_change_item_get_binary_data (SmlDataSyncChangeItem *self,
                                           gchar **data,
                                           gsize *size,
                                           GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (data, FALSE, error, SML_ERROR_GENERIC, "The data pointer must be present.");
	sml_return_val_error_if_fail (*data == NULL, FALSE, error, SML_ERROR_GENERIC, "The data pointer must be empty.");
	sml_return_val_error_if_fail (size, FALSE, error, SML_ERROR_GENERIC, "The size pointer must be present.");
	sml_return_val_error_if_fail (*size == 0, FALSE, error, SML_ERROR_GENERIC, "The size pointer must be 0.");

	/* check if there is something to convert */
	if (self->priv->data == NULL ||
	    strlen(self->priv->data) == 0) {
		*data = NULL;
		*size = 0;
		return TRUE;
	}
	*data = (gchar *) g_base64_decode(self->priv->data, size);
	sml_return_val_error_if_fail (size != 0, FALSE, error, SML_ERROR_GENERIC, "The base64 decoding failed.");
	
	return TRUE;
}

/**
 * sml_data_sync_change_item_set_binary_data:
 * @self: A #SmlDataSyncChangeItem
 * @data:
 * @size:
 *
 * Sets the data property.
 */
gboolean
sml_data_sync_change_item_set_binary_data (SmlDataSyncChangeItem *self,
                                 const gchar* data,
                                 gsize size,
                                 GError **error)
{
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (size, FALSE, error, SML_ERROR_GENERIC, "The size must be present.");

	g_free (self->priv->data);
	self->priv->data = g_base64_encode((const unsigned char *) data, size);
	if (!self->priv->data)
		g_error("The base 64 encoding of glib failed.");

	return TRUE;
}

/**
 * sml_data_sync_change_item_get_planned_size:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the planned size property.
 *
 * Return value: 
 */
gsize
sml_data_sync_change_item_get_planned_size (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), 0);
	return self->priv->planned_size - self->priv->removed_chars;
}

/**
 * sml_data_sync_change_item_set_planned_size:
 * @self: A #SmlDataSyncChangeItem
 * @planned_size:
 *
 * Sets the planned size property.
 */
void
sml_data_sync_change_item_set_planned_size (SmlDataSyncChangeItem *self,
                                  gsize planned_size)
{
	g_return_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self));
	self->priv->planned_size = planned_size;
}

/**
 * sml_data_sync_change_item_get_content_type:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the content-type property.
 *
 * Return value: 
 */
G_CONST_RETURN gchar*
sml_data_sync_change_item_get_content_type (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), NULL);
	return self->priv->content_type;
}

/**
 * sml_data_sync_change_item_set_content_type:
 * @self: A #SmlDataSyncChangeItem
 * @content_type:
 *
 * Sets the content-type property.
 */
gboolean
sml_data_sync_change_item_set_content_type (SmlDataSyncChangeItem *self,
                                  const gchar* content_type,
                                  GError **error)
{
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (content_type != NULL, FALSE, error, SML_ERROR_GENERIC, "The content-type must be different from NULL.");
	sml_return_val_error_if_fail (strlen(content_type), FALSE, error, SML_ERROR_GENERIC, "The content-type must be different from the empty word.");

	g_free (self->priv->content_type);
	self->priv->content_type = g_strdup (content_type);

	sml_return_val_error_if_fail (self->priv->content_type, FALSE, error, SML_ERROR_GENERIC, "Cannot copy the content-type - out of memory.");

	return TRUE;
}

/**
 * sml_data_sync_change_item_get_missing_data:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the MoreData flag property.
 *
 * Return value: 
 */
gboolean
sml_data_sync_change_item_get_missing_data (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE);
	return self->priv->missing_data;
}

/**
 * sml_data_sync_change_item_set_missing_data:
 * @self: A #SmlDataSyncChangeItem
 * @missing_data:
 *
 * Sets the MoreData flag property.
 */
void
sml_data_sync_change_item_set_missing_data (SmlDataSyncChangeItem *self,
                                  gboolean missing_data)
{
	g_return_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self));
	self->priv->missing_data = missing_data;
}

/**
 * sml_data_sync_change_item_get_action:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the change type property.
 *
 * Return value: 
 */
SmlChangeType
sml_data_sync_change_item_get_action (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), SML_CHANGE_UNKNOWN);
	return self->priv->type;
}

/**
 * sml_data_sync_change_item_set_action:
 * @self: A #SmlDataSyncChangeItem
 * @missing_data:
 *
 * Sets the change type property.
 */
void
sml_data_sync_change_item_set_action (SmlDataSyncChangeItem *self,
                            SmlChangeType type)
{
	g_return_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self));
	self->priv->type = type;
}

/**
 * sml_data_sync_change_item_get_data_store_session:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the data store property.
 *
 * Return value: 
 */
SmlDataSyncDataStoreSession*
sml_data_sync_change_item_get_data_store_session (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE);
	return self->priv->data_store;
}

/**
 * sml_data_sync_change_item_set_data_store_session:
 * @self: A #SmlDataSyncChangeItem
 * @ds:
 *
 * Sets the change type property.
 */
void
sml_data_sync_change_item_set_data_store_session (SmlDataSyncChangeItem *self,
                                                  SmlDataSyncDataStoreSession *ds)
{
	g_return_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self));
	self->priv->data_store = ds;
}

/**
 * sml_data_sync_change_item_get_userdata:
 * @self: A #SmlDataSyncChangeItem
 *
 * Gets the userdata property.
 *
 * Return value: 
 */
void*
sml_data_sync_change_item_get_userdata (SmlDataSyncChangeItem *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE);
	return self->priv->userdata;
}

/**
 * sml_data_sync_change_item_set_userdata:
 * @self: A #SmlDataSyncChangeItem
 * @userdata:
 *
 * Sets the userdata property.
 */
void
sml_data_sync_change_item_set_userdata (SmlDataSyncChangeItem *self,
                              void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self));
	self->priv->userdata = userdata;
}

/**
 * sml_data_sync_change_item_get_fragment:
 * @self: A #SmlDataSyncChangeItem
 *
 * 
 */
SmlDataSyncChangeItem*
sml_data_sync_change_item_get_fragment (SmlDataSyncChangeItem *self,
                              gsize start,
                              gsize max_size,
                              GError **error)
{
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), NULL, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (self->priv->data, NULL, error, SML_ERROR_GENERIC, "There must be some data.");
	sml_return_val_error_if_fail (max_size, NULL, error, SML_ERROR_GENERIC, "There is no maximum size set.");

	SmlDataSyncChangeItem *frag = sml_data_sync_change_item_new();
	sml_return_val_error_if_fail (frag, NULL, error, SML_ERROR_GENERIC, "Cannot create new instance of SmlDataSyncChangeItem - out of memory.");

	if (start == 0 && max_size < strlen(self->priv->data))
		sml_data_sync_change_item_set_planned_size(frag, strlen(self->priv->data));

	if (!sml_data_sync_change_item_set_data(frag, self->priv->data + start, max_size, error))
		goto error;

	if (start + max_size < strlen(self->priv->data))
		sml_data_sync_change_item_set_missing_data(frag, TRUE);
	else
		sml_data_sync_change_item_set_missing_data(frag, FALSE);

	if (sml_data_sync_change_item_get_location(self) != NULL &&
	    !sml_data_sync_change_item_set_location(frag, sml_data_sync_change_item_get_location(self), error))
		goto error;

	if (sml_data_sync_change_item_get_content_type(self) != NULL &&
	    !sml_data_sync_change_item_set_content_type(frag, sml_data_sync_change_item_get_content_type(self), error))
		goto error;

	/* Otherwise the source/target setting is in trouble
	 * because the assembler depends on the action
	 * which reference is set (source or target).
	 */
	sml_data_sync_change_item_set_action(frag, sml_data_sync_change_item_get_action (self));

	return frag;
error:
	if (frag)
		g_object_unref(frag);
	return NULL;
}

/**
 * sml_data_sync_change_item_attach_fragment:
 * @self: A #SmlDataSyncChangeItem
 *
 * 
 */
gboolean
sml_data_sync_change_item_attach_fragment (SmlDataSyncChangeItem *self,
                                 SmlDataSyncChangeItem *frag,
                                 GError **error)
{
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncChangeItem object.");
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_CHANGE_ITEM (frag), FALSE, error, SML_ERROR_GENERIC, "There must be a fragmentation object.");
	sml_return_val_error_if_fail (frag->priv->data, FALSE, error, SML_ERROR_GENERIC, "The fragment does not contain any data.");
	sml_return_val_error_if_fail (strlen(frag->priv->data), FALSE, error, SML_ERROR_GENERIC, "The fragment does only contain an empty string.");
	sml_return_val_error_if_fail ((!self->priv->content_type && !frag->priv->content_type) ||
	                              (self->priv->content_type && frag->priv->content_type &&
	                               strcmp(self->priv->content_type, frag->priv->content_type) == 0),
	                              FALSE, error, SML_ERROR_GENERIC, "The content-types do not match.");
	sml_return_val_error_if_fail (strlen(self->priv->data) <= self->priv->planned_size, FALSE, error, SML_ERROR_GENERIC, "The data is already complete.");
	sml_return_val_error_if_fail (strlen(self->priv->data)+strlen(frag->priv->data) <= self->priv->planned_size,
	                              FALSE, error, SML_ERROR_GENERIC, "The fragment is too large.");

	gchar *complete = NULL;
	if (self->priv->data)
		complete = g_strconcat(self->priv->data, frag->priv->data, NULL);
	else
		complete = g_strdup(frag->priv->data);
	sml_return_val_error_if_fail (complete, FALSE, error, SML_ERROR_GENERIC, "Cannot copy the fragment - out of memory.");

	g_free(self->priv->data);
	self->priv->data = complete;
	self->priv->removed_chars += frag->priv->removed_chars;

	if (self->priv->planned_size &&
	    self->priv->planned_size - self->priv->removed_chars == strlen(self->priv->data))
		self->priv->missing_data = FALSE;

	return TRUE;
}

