652 lines
16 KiB
C
652 lines
16 KiB
C
/*
|
||
* 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 <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#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
|
||
);
|
||
}
|
||
|