gnucash/src/libqof/qof/kvp_frame.c

1669 lines
40 KiB
C
Raw Normal View History

/********************************************************************
* kvp_frame.c -- Implements a key-value frame system *
* Copyright (C) 2000 Bill Gribble *
* Copyright (C) 2001,2003 Linas Vepstas <linas@linas.org> *
* *
* 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 2 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, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
********************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif
#include "config.h"
#include <glib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __cplusplus
}
#endif
#include "qof.h"
/* Note that we keep the keys for this hash table in the
* qof_string_cache, as it is very likely we will see the
* same keys over and over again */
struct _KvpFrame
{
GHashTable * hash;
};
typedef struct
{
void *data;
int datasize;
} KvpValueBinaryData;
struct _KvpValue
{
KvpValueType type;
union
{
gint64 int64;
double dbl;
gnc_numeric numeric;
gchar *str;
GncGUID *guid;
Timespec timespec;
KvpValueBinaryData binary;
GList *list;
KvpFrame *frame;
GDate gdate;
} value;
};
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = QOF_MOD_KVP;
/* *******************************************************************
* KvpFrame functions
********************************************************************/
static guint
kvp_hash_func(gconstpointer v)
{
return g_str_hash(v);
}
static gint
kvp_comp_func(gconstpointer v, gconstpointer v2)
{
return g_str_equal(v, v2);
}
static gboolean
init_frame_body_if_needed(KvpFrame *f)
{
if (!f->hash)
{
f->hash = g_hash_table_new(&kvp_hash_func, &kvp_comp_func);
}
return(f->hash != NULL);
}
KvpFrame *
kvp_frame_new(void)
{
KvpFrame * retval = g_new0(KvpFrame, 1);
/* Save space until the frame is actually used */
retval->hash = NULL;
return retval;
}
static void
kvp_frame_delete_worker(gpointer key, gpointer value, gpointer user_data)
{
qof_string_cache_remove(key);
kvp_value_delete((KvpValue *)value);
}
void
kvp_frame_delete(KvpFrame * frame)
{
if (!frame) return;
if (frame->hash)
{
/* free any allocated resource for frame or its children */
g_hash_table_foreach(frame->hash, & kvp_frame_delete_worker,
(gpointer)frame);
/* delete the hash table */
g_hash_table_destroy(frame->hash);
frame->hash = NULL;
}
g_free(frame);
}
gboolean
kvp_frame_is_empty(const KvpFrame * frame)
{
if (!frame) return TRUE;
if (!frame->hash) return TRUE;
return FALSE;
}
static void
kvp_frame_copy_worker(gpointer key, gpointer value, gpointer user_data)
{
KvpFrame * dest = (KvpFrame *)user_data;
g_hash_table_insert(dest->hash,
qof_string_cache_insert(key),
static_cast<void*>(kvp_value_copy(static_cast<KvpValue*>(value))));
}
KvpFrame *
kvp_frame_copy(const KvpFrame * frame)
{
KvpFrame * retval = kvp_frame_new();
if (!frame) return retval;
if (frame->hash)
{
if (!init_frame_body_if_needed(retval)) return(NULL);
g_hash_table_foreach(frame->hash,
& kvp_frame_copy_worker,
(gpointer)retval);
}
return retval;
}
/* Replace the old value with the new value. Return the old value.
* Passing in a null value into this routine has the effect of
* removing the key from the KVP tree.
*/
static KvpValue *
kvp_frame_replace_slot_nc (KvpFrame * frame, const char * slot,
KvpValue * new_value)
{
gpointer orig_key;
gpointer orig_value = NULL;
int key_exists;
if (!frame || !slot) return NULL;
if (!init_frame_body_if_needed(frame)) return NULL; /* Error ... */
key_exists = g_hash_table_lookup_extended(frame->hash, slot,
& orig_key, & orig_value);
if (key_exists)
{
g_hash_table_remove(frame->hash, slot);
qof_string_cache_remove(orig_key);
}
else
{
orig_value = NULL;
}
if (new_value)
{
g_hash_table_insert(frame->hash,
qof_string_cache_insert((gpointer) slot),
new_value);
}
return (KvpValue *) orig_value;
}
/* Passing in a null value into this routine has the effect
* of deleting the old value stored at this slot.
*/
static inline void
kvp_frame_set_slot_destructively(KvpFrame * frame, const char * slot,
KvpValue * new_value)
{
KvpValue * old_value;
old_value = kvp_frame_replace_slot_nc (frame, slot, new_value);
kvp_value_delete (old_value);
}
/* ============================================================ */
/* Get the named frame, or create it if it doesn't exist.
* gcc -O3 should inline it. It performs no error checks,
* the caller is responsible of passing good keys and frames.
*/
static inline KvpFrame *
get_or_make (KvpFrame *fr, const char * key)
{
KvpFrame *next_frame;
KvpValue *value;
value = kvp_frame_get_slot (fr, key);
if (value)
{
next_frame = kvp_value_get_frame (value);
}
else
{
next_frame = kvp_frame_new ();
kvp_frame_set_slot_nc (fr, key,
kvp_value_new_frame_nc (next_frame));
}
return next_frame;
}
/* Get pointer to last frame in path. If the path doesn't exist,
* it is created. The string stored in keypath will be hopelessly
* mangled .
*/
static inline KvpFrame *
kvp_frame_get_frame_slash_trash (KvpFrame *frame, char *key_path)
{
char *key, *next;
if (!frame || !key_path) return frame;
key = key_path;
key --;
while (key)
{
key ++;
while ('/' == *key)
{
key++;
}
if (0x0 == *key) break; /* trailing slash */
next = strchr (key, '/');
if (next) *next = 0x0;
frame = get_or_make (frame, key);
if (!frame) break; /* error - should never happen */
key = next;
}
return frame;
}
/* ============================================================ */
/* Get pointer to last frame in path, or NULL if the path doesn't
* exist. The string stored in keypath will be hopelessly mangled .
*/
static inline const KvpFrame *
kvp_frame_get_frame_or_null_slash_trash (const KvpFrame *frame, char *key_path)
{
KvpValue *value;
char *key, *next;
if (!frame || !key_path) return NULL;
key = key_path;
key --;
while (key)
{
key ++;
while ('/' == *key)
{
key++;
}
if (0x0 == *key) break; /* trailing slash */
next = strchr (key, '/');
if (next) *next = 0x0;
value = kvp_frame_get_slot (frame, key);
if (!value) return NULL;
frame = kvp_value_get_frame (value);
if (!frame) return NULL;
key = next;
}
return frame;
}
/* Return pointer to last frame in path, and also store the
* last dangling part of path in 'end_key'. If path doesn't
* exist, it is created.
*/
static inline KvpFrame *
get_trailer_make (KvpFrame * frame, const char * key_path, char **end_key)
{
char *last_key;
if (!frame || !key_path || (0 == key_path[0])) return NULL;
last_key = strrchr (key_path, '/');
if (NULL == last_key)
{
last_key = (char *) key_path;
}
else if (last_key == key_path)
{
last_key ++;
}
else if (0 == last_key[1])
{
return NULL;
}
else
{
char *root, *lkey;
root = g_strdup (key_path);
lkey = strrchr (root, '/');
*lkey = 0;
frame = kvp_frame_get_frame_slash_trash (frame, root);
g_free(root);
last_key ++;
}
*end_key = last_key;
return frame;
}
/* Return pointer to last frame in path, or NULL if the path
* doesn't exist. Also store the last dangling part of path
* in 'end_key'.
*/
static inline const KvpFrame *
get_trailer_or_null (const KvpFrame * frame, const char * key_path, char **end_key)
{
char *last_key;
if (!frame || !key_path || (0 == key_path[0])) return NULL;
last_key = strrchr (key_path, '/');
if (NULL == last_key)
{
last_key = (char *) key_path;
}
else if (last_key == key_path)
{
last_key ++;
}
else if (0 == last_key[1])
{
return NULL;
}
else
{
char *root, *lkey;
root = g_strdup (key_path);
lkey = strrchr (root, '/');
*lkey = 0;
frame = kvp_frame_get_frame_or_null_slash_trash (frame, root);
g_free(root);
last_key ++;
}
*end_key = last_key;
return frame;
}
/* ============================================================ */
void
kvp_frame_set_gint64(KvpFrame * frame, const char * path, gint64 ival)
{
KvpValue *value;
value = kvp_value_new_gint64 (ival);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_double(KvpFrame * frame, const char * path, double dval)
{
KvpValue *value;
value = kvp_value_new_double (dval);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_numeric(KvpFrame * frame, const char * path, gnc_numeric nval)
{
KvpValue *value;
value = kvp_value_new_gnc_numeric (nval);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_string(KvpFrame * frame, const char * path, const char* str)
{
KvpValue *value;
value = kvp_value_new_string (str);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_guid(KvpFrame * frame, const char * path, const GncGUID *guid)
{
KvpValue *value;
value = kvp_value_new_guid (guid);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_timespec(KvpFrame * frame, const char * path, Timespec ts)
{
KvpValue *value;
value = kvp_value_new_timespec (ts);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_frame(KvpFrame * frame, const char * path, KvpFrame *fr)
{
KvpValue *value;
value = kvp_value_new_frame (fr);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
void
kvp_frame_set_frame_nc(KvpFrame * frame, const char * path, KvpFrame *fr)
{
KvpValue *value;
value = kvp_value_new_frame_nc (fr);
frame = kvp_frame_set_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
/* ============================================================ */
KvpFrame *
kvp_frame_set_value_nc (KvpFrame * frame, const char * key_path,
KvpValue * value)
{
char *last_key;
frame = get_trailer_make (frame, key_path, &last_key);
if (!frame) return NULL;
kvp_frame_set_slot_destructively(frame, last_key, value);
return frame;
}
KvpFrame *
kvp_frame_set_value (KvpFrame * frame, const char * key_path,
const KvpValue * value)
{
KvpValue *new_value = NULL;
char *last_key;
frame = get_trailer_make (frame, key_path, &last_key);
if (!frame) return NULL;
if (value) new_value = kvp_value_copy(value);
kvp_frame_set_slot_destructively(frame, last_key, new_value);
return frame;
}
KvpValue *
kvp_frame_replace_value_nc (KvpFrame * frame, const char * key_path,
KvpValue * new_value)
{
KvpValue * old_value;
char *last_key;
last_key = NULL;
if (new_value)
{
frame = get_trailer_make (frame, key_path, &last_key);
}
else
{
frame = (KvpFrame *) get_trailer_or_null (frame, key_path, &last_key);
}
if (!frame) return NULL;
old_value = kvp_frame_replace_slot_nc (frame, last_key, new_value);
return old_value;
}
/* ============================================================ */
static KvpFrame *
kvp_frame_add_value_nc(KvpFrame * frame, const char * path, KvpValue *value)
{
char *key = NULL;
KvpValue *oldvalue;
KvpFrame* orig_frame = frame;
frame = (KvpFrame *) get_trailer_or_null (frame, path, &key);
oldvalue = kvp_frame_get_slot (frame, key);
ENTER ("old frame=%s", kvp_frame_to_string(frame));
if (oldvalue)
{
/* If already a glist here, just append */
if (KVP_TYPE_GLIST == oldvalue->type)
{
GList * vlist = oldvalue->value.list;
vlist = g_list_append (vlist, value);
oldvalue->value.list = vlist;
}
else
/* If some other value, convert it to a glist */
{
KvpValue *klist;
GList *vlist = NULL;
vlist = g_list_append (vlist, oldvalue);
vlist = g_list_append (vlist, value);
klist = kvp_value_new_glist_nc (vlist);
kvp_frame_replace_slot_nc (frame, key, klist);
}
LEAVE ("new frame=%s", kvp_frame_to_string(frame));
return frame;
}
/* Hmm, if we are here, the path doesn't exist. We need to
* create the path, add the value to it. */
frame = orig_frame;
frame = kvp_frame_set_value_nc (frame, path, value);
LEAVE ("new frame=%s", kvp_frame_to_string(frame));
return frame;
}
void
kvp_frame_add_frame_nc(KvpFrame * frame, const char * path, KvpFrame *fr)
{
KvpValue *value;
value = kvp_value_new_frame_nc (fr);
frame = kvp_frame_add_value_nc (frame, path, value);
if (!frame) kvp_value_delete (value);
}
/* ============================================================ */
void
kvp_frame_set_slot(KvpFrame * frame, const char * slot,
const KvpValue * value)
{
KvpValue *new_value = NULL;
if (!frame) return;
g_return_if_fail (slot && *slot != '\0');
if (value) new_value = kvp_value_copy(value);
kvp_frame_set_slot_destructively(frame, slot, new_value);
}
void
kvp_frame_set_slot_nc(KvpFrame * frame, const char * slot,
KvpValue * value)
{
if (!frame) return;
g_return_if_fail (slot && *slot != '\0');
kvp_frame_set_slot_destructively(frame, slot, value);
}
KvpValue *
kvp_frame_get_slot(const KvpFrame * frame, const char * slot)
{
KvpValue *v;
if (!frame) return NULL;
if (!frame->hash) return NULL; /* Error ... */
v = static_cast<KvpValue*>(g_hash_table_lookup(frame->hash, slot));
return v;
}
/* ============================================================ */
void
kvp_frame_set_slot_path (KvpFrame *frame,
const KvpValue *new_value,
const char *first_key, ...)
{
va_list ap;
const char *key;
if (!frame) return;
g_return_if_fail (first_key && *first_key != '\0');
va_start (ap, first_key);
key = first_key;
while (TRUE)
{
KvpValue *value;
const char *next_key;
next_key = va_arg (ap, const char *);
if (!next_key)
{
kvp_frame_set_slot (frame, key, new_value);
break;
}
g_return_if_fail (*next_key != '\0');
value = kvp_frame_get_slot (frame, key);
if (!value)
{
KvpFrame *new_frame = kvp_frame_new ();
KvpValue *frame_value = kvp_value_new_frame (new_frame);
kvp_frame_set_slot_nc (frame, key, frame_value);
value = kvp_frame_get_slot (frame, key);
if (!value) break;
}
frame = kvp_value_get_frame (value);
if (!frame) break;
key = next_key;
}
va_end (ap);
}
void
kvp_frame_set_slot_path_gslist (KvpFrame *frame,
const KvpValue *new_value,
GSList *key_path)
{
if (!frame || !key_path) return;
while (TRUE)
{
const char *key = static_cast<char*>(key_path->data);
KvpValue *value;
if (!key)
return;
g_return_if_fail (*key != '\0');
key_path = key_path->next;
if (!key_path)
{
kvp_frame_set_slot (frame, key, new_value);
return;
}
value = kvp_frame_get_slot (frame, key);
if (!value)
{
KvpFrame *new_frame = kvp_frame_new ();
KvpValue *frame_value = kvp_value_new_frame (new_frame);
kvp_frame_set_slot_nc (frame, key, frame_value);
value = kvp_frame_get_slot (frame, key);
if (!value)
return;
}
frame = kvp_value_get_frame (value);
if (!frame)
return;
}
}
gint64
kvp_frame_get_gint64(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_gint64(kvp_frame_get_slot (frame, key));
}
double
kvp_frame_get_double(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_double(kvp_frame_get_slot (frame, key));
}
gnc_numeric
kvp_frame_get_numeric(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_numeric(kvp_frame_get_slot (frame, key));
}
const char *
kvp_frame_get_string(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_string(kvp_frame_get_slot (frame, key));
}
GncGUID *
kvp_frame_get_guid(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_guid(kvp_frame_get_slot (frame, key));
}
Timespec
kvp_frame_get_timespec(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_timespec(kvp_frame_get_slot (frame, key));
}
KvpFrame *
kvp_frame_get_frame(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_value_get_frame(kvp_frame_get_slot (frame, key));
}
KvpValue *
kvp_frame_get_value(const KvpFrame *frame, const char *path)
{
char *key = NULL;
frame = get_trailer_or_null (frame, path, &key);
return kvp_frame_get_slot (frame, key);
}
/* ============================================================ */
KvpFrame *
kvp_frame_get_frame_slash (KvpFrame *frame, const char *key_path)
{
char *root;
if (!frame || !key_path) return frame;
root = g_strdup (key_path);
frame = kvp_frame_get_frame_slash_trash (frame, root);
g_free(root);
return frame;
}
/* ============================================================ */
KvpValue *
kvp_frame_get_slot_path (KvpFrame *frame,
const char *first_key, ...)
{
va_list ap;
KvpValue *value;
const char *key;
if (!frame || !first_key) return NULL;
va_start (ap, first_key);
key = first_key;
value = NULL;
while (TRUE)
{
value = kvp_frame_get_slot (frame, key);
if (!value) break;
key = va_arg (ap, const char *);
if (!key) break;
frame = kvp_value_get_frame (value);
if (!frame)
{
value = NULL;
break;
}
}
va_end (ap);
return value;
}
KvpValue *
kvp_frame_get_slot_path_gslist (KvpFrame *frame,
const GSList *key_path)
{
if (!frame || !key_path) return NULL;
while (TRUE)
{
const char *key = static_cast<const char*>(key_path->data);
KvpValue *value;
if (!key) break;
value = kvp_frame_get_slot (frame, key);
if (!value) break;
key_path = key_path->next;
if (!key_path) return value;
frame = kvp_value_get_frame (value);
if (!frame) break;
}
return NULL;
}
/* *******************************************************************
* kvp glist functions
********************************************************************/
void
kvp_glist_delete(GList * list)
{
GList *node;
if (!list) return;
/* Delete the data in the list */
for (node = list; node; node = node->next)
{
KvpValue *val = static_cast<KvpValue*>(node->data);
kvp_value_delete(val);
}
/* Free the backbone */
g_list_free(list);
}
GList *
kvp_glist_copy(const GList * list)
{
GList * retval = NULL;
GList * lptr;
if (!list) return retval;
/* Duplicate the backbone of the list (this duplicates the POINTERS
* to the values; we need to deep-copy the values separately) */
retval = g_list_copy((GList *) list);
/* This step deep-copies the values */
for (lptr = retval; lptr; lptr = lptr->next)
{
lptr->data = kvp_value_copy(static_cast<KvpValue*>(lptr->data));
}
return retval;
}
gint
kvp_glist_compare(const GList * list1, const GList * list2)
{
const GList *lp1;
const GList *lp2;
if (list1 == list2) return 0;
/* Nothing is always less than something */
if (!list1 && list2) return -1;
if (list1 && !list2) return 1;
lp1 = list1;
lp2 = list2;
while (lp1 && lp2)
{
KvpValue *v1 = (KvpValue *) lp1->data;
KvpValue *v2 = (KvpValue *) lp2->data;
gint vcmp = kvp_value_compare(v1, v2);
if (vcmp != 0) return vcmp;
lp1 = lp1->next;
lp2 = lp2->next;
}
if (!lp1 && lp2) return -1;
if (!lp2 && lp1) return 1;
return 0;
}
/* *******************************************************************
* KvpValue functions
********************************************************************/
KvpValue *
kvp_value_new_gint64(gint64 value)
{
KvpValue * retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_GINT64;
retval->value.int64 = value;
return retval;
}
KvpValue *
kvp_value_new_double(double value)
{
KvpValue * retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_DOUBLE;
retval->value.dbl = value;
return retval;
}
KvpValue *
kvp_value_new_numeric(gnc_numeric value)
{
KvpValue * retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_NUMERIC;
retval->value.numeric = value;
return retval;
}
KvpValue *
kvp_value_new_string(const char * value)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_STRING;
retval->value.str = g_strdup(value);
return retval;
}
KvpValue *
kvp_value_new_guid(const GncGUID * value)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_GUID;
retval->value.guid = g_new0(GncGUID, 1);
memcpy(retval->value.guid, value, sizeof(GncGUID));
return retval;
}
KvpValue *
kvp_value_new_timespec(Timespec value)
{
KvpValue * retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_TIMESPEC;
retval->value.timespec = value;
return retval;
}
KvpValue *
kvp_value_new_gdate(GDate value)
{
KvpValue * retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_GDATE;
retval->value.gdate = value;
return retval;
}
KvpValue *
kvp_value_new_binary(const void * value, guint64 datasize)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_BINARY;
retval->value.binary.data = g_new0(char, datasize);
retval->value.binary.datasize = datasize;
memcpy(retval->value.binary.data, value, datasize);
return retval;
}
KvpValue *
kvp_value_new_binary_nc(void * value, guint64 datasize)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_BINARY;
retval->value.binary.data = value;
retval->value.binary.datasize = datasize;
return retval;
}
KvpValue *
kvp_value_new_glist(const GList * value)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_GLIST;
retval->value.list = kvp_glist_copy(value);
return retval;
}
KvpValue *
kvp_value_new_glist_nc(GList * value)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_GLIST;
retval->value.list = value;
return retval;
}
KvpValue *
kvp_value_new_frame(const KvpFrame * value)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_FRAME;
retval->value.frame = kvp_frame_copy(value);
return retval;
}
KvpValue *
kvp_value_new_frame_nc(KvpFrame * value)
{
KvpValue * retval;
if (!value) return NULL;
retval = g_new0(KvpValue, 1);
retval->type = KVP_TYPE_FRAME;
retval->value.frame = value;
return retval;
}
void
kvp_value_delete(KvpValue * value)
{
if (!value) return;
switch (value->type)
{
case KVP_TYPE_STRING:
g_free(value->value.str);
break;
case KVP_TYPE_GUID:
g_free(value->value.guid);
break;
case KVP_TYPE_BINARY:
g_free(value->value.binary.data);
break;
case KVP_TYPE_GLIST:
kvp_glist_delete(value->value.list);
break;
case KVP_TYPE_FRAME:
kvp_frame_delete(value->value.frame);
break;
default:
break;
}
g_free(value);
}
KvpValueType
kvp_value_get_type(const KvpValue * value)
{
if (!value) return KVP_TYPE_INVALID;
return value->type;
}
gint64
kvp_value_get_gint64(const KvpValue * value)
{
if (!value) return 0;
if (value->type == KVP_TYPE_GINT64)
{
return value->value.int64;
}
else
{
return 0;
}
}
double
kvp_value_get_double(const KvpValue * value)
{
if (!value) return 0.0;
if (value->type == KVP_TYPE_DOUBLE)
{
return value->value.dbl;
}
else
{
return 0.0;
}
}
gnc_numeric
kvp_value_get_numeric(const KvpValue * value)
{
if (!value) return gnc_numeric_zero ();
if (value->type == KVP_TYPE_NUMERIC)
{
return value->value.numeric;
}
else
{
return gnc_numeric_zero ();
}
}
char *
kvp_value_get_string(const KvpValue * value)
{
if (!value) return NULL;
if (value->type == KVP_TYPE_STRING)
{
return value->value.str;
}
else
{
return NULL;
}
}
GncGUID *
kvp_value_get_guid(const KvpValue * value)
{
if (!value) return NULL;
if (value->type == KVP_TYPE_GUID)
{
return value->value.guid;
}
else
{
return NULL;
}
}
Timespec
kvp_value_get_timespec(const KvpValue * value)
{
Timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
if (!value) return ts;
if (value->type == KVP_TYPE_TIMESPEC)
return value->value.timespec;
else
return ts;
}
GDate
kvp_value_get_gdate(const KvpValue * value)
{
GDate date;
g_date_clear(&date, 1);
if (!value) return date;
if (value->type == KVP_TYPE_GDATE)
return value->value.gdate;
else
return date;
}
void *
kvp_value_get_binary(const KvpValue * value, guint64 * size_return)
{
if (!value)
{
if (size_return)
*size_return = 0;
return NULL;
}
if (value->type == KVP_TYPE_BINARY)
{
if (size_return)
*size_return = value->value.binary.datasize;
return value->value.binary.data;
}
else
{
if (size_return)
*size_return = 0;
return NULL;
}
}
GList *
kvp_value_get_glist(const KvpValue * value)
{
if (!value) return NULL;
if (value->type == KVP_TYPE_GLIST)
{
return value->value.list;
}
else
{
return NULL;
}
}
KvpFrame *
kvp_value_get_frame(const KvpValue * value)
{
if (!value) return NULL;
if (value->type == KVP_TYPE_FRAME)
{
return value->value.frame;
}
else
{
return NULL;
}
}
KvpFrame *
kvp_value_replace_frame_nc(KvpValue *value, KvpFrame * newframe)
{
KvpFrame *oldframe;
if (!value) return NULL;
if (KVP_TYPE_FRAME != value->type) return NULL;
oldframe = value->value.frame;
value->value.frame = newframe;
return oldframe;
}
GList *
kvp_value_replace_glist_nc(KvpValue * value, GList *newlist)
{
GList *oldlist;
if (!value) return NULL;
if (KVP_TYPE_GLIST != value->type) return NULL;
oldlist = value->value.list;
value->value.list = newlist;
return oldlist;
}
/* manipulators */
KvpValue *
kvp_value_copy(const KvpValue * value)
{
if (!value) return NULL;
switch (value->type)
{
case KVP_TYPE_GINT64:
return kvp_value_new_gint64(value->value.int64);
break;
case KVP_TYPE_DOUBLE:
return kvp_value_new_double(value->value.dbl);
break;
case KVP_TYPE_NUMERIC:
return kvp_value_new_gnc_numeric(value->value.numeric);
break;
case KVP_TYPE_STRING:
return kvp_value_new_string(value->value.str);
break;
case KVP_TYPE_GUID:
return kvp_value_new_guid(value->value.guid);
break;
case KVP_TYPE_GDATE:
return kvp_value_new_gdate(value->value.gdate);
break;
case KVP_TYPE_TIMESPEC:
return kvp_value_new_timespec(value->value.timespec);
break;
case KVP_TYPE_BINARY:
return kvp_value_new_binary(value->value.binary.data,
value->value.binary.datasize);
break;
case KVP_TYPE_GLIST:
return kvp_value_new_glist(value->value.list);
break;
case KVP_TYPE_FRAME:
return kvp_value_new_frame(value->value.frame);
break;
default:
break;
}
return NULL;
}
void
kvp_frame_for_each_slot(KvpFrame *f,
void (*proc)(const char *key,
KvpValue *value,
gpointer data),
gpointer data)
{
if (!f) return;
if (!proc) return;
if (!(f->hash)) return;
g_hash_table_foreach(f->hash, (GHFunc) proc, data);
}
#ifdef _MSC_VER
# define isnan _isnan
#endif
gint
double_compare(double d1, double d2)
{
if (isnan(d1) && isnan(d2)) return 0;
if (d1 < d2) return -1;
if (d1 > d2) return 1;
return 0;
}
gint
kvp_value_compare(const KvpValue * kva, const KvpValue * kvb)
{
if (kva == kvb) return 0;
/* nothing is always less than something */
if (!kva && kvb) return -1;
if (kva && !kvb) return 1;
if (kva->type < kvb->type) return -1;
if (kva->type > kvb->type) return 1;
switch (kva->type)
{
case KVP_TYPE_GINT64:
if (kva->value.int64 < kvb->value.int64) return -1;
if (kva->value.int64 > kvb->value.int64) return 1;
return 0;
break;
case KVP_TYPE_DOUBLE:
return double_compare(kva->value.dbl, kvb->value.dbl);
break;
case KVP_TYPE_NUMERIC:
return gnc_numeric_compare (kva->value.numeric, kvb->value.numeric);
break;
case KVP_TYPE_STRING:
return strcmp(kva->value.str, kvb->value.str);
break;
case KVP_TYPE_GUID:
return guid_compare(kva->value.guid, kvb->value.guid);
break;
case KVP_TYPE_TIMESPEC:
return timespec_cmp(&(kva->value.timespec), &(kvb->value.timespec));
break;
case KVP_TYPE_GDATE:
return g_date_compare(&(kva->value.gdate), &(kvb->value.gdate));
break;
case KVP_TYPE_BINARY:
/* I don't know that this is a good compare. Ab is bigger than Acef.
But I'm not sure that actually matters here. */
if (kva->value.binary.datasize < kvb->value.binary.datasize) return -1;
if (kva->value.binary.datasize > kvb->value.binary.datasize) return 1;
return memcmp(kva->value.binary.data,
kvb->value.binary.data,
kva->value.binary.datasize);
break;
case KVP_TYPE_GLIST:
return kvp_glist_compare(kva->value.list, kvb->value.list);
break;
case KVP_TYPE_FRAME:
return kvp_frame_compare(kva->value.frame, kvb->value.frame);
break;
default:
break;
}
PERR ("reached unreachable code.");
return FALSE;
}
typedef struct
{
gint compare;
KvpFrame *other_frame;
} kvp_frame_cmp_status;
static void
kvp_frame_compare_helper(const char *key, KvpValue * val, gpointer data)
{
kvp_frame_cmp_status *status = (kvp_frame_cmp_status *) data;
if (status->compare == 0)
{
KvpFrame *other_frame = status->other_frame;
KvpValue *other_val = kvp_frame_get_slot(other_frame, key);
if (other_val)
{
status->compare = kvp_value_compare(val, other_val);
}
else
{
status->compare = 1;
}
}
}
gint
kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb)
{
kvp_frame_cmp_status status;
if (fa == fb) return 0;
/* nothing is always less than something */
if (!fa && fb) return -1;
if (fa && !fb) return 1;
/* nothing is always less than something */
if (!fa->hash && fb->hash) return -1;
if (fa->hash && !fb->hash) return 1;
status.compare = 0;
status.other_frame = (KvpFrame *) fb;
kvp_frame_for_each_slot((KvpFrame *) fa, kvp_frame_compare_helper, &status);
if (status.compare != 0)
return status.compare;
status.other_frame = (KvpFrame *) fa;
kvp_frame_for_each_slot((KvpFrame *) fb, kvp_frame_compare_helper, &status);
return(-status.compare);
}
gchar*
binary_to_string(const void *data, guint32 size)
{
GString *output;
guint32 i;
guchar *data_str = (guchar*)data;
output = g_string_sized_new(size * sizeof(char));
for (i = 0; i < size; i++)
{
g_string_append_printf(output, "%02x", (unsigned int) (data_str[i]));
}
return output->str;
}
static gchar*
kvp_value_glist_to_string(const GList *list)
{
gchar *tmp1;
gchar *tmp2;
const GList *cursor;
tmp1 = g_strdup_printf("[ ");
for (cursor = list; cursor; cursor = cursor->next)
{
gchar *tmp3;
tmp3 = kvp_value_to_string((KvpValue *)cursor->data);
tmp2 = g_strdup_printf("%s %s,", tmp1, tmp3 ? tmp3 : "");
g_free(tmp1);
g_free(tmp3);
tmp1 = tmp2;
}
tmp2 = g_strdup_printf("%s ]", tmp1);
g_free(tmp1);
return tmp2;
}
/* struct for kvp frame static funtion testing*/
#ifdef __cplusplus
extern "C"
{
#endif
void init_static_test_pointers( void );
#ifdef __cplusplus
}
#endif
KvpFrame* ( *p_get_trailer_make )( KvpFrame *frame, const char *key_path, char **end_key );
gchar* ( *p_kvp_value_glist_to_string )( const GList *list );
KvpFrame* ( *p_get_or_make )( KvpFrame *fr, const char * key );
const KvpFrame* ( *p_kvp_frame_get_frame_or_null_slash_trash )( const KvpFrame *frame, char *key_path );
const KvpFrame* ( *p_get_trailer_or_null )( const KvpFrame * frame, const char * key_path, char **end_key );
void
init_static_test_pointers( void )
{
p_get_trailer_make = get_trailer_make;
p_kvp_value_glist_to_string = kvp_value_glist_to_string;
p_get_or_make = get_or_make;
p_kvp_frame_get_frame_or_null_slash_trash = kvp_frame_get_frame_or_null_slash_trash;
p_get_trailer_or_null = get_trailer_or_null;
}
/* ----- */
gchar*
kvp_value_to_string(const KvpValue *val)
{
gchar *tmp1;
gchar *tmp2;
const gchar *ctmp;
g_return_val_if_fail(val, NULL);
switch (kvp_value_get_type(val))
{
case KVP_TYPE_GINT64:
return g_strdup_printf("KVP_VALUE_GINT64(%" G_GINT64_FORMAT ")",
kvp_value_get_gint64(val));
break;
case KVP_TYPE_DOUBLE:
return g_strdup_printf("KVP_VALUE_DOUBLE(%g)",
kvp_value_get_double(val));
break;
case KVP_TYPE_NUMERIC:
tmp1 = gnc_numeric_to_string(kvp_value_get_numeric(val));
tmp2 = g_strdup_printf("KVP_VALUE_NUMERIC(%s)", tmp1 ? tmp1 : "");
g_free(tmp1);
return tmp2;
break;
case KVP_TYPE_STRING:
tmp1 = kvp_value_get_string (val);
return g_strdup_printf("KVP_VALUE_STRING(%s)", tmp1 ? tmp1 : "");
break;
case KVP_TYPE_GUID:
/* THREAD-UNSAFE */
ctmp = guid_to_string(kvp_value_get_guid(val));
tmp2 = g_strdup_printf("KVP_VALUE_GUID(%s)", ctmp ? ctmp : "");
return tmp2;
break;
case KVP_TYPE_TIMESPEC:
tmp1 = g_new0 (char, 40);
gnc_timespec_to_iso8601_buff (kvp_value_get_timespec (val), tmp1);
tmp2 = g_strdup_printf("KVP_VALUE_TIMESPEC(%s)", tmp1);
g_free(tmp1);
return tmp2;
break;
case KVP_TYPE_BINARY:
{
guint64 len;
void *data;
data = kvp_value_get_binary(val, &len);
tmp1 = binary_to_string(data, len);
return g_strdup_printf("KVP_VALUE_BINARY(%s)", tmp1 ? tmp1 : "");
}
break;
case KVP_TYPE_GLIST:
tmp1 = kvp_value_glist_to_string(kvp_value_get_glist(val));
tmp2 = g_strdup_printf("KVP_VALUE_GLIST(%s)", tmp1 ? tmp1 : "");
g_free(tmp1);
return tmp2;
break;
case KVP_TYPE_FRAME:
tmp1 = kvp_frame_to_string(kvp_value_get_frame(val));
tmp2 = g_strdup_printf("KVP_VALUE_FRAME(%s)", tmp1 ? tmp1 : "");
g_free(tmp1);
return tmp2;
break;
case KVP_TYPE_GDATE:
return g_strdup_printf("KVP_VALUE_GDATE(%04d-%02d-%02d)",
g_date_get_year(&val->value.gdate),
g_date_get_month(&val->value.gdate),
g_date_get_day(&val->value.gdate));
default:
break;
}
g_assert(FALSE); /* must not be reached */
return g_strdup("");
}
static void
kvp_frame_to_string_helper(gpointer key, gpointer value, gpointer data)
{
gchar *tmp_val;
gchar **str = (gchar**)data;
gchar *old_data = *str;
tmp_val = kvp_value_to_string((KvpValue *)value);
*str = g_strdup_printf("%s %s => %s,\n",
*str ? *str : "",
key ? (char *) key : "",
tmp_val ? tmp_val : "");
g_free(old_data);
g_free(tmp_val);
}
gchar*
kvp_frame_to_string(const KvpFrame *frame)
{
gchar *tmp1;
g_return_val_if_fail (frame != NULL, NULL);
tmp1 = g_strdup_printf("{\n");
if (frame->hash)
g_hash_table_foreach(frame->hash, kvp_frame_to_string_helper, &tmp1);
{
gchar *tmp2;
tmp2 = g_strdup_printf("%s}\n", tmp1);
g_free(tmp1);
tmp1 = tmp2;
}
return tmp1;
}
GHashTable*
kvp_frame_get_hash(const KvpFrame *frame)
{
g_return_val_if_fail (frame != NULL, NULL);
return frame->hash;
}
/* ========================== END OF FILE ======================= */