QOF 0.8.4
qofid.c
00001 /********************************************************************\
00002  * qofid.c -- QOF entity identifier implementation                  *
00003  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com>              *
00004  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>               *
00005  *                                                                  *
00006  * This program is free software; you can redistribute it and/or    *
00007  * modify it under the terms of the GNU General Public License as   *
00008  * published by the Free Software Foundation; either version 2 of   *
00009  * the License, or (at your option) any later version.              *
00010  *                                                                  *
00011  * This program is distributed in the hope that it will be useful,  *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00014  * GNU General Public License for more details.                     *
00015  *                                                                  *
00016  * You should have received a copy of the GNU General Public License*
00017  * along with this program; if not, contact:                        *
00018  *                                                                  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942       *
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00022  *                                                                  *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <string.h>
00028 #include <glib.h>
00029 
00030 #include "qof.h"
00031 #include "qofid-p.h"
00032 
00033 static QofLogModule log_module = QOF_MOD_ENGINE;
00034 
00035 struct QofCollection_s
00036 {
00037     QofIdType e_type;
00038     gboolean is_dirty;
00039 
00040     GHashTable *hash_of_entities;
00041     gpointer data;              /* place where object class can hang arbitrary data */
00042 };
00043 
00044 /* =============================================================== */
00045 
00046 static void qof_collection_remove_entity (QofEntity * ent);
00047 
00048 void
00049 qof_entity_init (QofEntity * ent, QofIdType type, QofCollection * tab)
00050 {
00051     g_return_if_fail (NULL != tab);
00052 
00053     /* XXX We passed redundant info to this routine ... but I think that's
00054      * OK, it might eliminate programming errors. */
00055     if (safe_strcmp (tab->e_type, type))
00056     {
00057         PERR ("attempt to insert \"%s\" into \"%s\"", type, tab->e_type);
00058         return;
00059     }
00060     ent->e_type = CACHE_INSERT (type);
00061 
00062     do
00063     {
00064         guid_new (&ent->guid);
00065 
00066         if (NULL == qof_collection_lookup_entity (tab, &ent->guid))
00067             break;
00068 
00069         PWARN ("duplicate id created, trying again");
00070     }
00071     while (1);
00072 
00073     ent->collection = tab;
00074 
00075     qof_collection_insert_entity (tab, ent);
00076 }
00077 
00078 void
00079 qof_entity_release (QofEntity * ent)
00080 {
00081     if (!ent->collection)
00082         return;
00083     qof_collection_remove_entity (ent);
00084     CACHE_REMOVE (ent->e_type);
00085     ent->e_type = NULL;
00086 }
00087 
00088 
00089 /* This is a restricted function, should be used only during 
00090  * read from file */
00091 void
00092 qof_entity_set_guid (QofEntity * ent, const GUID * guid)
00093 {
00094     QofCollection *col;
00095     if (guid_equal (guid, &ent->guid))
00096         return;
00097 
00098     col = ent->collection;
00099     qof_collection_remove_entity (ent);
00100     ent->guid = *guid;
00101     qof_collection_insert_entity (col, ent);
00102 }
00103 
00104 const GUID *
00105 qof_entity_get_guid (QofEntity * ent)
00106 {
00107     if (!ent)
00108         return guid_null ();
00109     return &ent->guid;
00110 }
00111 
00112 /* =============================================================== */
00113 
00114 static gboolean
00115 id_compare (gconstpointer key_1, gconstpointer key_2)
00116 {
00117     return guid_equal (key_1, key_2);
00118 }
00119 
00120 QofCollection *
00121 qof_collection_new (QofIdType type)
00122 {
00123     QofCollection *col;
00124     col = g_new0 (QofCollection, 1);
00125     col->e_type = CACHE_INSERT (type);
00126     col->hash_of_entities = g_hash_table_new (guid_hash_to_guint, id_compare);
00127     col->data = NULL;
00128     return col;
00129 }
00130 
00131 void
00132 qof_collection_destroy (QofCollection * col)
00133 {
00134     CACHE_REMOVE (col->e_type);
00135     g_hash_table_destroy (col->hash_of_entities);
00136     col->e_type = NULL;
00137     col->hash_of_entities = NULL;
00138     col->data = NULL; 
00139     g_free (col);
00140 }
00141 
00142 /* =============================================================== */
00143 /* getters */
00144 
00145 QofIdType
00146 qof_collection_get_type (QofCollection * col)
00147 {
00148     return col->e_type;
00149 }
00150 
00151 /* =============================================================== */
00152 
00153 static void
00154 qof_collection_remove_entity (QofEntity * ent)
00155 {
00156     QofCollection *col;
00157     if (!ent)
00158         return;
00159     col = ent->collection;
00160     if (!col)
00161         return;
00162     g_hash_table_remove (col->hash_of_entities, &ent->guid);
00163     qof_collection_mark_dirty (col);
00164     ent->collection = NULL;
00165 }
00166 
00167 void
00168 qof_collection_insert_entity (QofCollection * col, QofEntity * ent)
00169 {
00170     if (!col || !ent)
00171         return;
00172     if (guid_equal (&ent->guid, guid_null ()))
00173         return;
00174     g_return_if_fail (col->e_type == ent->e_type);
00175     qof_collection_remove_entity (ent);
00176     g_hash_table_insert (col->hash_of_entities, &ent->guid, ent);
00177     qof_collection_mark_dirty (col);
00178     ent->collection = col;
00179 }
00180 
00181 gboolean
00182 qof_collection_add_entity (QofCollection * coll, QofEntity * ent)
00183 {
00184     QofEntity *e;
00185 
00186     e = NULL;
00187     if (!coll || !ent)
00188     {
00189         return FALSE;
00190     }
00191     if (guid_equal (&ent->guid, guid_null ()))
00192     {
00193         return FALSE;
00194     }
00195     g_return_val_if_fail (coll->e_type == ent->e_type, FALSE);
00196     e = qof_collection_lookup_entity (coll, &ent->guid);
00197     if (e != NULL)
00198     {
00199         return FALSE;
00200     }
00201     g_hash_table_insert (coll->hash_of_entities, &ent->guid, ent);
00202     qof_collection_mark_dirty (coll);
00203     return TRUE;
00204 }
00205 
00206 static void
00207 collection_merge_cb (QofEntity * ent, gpointer data)
00208 {
00209     QofCollection *target;
00210 
00211     target = (QofCollection *) data;
00212     qof_collection_add_entity (target, ent);
00213 }
00214 
00215 gboolean
00216 qof_collection_merge (QofCollection * target, QofCollection * merge)
00217 {
00218     if (!target || !merge)
00219     {
00220         return FALSE;
00221     }
00222     g_return_val_if_fail (target->e_type == merge->e_type, FALSE);
00223     qof_collection_foreach (merge, collection_merge_cb, target);
00224     return TRUE;
00225 }
00226 
00227 static void
00228 collection_compare_cb (QofEntity * ent, gpointer user_data)
00229 {
00230     QofCollection *target;
00231     QofEntity *e;
00232     gint value;
00233 
00234     e = NULL;
00235     target = (QofCollection *) user_data;
00236     if (!target || !ent)
00237     {
00238         return;
00239     }
00240     value = *(gint *) qof_collection_get_data (target);
00241     if (value != 0)
00242     {
00243         return;
00244     }
00245     if (guid_equal (&ent->guid, guid_null ()))
00246     {
00247         value = -1;
00248         qof_collection_set_data (target, &value);
00249         return;
00250     }
00251     g_return_if_fail (target->e_type == ent->e_type);
00252     e = qof_collection_lookup_entity (target, &ent->guid);
00253     if (e == NULL)
00254     {
00255         value = 1;
00256         qof_collection_set_data (target, &value);
00257         return;
00258     }
00259     value = 0;
00260     qof_collection_set_data (target, &value);
00261 }
00262 
00263 gint
00264 qof_collection_compare (QofCollection * target, QofCollection * merge)
00265 {
00266     gint value;
00267 
00268     value = 0;
00269     if (!target && !merge)
00270         return 0;
00271     if (target == merge)
00272         return 0;
00273     if (!target && merge)
00274         return -1;
00275     if (target && !merge)
00276         return 1;
00277     if (target->e_type != merge->e_type)
00278         return -1;
00279     qof_collection_set_data (target, &value);
00280     qof_collection_foreach (merge, collection_compare_cb, target);
00281     value = *(gint *) qof_collection_get_data (target);
00282     if (value == 0)
00283     {
00284         qof_collection_set_data (merge, &value);
00285         qof_collection_foreach (target, collection_compare_cb, merge);
00286         value = *(gint *) qof_collection_get_data (merge);
00287     }
00288     return value;
00289 }
00290 
00291 QofEntity *
00292 qof_collection_lookup_entity (QofCollection * col, const GUID * guid)
00293 {
00294     QofEntity *ent;
00295     g_return_val_if_fail (col, NULL);
00296     if (guid == NULL)
00297         return NULL;
00298     ent = g_hash_table_lookup (col->hash_of_entities, guid);
00299     return ent;
00300 }
00301 
00302 QofCollection *
00303 qof_collection_from_glist (QofIdType type, GList * glist)
00304 {
00305     QofCollection *coll;
00306     QofEntity *ent;
00307     GList *list;
00308 
00309     coll = qof_collection_new (type);
00310     for (list = glist; list != NULL; list = list->next)
00311     {
00312         ent = (QofEntity *) list->data;
00313         if (FALSE == qof_collection_add_entity (coll, ent))
00314         {
00315             return NULL;
00316         }
00317     }
00318     return coll;
00319 }
00320 
00321 guint
00322 qof_collection_count (QofCollection * col)
00323 {
00324     guint c;
00325 
00326     c = g_hash_table_size (col->hash_of_entities);
00327     return c;
00328 }
00329 
00330 /* =============================================================== */
00331 
00332 gboolean
00333 qof_collection_is_dirty (QofCollection * col)
00334 {
00335     return col ? col->is_dirty : FALSE;
00336 }
00337 
00338 void
00339 qof_collection_mark_clean (QofCollection * col)
00340 {
00341     if (col)
00342     {
00343         col->is_dirty = FALSE;
00344     }
00345 }
00346 
00347 void
00348 qof_collection_mark_dirty (QofCollection * col)
00349 {
00350     if (col)
00351     {
00352         col->is_dirty = TRUE;
00353     }
00354 }
00355 
00356 /* =============================================================== */
00357 
00358 gpointer
00359 qof_collection_get_data (QofCollection * col)
00360 {
00361     return col ? col->data : NULL;
00362 }
00363 
00364 void
00365 qof_collection_set_data (QofCollection * col, gpointer user_data)
00366 {
00367     if (col)
00368     {
00369         col->data = user_data;
00370     }
00371 }
00372 
00373 /* =============================================================== */
00374 
00375 struct _iterate
00376 {
00377     QofEntityForeachCB fcn;
00378     gpointer data;
00379 };
00380 
00381 static void
00382 foreach_cb (gpointer key __attribute__ ((unused)), gpointer item, 
00383             gpointer arg)
00384 {
00385     struct _iterate *qiter = arg;
00386     QofEntity *ent = item;
00387 
00388     qiter->fcn (ent, qiter->data);
00389 }
00390 
00391 void
00392 qof_collection_foreach (QofCollection * col, QofEntityForeachCB cb_func,
00393     gpointer user_data)
00394 {
00395     struct _iterate qiter;
00396 
00397     g_return_if_fail (col);
00398     g_return_if_fail (cb_func);
00399 
00400     qiter.fcn = cb_func;
00401     qiter.data = user_data;
00402 
00403     g_hash_table_foreach (col->hash_of_entities, foreach_cb, &qiter);
00404 }
00405 
00406 /* =============================================================== */