QOF  0.7.5
qofid.c
1 /********************************************************************\
2  * qofid.c -- QOF entity identifier implementation *
3  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
4  * Copyright (C) 2003 Linas Vepstas <linas@linas.org> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23 \********************************************************************/
24 
25 #include "config.h"
26 
27 #include <string.h>
28 #include <glib.h>
29 
30 #include "qof.h"
31 #include "qofid-p.h"
32 
33 static QofLogModule log_module = QOF_MOD_ENGINE;
34 
35 struct QofCollection_s
36 {
37  QofIdType e_type;
38  gboolean is_dirty;
39 
40  GHashTable *hash_of_entities;
41  gpointer data; /* place where object class can hang arbitrary data */
42 };
43 
44 /* =============================================================== */
45 
46 static void qof_collection_remove_entity (QofEntity * ent);
47 
48 void
50 {
51  g_return_if_fail (NULL != tab);
52 
53  /* XXX We passed redundant info to this routine ... but I think that's
54  * OK, it might eliminate programming errors. */
55  if (safe_strcmp (tab->e_type, type))
56  {
57  PERR ("attempt to insert \"%s\" into \"%s\"", type, tab->e_type);
58  return;
59  }
60  ent->e_type = CACHE_INSERT (type);
61 
62  do
63  {
64  guid_new (&ent->guid);
65 
66  if (NULL == qof_collection_lookup_entity (tab, &ent->guid))
67  break;
68 
69  PWARN ("duplicate id created, trying again");
70  }
71  while (1);
72 
73  ent->collection = tab;
74 
76 }
77 
78 void
80 {
81  if (!ent->collection)
82  return;
83  qof_collection_remove_entity (ent);
84  CACHE_REMOVE (ent->e_type);
85  ent->e_type = NULL;
86 }
87 
88 
89 /* This is a restricted function, should be used only during
90  * read from file */
91 void
92 qof_entity_set_guid (QofEntity * ent, const GUID * guid)
93 {
94  QofCollection *col;
95  if (guid_equal (guid, &ent->guid))
96  return;
97 
98  col = ent->collection;
99  qof_collection_remove_entity (ent);
100  ent->guid = *guid;
101  qof_collection_insert_entity (col, ent);
102 }
103 
104 const GUID *
106 {
107  if (!ent)
108  return guid_null ();
109  return &ent->guid;
110 }
111 
112 /* =============================================================== */
113 
114 static guint
115 id_hash (gconstpointer key)
116 {
117  const GUID *guid = key;
118 
119  if (key == NULL)
120  return 0;
121 
122  /* Compiler should optimize this all away! */
123  if (sizeof (guint) <= 16)
124  return *((guint *) guid->data);
125  else
126  {
127  guint hash = 0;
128  guint i, j;
129 
130  for (i = 0, j = 0; i < sizeof (guint); i++, j++)
131  {
132  if (j == 16)
133  j = 0;
134 
135  hash <<= 4;
136  hash |= guid->data[j];
137  }
138 
139  return hash;
140  }
141 }
142 
143 static gboolean
144 id_compare (gconstpointer key_1, gconstpointer key_2)
145 {
146  return guid_equal (key_1, key_2);
147 }
148 
151 {
152  QofCollection *col;
153  col = g_new0 (QofCollection, 1);
154  col->e_type = CACHE_INSERT (type);
155  col->hash_of_entities = g_hash_table_new (id_hash, id_compare);
156  col->data = NULL;
157  return col;
158 }
159 
160 void
162 {
163  CACHE_REMOVE (col->e_type);
164  g_hash_table_destroy (col->hash_of_entities);
165  col->e_type = NULL;
166  col->hash_of_entities = NULL;
167  col->data = NULL;
168  g_free (col);
169 }
170 
171 /* =============================================================== */
172 /* getters */
173 
174 QofIdType
176 {
177  return col->e_type;
178 }
179 
180 /* =============================================================== */
181 
182 static void
183 qof_collection_remove_entity (QofEntity * ent)
184 {
185  QofCollection *col;
186  if (!ent)
187  return;
188  col = ent->collection;
189  if (!col)
190  return;
191  g_hash_table_remove (col->hash_of_entities, &ent->guid);
192  qof_collection_mark_dirty (col);
193  ent->collection = NULL;
194 }
195 
196 void
198 {
199  if (!col || !ent)
200  return;
201  if (guid_equal (&ent->guid, guid_null ()))
202  return;
203  g_return_if_fail (col->e_type == ent->e_type);
204  qof_collection_remove_entity (ent);
205  g_hash_table_insert (col->hash_of_entities, &ent->guid, ent);
206  qof_collection_mark_dirty (col);
207  ent->collection = col;
208 }
209 
210 gboolean
212 {
213  QofEntity *e;
214 
215  e = NULL;
216  if (!coll || !ent)
217  {
218  return FALSE;
219  }
220  if (guid_equal (&ent->guid, guid_null ()))
221  {
222  return FALSE;
223  }
224  g_return_val_if_fail (coll->e_type == ent->e_type, FALSE);
225  e = qof_collection_lookup_entity (coll, &ent->guid);
226  if (e != NULL)
227  {
228  return FALSE;
229  }
230  g_hash_table_insert (coll->hash_of_entities, &ent->guid, ent);
231  qof_collection_mark_dirty (coll);
232  return TRUE;
233 }
234 
235 static void
236 collection_merge_cb (QofEntity * ent, gpointer data)
237 {
238  QofCollection *target;
239 
240  target = (QofCollection *) data;
241  qof_collection_add_entity (target, ent);
242 }
243 
244 gboolean
246 {
247  if (!target || !merge)
248  {
249  return FALSE;
250  }
251  g_return_val_if_fail (target->e_type == merge->e_type, FALSE);
252  qof_collection_foreach (merge, collection_merge_cb, target);
253  return TRUE;
254 }
255 
256 static void
257 collection_compare_cb (QofEntity * ent, gpointer user_data)
258 {
259  QofCollection *target;
260  QofEntity *e;
261  gint value;
262 
263  e = NULL;
264  target = (QofCollection *) user_data;
265  if (!target || !ent)
266  {
267  return;
268  }
269  value = *(gint *) qof_collection_get_data (target);
270  if (value != 0)
271  {
272  return;
273  }
274  if (guid_equal (&ent->guid, guid_null ()))
275  {
276  value = -1;
277  qof_collection_set_data (target, &value);
278  return;
279  }
280  g_return_if_fail (target->e_type == ent->e_type);
281  e = qof_collection_lookup_entity (target, &ent->guid);
282  if (e == NULL)
283  {
284  value = 1;
285  qof_collection_set_data (target, &value);
286  return;
287  }
288  value = 0;
289  qof_collection_set_data (target, &value);
290 }
291 
292 gint
294 {
295  gint value;
296 
297  value = 0;
298  if (!target && !merge)
299  return 0;
300  if (target == merge)
301  return 0;
302  if (!target && merge)
303  return -1;
304  if (target && !merge)
305  return 1;
306  if (target->e_type != merge->e_type)
307  return -1;
308  qof_collection_set_data (target, &value);
309  qof_collection_foreach (merge, collection_compare_cb, target);
310  value = *(gint *) qof_collection_get_data (target);
311  if (value == 0)
312  {
313  qof_collection_set_data (merge, &value);
314  qof_collection_foreach (target, collection_compare_cb, merge);
315  value = *(gint *) qof_collection_get_data (merge);
316  }
317  return value;
318 }
319 
320 QofEntity *
322 {
323  QofEntity *ent;
324  g_return_val_if_fail (col, NULL);
325  if (guid == NULL)
326  return NULL;
327  ent = g_hash_table_lookup (col->hash_of_entities, guid);
328  return ent;
329 }
330 
333 {
334  QofCollection *coll;
335  QofEntity *ent;
336  GList *list;
337 
338  coll = qof_collection_new (type);
339  for (list = glist; list != NULL; list = list->next)
340  {
341  ent = (QofEntity *) list->data;
342  if (FALSE == qof_collection_add_entity (coll, ent))
343  {
344  return NULL;
345  }
346  }
347  return coll;
348 }
349 
350 guint
352 {
353  guint c;
354 
355  c = g_hash_table_size (col->hash_of_entities);
356  return c;
357 }
358 
359 /* =============================================================== */
360 
361 gboolean
363 {
364  return col ? col->is_dirty : FALSE;
365 }
366 
367 void
369 {
370  if (col)
371  {
372  col->is_dirty = FALSE;
373  }
374 }
375 
376 void
377 qof_collection_mark_dirty (QofCollection * col)
378 {
379  if (col)
380  {
381  col->is_dirty = TRUE;
382  }
383 }
384 
385 /* =============================================================== */
386 
387 gpointer
389 {
390  return col ? col->data : NULL;
391 }
392 
393 void
394 qof_collection_set_data (QofCollection * col, gpointer user_data)
395 {
396  if (col)
397  {
398  col->data = user_data;
399  }
400 }
401 
402 /* =============================================================== */
403 
404 struct _iterate
405 {
406  QofEntityForeachCB fcn;
407  gpointer data;
408 };
409 
410 static void
411 foreach_cb (gpointer key __attribute__ ((unused)), gpointer item,
412  gpointer arg)
413 {
414  struct _iterate *qiter = arg;
415  QofEntity *ent = item;
416 
417  qiter->fcn (ent, qiter->data);
418 }
419 
420 void
422  gpointer user_data)
423 {
424  struct _iterate qiter;
425 
426  g_return_if_fail (col);
427  g_return_if_fail (cb_func);
428 
429  qiter.fcn = cb_func;
430  qiter.data = user_data;
431 
432  g_hash_table_foreach (col->hash_of_entities, foreach_cb, &qiter);
433 }
434 
435 /* =============================================================== */