Provide for easily moving GValues to and from KVP

So that all interaction with KVP is easily handled with properties.
Includes convenience functions for simplifying set/get property
functions.
This commit is contained in:
John Ralls
2013-10-15 16:47:49 -07:00
parent c2d93cbe4c
commit 4a4b18059e
5 changed files with 505 additions and 12 deletions

View File

@@ -1645,4 +1645,192 @@ kvp_frame_get_hash(const KvpFrame *frame)
return frame->hash;
}
static GValue *gvalue_from_kvp_value (KvpValue*);
static KvpValue *kvp_value_from_gvalue (const GValue*);
static void
gvalue_list_from_kvp_value (KvpValue *kval, gpointer pList)
{
GList **gvlist = NULL;
GValue *gval = gvalue_from_kvp_value (kval);
gvlist = (GList**)pList;
if (G_VALUE_TYPE (gval))
*gvlist = g_list_prepend (*gvlist, gval);
}
static void
kvp_value_list_from_gvalue (GValue *gval, gpointer pList)
{
GList **kvplist = (GList**)pList;
KvpValue *kvp;
if (!(gval && G_VALUE_TYPE (gval)))
return;
kvp = kvp_value_from_gvalue (gval);
*kvplist = g_list_prepend (*kvplist, kvp);
}
static GValue*
gvalue_from_kvp_value (KvpValue *kval)
{
GValue *val;
gnc_numeric num;
Timespec tm;
GDate gdate;
if (kval == NULL) return NULL;
val = g_slice_new0 (GValue);
switch (kval->type)
{
case KVP_TYPE_GINT64:
g_value_init (val, G_TYPE_INT64);
g_value_set_int64 (val, kvp_value_get_gint64 (kval));
break;
case KVP_TYPE_DOUBLE:
g_value_init (val, G_TYPE_DOUBLE);
g_value_set_double (val, kvp_value_get_double (kval));
break;
case KVP_TYPE_NUMERIC:
g_value_init (val, GNC_TYPE_NUMERIC);
num = kvp_value_get_numeric (kval);
g_value_set_boxed (val, &num);
break;
case KVP_TYPE_STRING:
g_value_init (val, G_TYPE_STRING);
g_value_set_string (val, kvp_value_get_string (kval));
break;
case KVP_TYPE_GUID:
g_value_init (val, GNC_TYPE_GUID);
g_value_set_boxed (val, kvp_value_get_guid (kval));
break;
case KVP_TYPE_TIMESPEC:
g_value_init (val, GNC_TYPE_TIMESPEC);
tm = kvp_value_get_timespec (kval);
g_value_set_boxed (val, &tm);
break;
case KVP_TYPE_BINARY:
PWARN ("Error! Don't use Kvp Binary!");
g_slice_free (GValue, val);
val = NULL;
break;
case KVP_TYPE_GDATE:
g_value_init (val, G_TYPE_DATE);
gdate = kvp_value_get_gdate (kval);
g_value_set_boxed (val, &gdate);
break;
case KVP_TYPE_GLIST:
{
GList *gvalue_list = NULL;
GList *kvp_list = kvp_value_get_glist (kval);
g_list_foreach (kvp_list, (GFunc)gvalue_list_from_kvp_value, &gvalue_list);
g_value_init (val, GNC_TYPE_VALUE_LIST);
gvalue_list = g_list_reverse (gvalue_list);
g_value_set_boxed (val, gvalue_list);
break;
}
/* No transfer of KVP frames outside of QofInstance-derived classes! */
case KVP_TYPE_FRAME:
PWARN ("Error! Attempt to transfer KvpFrame!");
default:
PWARN ("Error! Invalid KVP Transfer Request!");
g_slice_free (GValue, val);
val = NULL;
break;
}
return val;
}
KvpValue*
kvp_value_from_gvalue (const GValue *gval)
{
KvpValue *val = NULL;
GType type = G_VALUE_TYPE (gval);
g_return_val_if_fail (G_VALUE_TYPE (gval), NULL);
if (type == G_TYPE_INT64)
val = kvp_value_new_gint64 (g_value_get_int64 (gval));
else if (type == G_TYPE_DOUBLE)
val = kvp_value_new_double (g_value_get_double (gval));
else if (type == GNC_TYPE_NUMERIC)
val = kvp_value_new_numeric (*(gnc_numeric*)g_value_get_boxed (gval));
else if (type == G_TYPE_STRING)
val = kvp_value_new_string (g_value_get_string (gval));
else if (type == GNC_TYPE_GUID)
val = kvp_value_new_guid ((GncGUID*)g_value_get_boxed (gval));
else if (type == GNC_TYPE_TIMESPEC)
val = kvp_value_new_timespec (*(Timespec*)g_value_get_boxed (gval));
else if (type == G_TYPE_DATE)
val = kvp_value_new_gdate (*(GDate*)g_value_get_boxed (gval));
else if (type == GNC_TYPE_VALUE_LIST)
{
GList *gvalue_list = (GList*)g_value_get_boxed (gval);
GList *kvp_list = NULL;
g_list_foreach (gvalue_list, (GFunc)kvp_value_list_from_gvalue, &kvp_list);
kvp_list = g_list_reverse (kvp_list);
val = kvp_value_new_glist_nc (kvp_list);
// g_list_free_full (gvalue_list, (GDestroyNotify)g_value_unset);
// gvalue_list = NULL;
}
else
PWARN ("Error! Don't know how to make a KvpValue from a %s",
G_VALUE_TYPE_NAME (gval));
return val;
}
GValue*
kvp_frame_get_gvalue (KvpFrame *frame, const gchar *key)
{
KvpValue *kval = kvp_frame_get_value (frame, key);
GValue *value = gvalue_from_kvp_value (kval);
return value;
}
void
kvp_frame_set_gvalue (KvpFrame *frame, const gchar *key, const GValue *value)
{
kvp_frame_set_value_nc (frame, key, kvp_value_from_gvalue (value));
}
static GValue*
gnc_gvalue_copy (GValue *src, gpointer uData)
{
GValue *dest = g_value_init (g_slice_new0 (GValue), G_VALUE_TYPE (src));
g_value_copy (src, dest);
return dest;
}
void
gnc_gvalue_free (GValue *val)
{
if (val == NULL || ! G_IS_VALUE (val)) return;
g_value_unset (val);
g_slice_free (GValue, val);
}
static GList*
gnc_value_list_copy (GList *list)
{
return g_list_copy_deep (list, (GCopyFunc)gnc_gvalue_copy, NULL);
}
static void
gnc_value_list_free (GList *list)
{
g_list_free_full (list, (GDestroyNotify)gnc_gvalue_free);
}
GType
gnc_value_list_get_type (void)
{
static GType type = 0;
if (type == 0)
{
type = g_boxed_type_register_static ("gnc_value_list",
(GBoxedCopyFunc)gnc_value_list_copy,
(GBoxedFreeFunc)gnc_value_list_free);
}
return type;
}
/* ========================== END OF FILE ======================= */

