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