/*
* Copyright (C) 2012 - Juan Ferrer Toribio
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include "gvn-value.h"
#include "gvn-misc.h"
/**
* SECTION: gvn-value
* @Short_description: additional value manipulation functions
* @Title: GvnValue
* @See_also: #DbForm, #DbConn
*
* This functions are intended to expand or ease the use of the original GObject
* library #GValue utilites.
**/
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
static void gvn_value_new_from_string_real (GValue * value, GType type, const gchar * string, gsize len)
{
switch (type)
{
case G_TYPE_BOOLEAN:
g_value_set_boolean (value, atoi (string));
break;
case G_TYPE_CHAR:
g_value_set_schar (value, atoi (string));
break;
case G_TYPE_INT:
g_value_set_int (value, atoi (string));
break;
case G_TYPE_UINT:
g_value_set_uint (value, (guint) atoi (string));
break;
case G_TYPE_LONG:
g_value_set_long (value, g_ascii_strtoll (string, NULL, 0));
break;
case G_TYPE_ULONG:
g_value_set_ulong (value, g_ascii_strtoull (string, NULL, 0));
break;
case G_TYPE_FLOAT:
g_value_set_float (value, atof (string));
break;
case G_TYPE_DOUBLE:
g_value_set_double (value, g_ascii_strtod (string, NULL));
break;
case G_TYPE_STRING:
g_value_set_string (value, string);
break;
default:
{
if (len == -1)
len = strlen (string);
if (type == G_TYPE_DATE)
{
GDate * date = g_date_new ();
g_date_set_parse (date, string);
if (g_date_valid (date))
g_value_take_boxed (value, date);
else
{
g_warning ("Gvn: Can't transform string to GDate");
g_date_free (date);
}
}
else if (type == G_TYPE_DATE_TIME)
{
GDateTime * datetime;
gchar ** dt = g_strsplit_set (string, " -:", 0);
if (g_strv_length (dt) >= 6)
datetime = g_date_time_new_local
(atoi (dt[0]), atoi (dt[1]), atoi (dt[2]),
atoi (dt[3]), atoi (dt[4]), atoi (dt[5]));
else
g_warning ("Gvn: Can't transform string to GDateTime");
g_value_take_boxed (value, datetime);
g_strfreev (dt);
}
else if (type == G_TYPE_BYTES)
{
if (len > 0)
g_value_take_boxed (value, g_bytes_new (string, len));
else
g_warning ("Gvn: Can't transform string to GBytes");
}
else if (type != GVN_TYPE_NULL)
g_return_if_reached ();
}
}
}
static void gvn_value_transform_string_to_any (const GValue * src, GValue * dst)
{
gvn_value_new_from_string_real (dst,
G_VALUE_TYPE (dst), g_value_get_string (src), -1);
}
static void gvn_value_transform_null_to_any (const GValue * src, GValue * dst) {}
static void gvn_value_transform_any_to_null (const GValue * src, GValue * dst)
{
gvn_value_set_null (dst);
}
static void gvn_value_transform_date_to_string (const GValue * src, GValue * dst)
{
GDate * date = g_value_get_boxed (src);
if (date)
{
g_value_take_string (dst, g_strdup_printf ("%04u-%02u-%02u"
,g_date_get_year (date)
,g_date_get_month (date)
,g_date_get_day (date)
));
}
else
g_value_take_string (dst, NULL);
}
static void gvn_value_transform_date_time_to_string (const GValue * src, GValue * dst)
{
gpointer date = g_value_get_boxed (src);
if (date)
g_value_take_string (dst, g_date_time_format (date, "%Y-%m-%d %T"));
else
g_value_take_string (dst, NULL);
}
static void gvn_value_transform_date_time_to_date (const GValue * src, GValue * dst)
{
GDateTime * date_time = g_value_get_boxed (src);
if (date_time)
{
GDate * date = g_date_new_dmy (
g_date_time_get_day_of_month (date_time)
,g_date_time_get_month (date_time)
,g_date_time_get_year (date_time)
);
g_value_take_boxed (dst, date);
}
else
g_value_take_boxed (dst, NULL);
}
static void gvn_value_transform_date_to_date_time (const GValue * src, GValue * dst)
{
GDate * date = g_value_get_boxed (src);
if (date)
{
GDateTime * date_time = g_date_time_new_local (
g_date_get_year (date)
,g_date_get_month (date)
,g_date_get_day (date)
,0
,0
,0.0
);
g_value_take_boxed (dst, date_time);
}
else
g_value_take_boxed (dst, NULL);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public
/**
* gvn_value_new:
* @type: the type of new #GValue
*
* Creates a new #GValue
*
* Return value: a #GValue.
**/
GValue * gvn_value_new (GType type)
{
GValue * value = g_new0 (GValue, 1);
return g_value_init (value, type);
}
/**
* gvn_value_is_null:
* @value: the value to be checked
*
* Checks if a @value is of type GVN_TYPE_NULL
*
* Return value: %TRUE if its NULL, %FALSE otherwise.
**/
/**
* gvn_value_set_null:
* @value: the value to be nulled
*
* Checks if a @value is of type GVN_TYPE_NULL, if not, sets it.
**/
void gvn_value_set_null (GValue * value)
{
g_return_if_fail (G_IS_VALUE (value));
if (!gvn_value_is_null (value))
{
g_value_unset (value);
g_value_init (value, GVN_TYPE_NULL);
}
}
/**
* gvn_value_new_from_string:
* @value: an uninitilized #GValue
* @string: a string to be converted
* @length: length of @string or -1 if its null terminated
* @type: the type to be converted
*
* Creates a new @value from a string
**/
void gvn_value_new_from_string (GValue * value, GType type, const gchar * string, gsize len)
{
g_return_if_fail (string != NULL);
g_value_init (value, type);
gvn_value_new_from_string_real (value, type, string, len);
}
/**
* gvn_value_new_valist:
* @value: an uninitilized #GValue
* @type: the type of value
* @content: a pointer to the value content
*
* Initializes the @value with the specified type and content.
**/
void gvn_value_new_with_content (GValue * value, GType type, gpointer content)
{
g_value_init (value, type);
switch (type)
{
case G_TYPE_BOOLEAN:
g_value_set_boolean (value, *((gboolean *) content));
break;
case G_TYPE_CHAR:
g_value_set_schar (value, *((gint8 *) content));
break;
case G_TYPE_INT:
g_value_set_int (value, *((gint *) content));
break;
case G_TYPE_UINT:
g_value_set_uint (value, *((guint *) content));
break;
case G_TYPE_LONG:
g_value_set_long (value, *((glong *) content));
break;
case G_TYPE_ULONG:
g_value_set_ulong (value, *((gulong *) content));
break;
case G_TYPE_FLOAT:
g_value_set_float (value, *((gfloat *) content));
break;
case G_TYPE_DOUBLE:
g_value_set_double (value, *((gdouble *) content));
break;
case G_TYPE_STRING:
g_value_set_string (value, (gchar *) content);
break;
default:
if (type == G_TYPE_DATE || type == G_TYPE_DATE_TIME
|| type == G_TYPE_BYTES)
g_value_set_boxed (value, content);
else if (G_TYPE_IS_OBJECT (type))
g_value_set_object (value, content);
else if (type != GVN_TYPE_NULL)
g_return_if_reached ();
}
}
/**
* gvn_value_get_valist:
* @value: the type of value
* @va: a string to be converted
*
* Sets the value of the initilized @value with the value of next
* element in @va
**/
void gvn_value_get_valist (const GValue * value, va_list va)
{
GType type = G_VALUE_TYPE (value);
switch (type)
{
case G_TYPE_BOOLEAN:
*va_arg (va, gboolean*) = g_value_get_boolean (value);
break;
case G_TYPE_CHAR:
*va_arg (va, gint8*) = g_value_get_schar (value);
break;
case G_TYPE_INT:
*va_arg (va, gint*) = g_value_get_int (value);
break;
case G_TYPE_UINT:
*va_arg (va, guint*) = g_value_get_uint (value);
break;
case G_TYPE_LONG:
*va_arg (va, glong*) = g_value_get_long (value);
break;
case G_TYPE_ULONG:
*va_arg (va, gulong*) = g_value_get_ulong (value);
break;
case G_TYPE_FLOAT:
*va_arg (va, gfloat*) = g_value_get_float (value);
break;
case G_TYPE_DOUBLE:
*va_arg (va, gdouble*) = g_value_get_double (value);
break;
case G_TYPE_STRING:
*va_arg (va, gchar**) = g_value_dup_string (value);
break;
default:
if (type == G_TYPE_DATE || type == G_TYPE_DATE_TIME
|| type == G_TYPE_BYTES)
*va_arg (va, gpointer*) = g_value_dup_boxed (value);
else if (G_TYPE_IS_OBJECT (type))
*va_arg (va, gpointer*) = g_value_dup_object (value);
else if (type != GVN_TYPE_NULL)
g_return_if_reached ();
}
}
/**
* gvn_value_compare:
* @a: a #GValue to be compared
* @b: a #GValue to be compared
*
* Does the same as g_value_compare0(), but returns a %gboolean.
*
* Return value: %TRUE if two values are equal, %FALSE otherwise.
**/
/**
* gvn_value_compare0:
* @a: #GValue to be compared
* @b: #GValue to be compared
*
* Compares a and b. The two values must be of the same type or GVN_TYPE_NULL.
*
* Return value: -1, 0 or 1, if @a is <, == or > than @b.
**/
gint gvn_value_compare0 (const GValue * a, const GValue * b)
{
GType a_type = G_VALUE_TYPE (a);
gboolean a_is_val = G_IS_VALUE (a);
gboolean b_is_val = G_IS_VALUE (b);
if (!(a_is_val && b_is_val))
{
if (a_is_val)
return 1;
if (b_is_val)
return -1;
}
else if (a_type == G_VALUE_TYPE (b))
{
switch (a_type)
{
case G_TYPE_FLOAT:
{
gfloat aux = g_value_get_float (a) - g_value_get_float (b);
return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0;
}
case G_TYPE_DOUBLE:
{
gdouble aux = g_value_get_double (a) - g_value_get_double (b);
return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0;
}
case G_TYPE_INT:
return g_value_get_int (a) - g_value_get_int (b);
case G_TYPE_UINT:
return (gint) (g_value_get_uint (a) - g_value_get_uint (b));
case G_TYPE_LONG:
return (gint) (g_value_get_long (a) - g_value_get_long (b));
case G_TYPE_ULONG:
return (gint) (g_value_get_ulong (a) - g_value_get_ulong (b));
case G_TYPE_BOOLEAN:
return (gint) (g_value_get_boolean (a) - g_value_get_boolean (b));
case G_TYPE_CHAR:
return (gint) (g_value_get_schar (a) - g_value_get_schar (b));
case G_TYPE_STRING:
return g_strcmp0 (g_value_get_string (a), g_value_get_string (b));
default:
if (a_type == GVN_TYPE_NULL)
return 0;
if (G_TYPE_FUNDAMENTAL (a_type) == G_TYPE_BOXED)
{
gpointer a_boxed = g_value_get_boxed (a);
gpointer b_boxed = g_value_get_boxed (b);
if (!a_boxed)
return (gint) (a_boxed - b_boxed);
if (!b_boxed)
return (gint) (a_boxed - b_boxed);
if (a_type == G_TYPE_DATE)
return g_date_compare (a_boxed, b_boxed);
if (a_type == G_TYPE_DATE_TIME)
return g_date_time_compare (a_boxed, b_boxed);
else if (a_type == G_TYPE_BYTES)
return (gint) (a_boxed - b_boxed);
}
g_warning (_("Attempting to compare invalid types: %s\n"),
g_type_name (a_type));
}
}
else if (gvn_value_is_null (a))
return -1;
else if (gvn_value_is_null (b))
return 1;
return 1;
}
/**
* gvn_value_copy:
* @src: source #GValue
* @dst: destination #GValue
*
* Copies the value of @src into @dst. This function also works with #GvnNull
* values.
**/
void gvn_value_copy (const GValue * src, GValue * dst)
{
g_return_if_fail (G_IS_VALUE (src));
g_return_if_fail (G_IS_VALUE (dst));
if (G_VALUE_TYPE (src) != G_VALUE_TYPE (dst))
{
g_value_unset (dst);
g_value_init (dst, G_VALUE_TYPE (src));
}
g_value_copy (src, dst);
}
/**
* gvn_value_ccopy:
* @src: source #GValue
* @dst: destination #GValue
*
* Compares @src with @dst, if they are different copies the value of @src into
* @dst. This function also works with #GvnNull values.
*
* Return value: %TRUE if they are copied, %FALSE if they are equals.
**/
gboolean gvn_value_ccopy (const GValue * src, GValue * dst)
{
if (gvn_value_compare (src, dst))
return FALSE;
gvn_value_copy (src, dst);
return TRUE;
}
/*
* Checks the validity of @format attending to the valid specifier characters in
* @specifiers, the other flags in the format string won't be checked.
*/
gboolean format_valid (const gchar * format, const gchar * specifiers)
{
gboolean started = FALSE;
gint i, j;
if (format && specifiers)
for (i = 0; format[i] != '\0'; i++)
{
if (format[i] == '%')
{
started = TRUE;
continue;
}
else if (started && format[i] == ' ')
break;
for (j = 0; specifiers[j] != '\0'; j++)
if (started && format[i] == specifiers[j]
&& (format[i+1] == ' ' || format[i+1] == '\0'))
return TRUE;
}
return FALSE;
}
/**
* gvn_value_to_format_string:
* @src: source #GValue
* @format: a format string
* @dst: destination #GValue
*
* Sets the content of @dst to a string value depending on the type of @src and
* according to @format. @format must be a valid format string as the used by
* the printf() family of functions and, in the case of dates, the ones used by
* strftime(). See g_ascii_formatd() and g_date_time_format(). The output may
* vary depending on the locale.
*
* The accepted types are #gboolean, #guint, #gint, #gfloat, #gdouble, #guchar,
* #gchar, #gchararray, #GDate and #GDateTime, any other types may have an
* unexpected output.
**/
void gvn_value_to_format_string (const GValue * src, const gchar * format, GValue * dst)
{
GType type = G_VALUE_TYPE (src);
g_value_init (dst, G_TYPE_STRING);
switch (type)
{
case G_TYPE_BOOLEAN:
{
gboolean b = g_value_get_boolean (src);
g_value_set_string (dst, b ? _("Yes") : _("No"));
break;
}
case G_TYPE_INT:
{
gchar * str;
gint i = g_value_get_int (src);
str = g_strdup_printf (format_valid (format, "duoXxp") ? format : "%d", i);
g_value_set_string (dst, str);
g_free (str);
break;
}
case G_TYPE_UINT:
{
gchar * str;
guint u = g_value_get_uint (src);
str = g_strdup_printf (format_valid (format, "udoXxp") ? format : "%u", u);
g_value_set_string (dst, str);
g_free (str);
break;
}
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
{
gdouble dec;
gchar buffer [G_ASCII_DTOSTR_BUF_SIZE];
if (type == G_TYPE_FLOAT)
dec = (gdouble) g_value_get_float (src);
else
dec = g_value_get_double (src);
g_ascii_formatd (buffer, G_ASCII_DTOSTR_BUF_SIZE,
format_valid (format, "fFeEgGaA") ? format : "%.2f", dec);
g_value_set_string (dst, buffer);
break;
}
default:
if (type == G_TYPE_DATE)
{
gchar buffer[120];
GDate * date = g_value_get_boxed (src);
g_date_strftime (buffer, 120, format ? format : "%Y-%m-%d", date);
g_value_set_string (dst, buffer);
}
else if (type == G_TYPE_DATE_TIME)
{
gchar * str;
GDateTime * dt = g_value_get_boxed (src);
str = g_date_time_format (dt, format ? format : "%Y-%m-%d %T");
g_value_set_string (dst, str);
g_free (str);
}
else if (!gvn_value_is_null (src))
g_value_transform (src, dst);
else
g_value_set_string (dst, "");
}
}
/**
* gvn_type_init:
*
* Initializes library types, also calls g_type_init().
**/
void gvn_type_init ()
{
gint n;
GType types[] = {
G_TYPE_BOOLEAN
,G_TYPE_CHAR
,G_TYPE_INT
,G_TYPE_UINT
,G_TYPE_LONG
,G_TYPE_ULONG
,G_TYPE_FLOAT
,G_TYPE_DOUBLE
,G_TYPE_STRING
,G_TYPE_DATE
,G_TYPE_DATE_TIME
,G_TYPE_BYTES
,G_TYPE_NONE
};
for (n = 0; types[n] != G_TYPE_NONE; n++)
{
g_value_register_transform_func (types[n], GVN_TYPE_NULL,
gvn_value_transform_any_to_null
);
g_value_register_transform_func (GVN_TYPE_NULL, types[n],
gvn_value_transform_null_to_any
);
g_value_register_transform_func (G_TYPE_STRING, types[n],
gvn_value_transform_string_to_any
);
}
g_value_register_transform_func (G_TYPE_DATE, G_TYPE_STRING,
gvn_value_transform_date_to_string
);
g_value_register_transform_func (G_TYPE_DATE_TIME, G_TYPE_STRING,
gvn_value_transform_date_time_to_string
);
g_value_register_transform_func (G_TYPE_DATE_TIME, G_TYPE_DATE,
gvn_value_transform_date_time_to_date
);
g_value_register_transform_func (G_TYPE_DATE, G_TYPE_DATE_TIME,
gvn_value_transform_date_to_date_time
);
}