You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/lib/libqof/qof/qofid.c

416 lines
10 KiB

/********************************************************************\
* qofid.c -- QOF entity identifier implementation *
* Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
* Copyright (C) 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 *
* *
\********************************************************************/
#include "config.h"
#include <string.h>
#include <glib.h>
#include "qof.h"
#include "qofid-p.h"
static QofLogModule log_module = QOF_MOD_ENGINE;
static gboolean qof_alt_dirty_mode = FALSE;
struct QofCollection_s
{
QofIdType e_type;
gboolean is_dirty;
GHashTable * hash_of_entities;
gpointer data; /* place where object class can hang arbitrary data */
};
/* =============================================================== */
gboolean
qof_get_alt_dirty_mode (void)
{
return qof_alt_dirty_mode;
}
void
qof_set_alt_dirty_mode (gboolean enabled)
{
qof_alt_dirty_mode = enabled;
}
/* =============================================================== */
static void qof_collection_remove_entity (QofEntity *ent);
void
qof_entity_init (QofEntity *ent, QofIdType type, QofCollection * tab)
{
g_return_if_fail (NULL != tab);
/* XXX We passed redundant info to this routine ... but I think that's
* OK, it might eliminate programming errors. */
if (safe_strcmp(tab->e_type, type))
{
PERR ("attempt to insert \"%s\" into \"%s\"", type, tab->e_type);
return;
}
ent->e_type = CACHE_INSERT (type);
do
{
guid_new(&ent->guid);
if (NULL == qof_collection_lookup_entity (tab, &ent->guid)) break;
PWARN("duplicate id created, trying again");
} while(1);
ent->collection = tab;
qof_collection_insert_entity (tab, ent);
}
void
qof_entity_release (QofEntity *ent)
{
if (!ent->collection) return;
qof_collection_remove_entity (ent);
CACHE_REMOVE (ent->e_type);
ent->e_type = NULL;
}
/* This is a restricted function, should be used only during
* read from file */
void
qof_entity_set_guid (QofEntity *ent, const GUID *guid)
{
QofCollection *col;
if (guid_equal (guid, &ent->guid)) return;
col = ent->collection;
qof_collection_remove_entity (ent);
ent->guid = *guid;
qof_collection_insert_entity (col, ent);
}
const GUID *
qof_entity_get_guid (const QofEntity *ent)
{
if (!ent) return guid_null();
return &ent->guid;
}
/* =============================================================== */
static guint
id_hash (gconstpointer key)
{
const GUID *guid = key;
if (key == NULL)
return 0;
/* Compiler should optimize this all away! */
if (sizeof(guint) <= 16)
return *((guint *) guid->data);
else
{
guint hash = 0;
unsigned int i, j;
for (i = 0, j = 0; i < sizeof(guint); i++, j++)
{
if (j == 16)
j = 0;
hash <<= 4;
hash |= guid->data[j];
}
return hash;
}
}
static gboolean
id_compare(gconstpointer key_1, gconstpointer key_2)
{
return guid_equal (key_1, key_2);
}
QofCollection *
qof_collection_new (QofIdType type)
{
QofCollection *col;
col = g_new0(QofCollection, 1);
col->e_type = CACHE_INSERT (type);
col->hash_of_entities = g_hash_table_new (id_hash, id_compare);
col->data = NULL;
return col;
}
void
qof_collection_destroy (QofCollection *col)
{
CACHE_REMOVE (col->e_type);
g_hash_table_destroy(col->hash_of_entities);
col->e_type = NULL;
col->hash_of_entities = NULL;
col->data = NULL; /** XXX there should be a destroy notifier for this */
g_free (col);
}
/* =============================================================== */
/* getters */
QofIdType
qof_collection_get_type (const QofCollection *col)
{
return col->e_type;
}
/* =============================================================== */
static void
qof_collection_remove_entity (QofEntity *ent)
{
QofCollection *col;
if (!ent) return;
col = ent->collection;
if (!col) return;
g_hash_table_remove (col->hash_of_entities, &ent->guid);
if (!qof_alt_dirty_mode)
qof_collection_mark_dirty(col);
ent->collection = NULL;
}
void
qof_collection_insert_entity (QofCollection *col, QofEntity *ent)
{
if (!col || !ent) return;
if (guid_equal(&ent->guid, guid_null())) return;
g_return_if_fail (col->e_type == ent->e_type);
qof_collection_remove_entity (ent);
g_hash_table_insert (col->hash_of_entities, &ent->guid, ent);
if (!qof_alt_dirty_mode)
qof_collection_mark_dirty(col);
ent->collection = col;
}
gboolean
qof_collection_add_entity (QofCollection *coll, QofEntity *ent)
{
QofEntity *e;
e = NULL;
if (!coll || !ent) { return FALSE; }
if (guid_equal(&ent->guid, guid_null())) { return FALSE; }
g_return_val_if_fail (coll->e_type == ent->e_type, FALSE);
e = qof_collection_lookup_entity(coll, &ent->guid);
if ( e != NULL ) { return FALSE; }
g_hash_table_insert (coll->hash_of_entities, &ent->guid, ent);
if (!qof_alt_dirty_mode)
qof_collection_mark_dirty(coll);
return TRUE;
}
static void
collection_merge_cb (QofEntity *ent, gpointer data)
{
QofCollection *target;
target = (QofCollection*)data;
qof_collection_add_entity(target, ent);
}
gboolean
qof_collection_merge (QofCollection *target, QofCollection *merge)
{
if(!target || !merge) { return FALSE; }
g_return_val_if_fail (target->e_type == merge->e_type, FALSE);
qof_collection_foreach(merge, collection_merge_cb, target);
return TRUE;
}
static void
collection_compare_cb (QofEntity *ent, gpointer user_data)
{
QofCollection *target;
QofEntity *e;
gint value;
e = NULL;
target = (QofCollection*)user_data;
if (!target || !ent) { return; }
value = *(gint*)qof_collection_get_data(target);
if (value != 0) { return; }
if (guid_equal(&ent->guid, guid_null()))
{
value = -1;
qof_collection_set_data(target, &value);
return;
}
g_return_if_fail (target->e_type == ent->e_type);
e = qof_collection_lookup_entity(target, &ent->guid);
if ( e == NULL )
{
value = 1;
qof_collection_set_data(target, &value);
return;
}
value = 0;
qof_collection_set_data(target, &value);
}
gint
qof_collection_compare (QofCollection *target, QofCollection *merge)
{
gint value;
value = 0;
if (!target && !merge) { return 0; }
if (target == merge) { return 0; }
if (!target && merge) { return -1; }
if (target && !merge) { return 1; }
if(target->e_type != merge->e_type) { return -1; }
qof_collection_set_data(target, &value);
qof_collection_foreach(merge, collection_compare_cb, target);
value = *(gint*)qof_collection_get_data(target);
if(value == 0) {
qof_collection_set_data(merge, &value);
qof_collection_foreach(target, collection_compare_cb, merge);
value = *(gint*)qof_collection_get_data(merge);
}
return value;
}
QofEntity *
qof_collection_lookup_entity (const QofCollection *col, const GUID * guid)
{
QofEntity *ent;
g_return_val_if_fail (col, NULL);
if (guid == NULL) return NULL;
ent = g_hash_table_lookup (col->hash_of_entities, guid);
return ent;
}
QofCollection *
qof_collection_from_glist (QofIdType type, GList *glist)
{
QofCollection *coll;
QofEntity *ent;
GList *list;
coll = qof_collection_new(type);
for(list = glist; list != NULL; list = list->next)
{
ent = (QofEntity*)list->data;
if(FALSE == qof_collection_add_entity(coll, ent))
{
return NULL;
}
}
return coll;
}
guint
qof_collection_count (const QofCollection *col)
{
guint c;
c = g_hash_table_size(col->hash_of_entities);
return c;
}
/* =============================================================== */
gboolean
qof_collection_is_dirty (const QofCollection *col)
{
return col ? col->is_dirty : FALSE;
}
void
qof_collection_mark_clean (QofCollection *col)
{
if (col) { col->is_dirty = FALSE; }
}
void
qof_collection_mark_dirty (QofCollection *col)
{
if (col) { col->is_dirty = TRUE; }
}
void
qof_collection_print_dirty (const QofCollection *col, gpointer dummy)
{
if (col->is_dirty)
printf("%s collection is dirty.\n", col->e_type);
qof_collection_foreach(col, (QofEntityForeachCB)qof_instance_print_dirty, NULL);
}
/* =============================================================== */
gpointer
qof_collection_get_data (const QofCollection *col)
{
return col ? col->data : NULL;
}
void
qof_collection_set_data (QofCollection *col, gpointer user_data)
{
if (col) { col->data = user_data; }
}
/* =============================================================== */
struct _iterate {
QofEntityForeachCB fcn;
gpointer data;
};
static void foreach_cb (gpointer key, gpointer item, gpointer arg)
{
struct _iterate *iter = arg;
QofEntity *ent = item;
iter->fcn (ent, iter->data);
}
void
qof_collection_foreach (const QofCollection *col, QofEntityForeachCB cb_func,
gpointer user_data)
{
struct _iterate iter;
g_return_if_fail (col);
g_return_if_fail (cb_func);
iter.fcn = cb_func;
iter.data = user_data;
g_hash_table_foreach (col->hash_of_entities, foreach_cb, &iter);
}
/* =============================================================== */