View File

@@ -588,5 +588,58 @@ gchar* kvp_frame_to_string(const KvpFrame *frame);
gchar* binary_to_string(const void *data, guint32 size);
GHashTable* kvp_frame_get_hash(const KvpFrame *frame);
/** KvpItem: GValue Exchange
* \brief Transfer of KVP to and from GValue, with the key
*
* Used to parameterize KVP <-> GValue exchanges.
*/
typedef struct
{
const gchar *key;
GValue *value;
}KvpItem;
/** Return a KvpItem containing the value of a KvpFrame
*
* Structure types (gnc_numeric, Timespec) are converted to pointers
* and must be extracted with g_value_get_boxed. A KVP_TYPE_GLIST will
* have all of its contents converted from KvpValues to GValues, so
* the return type will be a GValue containing a GList of GValue*, not
* GValue. Use gnc_value_list_free() to free such a list if you take
* it out of the GValue.
*
* \param frame: (transfer-none) The KvpFrame retrieved with kvp_get_frame_foo()
* \param key: (transfer-none) A slash-delimited string with the path to
* the stored value. Must not be NULL or empty.
* \return (transfer-full) A KvpItem* which must be freed with kvp_item_free().
*/
GValue *kvp_frame_get_gvalue (KvpFrame *frame, const gchar *key);
/** Replace or create a Kvp slot from a KvpItem
*
* Structure types (gnc_numeric, Timespec) should be stored as
* boxed-type pointers in the GValue with the appropriate type from
* qof.h. Lists should be stored as a GValue containing a GList of
* GValues of type convertable to KvpValues. Unsupported types will
* emit a warning message and will be skipped.
*
* \param frame: (transfer none) The KvpFrame into which the value
* will be added.
* \param key: (transfer none) The subkey of the frame at which to
* store the value
* \param value: GValue containing the paramter to store.
*/
void kvp_frame_set_gvalue (KvpFrame *frame, const gchar *key, const GValue *value);
/**
* \brief Convenience function to release the value in a GValue
* acquired by kvp_frame_get_gvalue and to free the GValue.
* \param value: A GValue* created by kvp_frame_get_gvalue
*/
void gnc_gvalue_free (GValue *value);
GType gnc_value_list_get_type (void);
#define GNC_TYPE_VALUE_LIST (gnc_value_list_get_type ())
/** @} */
#endif

