GnuCash  5.6-139-g03622b03d0+
qofinstance.cpp
1 /********************************************************************\
2  * qofinstance.c -- handler for fields common to all objects *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20  * *
21 \********************************************************************/
22 
23 /*
24  * Object instance holds many common fields that most
25  * gnucash objects use.
26  *
27  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
28  * Copyright (c) 2007 David Hampton <hampton@employees.org>
29  * Copyright 2017 Aaron Laws <dartme18@gmail.com>
30  */
31 
32 #include "guid.hpp"
33 #include <config.h>
34 #include <glib.h>
35 
36 #include <utility>
37 #include "qof.h"
38 #include "qofbook-p.h"
39 #include "qofid-p.h"
40 #include "kvp-frame.hpp"
41 #include "qofinstance-p.h"
42 #include "qof-backend.hpp"
43 
44 static QofLogModule log_module = QOF_MOD_ENGINE;
45 
46 /* ========================================================== */
47 
48 enum
49 {
50  LAST_SIGNAL
51 };
52 
53 enum
54 {
55  PROP_0,
56  PROP_TYPE,
57  PROP_GUID,
58  PROP_COLLECTION,
59  PROP_BOOK,
60  PROP_LAST_UPDATE,
61  PROP_EDITLEVEL,
62  PROP_DESTROYING,
63  PROP_DIRTY,
64  PROP_INFANT,
65 
66  PROP_VERSION,
67  PROP_VERSION_CHECK,
68  PROP_IDATA,
69 };
70 
71 typedef struct QofInstancePrivate
72 {
73 // QofIdType e_type; /**< Entity type */
75  QofCollection *collection;
77  /* The entity_table in which this instance is stored */
78  QofBook * book;
79 
80  /* Timestamp used to track the last modification to this
81  * instance. Typically used to compare two versions of the
82  * same object, to see which is newer. When used with the
83  * SQL backend, this field is reserved for SQL use, to compare
84  * the version in local memory to the remote, server version.
85  */
86  time64 last_update;
87 
88  /* Keep track of nesting level of begin/end edit calls */
89  int editlevel;
90 
91  /* In process of being destroyed */
92  gboolean do_free;
93 
94  /* dirty/clean flag. If dirty, then this instance has been modified,
95  * but has not yet been written out to storage (file/database)
96  */
97  gboolean dirty;
98 
99  /* True iff this instance has never been committed. */
100  gboolean infant;
101 
102  /* version number, used for tracking multiuser updates */
103  gint32 version;
104  guint32 version_check; /* data aging timestamp */
105 
106  /* -------------------------------------------------------------- */
107  /* Backend private expansion data */
108  guint32 idata; /* used by the sql backend for kvp management */
110 
111 #define GET_PRIVATE(o) \
112  ((QofInstancePrivate*)qof_instance_get_instance_private((QofInstance*)o))
113 
114 G_DEFINE_TYPE_WITH_PRIVATE(QofInstance, qof_instance, G_TYPE_OBJECT)
115 QOF_GOBJECT_FINALIZE(qof_instance);
116 #undef G_PARAM_READWRITE
117 #define G_PARAM_READWRITE static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_WRITABLE)
118 
119 static void qof_instance_get_property (GObject *object,
120  guint prop_id,
121  GValue *value,
122  GParamSpec *pspec);
123 static void qof_instance_set_property (GObject *object,
124  guint prop_id,
125  const GValue *value,
126  GParamSpec *pspec);
127 static void qof_instance_dispose(GObject*);
128 static void qof_instance_class_init(QofInstanceClass *klass)
129 {
130  GObjectClass *object_class = G_OBJECT_CLASS(klass);
131  object_class->finalize = qof_instance_finalize;
132  object_class->dispose = qof_instance_dispose;
133  object_class->set_property = qof_instance_set_property;
134  object_class->get_property = qof_instance_get_property;
135 
136  klass->get_display_name = nullptr;
137  klass->refers_to_object = nullptr;
138  klass->get_typed_referring_object_list = nullptr;
139 
140  g_object_class_install_property
141  (object_class,
142  PROP_GUID,
143  g_param_spec_boxed ("guid",
144  "Object GncGUID",
145  "The object Globally Unique ID.",
146  GNC_TYPE_GUID,
147  G_PARAM_READWRITE));
148 
149  g_object_class_install_property
150  (object_class,
151  PROP_COLLECTION,
152  g_param_spec_pointer ("collection",
153  "Object Collection",
154  "A collection of like objects of which this "
155  "particular object is amember. E.g.. A "
156  "collection of accounts, or a collection of "
157  "splits.",
158  G_PARAM_READWRITE));
159 
160  g_object_class_install_property
161  (object_class,
162  PROP_BOOK,
163  g_param_spec_object ("book",
164  "Object Book",
165  "The book that contains this object.",
166  QOF_TYPE_BOOK,
167  G_PARAM_READWRITE));
168 
169  g_object_class_install_property
170  (object_class,
171  PROP_LAST_UPDATE,
172  g_param_spec_pointer ("last-update",
173  "Object Last Update",
174  "A pointer to the last time this object was "
175  "updated. This value is present for use by "
176  "backends and shouldn't be written by other "
177  "code.",
178  G_PARAM_READWRITE));
179 
180  g_object_class_install_property
181  (object_class,
182  PROP_EDITLEVEL,
183  g_param_spec_int ("editlevel",
184  "Object Edit Level",
185  "The object edit level.",
186  0, G_MAXINT32, 0,
187  G_PARAM_READABLE));
188 
189  g_object_class_install_property
190  (object_class,
191  PROP_DESTROYING,
192  g_param_spec_boolean ("destroying",
193  "Object Destroying",
194  "This flag is set to TRUE if the object is "
195  "about to be destroyed.",
196  FALSE,
197  G_PARAM_READWRITE));
198 
199  g_object_class_install_property
200  (object_class,
201  PROP_DIRTY,
202  g_param_spec_boolean ("dirty",
203  "Object Dirty",
204  "This flag is set to TRUE if the object has "
205  "unsaved changes.",
206  FALSE,
207  G_PARAM_READWRITE));
208 
209  g_object_class_install_property
210  (object_class,
211  PROP_INFANT,
212  g_param_spec_boolean ("infant",
213  "Object Infant",
214  "This flag is set to TRUE if the object has "
215  "never been added to a book. This implies "
216  "that its destruction does not affect the "
217  "state of the book, and therefore the saved "
218  "state of the data file.",
219  FALSE,
220  G_PARAM_READABLE));
221 
222  g_object_class_install_property
223  (object_class,
224  PROP_VERSION,
225  g_param_spec_int ("version",
226  "Version",
227  "The version number of the current instance state.",
228  0,
229  G_MAXINT32,
230  0,
231  G_PARAM_READWRITE));
232 
233  g_object_class_install_property
234  (object_class,
235  PROP_VERSION_CHECK,
236  g_param_spec_uint ("version-check",
237  "Version Check",
238  "The version check number of the current instance state.",
239  0,
240  G_MAXUINT32,
241  0,
242  G_PARAM_READWRITE));
243 
244  g_object_class_install_property
245  (object_class,
246  PROP_EDITLEVEL,
247  g_param_spec_uint ("idata",
248  "Object IData",
249  "Per instance backend private data.",
250  0, G_MAXUINT32, 0,
251  G_PARAM_READWRITE));
252 }
253 
254 static void
255 qof_instance_init (QofInstance *inst)
256 {
257  QofInstancePrivate *priv;
258 
259  priv = GET_PRIVATE(inst);
260  priv->book = nullptr;
261  inst->kvp_data = new KvpFrame;
262  priv->last_update = 0;
263  priv->editlevel = 0;
264  priv->do_free = FALSE;
265  priv->dirty = FALSE;
266  priv->infant = TRUE;
267 }
268 
269 void
270 qof_instance_init_data (QofInstance *inst, QofIdType type, QofBook *book)
271 {
272  QofInstancePrivate *priv;
273  QofCollection *col;
274  QofIdType col_type;
275 
276  g_return_if_fail(QOF_IS_INSTANCE(inst));
277  priv = GET_PRIVATE(inst);
278  g_return_if_fail(!priv->book);
279 
280  priv->book = book;
281  col = qof_book_get_collection (book, type);
282  g_return_if_fail(col != nullptr);
283 
284  /* XXX We passed redundant info to this routine ... but I think that's
285  * OK, it might eliminate programming errors. */
286 
287  col_type = qof_collection_get_type(col);
288  if (g_strcmp0(col_type, type))
289  {
290  PERR ("attempt to insert \"%s\" into \"%s\"", type, col_type);
291  return;
292  }
293  priv = GET_PRIVATE(inst);
294  inst->e_type = static_cast<QofIdType>(CACHE_INSERT (type));
295 
296  do
297  {
298  guid_replace(&priv->guid);
299 
300  if (nullptr == qof_collection_lookup_entity (col, &priv->guid))
301  break;
302 
303  PWARN("duplicate id created, trying again");
304  }
305  while (1);
306 
307  priv->collection = col;
308 
309  qof_collection_insert_entity (col, inst);
310 }
311 
312 static void
313 qof_instance_dispose (GObject *instp)
314 {
315  QofInstancePrivate *priv;
316  QofInstance* inst = QOF_INSTANCE(instp);
317 
318  priv = GET_PRIVATE(instp);
319  if (priv->collection)
320  qof_collection_remove_entity(inst);
321 
322  CACHE_REMOVE(inst->e_type);
323  inst->e_type = nullptr;
324 
325  G_OBJECT_CLASS(qof_instance_parent_class)->dispose(instp);
326 }
327 
328 static void
329 qof_instance_finalize_real (GObject *instp)
330 {
331  QofInstancePrivate *priv;
332  QofInstance* inst = QOF_INSTANCE(instp);
333 
334  delete inst->kvp_data;
335  inst->kvp_data = nullptr;
336 
337  priv = GET_PRIVATE(inst);
338  priv->editlevel = 0;
339  priv->do_free = FALSE;
340  priv->dirty = FALSE;
341 }
342 
343 /* Note that g_value_set_object() refs the object, as does
344  * g_object_get(). But g_object_get() only unrefs once when it disgorges
345  * the object, leaving an unbalanced ref, which leaks. So instead of
346  * using g_value_set_object(), use g_value_take_object() which doesn't
347  * ref the object when used in get_property().
348  */
349 static void
350 qof_instance_get_property (GObject *object,
351  guint prop_id,
352  GValue *value,
353  GParamSpec *pspec)
354 {
355  QofInstance *inst;
356  QofInstancePrivate *priv;
357 
358  g_return_if_fail(QOF_IS_INSTANCE(object));
359 
360  inst = QOF_INSTANCE(object);
361  priv = GET_PRIVATE(inst);
362 
363  switch (prop_id)
364  {
365  case PROP_GUID:
366  g_value_set_boxed(value, &priv->guid);
367  break;
368  case PROP_COLLECTION:
369  g_value_set_pointer(value, priv->collection);
370  break;
371  case PROP_BOOK:
372  g_value_take_object(value, priv->book);
373  break;
374  case PROP_LAST_UPDATE:
375  g_value_set_pointer(value, &priv->last_update);
376  break;
377  case PROP_EDITLEVEL:
378  g_value_set_int(value, priv->editlevel);
379  break;
380  case PROP_DESTROYING:
381  g_value_set_boolean(value, priv->do_free);
382  break;
383  case PROP_DIRTY:
384  g_value_set_boolean(value, qof_instance_get_dirty(inst));
385  break;
386  case PROP_INFANT:
387  g_value_set_boolean(value, priv->infant);
388  break;
389  case PROP_VERSION:
390  g_value_set_int(value, priv->version);
391  break;
392  case PROP_VERSION_CHECK:
393  g_value_set_uint(value, priv->version_check);
394  break;
395  case PROP_IDATA:
396  g_value_set_uint(value, priv->idata);
397  break;
398  default:
399  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
400  break;
401  }
402 }
403 
404 static void
405 qof_instance_set_property (GObject *object,
406  guint prop_id,
407  const GValue *value,
408  GParamSpec *pspec)
409 {
410  QofInstance *inst;
411  Time64 t;
412 
413  g_return_if_fail(QOF_IS_INSTANCE(object));
414 
415  inst = QOF_INSTANCE(object);
416 
417  switch (prop_id)
418  {
419  case PROP_GUID:
420  qof_instance_set_guid(inst,
421  static_cast<GncGUID*>(g_value_get_boxed(value)));
422  break;
423  case PROP_COLLECTION:
424  qof_instance_set_collection(inst, static_cast<QofCollection*>(g_value_get_pointer(value)));
425  break;
426  case PROP_BOOK:
428  static_cast<QofBook*>(g_value_get_object(value)));
429  break;
430  case PROP_LAST_UPDATE:
431  t = *(static_cast<Time64*>(g_value_get_pointer(value)));
432  qof_instance_set_last_update(inst, t.t);
433  break;
434  case PROP_DESTROYING:
435  qof_instance_set_destroying(inst, g_value_get_boolean(value));
436  break;
437  case PROP_DIRTY:
438  qof_instance_set_dirty(inst);
439  break;
440  case PROP_VERSION:
441  qof_instance_set_version(inst, g_value_get_int(value));
442  break;
443  case PROP_VERSION_CHECK:
444  qof_instance_set_version_check(inst, g_value_get_uint(value));
445  break;
446  case PROP_IDATA:
447  qof_instance_set_idata(inst, g_value_get_uint(value));
448  break;
449  default:
450  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
451  break;
452  }
453 }
454 
455 const GncGUID *
456 qof_instance_get_guid (gconstpointer inst)
457 {
458  QofInstancePrivate *priv;
459 
460  if (!inst) return nullptr;
461  g_return_val_if_fail(QOF_IS_INSTANCE(inst), guid_null());
462  priv = GET_PRIVATE(inst);
463  return &(priv->guid);
464 }
465 
466 const GncGUID *
467 qof_entity_get_guid (gconstpointer ent)
468 {
469  return ent ? qof_instance_get_guid(ent) : guid_null();
470 }
471 
472 void
473 qof_instance_set_guid (gpointer ptr, const GncGUID *guid)
474 {
475  QofInstancePrivate *priv;
476  QofInstance *inst;
477  QofCollection *col;
478 
479  g_return_if_fail(QOF_IS_INSTANCE(ptr));
480 
481  inst = QOF_INSTANCE(ptr);
482  priv = GET_PRIVATE(inst);
483  if (guid_equal (guid, &priv->guid))
484  return;
485 
486  col = priv->collection;
487  qof_collection_remove_entity(inst);
488  priv->guid = *guid;
489  qof_collection_insert_entity(col, inst);
490 }
491 
492 void
493 qof_instance_copy_guid (gpointer to, gconstpointer from)
494 {
495  g_return_if_fail(QOF_IS_INSTANCE(to));
496  g_return_if_fail(QOF_IS_INSTANCE(from));
497 
498  GET_PRIVATE(to)->guid = GET_PRIVATE(from)->guid;
499 }
500 
501 gint
502 qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
503 {
504  const QofInstancePrivate *priv1, *priv2;
505 
506  g_return_val_if_fail(QOF_IS_INSTANCE(ptr1), -1);
507  g_return_val_if_fail(QOF_IS_INSTANCE(ptr2), 1);
508 
509  priv1 = GET_PRIVATE(ptr1);
510  priv2 = GET_PRIVATE(ptr2);
511 
512  return guid_compare(&priv1->guid, &priv2->guid);
513 }
514 
515 QofCollection *
516 qof_instance_get_collection (gconstpointer ptr)
517 {
518 
519  g_return_val_if_fail(QOF_IS_INSTANCE(ptr), nullptr);
520  return GET_PRIVATE(ptr)->collection;
521 }
522 
523 void
524 qof_instance_set_collection (gconstpointer ptr, QofCollection *col)
525 {
526  g_return_if_fail(QOF_IS_INSTANCE(ptr));
527  GET_PRIVATE(ptr)->collection = col;
528 }
529 
530 QofBook *
531 qof_instance_get_book (gconstpointer inst)
532 {
533  if (!inst) return nullptr;
534  g_return_val_if_fail(QOF_IS_INSTANCE(inst), nullptr);
535  return GET_PRIVATE(inst)->book;
536 }
537 
538 void
539 qof_instance_set_book (gconstpointer inst, QofBook *book)
540 {
541  g_return_if_fail(QOF_IS_INSTANCE(inst));
542  GET_PRIVATE(inst)->book = book;
543 }
544 
545 void
546 qof_instance_copy_book (gpointer ptr1, gconstpointer ptr2)
547 {
548  g_return_if_fail(QOF_IS_INSTANCE(ptr1));
549  g_return_if_fail(QOF_IS_INSTANCE(ptr2));
550 
551  GET_PRIVATE(ptr1)->book = GET_PRIVATE(ptr2)->book;
552 }
553 
554 gboolean
555 qof_instance_books_equal (gconstpointer ptr1, gconstpointer ptr2)
556 {
557  const QofInstancePrivate *priv1, *priv2;
558 
559  g_return_val_if_fail(QOF_IS_INSTANCE(ptr1), FALSE);
560  g_return_val_if_fail(QOF_IS_INSTANCE(ptr2), FALSE);
561 
562  priv1 = GET_PRIVATE(ptr1);
563  priv2 = GET_PRIVATE(ptr2);
564 
565  return (priv1->book == priv2->book);
566 }
567 
568 /* Watch out: This function is still used (as a "friend") in src/import-export/aqb/gnc-ab-kvp.c */
569 KvpFrame*
570 qof_instance_get_slots (const QofInstance *inst)
571 {
572  if (!inst) return nullptr;
573  return inst->kvp_data;
574 }
575 
576 void
577 qof_instance_set_slots (QofInstance *inst, KvpFrame *frm)
578 {
579  QofInstancePrivate *priv;
580 
581  if (!inst) return;
582 
583  priv = GET_PRIVATE(inst);
584  if (inst->kvp_data && (inst->kvp_data != frm))
585  {
586  delete inst->kvp_data;
587  }
588 
589  priv->dirty = TRUE;
590  inst->kvp_data = frm;
591 }
592 
593 void
594 qof_instance_set_last_update (QofInstance *inst, time64 t)
595 {
596  if (!inst) return;
597  GET_PRIVATE(inst)->last_update = t;
598 }
599 
600 gint
601 qof_instance_get_editlevel (gconstpointer ptr)
602 {
603  g_return_val_if_fail(QOF_IS_INSTANCE(ptr), 0);
604  return GET_PRIVATE(ptr)->editlevel;
605 }
606 
607 void qof_instance_increase_editlevel (gpointer ptr)
608 {
609  g_return_if_fail(QOF_IS_INSTANCE(ptr));
610  GET_PRIVATE(ptr)->editlevel++;
611 }
612 
613 void qof_instance_decrease_editlevel (gpointer ptr)
614 {
615  g_return_if_fail(QOF_IS_INSTANCE(ptr));
616  GET_PRIVATE(ptr)->editlevel--;
617 }
618 
619 void qof_instance_reset_editlevel (gpointer ptr)
620 {
621  g_return_if_fail(QOF_IS_INSTANCE(ptr));
622  GET_PRIVATE(ptr)->editlevel = 0;
623 }
624 
625 int
627 {
628  QofInstancePrivate *lpriv, *rpriv;
629 
630  if (!left && !right) return 0;
631  if (!left) return -1;
632  if (!right) return +1;
633 
634  lpriv = GET_PRIVATE(left);
635  rpriv = GET_PRIVATE(right);
636  return lpriv->last_update < rpriv->last_update ? -1 :
637  lpriv->last_update > rpriv->last_update ? 1 : 0;
638 }
639 
640 gboolean
641 qof_instance_get_destroying (gconstpointer ptr)
642 {
643  g_return_val_if_fail(QOF_IS_INSTANCE(ptr), FALSE);
644  return GET_PRIVATE(ptr)->do_free;
645 }
646 
647 void
648 qof_instance_set_destroying (gpointer ptr, gboolean value)
649 {
650  g_return_if_fail(QOF_IS_INSTANCE(ptr));
651  GET_PRIVATE(ptr)->do_free = value;
652 }
653 
654 gboolean
655 qof_instance_get_dirty_flag (gconstpointer ptr)
656 {
657  g_return_val_if_fail(QOF_IS_INSTANCE(ptr), FALSE);
658  return GET_PRIVATE(ptr)->dirty;
659 }
660 
661 void
662 qof_instance_set_dirty_flag (gconstpointer inst, gboolean flag)
663 {
664  g_return_if_fail(QOF_IS_INSTANCE(inst));
665  GET_PRIVATE(inst)->dirty = flag;
666 }
667 
668 void
669 qof_instance_mark_clean (QofInstance *inst)
670 {
671  if (!inst) return;
672  GET_PRIVATE(inst)->dirty = FALSE;
673 }
674 
675 void
676 qof_instance_print_dirty (const QofInstance *inst, gpointer dummy)
677 {
678  QofInstancePrivate *priv;
679 
680  priv = GET_PRIVATE(inst);
681  if (priv->dirty)
682  {
683  gchar guidstr[GUID_ENCODING_LENGTH+1];
684  guid_to_string_buff(&priv->guid, guidstr);
685  printf("%s instance %s is dirty.\n", inst->e_type, guidstr);
686  }
687 }
688 
689 gboolean
690 qof_instance_get_dirty (QofInstance *inst)
691 {
692  QofInstancePrivate *priv;
693 
694  if (!inst)
695  {
696  return FALSE;
697  }
698 
699  priv = GET_PRIVATE(inst);
700  return priv->dirty;
701 }
702 
703 void
704 qof_instance_set_dirty(QofInstance* inst)
705 {
706  QofInstancePrivate *priv;
707 
708  priv = GET_PRIVATE(inst);
709  priv->dirty = TRUE;
710 }
711 
712 gboolean
713 qof_instance_get_infant(const QofInstance *inst)
714 {
715  g_return_val_if_fail(QOF_IS_INSTANCE(inst), FALSE);
716  return GET_PRIVATE(inst)->infant;
717 }
718 
719 gint32
720 qof_instance_get_version (gconstpointer inst)
721 {
722  g_return_val_if_fail(QOF_IS_INSTANCE(inst), 0);
723  return GET_PRIVATE(inst)->version;
724 }
725 
726 void
727 qof_instance_set_version (gpointer inst, gint32 vers)
728 {
729  g_return_if_fail(QOF_IS_INSTANCE(inst));
730  GET_PRIVATE(inst)->version = vers;
731 }
732 
733 void
734 qof_instance_copy_version (gpointer to, gconstpointer from)
735 {
736  g_return_if_fail(QOF_IS_INSTANCE(to));
737  g_return_if_fail(QOF_IS_INSTANCE(from));
738  GET_PRIVATE(to)->version = GET_PRIVATE(from)->version;
739 }
740 
741 guint32
742 qof_instance_get_version_check (gconstpointer inst)
743 {
744  g_return_val_if_fail(QOF_IS_INSTANCE(inst), 0);
745  return GET_PRIVATE(inst)->version_check;
746 }
747 
748 void
749 qof_instance_set_version_check (gpointer inst, guint32 value)
750 {
751  g_return_if_fail(QOF_IS_INSTANCE(inst));
752  GET_PRIVATE(inst)->version_check = value;
753 }
754 
755 void
756 qof_instance_copy_version_check (gpointer to, gconstpointer from)
757 {
758  g_return_if_fail(QOF_IS_INSTANCE(to));
759  g_return_if_fail(QOF_IS_INSTANCE(from));
760  GET_PRIVATE(to)->version_check = GET_PRIVATE(from)->version_check;
761 }
762 
763 guint32 qof_instance_get_idata (gconstpointer inst)
764 {
765  if (!inst)
766  {
767  return 0;
768  }
769  g_return_val_if_fail(QOF_IS_INSTANCE(inst), 0);
770  return GET_PRIVATE(inst)->idata;
771 }
772 
773 void qof_instance_set_idata(gpointer inst, guint32 idata)
774 {
775  if (!inst)
776  {
777  return;
778  }
779  g_return_if_fail(QOF_IS_INSTANCE(inst));
780  GET_PRIVATE(inst)->idata = idata;
781 }
782 
783 /* ========================================================== */
784 
785 /* Returns a displayable name to represent this object */
787 {
788  g_return_val_if_fail( inst != nullptr, nullptr );
789 
790  if ( QOF_INSTANCE_GET_CLASS(inst)->get_display_name != nullptr )
791  {
792  return QOF_INSTANCE_GET_CLASS(inst)->get_display_name(inst);
793  }
794  else
795  {
796  /* Not implemented - return default string */
797  return g_strdup_printf("Object %s %p",
799  inst);
800  }
801 }
802 
803 typedef struct
804 {
805  const QofInstance* inst;
806  GList* list;
808 
809 static void
810 get_referring_object_instance_helper(QofInstance* inst, gpointer user_data)
811 {
812  QofInstance** pInst = (QofInstance**)user_data;
813 
814  if (*pInst == nullptr)
815  {
816  *pInst = inst;
817  }
818 }
819 
820 static void
821 get_referring_object_helper(QofCollection* coll, gpointer user_data)
822 {
823  QofInstance* first_instance = nullptr;
825 
826  qof_collection_foreach(coll, get_referring_object_instance_helper, &first_instance);
827 
828  if (first_instance != nullptr)
829  {
830  GList* new_list = qof_instance_get_typed_referring_object_list(first_instance, data->inst);
831  data->list = g_list_concat(data->list, new_list);
832  }
833 }
834 
835 /* Returns a list of objects referring to this object */
837 {
839 
840  g_return_val_if_fail( inst != nullptr, nullptr );
841 
842  /* scan all collections */
843  data.inst = inst;
844  data.list = nullptr;
845 
846  qof_book_foreach_collection(qof_instance_get_book(inst),
847  get_referring_object_helper,
848  &data);
849  return data.list;
850 }
851 
852 static void
853 get_typed_referring_object_instance_helper(QofInstance* inst, gpointer user_data)
854 {
856 
857  if (qof_instance_refers_to_object(inst, data->inst))
858  {
859  data->list = g_list_prepend(data->list, inst);
860  }
861 }
862 
863 GList*
865 {
867 
868  g_return_val_if_fail( coll != nullptr, nullptr );
869  g_return_val_if_fail( ref != nullptr, nullptr );
870 
871  data.inst = ref;
872  data.list = nullptr;
873 
874  qof_collection_foreach(coll, get_typed_referring_object_instance_helper, &data);
875  return data.list;
876 }
877 
878 GList*
880 {
881  g_return_val_if_fail( inst != nullptr, nullptr );
882  g_return_val_if_fail( ref != nullptr, nullptr );
883 
884  if ( QOF_INSTANCE_GET_CLASS(inst)->get_typed_referring_object_list != nullptr )
885  {
886  return QOF_INSTANCE_GET_CLASS(inst)->get_typed_referring_object_list(inst, ref);
887  }
888  else
889  {
890  /* Not implemented - by default, loop through all objects of this object's type and check
891  them individually. */
892  QofCollection* coll;
893 
894  coll = qof_instance_get_collection(inst);
896  }
897 }
898 
899 /* Check if this object refers to a specific object */
900 gboolean qof_instance_refers_to_object(const QofInstance* inst, const QofInstance* ref)
901 {
902  g_return_val_if_fail( inst != nullptr, FALSE );
903  g_return_val_if_fail( ref != nullptr, FALSE );
904 
905  if ( QOF_INSTANCE_GET_CLASS(inst)->refers_to_object != nullptr )
906  {
907  return QOF_INSTANCE_GET_CLASS(inst)->refers_to_object(inst, ref);
908  }
909  else
910  {
911  /* Not implemented - default = NO */
912  return FALSE;
913  }
914 }
915 
916 /* g_object_set/get wrappers */
917 void
918 qof_instance_get (const QofInstance *inst, const gchar *first_prop, ...)
919 {
920  va_list ap;
921  g_return_if_fail (QOF_IS_INSTANCE (inst));
922 
923  va_start (ap, first_prop);
924  g_object_get_valist (G_OBJECT (inst), first_prop, ap);
925  va_end (ap);
926 }
927 
928 void
929 qof_instance_set (QofInstance *inst, const gchar *first_prop, ...)
930 {
931  va_list ap;
932  g_return_if_fail (QOF_IS_INSTANCE (inst));
933 
934  qof_instance_set_dirty (inst);
935  va_start (ap, first_prop);
936  g_object_set_valist (G_OBJECT (inst), first_prop, ap);
937  va_end (ap);
938 }
939 
940 
941 /* =================================================================== */
942 /* Entity edit and commit utilities */
943 /* =================================================================== */
944 
945 gboolean
947 {
948  QofInstancePrivate *priv;
949 
950  if (!inst) return FALSE;
951 
952  priv = GET_PRIVATE(inst);
953  priv->editlevel++;
954  if (1 < priv->editlevel) return FALSE;
955  if (0 >= priv->editlevel)
956  priv->editlevel = 1;
957 
958  auto be = qof_book_get_backend(priv->book);
959  if (be)
960  be->begin(inst);
961  else
962  priv->dirty = TRUE;
963 
964  return TRUE;
965 }
966 
967 gboolean qof_commit_edit (QofInstance *inst)
968 {
969  QofInstancePrivate *priv;
970 
971  if (!inst) return FALSE;
972 
973  priv = GET_PRIVATE(inst);
974  priv->editlevel--;
975  if (0 < priv->editlevel) return FALSE;
976 
977  if (0 > priv->editlevel)
978  {
979  PERR ("unbalanced call - resetting (was %d)", priv->editlevel);
980  priv->editlevel = 0;
981  }
982  return TRUE;
983 }
984 
985 gboolean
987  void (*on_error)(QofInstance *, QofBackendError),
988  void (*on_done)(QofInstance *),
989  void (*on_free)(QofInstance *))
990 {
991  QofInstancePrivate *priv;
992 
993  priv = GET_PRIVATE(inst);
994 
995  if (priv->dirty &&
996  !(priv->infant && priv->do_free)) {
997  qof_collection_mark_dirty(priv->collection);
998  qof_book_mark_session_dirty(priv->book);
999  }
1000 
1001  /* See if there's a backend. If there is, invoke it. */
1002  auto be = qof_book_get_backend(priv->book);
1003  if (be)
1004  {
1005  QofBackendError errcode;
1006 
1007  /* clear errors */
1008  do
1009  {
1010  errcode = be->get_error();
1011  }
1012  while (errcode != ERR_BACKEND_NO_ERR);
1013 
1014  be->commit(inst);
1015  errcode = be->get_error();
1016  if (errcode != ERR_BACKEND_NO_ERR)
1017  {
1018  /* XXX Should perform a rollback here */
1019  priv->do_free = FALSE;
1020 
1021  /* Push error back onto the stack */
1022  be->set_error (errcode);
1023  if (on_error)
1024  on_error(inst, errcode);
1025  return FALSE;
1026  }
1027  if (!priv->dirty) //Cleared if the save was successful
1028  priv->infant = FALSE;
1029  }
1030 
1031  if (priv->do_free)
1032  {
1033  if (on_free)
1034  on_free(inst);
1035  return TRUE;
1036  }
1037 
1038  if (on_done)
1039  on_done(inst);
1040  return TRUE;
1041 }
1042 
1043 gboolean
1045 {
1046  return (inst->kvp_data != nullptr && !inst->kvp_data->empty());
1047 }
1048 
1049 void qof_instance_set_path_kvp (QofInstance * inst, GValue const * value, std::vector<std::string> const & path)
1050 {
1051  delete inst->kvp_data->set_path (path, kvp_value_from_gvalue (value));
1052 }
1053 
1054 void
1055 qof_instance_set_kvp (QofInstance * inst, GValue const * value, unsigned count, ...)
1056 {
1057  std::vector<std::string> path;
1058  va_list args;
1059  va_start (args, count);
1060  for (unsigned i{0}; i < count; ++i)
1061  path.push_back (va_arg (args, char const *));
1062  va_end (args);
1063  delete inst->kvp_data->set_path (path, kvp_value_from_gvalue (value));
1064 }
1065 
1066 void qof_instance_get_path_kvp (QofInstance * inst, GValue * value, std::vector<std::string> const & path)
1067 {
1068  gvalue_from_kvp_value (inst->kvp_data->get_slot (path), value);
1069 }
1070 
1071 void
1072 qof_instance_get_kvp (QofInstance * inst, GValue * value, unsigned count, ...)
1073 {
1074  std::vector<std::string> path;
1075  va_list args;
1076  va_start (args, count);
1077  for (unsigned i{0}; i < count; ++i)
1078  path.push_back (va_arg (args, char const *));
1079  va_end (args);
1080  gvalue_from_kvp_value (inst->kvp_data->get_slot (path), value);
1081 }
1082 
1083 void
1084 qof_instance_copy_kvp (QofInstance *to, const QofInstance *from)
1085 {
1086  delete to->kvp_data;
1087  to->kvp_data = new KvpFrame(*from->kvp_data);
1088 }
1089 
1090 void
1091 qof_instance_swap_kvp (QofInstance *a, QofInstance *b)
1092 {
1093  std::swap(a->kvp_data, b->kvp_data);
1094 }
1095 
1096 int
1097 qof_instance_compare_kvp (const QofInstance *a, const QofInstance *b)
1098 {
1099  return compare(a->kvp_data, b->kvp_data);
1100 }
1101 
1102 char*
1103 qof_instance_kvp_as_string (const QofInstance *inst)
1104 {
1105  auto str{inst->kvp_data->to_string()};
1106  return g_strdup(str.c_str());
1107 }
1108 
1109 void
1110 qof_instance_kvp_add_guid (const QofInstance *inst, const char* path,
1111  time64 time, const char *key,
1112  const GncGUID *guid)
1113 {
1114  g_return_if_fail (inst->kvp_data != nullptr);
1115 
1116  auto container = new KvpFrame;
1117  Time64 t{time};
1118  container->set({key}, new KvpValue(const_cast<GncGUID*>(guid)));
1119  container->set({"date"}, new KvpValue(t));
1120  delete inst->kvp_data->set_path({path}, new KvpValue(container));
1121 }
1122 
1123 inline static gboolean
1124 kvp_match_guid (KvpValue *v, std::vector<std::string> const & path, const GncGUID *guid)
1125 {
1126  if (v->get_type() != KvpValue::Type::FRAME)
1127  return FALSE;
1128  auto frame = v->get<KvpFrame*>();
1129  auto val = frame->get_slot(path);
1130  if (val == nullptr || val->get_type() != KvpValue::Type::GUID)
1131  return FALSE;
1132  auto this_guid = val->get<GncGUID*>();
1133 
1134  return guid_equal (this_guid, guid);
1135 }
1136 
1137 gboolean
1138 qof_instance_kvp_has_guid (const QofInstance *inst, const char *path,
1139  const char* key, const GncGUID *guid)
1140 {
1141  g_return_val_if_fail (inst->kvp_data != nullptr, FALSE);
1142  g_return_val_if_fail (guid != nullptr, FALSE);
1143 
1144  auto v = inst->kvp_data->get_slot({path});
1145  if (v == nullptr) return FALSE;
1146 
1147  switch (v->get_type())
1148  {
1149  case KvpValue::Type::FRAME:
1150  return kvp_match_guid (v, {key}, guid);
1151  break;
1152  case KvpValue::Type::GLIST:
1153  {
1154  auto list = v->get<GList*>();
1155  for (auto node = list; node != nullptr; node = node->next)
1156  {
1157  auto val = static_cast<KvpValue*>(node->data);
1158  if (kvp_match_guid (val, {key}, guid))
1159  {
1160  return TRUE;
1161  }
1162  }
1163  break;
1164  }
1165  default:
1166  PWARN ("Instance KVP on path %s contains the wrong type.", path);
1167  break;
1168  }
1169  return FALSE;
1170 }
1171 
1172 void
1173 qof_instance_kvp_remove_guid (const QofInstance *inst, const char *path,
1174  const char *key, const GncGUID *guid)
1175 {
1176  g_return_if_fail (inst->kvp_data != nullptr);
1177  g_return_if_fail (guid != nullptr);
1178 
1179  auto v = inst->kvp_data->get_slot({path});
1180  if (v == nullptr) return;
1181 
1182  switch (v->get_type())
1183  {
1184  case KvpValue::Type::FRAME:
1185  if (kvp_match_guid (v, {key}, guid))
1186  {
1187  delete inst->kvp_data->set_path({path}, nullptr);
1188  delete v;
1189  }
1190  break;
1191  case KvpValue::Type::GLIST:
1192  {
1193  auto list = v->get<GList*>();
1194  for (auto node = list; node != nullptr; node = node->next)
1195  {
1196  auto val = static_cast<KvpValue*>(node->data);
1197  if (kvp_match_guid (val, {key}, guid))
1198  {
1199  list = g_list_delete_link (list, node);
1200  v->set(list);
1201  delete val;
1202  break;
1203  }
1204  }
1205  break;
1206  }
1207  default:
1208  PWARN ("Instance KVP on path %s contains the wrong type.", path);
1209  break;
1210  }
1211  return;
1212 }
1213 
1214 void
1215 qof_instance_kvp_merge_guids (const QofInstance *target,
1216  const QofInstance *donor, const char *path)
1217 {
1218  g_return_if_fail (target != nullptr);
1219  g_return_if_fail (donor != nullptr);
1220 
1221  if (! qof_instance_has_slot (donor, path)) return;
1222  auto v = donor->kvp_data->get_slot({path});
1223  if (v == nullptr) return;
1224 
1225  auto target_val = target->kvp_data->get_slot({path});
1226  switch (v->get_type())
1227  {
1228  case KvpValue::Type::FRAME:
1229  if (target_val)
1230  target_val->add(v);
1231  else
1232  target->kvp_data->set_path({path}, v);
1233  donor->kvp_data->set({path}, nullptr); //Contents moved, Don't delete!
1234  break;
1235  case KvpValue::Type::GLIST:
1236  if (target_val)
1237  {
1238  auto list = target_val->get<GList*>();
1239  list = g_list_concat(list, v->get<GList*>());
1240  target_val->set(list);
1241  }
1242  else
1243  target->kvp_data->set({path}, v);
1244  donor->kvp_data->set({path}, nullptr); //Contents moved, Don't delete!
1245  break;
1246  default:
1247  PWARN ("Instance KVP on path %s contains the wrong type.", path);
1248  break;
1249  }
1250 }
1251 
1252 bool qof_instance_has_path_slot (QofInstance const * inst, std::vector<std::string> const & path)
1253 {
1254  return inst->kvp_data->get_slot (path) != nullptr;
1255 }
1256 
1257 gboolean
1258 qof_instance_has_slot (const QofInstance *inst, const char *path)
1259 {
1260  return inst->kvp_data->get_slot({path}) != nullptr;
1261 }
1262 
1263 void qof_instance_slot_path_delete (QofInstance const * inst, std::vector<std::string> const & path)
1264 {
1265  delete inst->kvp_data->set (path, nullptr);
1266 }
1267 
1268 void
1269 qof_instance_slot_delete (QofInstance const *inst, char const * path)
1270 {
1271  delete inst->kvp_data->set ({path}, nullptr);
1272 }
1273 
1274 void qof_instance_slot_path_delete_if_empty (QofInstance const * inst, std::vector<std::string> const & path)
1275 {
1276  auto slot = inst->kvp_data->get_slot (path);
1277  if (slot)
1278  {
1279  auto frame = slot->get <KvpFrame*> ();
1280  if (frame && frame->empty())
1281  delete inst->kvp_data->set (path, nullptr);
1282  }
1283 }
1284 
1285 void
1286 qof_instance_slot_delete_if_empty (QofInstance const *inst, char const * path)
1287 {
1288  auto slot = inst->kvp_data->get_slot ({path});
1289  if (slot)
1290  {
1291  auto frame = slot->get <KvpFrame*> ();
1292  if (frame && frame->empty ())
1293  delete inst->kvp_data->set ({path}, nullptr);
1294  }
1295 }
1296 
1297 std::vector <std::pair <std::string, KvpValue*>>
1298 qof_instance_get_slots_prefix (QofInstance const * inst, std::string const & prefix)
1299 {
1300  std::vector <std::pair <std::string, KvpValue*>> ret;
1301  inst->kvp_data->for_each_slot_temp ([&prefix, &ret] (std::string const & key, KvpValue * val) {
1302  if (key.find (prefix) == 0)
1303  ret.emplace_back (key, val);
1304  });
1305  return ret;
1306 }
1307 
1308 namespace {
1309 struct wrap_param
1310 {
1311  void (*proc)(const char*, const GValue*, void*);
1312  void *user_data;
1313 };
1314 }
1315 
1316 static void
1317 wrap_gvalue_function (const char* key, KvpValue *val, wrap_param & param)
1318 {
1319  GValue *gv;
1320  if (val->get_type() != KvpValue::Type::FRAME)
1321  gv = gvalue_from_kvp_value(val);
1322  else
1323  {
1324  gv = g_slice_new0 (GValue);
1325  g_value_init (gv, G_TYPE_STRING);
1326  g_value_set_string (gv, nullptr);
1327  }
1328  param.proc(key, gv, param.user_data);
1329  g_slice_free (GValue, gv);
1330 }
1331 
1332 void
1333 qof_instance_foreach_slot (const QofInstance *inst, const char* head, const char* category,
1334  void (*proc)(const char*, const GValue*, void*), void* data)
1335 {
1336  std::vector<std::string> path {head};
1337  if (category)
1338  path.emplace_back (category);
1339 
1340  auto slot = inst->kvp_data->get_slot(path);
1341  if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
1342  return;
1343  auto frame = slot->get<KvpFrame*>();
1344  wrap_param new_data {proc, data};
1345  frame->for_each_slot_temp(&wrap_gvalue_function, new_data);
1346 }
1347 
1348 /* ========================== END OF FILE ======================= */
1349 
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
Compare two instances, based on their last update times.
void guid_replace(GncGUID *guid)
Generate a new guid.
Definition: guid.cpp:143
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
void qof_instance_set_kvp(QofInstance *, GValue const *value, unsigned count,...)
Sets a KVP slot to a value from a GValue.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition: qofid.cpp:212
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
GncGUID guid
GncGUID for the entity.
Definition: qofinstance.cpp:74
GList * qof_instance_get_referring_object_list_from_collection(const QofCollection *coll, const QofInstance *ref)
Returns a list of objects from the collection which refer to the specific object. ...
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
void qof_instance_set(QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_set Group setting multiple parameters in a single begin/commit/rollback.
KvpValue * kvp_value_from_gvalue(const GValue *gval)
Convert a gvalue into a kvpvalue.
Definition: kvp-frame.cpp:285
QofCollection * qof_instance_get_collection(gconstpointer ptr)
Return the collection this instance belongs to.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:173
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void qof_collection_foreach(const QofCollection *col, QofInstanceForeachCB cb_func, gpointer user_data)
Call the callback for each entity in the collection.
Definition: qofid.cpp:321
void qof_instance_get_kvp(QofInstance *, GValue *value, unsigned count,...)
Retrieves the contents of a KVP slot into a provided GValue.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
const gchar * QofIdType
QofIdType declaration.
Definition: qofid.h:80
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
guint32 qof_instance_get_idata(gconstpointer inst)
get the instance tag number used for kvp management in sql backends.
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
Definition: guid.cpp:204
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
void qof_instance_copy_book(gpointer ptr1, gconstpointer ptr2)
Copy the book from one QofInstances to another.
GList * qof_instance_get_typed_referring_object_list(const QofInstance *inst, const QofInstance *ref)
Returns a list of my type of object which refers to an object.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
void qof_collection_insert_entity(QofCollection *, QofInstance *)
Take entity, remove it from whatever collection its currently in, and place it in a new collection...
Definition: qofid.cpp:95
const GncGUID * qof_entity_get_guid(gconstpointer ent)
gboolean qof_instance_books_equal(gconstpointer ptr1, gconstpointer ptr2)
See if two QofInstances share the same book.
void qof_book_mark_session_dirty(QofBook *book)
The qof_book_mark_dirty() routine marks the book as having been modified.
Definition: qofbook.cpp:397
GList * qof_instance_get_referring_object_list(const QofInstance *inst)
Returns a list of objects which refer to a specific object.
QofIdType qof_collection_get_type(const QofCollection *col)
return the type that the collection stores
Definition: qofid.cpp:73
GValue * gvalue_from_kvp_value(const KvpValue *kval, GValue *val)
Convert a kvp_value into a GValue.
Definition: kvp-frame.cpp:236
QofIdType e_type
Entity type.
Definition: qofinstance.h:75
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:130
void qof_instance_set_book(gconstpointer inst, QofBook *book)
Set the book pointer.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
gboolean qof_instance_refers_to_object(const QofInstance *inst, const QofInstance *ref)
Does this object refer to a specific object.
gboolean qof_instance_has_kvp(QofInstance *inst)
Report whether a QofInstance has anything stored in KVP.
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
Definition: qofbook.cpp:440
The type used to store guids in C.
Definition: guid.h:75
QofCollection * collection
Entity collection.
Definition: qofinstance.cpp:75
gchar * qof_instance_get_display_name(const QofInstance *inst)
Returns a displayable name for this object.