View File

@@ -49,4 +49,9 @@ void qof_instance_set_last_update (QofInstance *inst, Timespec ts);
* collection flag at all. */
void qof_instance_set_dirty_flag (gconstpointer inst, gboolean flag);
/* Convenience functions to save some typing in property handlers */
void qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value);
void qof_instance_get_kvp (QofInstance *inst, const gchar *key, GValue *value);
#endif /* QOF_INSTANCE_P_H */

View File

@@ -1050,5 +1050,25 @@ qof_commit_edit_part2(QofInstance *inst,
return TRUE;
}
void
qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value)
{
KvpFrame *frame = qof_instance_get_slots (inst);
kvp_frame_set_gvalue (frame, key, value);
}
void
qof_instance_get_kvp (QofInstance *inst, const gchar *key, GValue *value)
{
KvpFrame *frame = qof_instance_get_slots (inst);
GValue *temp = kvp_frame_get_gvalue (frame, key);
if (temp)
{
g_value_copy (temp, value);
gnc_gvalue_free (temp);
}
}
/* ========================== END OF FILE ======================= */

View File

@@ -22,7 +22,7 @@
#include "config.h"
#include <string.h>
#include <glib.h>
#include "unittest-support.h"
#include <unittest-support.h>
#include "qof.h"
static const gchar *suitename = "/qof/kvp_frame";
@@ -31,18 +31,22 @@ void test_suite_kvp_frame ( void );
typedef struct
{
KvpFrame *frame;
GSList *hdlrs;
} Fixture;
static void
setup( Fixture *fixture, gconstpointer pData )
{
fixture->frame = kvp_frame_new();
fixture->hdlrs = NULL;
}
static void
teardown( Fixture *fixture, gconstpointer pData )
{
kvp_frame_delete( fixture->frame );
g_slist_free_full (fixture->hdlrs, test_free_log_handler);
test_clear_error_list ();
}
extern KvpFrame* ( *p_get_trailer_make )( KvpFrame *frame, const char *key_path, char **end_key );
@@ -57,6 +61,7 @@ static void
setup_static( Fixture *fixture, gconstpointer pData )
{
fixture->frame = kvp_frame_new();
fixture->hdlrs = NULL;
init_static_test_pointers();
g_assert( p_get_trailer_make && p_kvp_value_glist_to_string &&
p_get_or_make && p_kvp_frame_get_frame_or_null_slash_trash &&
@@ -67,6 +72,8 @@ static void
teardown_static( Fixture *fixture, gconstpointer pData )
{
kvp_frame_delete( fixture->frame );
g_slist_free_full (fixture->hdlrs, test_free_log_handler);
test_clear_error_list ();
p_get_trailer_make = NULL;
p_kvp_value_glist_to_string = NULL;
p_get_or_make = NULL;
@@ -74,21 +81,19 @@ teardown_static( Fixture *fixture, gconstpointer pData )
p_get_trailer_or_null = NULL;
}
static void
test_kvp_frame_new_delete( void )
static GncGUID*
populate_frame (KvpFrame *frame)
{
KvpFrame *frame;
GList *list = NULL;
Timespec ts;
GncGUID *guid;
GDate gdate;
ts.tv_sec = 1;
ts.tv_nsec = 1;
guid = guid_malloc();
guid_new( guid );
frame = kvp_frame_new();
g_assert( frame );
g_assert( kvp_frame_is_empty( frame ) );
guid = guid_malloc ();
guid_new (guid);
g_date_set_dmy (&gdate, 26, 1, 1957);
kvp_frame_set_gint64( frame, "gint64-type", 100 );
kvp_frame_set_double( frame, "double-type", 3.14159 );
@@ -96,14 +101,37 @@ test_kvp_frame_new_delete( void )
kvp_frame_set_timespec( frame, "timespec-type", ts );
kvp_frame_set_string( frame, "string-type", "abcdefghijklmnop" );
kvp_frame_set_guid( frame, "guid-type", guid );
kvp_frame_set_value_nc (frame, "gdate-type", kvp_value_new_gdate (gdate));
kvp_frame_set_frame( frame, "frame-type", kvp_frame_new() );
list = g_list_prepend (list, kvp_value_new_guid (guid));
list = g_list_prepend (list, kvp_value_new_string ("qrstuvwxyz"));
list = g_list_prepend (list, kvp_value_new_timespec (ts));
list = g_list_prepend (list, kvp_value_new_numeric (gnc_numeric_create (256, 120)));
list = g_list_prepend (list, kvp_value_new_double (0.4342944819));
list = g_list_prepend (list, kvp_value_new_gint64 (0x1f2e3d4c5b6a79LL));
kvp_frame_set_value (frame, "list-type", kvp_value_new_glist_nc (list));
return guid;
}
static void
test_kvp_frame_new_delete( void )
{
KvpFrame *frame;
GncGUID *guid;
frame = kvp_frame_new();
g_assert( frame );
g_assert( kvp_frame_is_empty( frame ) );
guid = populate_frame (frame);
g_assert( !kvp_frame_is_empty( frame ) );
kvp_frame_delete( frame );
g_assert( frame );
guid_free( guid );
guid_free (guid);
}
static void
@@ -1455,6 +1483,203 @@ test_get_trailer_or_null( Fixture *fixture, gconstpointer pData )
g_assert_cmpstr( last_key, == , "test2" );
}
static void
test_kvp_frame_get_gvalue (Fixture *fixture, gconstpointer pData)
{
KvpFrame *frame = fixture->frame;
GValue *value;
Timespec ts = {1, 1};
GncGUID *guid = populate_frame (frame);
GDate date;
gchar *log_domain = "qof.kvp";
gint log_level = G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL;
gchar *msg1 = "[gvalue_from_kvp_value()] Error! Attempt to transfer KvpFrame!";
gchar *msg2 = "[gvalue_from_kvp_value()] Error! Invalid KVP Transfer Request!";
#undef _func
TestErrorStruct *check1 = test_error_struct_new (log_domain, log_level,
msg1);
TestErrorStruct *check2 = test_error_struct_new (log_domain, log_level,
msg2);
fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check1,
(GLogFunc)test_list_handler);
test_add_error (check1);
test_add_error (check2);
g_date_clear (&date, 1);
g_date_set_dmy (&date, 26, 1, 1957);
value = kvp_frame_get_gvalue (frame, "gint64-type");
g_assert (value != NULL);
g_assert (G_VALUE_HOLDS_INT64 (value));
g_assert_cmpint (g_value_get_int64 (value), ==, 100);
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "double-type");
g_assert (value != NULL);
g_assert (G_VALUE_HOLDS_DOUBLE (value));
g_assert_cmpfloat (g_value_get_double (value), ==, 3.14159);
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "numeric-type");
g_assert (value != NULL);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_NUMERIC);
g_assert (gnc_numeric_zero_p (*(gnc_numeric*)g_value_get_boxed (value)));
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "timespec-type");
g_assert (value != NULL);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_TIMESPEC);
g_assert (timespec_equal (&ts, (Timespec*)g_value_get_boxed (value)));
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "string-type");
g_assert (value != NULL);
g_assert (G_VALUE_HOLDS_STRING (value));
g_assert_cmpstr (g_value_get_string (value), ==, "abcdefghijklmnop");
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "guid-type");
g_assert (value != NULL);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_GUID);
g_assert (guid_equal (guid, (GncGUID*)g_value_get_boxed (value)));
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "gdate-type");
g_assert (value != NULL);
g_assert_cmpint (G_VALUE_TYPE (value), ==, G_TYPE_DATE);
g_assert_cmpint (g_date_compare (&date, (GDate*)g_value_get_boxed (value)), ==, 0);
gnc_gvalue_free (value);
value = kvp_frame_get_gvalue (frame, "frame-type");
g_assert (value == NULL);
g_assert_cmpint (check1->hits, ==, 1);
g_assert_cmpint (check2->hits, ==, 1);
value = kvp_frame_get_gvalue (frame, "list-type");
g_assert (value != NULL);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_VALUE_LIST);
{
GList *list = (GList*)g_value_get_boxed (value);
GValue *value = NULL;
value = (GValue*)(list->data);
g_assert (G_VALUE_HOLDS_INT64 (value));
g_assert (g_value_get_int64 (value) == 0x1f2e3d4c5b6a79LL);
list = g_list_next (list);
value = (GValue*)(list->data);
g_assert (G_VALUE_HOLDS_DOUBLE (value));
g_assert_cmpfloat (g_value_get_double (value), ==, 0.4342944819);
list = g_list_next (list);
value = (GValue*)(list->data);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_NUMERIC);
g_assert (gnc_numeric_eq (*(gnc_numeric*)g_value_get_boxed (value),
gnc_numeric_create (256, 120)));
list = g_list_next (list);
value = (GValue*)(list->data);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_TIMESPEC);
g_assert (timespec_equal (&ts, (Timespec*)g_value_get_boxed (value)));
list = g_list_next (list);
value = (GValue*)(list->data);
g_assert (G_VALUE_HOLDS_STRING (value));
g_assert_cmpstr (g_value_get_string (value), ==, "qrstuvwxyz");
list = g_list_next (list);
value = (GValue*)(list->data);
g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_GUID);
g_assert (guid_equal (guid, (GncGUID*)g_value_get_boxed (value)));
list = g_list_next (list);
g_assert (list == NULL);
}
gnc_gvalue_free (value);
}
static void
test_kvp_frame_set_gvalue (Fixture *fixture, gconstpointer pData)
{
/* Bit of a shortcut: We'll use kvp_frame_get_item to make our KvpItem
* and feed it into a new frame; something of a round-trip test.
*/
KvpFrame *o_frame = fixture->frame;
KvpFrame *n_frame = kvp_frame_new ();
GValue *value;
GList *o_list, *n_list;
populate_frame (o_frame);
value = kvp_frame_get_gvalue (o_frame, "gint64-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "gint64-type", value);
g_assert_cmpint (kvp_frame_get_gint64 (o_frame, "gint64-type"), ==,
kvp_frame_get_gint64 (n_frame, "gint64-type"));
value = kvp_frame_get_gvalue (o_frame, "double-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "double-type", value);
g_assert_cmpint (kvp_frame_get_double (o_frame, "double-type"), ==,
kvp_frame_get_double (n_frame, "double-type"));
value = kvp_frame_get_gvalue (o_frame, "numeric-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "numeric-type", value);
g_assert (gnc_numeric_equal (kvp_frame_get_numeric (o_frame, "numeric-type"),
kvp_frame_get_numeric (n_frame, "numeric-type")));
value = kvp_frame_get_gvalue (o_frame, "timespec-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "timespec-type", value);
{
Timespec o_ts = kvp_frame_get_timespec (o_frame, "timespec-type");
Timespec n_ts = kvp_frame_get_timespec (n_frame, "timespec-type");
g_assert (timespec_equal (&o_ts, &n_ts));
}
value = kvp_frame_get_gvalue (o_frame, "string-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "string-type", value);
g_assert_cmpstr (kvp_frame_get_string (o_frame, "string-type"), ==,
kvp_frame_get_string (n_frame, "string-type"));
value = kvp_frame_get_gvalue (o_frame, "gdate-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "gdate-type", value);
{
GDate o_date = kvp_value_get_gdate (kvp_frame_get_slot (o_frame,
"gdate-type"));
GDate n_date = kvp_value_get_gdate (kvp_frame_get_slot (n_frame,
"gdate-type"));
g_assert_cmpint (g_date_compare (&o_date, &n_date), ==, 0);
}
value = kvp_frame_get_gvalue (o_frame, "guid-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "guid-type", value);
g_assert (guid_equal (kvp_frame_get_guid (o_frame, "guid-type"),
kvp_frame_get_guid (n_frame, "guid-type")));
value = kvp_frame_get_gvalue (o_frame, "list-type");
g_assert (value != NULL);
kvp_frame_set_gvalue (n_frame, "list-type", value);
o_list = kvp_value_get_glist (kvp_frame_get_slot (o_frame, "list_type"));
n_list = kvp_value_get_glist (kvp_frame_get_slot (n_frame, "list_type"));
g_assert_cmpint (g_list_length (o_list), ==, g_list_length (n_list));
while (o_list && n_list)
{
g_assert_cmpint (kvp_value_compare ((KvpValue*)o_list->data,
(KvpValue*)n_list->data), ==, 0);
o_list = g_list_next (o_list);
n_list = g_list_next (n_list);
}
kvp_frame_delete (n_frame);
}
void
test_suite_kvp_frame( void )
{
@@ -1482,4 +1707,6 @@ test_suite_kvp_frame( void )
GNC_TEST_ADD( suitename, "get or make", Fixture, NULL, setup_static, test_get_or_make, teardown_static );
GNC_TEST_ADD( suitename, "kvp frame get frame or null slash trash", Fixture, NULL, setup_static, test_kvp_frame_get_frame_or_null_slash_trash, teardown_static );
GNC_TEST_ADD( suitename, "get trailer or null", Fixture, NULL, setup_static, test_get_trailer_or_null, teardown_static );
GNC_TEST_ADD ( suitename, "kvp frame get gvalue", Fixture, NULL, setup, test_kvp_frame_get_gvalue, teardown);
GNC_TEST_ADD ( suitename, "kvp frame set gvalue", Fixture, NULL, setup, test_kvp_frame_set_gvalue, teardown);
}