QOF: qofbookmerge.c Source File

QOF 0.7.5

qofbookmerge.c
1 /*********************************************************************
2 * QofBookMerge.c -- api for QoFBook merge with collision handling *
3 * Copyright (C) 2004,2005,2006 *
4 * Neil Williams <linux@codehelp.co.uk> *
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 #include <glib.h>
27 #include "qof.h"
28
29 static QofLogModule log_module = QOF_MOD_MERGE;
30
31 /* private rule iteration struct */
32 struct QofBookMergeRuleIterate
33 {
34 QofBookMergeRuleForeachCB fcn;
35 QofBookMergeData *data;
36 QofBookMergeRule *rule;
37 GList *ruleList;
38 guint remainder;
39 };
40
41 /* Make string type parameters 3 times more
42 important in the match than default types.
43 i.e. even if two other parameters differ,
44 a string match will still provide a better target
45 than when other types match and the string does not.
46 */
47 #define DEFAULT_MERGE_WEIGHT 1
48 #define QOF_STRING_WEIGHT 3
49 #define QOF_DATE_STRING_LENGTH MAX_DATE_LENGTH
50
51 static QofBookMergeRule *
52 qof_book_merge_update_rule (QofBookMergeRule * currentRule, gboolean match,
53 gint weight)
54 {
55 gboolean absolute;
56
57 absolute = currentRule->mergeAbsolute;
58 if (absolute && match && currentRule->mergeResult == MERGE_UNDEF)
59 currentRule->mergeResult = MERGE_ABSOLUTE;
60 if (absolute && !match)
61 currentRule->mergeResult = MERGE_UPDATE;
62 if (!absolute && match && currentRule->mergeResult == MERGE_UNDEF)
63 currentRule->mergeResult = MERGE_DUPLICATE;
64 if (!absolute && !match)
65 {
66 currentRule->difference += weight;
67 if (currentRule->mergeResult == MERGE_DUPLICATE)
68 currentRule->mergeResult = MERGE_REPORT;
69 }
70 return currentRule;
71 }
72
73 struct collect_list_s
74 {
75 GSList *linkedEntList;
76 };
77
78 static void
79 collect_reference_cb (QofEntity * ent, gpointer user_data)
80 {
81 struct collect_list_s *s;
82
83 s = (struct collect_list_s *) user_data;
84 if (!ent || !s)
85 return;
86 s->linkedEntList = g_slist_prepend (s->linkedEntList, ent);
87 }
88
89 static gint
90 qof_book_merge_compare (QofBookMergeData * mergeData)
91 {
92 QofBookMergeRule *currentRule;
93 QofCollection *mergeColl, *targetColl;
94 gchar *stringImport, *stringTarget;
95 QofEntity *mergeEnt, *targetEnt, *referenceEnt;
96 const GUID *guidImport, *guidTarget;
97 QofParam *qtparam;
98 KvpFrame *kvpImport, *kvpTarget;
99 QofIdType mergeParamName;
100 QofType mergeType;
101 GSList *paramList;
102 gboolean absolute, mergeError, knowntype, mergeMatch, booleanImport,
103 booleanTarget, (*boolean_getter) (QofEntity *, QofParam *);
104 QofNumeric numericImport, numericTarget,
105 (*numeric_getter) (QofEntity *, QofParam *);
106 gdouble doubleImport, doubleTarget, (*double_getter) (QofEntity *,
107 QofParam *);
108 gint32 i32Import, i32Target, (*int32_getter) (QofEntity *, QofParam *);
109 gint64 i64Import, i64Target, (*int64_getter) (QofEntity *, QofParam *);
110 gchar charImport, charTarget, (*char_getter) (QofEntity *, QofParam *);
111
112 g_return_val_if_fail ((mergeData != NULL), -1);
113 currentRule = mergeData->currentRule;
114 g_return_val_if_fail ((currentRule != NULL), -1);
115 absolute = currentRule->mergeAbsolute;
116 mergeEnt = currentRule->importEnt;
117 targetEnt = currentRule->targetEnt;
118 paramList = currentRule->mergeParam;
119 currentRule->difference = 0;
120 currentRule->mergeResult = MERGE_UNDEF;
121 currentRule->linkedEntList = NULL;
122 g_return_val_if_fail ((targetEnt) || (mergeEnt) || (paramList), -1);
123 kvpImport = kvp_frame_new ();
124 kvpTarget = kvp_frame_new ();
125 mergeError = FALSE;
126 while (paramList != NULL)
127 {
128 mergeMatch = FALSE;
129 knowntype = FALSE;
130 qtparam = paramList->data;
131 mergeParamName = qtparam->param_name;
132 g_return_val_if_fail (mergeParamName != NULL, -1);
133 mergeType = qtparam->param_type;
134 if (safe_strcmp (mergeType, QOF_TYPE_STRING) == 0)
135 {
136 stringImport = qtparam->param_getfcn (mergeEnt, qtparam);
137 stringTarget = qtparam->param_getfcn (targetEnt, qtparam);
138 /* very strict string matches may need to be relaxed. */
139 if (stringImport == NULL)
140 stringImport = "";
141 if (stringTarget == NULL)
142 stringTarget = "";
143 if (safe_strcmp (stringImport, stringTarget) == 0)
144 mergeMatch = TRUE;
145 /* Give special weight to a string match */
146 currentRule = qof_book_merge_update_rule (currentRule,
147 mergeMatch, QOF_STRING_WEIGHT);
148 stringImport = stringTarget = NULL;
149 knowntype = TRUE;
150 }
151 if (safe_strcmp (mergeType, QOF_TYPE_TIME) == 0)
152 {
153 QofTime *qtImport, *qtTarget;
154
155 qtImport = qtparam->param_getfcn (mergeEnt, qtparam);
156 qtTarget = qtparam->param_getfcn (targetEnt, qtparam);
157 if (qof_time_cmp (qtImport, qtTarget) == 0)
158 currentRule = qof_book_merge_update_rule (currentRule,
159 mergeMatch, DEFAULT_MERGE_WEIGHT);
160 knowntype = TRUE;
161 }
162 #ifndef QOF_DISABLE_DEPRECATED
163 if (safe_strcmp (mergeType, QOF_TYPE_DATE) == 0)
164 {
165 Timespec tsImport, tsTarget, (*date_getter) (QofEntity *, QofParam *);
166 date_getter =
167 (Timespec (*)(QofEntity *,
168 QofParam *)) qtparam->param_getfcn;
169 tsImport = date_getter (mergeEnt, qtparam);
170 tsTarget = date_getter (targetEnt, qtparam);
171 if (timespec_cmp (&tsImport, &tsTarget) == 0)
172 mergeMatch = TRUE;
173 currentRule = qof_book_merge_update_rule (currentRule,
174 mergeMatch, DEFAULT_MERGE_WEIGHT);
175 knowntype = TRUE;
176 }
177 #endif
178 if ((safe_strcmp (mergeType, QOF_TYPE_NUMERIC) == 0) ||
179 (safe_strcmp (mergeType, QOF_TYPE_DEBCRED) == 0))
180 {
181 numeric_getter =
182 (QofNumeric (*)(QofEntity *, QofParam *)) qtparam->
183 param_getfcn;
184 numericImport = numeric_getter (mergeEnt, qtparam);
185 numericTarget = numeric_getter (targetEnt, qtparam);
186 if (qof_numeric_compare (numericImport, numericTarget) == 0)
187 mergeMatch = TRUE;
188 currentRule = qof_book_merge_update_rule (currentRule,
189 mergeMatch, DEFAULT_MERGE_WEIGHT);
190 knowntype = TRUE;
191 }
192 if (safe_strcmp (mergeType, QOF_TYPE_GUID) == 0)
193 {
194 guidImport = qtparam->param_getfcn (mergeEnt, qtparam);
195 guidTarget = qtparam->param_getfcn (targetEnt, qtparam);
196 if (guid_compare (guidImport, guidTarget) == 0)
197 mergeMatch = TRUE;
198 currentRule = qof_book_merge_update_rule (currentRule,
199 mergeMatch, DEFAULT_MERGE_WEIGHT);
200 knowntype = TRUE;
201 }
202 if (safe_strcmp (mergeType, QOF_TYPE_INT32) == 0)
203 {
204 int32_getter =
205 (gint32 (*)(QofEntity *,
206 QofParam *)) qtparam->param_getfcn;
207 i32Import = int32_getter (mergeEnt, qtparam);
208 i32Target = int32_getter (targetEnt, qtparam);
209 if (i32Target == i32Import)
210 mergeMatch = TRUE;
211 currentRule = qof_book_merge_update_rule (currentRule,
212 mergeMatch, DEFAULT_MERGE_WEIGHT);
213 knowntype = TRUE;
214 }
215 if (safe_strcmp (mergeType, QOF_TYPE_INT64) == 0)
216 {
217 int64_getter =
218 (gint64 (*)(QofEntity *,
219 QofParam *)) qtparam->param_getfcn;
220 i64Import = int64_getter (mergeEnt, qtparam);
221 i64Target = int64_getter (targetEnt, qtparam);
222 if (i64Target == i64Import)
223 mergeMatch = TRUE;
224 currentRule = qof_book_merge_update_rule (currentRule,
225 mergeMatch, DEFAULT_MERGE_WEIGHT);
226 knowntype = TRUE;
227 }
228 if (safe_strcmp (mergeType, QOF_TYPE_DOUBLE) == 0)
229 {
230 double_getter =
231 (double (*)(QofEntity *,
232 QofParam *)) qtparam->param_getfcn;
233 doubleImport = double_getter (mergeEnt, qtparam);
234 doubleTarget = double_getter (mergeEnt, qtparam);
235 if (doubleImport == doubleTarget)
236 mergeMatch = TRUE;
237 currentRule = qof_book_merge_update_rule (currentRule,
238 mergeMatch, DEFAULT_MERGE_WEIGHT);
239 knowntype = TRUE;
240 }
241 if (safe_strcmp (mergeType, QOF_TYPE_BOOLEAN) == 0)
242 {
243 boolean_getter =
244 (gboolean (*)(QofEntity *,
245 QofParam *)) qtparam->param_getfcn;
246 booleanImport = boolean_getter (mergeEnt, qtparam);
247 booleanTarget = boolean_getter (targetEnt, qtparam);
248 if (booleanImport != FALSE && booleanImport != TRUE)
249 booleanImport = FALSE;
250 if (booleanTarget != FALSE && booleanTarget != TRUE)
251 booleanTarget = FALSE;
252 if (booleanImport == booleanTarget)
253 mergeMatch = TRUE;
254 currentRule = qof_book_merge_update_rule (currentRule,
255 mergeMatch, DEFAULT_MERGE_WEIGHT);
256 knowntype = TRUE;
257 }
258 if (safe_strcmp (mergeType, QOF_TYPE_KVP) == 0)
259 {
260 kvpImport =
261 kvp_frame_copy (qtparam->param_getfcn (mergeEnt, qtparam));
262 kvpTarget =
263 kvp_frame_copy (qtparam->param_getfcn (targetEnt,
264 qtparam));
265 if (kvp_frame_compare (kvpImport, kvpTarget) == 0)
266 mergeMatch = TRUE;
267 currentRule = qof_book_merge_update_rule (currentRule,
268 mergeMatch, DEFAULT_MERGE_WEIGHT);
269 knowntype = TRUE;
270 }
271 if (safe_strcmp (mergeType, QOF_TYPE_CHAR) == 0)
272 {
273 char_getter =
274 (gchar (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
275 charImport = char_getter (mergeEnt, qtparam);
276 charTarget = char_getter (targetEnt, qtparam);
277 if (charImport == charTarget)
278 mergeMatch = TRUE;
279 currentRule = qof_book_merge_update_rule (currentRule,
280 mergeMatch, DEFAULT_MERGE_WEIGHT);
281 knowntype = TRUE;
282 }
283 /* No object should have QofSetterFunc defined for the book,
284 but just to be safe, do nothing. */
285 if (safe_strcmp (mergeType, QOF_ID_BOOK) == 0)
286 knowntype = TRUE;
287 if (safe_strcmp (mergeType, QOF_TYPE_COLLECT) == 0)
288 {
289 struct collect_list_s s;
290 s.linkedEntList = NULL;
291 mergeColl = qtparam->param_getfcn (mergeEnt, qtparam);
292 targetColl = qtparam->param_getfcn (targetEnt, qtparam);
293 s.linkedEntList = g_slist_copy (currentRule->linkedEntList);
294 qof_collection_foreach (mergeColl, collect_reference_cb, &s);
295 currentRule->linkedEntList = g_slist_copy (s.linkedEntList);
296 if (0 == qof_collection_compare (mergeColl, targetColl))
297 mergeMatch = TRUE;
298 currentRule = qof_book_merge_update_rule (currentRule,
299 mergeMatch, DEFAULT_MERGE_WEIGHT);
300 knowntype = TRUE;
301 }
302 if (safe_strcmp (mergeType, QOF_TYPE_CHOICE) == 0)
303 {
304 referenceEnt = qtparam->param_getfcn (mergeEnt, qtparam);
305 currentRule->linkedEntList =
306 g_slist_prepend (currentRule->linkedEntList, referenceEnt);
307 if (referenceEnt == qtparam->param_getfcn (targetEnt, qtparam))
308 mergeMatch = TRUE;
309 knowntype = TRUE;
310 }
311 if (knowntype == FALSE)
312 {
313 referenceEnt = qtparam->param_getfcn (mergeEnt, qtparam);
314 if ((referenceEnt != NULL)
315 && (safe_strcmp (referenceEnt->e_type, mergeType) == 0))
316 {
317 currentRule->linkedEntList =
318 g_slist_prepend (currentRule->linkedEntList,
319 referenceEnt);
320 if (referenceEnt ==
321 qtparam->param_getfcn (targetEnt, qtparam))
322 mergeMatch = TRUE;
323 currentRule = qof_book_merge_update_rule (currentRule,
324 mergeMatch, DEFAULT_MERGE_WEIGHT);
325 }
326 }
327 paramList = g_slist_next (paramList);
328 }
329 mergeData->currentRule = currentRule;
330 g_free (kvpImport);
331 g_free (kvpTarget);
332 return 0;
333 }
334
335 static void
336 qof_book_merge_commit_foreach_cb (gpointer rule, gpointer arg)
337 {
338 struct QofBookMergeRuleIterate *qiter;
339
340 g_return_if_fail (arg != NULL);
341 qiter = (struct QofBookMergeRuleIterate *) arg;
342 g_return_if_fail (qiter->data != NULL);
343 qiter->fcn (qiter->data, (QofBookMergeRule *) rule,
344 qiter->remainder);
345 qiter->remainder--;
346 }
347
348 static void
349 qof_book_merge_commit_foreach (QofBookMergeRuleForeachCB cb,
350 QofBookMergeResult mergeResult, QofBookMergeData * mergeData)
351 {
352 struct QofBookMergeRuleIterate qiter;
353 QofBookMergeRule *currentRule;
354 GList *subList, *node;
355
356 g_return_if_fail (cb != NULL);
357 g_return_if_fail (mergeData != NULL);
358 currentRule = mergeData->currentRule;
359 g_return_if_fail (currentRule != NULL);
360 g_return_if_fail (mergeResult > 0);
361 g_return_if_fail ((mergeResult != MERGE_INVALID) ||
362 (mergeResult != MERGE_UNDEF) ||
363 (mergeResult != MERGE_REPORT));
364
365 qiter.fcn = cb;
366 subList = NULL;
367 qiter.ruleList = NULL;
368 for (node = mergeData->mergeList; node != NULL; node = node->next)
369 {
370 currentRule = node->data;
371 if (currentRule->mergeResult == mergeResult)
372 subList = g_list_prepend (subList, currentRule);
373 }
374 qiter.remainder = g_list_length (subList);
375 qiter.data = mergeData;
376 g_list_foreach (subList, qof_book_merge_commit_foreach_cb, &qiter);
377 }
378
379 /* build the table of target comparisons
380
381 This can get confusing, so bear with me. (!)
382
383 Whilst iterating through the entities in the mergeBook, qof_book_mergeForeach assigns
384 a targetEnt to each mergeEnt (until it runs out of targetEnt or mergeEnt). Each match
385 is made against the one targetEnt that best matches the mergeEnt. Fine so far.
386
387 Any mergeEnt is only ever assigned a targetEnt if the calculated difference between
388 the two is less than the difference between that targetEnt and any previous mergeEnt
389 match.
390
391 The next mergeEnt may be a much better match for that targetEnt and the target_table
392 is designed to solve the issues that result from this conflict. The previous match
393 must be re-assigned because if two mergeEnt's are matched with only one targetEnt,
394 data loss \b WILL follow. Equally, the current mergeEnt must replace the previous
395 one as it is a better match. qof_entity_rating holds the details required to identify
396 the correct mergeEnt to be re-assigned and these mergeEnt entities are therefore
397 orphaned - to be re-matched later.
398
399 Meanwhile, the current mergeEnt is entered into target_table with it's difference and
400 rule data, in case an even better match is found later in the mergeBook.
401
402 Finally, each mergeEnt in the orphan_list is now put through the comparison again.
403
404 */
405 static gboolean
406 qof_book_merge_rule_cmp (gconstpointer a, gconstpointer b)
407 {
408 QofBookMergeRule *ra = (QofBookMergeRule *) a;
409 QofBookMergeRule *rb = (QofBookMergeRule *) b;
410 if (ra->difference == rb->difference)
411 return TRUE;
412 else
413 return FALSE;
414 }
415
416 static void
417 qof_book_merge_orphan_check (double difference,
418 QofBookMergeRule * mergeRule, QofBookMergeData * mergeData)
419 {
420 /* Called when difference is lower than previous
421 Lookup target to find previous match
422 and re-assign mergeEnt to orphan_list */
423 QofBookMergeRule *rule;
424
425 g_return_if_fail (mergeRule != NULL);
426 g_return_if_fail (mergeData != NULL);
427 if (g_hash_table_size (mergeData->target_table) == 0)
428 return;
429 rule =
430 (QofBookMergeRule *) g_hash_table_lookup (mergeData->target_table,
431 mergeRule->targetEnt);
432 /* If NULL, no match was found. */
433 if (rule == NULL)
434 return;
435 /* Only orphan if this is a better match than already exists. */
436 if (difference >= rule->difference)
437 return;
438 rule->targetEnt = NULL;
439 rule->mergeResult = MERGE_UNDEF;
440 mergeData->orphan_list = g_slist_append (mergeData->orphan_list, rule);
441 }
442
443 static void
444 qof_book_merge_match_orphans (QofBookMergeData * mergeData)
445 {
446 GSList *orphans, *targets;
447 QofBookMergeRule *rule, *currentRule;
448 QofEntity *best_matchEnt;
449 double difference;
450
451 g_return_if_fail (mergeData != NULL);
452 currentRule = mergeData->currentRule;
453 g_return_if_fail (currentRule != NULL);
454 /* This routine does NOT copy the orphan list, it
455 is used recursively until empty. */
456 orphans = mergeData->orphan_list;
457 targets = g_slist_copy (mergeData->targetList);
458 while (orphans != NULL)
459 {
460 rule = orphans->data;
461 g_return_if_fail (rule != NULL);
462 difference = g_slist_length (mergeData->mergeObjectParams);
463 if (rule->targetEnt == NULL)
464 {
465 rule->mergeResult = MERGE_NEW;
466 rule->difference = 0;
467 mergeData->mergeList =
468 g_list_prepend (mergeData->mergeList, rule);
469 orphans = g_slist_next (orphans);
470 continue;
471 }
472 mergeData->currentRule = rule;
473 g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
474 if (difference > mergeData->currentRule->difference)
475 {
476 best_matchEnt = currentRule->targetEnt;
477 difference = currentRule->difference;
478 rule = currentRule;
479 mergeData->mergeList =
480 g_list_prepend (mergeData->mergeList, rule);
481 qof_book_merge_orphan_check (difference, rule, mergeData);
482 }
483 orphans = g_slist_next (orphans);
484 }
485 g_slist_free (mergeData->orphan_list);
486 g_slist_free (targets);
487 }
488
489 static void
490 qof_book_merge_foreach_target (QofEntity * targetEnt, gpointer user_data)
491 {
492 QofBookMergeData *mergeData;
493
494 g_return_if_fail (user_data != NULL);
495 mergeData = (QofBookMergeData *) user_data;
496 g_return_if_fail (targetEnt != NULL);
497 mergeData->targetList =
498 g_slist_prepend (mergeData->targetList, targetEnt);
499 }
500
501 static void
502 qof_book_merge_foreach_type_target (QofObject * merge_obj,
503 gpointer user_data)
504 {
505 QofBookMergeData *mergeData;
506 QofBookMergeRule *currentRule;
507
508 g_return_if_fail (user_data != NULL);
509 mergeData = (QofBookMergeData *) user_data;
510 currentRule = mergeData->currentRule;
511 g_return_if_fail (currentRule != NULL);
512 g_return_if_fail (merge_obj != NULL);
513 if (safe_strcmp (merge_obj->e_type,
514 currentRule->importEnt->e_type) == 0)
515 {
516 qof_object_foreach (currentRule->importEnt->e_type,
517 mergeData->targetBook,
518 qof_book_merge_foreach_target, user_data);
519 }
520 }
521
522 static void
523 qof_book_merge_foreach (QofEntity * mergeEnt, gpointer user_data)
524 {
525 QofBookMergeRule *mergeRule, *currentRule;
526 QofBookMergeData *mergeData;
527 QofEntity *targetEnt, *best_matchEnt;
528 GUID *g;
529 double difference;
530 GSList *c;
531
532 g_return_if_fail (user_data != NULL);
533 mergeData = (QofBookMergeData *) user_data;
534 g_return_if_fail (mergeEnt != NULL);
535 currentRule = mergeData->currentRule;
536 g_return_if_fail (currentRule != NULL);
537 g = guid_malloc ();
538 *g = mergeEnt->guid;
539 mergeRule = g_new0 (QofBookMergeRule, 1);
540 mergeRule->importEnt = mergeEnt;
541 mergeRule->difference = difference = 0;
542 mergeRule->mergeAbsolute = FALSE;
543 mergeRule->mergeResult = MERGE_UNDEF;
544 mergeRule->updated = FALSE;
545 mergeRule->mergeType = mergeEnt->e_type;
546 mergeRule->mergeLabel = qof_object_get_type_label (mergeEnt->e_type);
547 mergeRule->mergeParam = g_slist_copy (mergeData->mergeObjectParams);
548 mergeRule->linkedEntList = NULL;
549 mergeData->currentRule = mergeRule;
550 targetEnt = best_matchEnt = NULL;
551 targetEnt =
552 qof_collection_lookup_entity (qof_book_get_collection
553 (mergeData->targetBook, mergeEnt->e_type), g);
554 if (targetEnt != NULL)
555 {
556 mergeRule->mergeAbsolute = TRUE;
557 mergeRule->targetEnt = targetEnt;
558 g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
559 mergeRule->linkedEntList =
560 g_slist_copy (currentRule->linkedEntList);
561 mergeData->mergeList =
562 g_list_prepend (mergeData->mergeList, mergeRule);
563 return;
564 }
565 /* no absolute match exists */
566 g_slist_free (mergeData->targetList);
567 mergeData->targetList = NULL;
568 qof_object_foreach_type (qof_book_merge_foreach_type_target,
569 mergeData);
570 if (g_slist_length (mergeData->targetList) == 0)
571 mergeRule->mergeResult = MERGE_NEW;
572 difference = g_slist_length (mergeRule->mergeParam);
573 c = g_slist_copy (mergeData->targetList);
574 while (c != NULL)
575 {
576 mergeRule->targetEnt = c->data;
577 currentRule = mergeRule;
578 /* compare two entities and sum the differences */
579 g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
580 if (mergeRule->difference == 0)
581 {
582 /* check if this is a better match than one already assigned */
583 best_matchEnt = mergeRule->targetEnt;
584 mergeRule->mergeResult = MERGE_DUPLICATE;
585 difference = 0;
586 mergeRule->linkedEntList =
587 g_slist_copy (currentRule->linkedEntList);
588 g_slist_free (c);
589 guid_free (g);
590 /* exact match, return */
591 return;
592 }
593 if (difference > mergeRule->difference)
594 {
595 /* The chosen targetEnt determines the parenting of any child object */
596 /* check if this is a better match than one already assigned */
597 best_matchEnt = mergeRule->targetEnt;
598 difference = mergeRule->difference;
599 /* Use match to lookup the previous entity that matched this targetEnt (if any)
600 and remove targetEnt from the rule for that mergeEnt.
601 Add the previous mergeEnt to orphan_list.
602 */
603 qof_book_merge_orphan_check (difference, mergeRule, mergeData);
604 }
605 c = g_slist_next (c);
606 }
607 g_slist_free (c);
608 if (best_matchEnt != NULL)
609 {
610 mergeRule->targetEnt = best_matchEnt;
611 mergeRule->difference = difference;
612 /* Set this entity in the target_table in case a better match can be made
613 with the next mergeEnt. */
614 g_hash_table_insert (mergeData->target_table, mergeRule->targetEnt,
615 mergeRule);
616 /* compare again with the best partial match */
617 g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
618 mergeRule->linkedEntList =
619 g_slist_copy (currentRule->linkedEntList);
620 }
621 else
622 {
623 mergeRule->targetEnt = NULL;
624 mergeRule->difference = 0;
625 mergeRule->mergeResult = MERGE_NEW;
626 mergeRule->linkedEntList =
627 g_slist_copy (currentRule->linkedEntList);
628 }
629 mergeData->mergeList =
630 g_list_prepend (mergeData->mergeList, mergeRule);
631 guid_free (g);
632 /* return to qof_book_merge_init */
633 }
634
635 static void
636 qof_book_merge_foreach_param (QofParam * param, gpointer user_data)
637 {
638 QofBookMergeData *mergeData;
639
640 g_return_if_fail (user_data != NULL);
641 mergeData = (QofBookMergeData *) user_data;
642 g_return_if_fail (param != NULL);
643 if ((param->param_getfcn != NULL) && (param->param_setfcn != NULL))
644 {
645 mergeData->mergeObjectParams =
646 g_slist_append (mergeData->mergeObjectParams, param);
647 }
648 }
649
650 static void
651 qof_book_merge_foreach_type (QofObject * merge_obj, gpointer user_data)
652 {
653 QofBookMergeData *mergeData;
654
655 g_return_if_fail (user_data != NULL);
656 mergeData = (QofBookMergeData *) user_data;
657 g_return_if_fail ((merge_obj != NULL));
658 /* Skip unsupported objects */
659 if ((merge_obj->create == NULL) || (merge_obj->foreach == NULL))
660 {
661 DEBUG (" merge_obj QOF support failed %s", merge_obj->e_type);
662 return;
663 }
664 if (mergeData->mergeObjectParams != NULL)
665 g_slist_free (mergeData->mergeObjectParams);
666 mergeData->mergeObjectParams = NULL;
667 qof_class_param_foreach (merge_obj->e_type,
668 qof_book_merge_foreach_param, mergeData);
669 qof_object_foreach (merge_obj->e_type, mergeData->mergeBook,
670 qof_book_merge_foreach, mergeData);
671 }
672
673 static void
674 qof_book_merge_rule_cb (gpointer rule, gpointer arg)
675 {
676 struct QofBookMergeRuleIterate *qiter;
677 QofBookMergeData *mergeData;
678
679 g_return_if_fail (arg != NULL);
680 qiter = (struct QofBookMergeRuleIterate *) arg;
681 mergeData = qiter->data;
682 g_return_if_fail (mergeData != NULL);
683 g_return_if_fail (mergeData->abort == FALSE);
684 qiter->fcn (mergeData, (QofBookMergeRule *) rule, qiter->remainder);
685 qiter->data = mergeData;
686 qiter->remainder--;
687 }
688
689 static void
690 qof_book_merge_commit_rule_loop (QofBookMergeData * mergeData,
691 QofBookMergeRule * rule, guint remainder __attribute__ ((unused)))
692 {
693 QofInstance *inst;
694 gboolean registered_type;
695 QofEntity *referenceEnt;
696 /* cm_ prefix used for variables that hold the data to commit */
697 QofCollection *cm_coll;
698 QofParam *cm_param;
699 gchar *cm_string;
700 const GUID *cm_guid;
701 KvpFrame *cm_kvp;
702 QofTime *cm_qt;
703 /* function pointers and variables for parameter getters that don't use pointers normally */
704 QofNumeric cm_numeric, (*numeric_getter) (QofEntity *, QofParam *);
705 gdouble cm_double, (*double_getter) (QofEntity *, QofParam *);
706 gboolean cm_boolean, (*boolean_getter) (QofEntity *, QofParam *);
707 gint32 cm_i32, (*int32_getter) (QofEntity *, QofParam *);
708 gint64 cm_i64, (*int64_getter) (QofEntity *, QofParam *);
709 gchar cm_char, (*char_getter) (QofEntity *, QofParam *);
710 /* function pointers to the parameter setters */
711 void (*string_setter) (QofEntity *, const gchar *);
712 void (*time_setter) (QofEntity *, QofTime *);
713 void (*numeric_setter) (QofEntity *, QofNumeric);
714 void (*guid_setter) (QofEntity *, const GUID *);
715 void (*double_setter) (QofEntity *, double);
716 void (*boolean_setter) (QofEntity *, gboolean);
717 void (*i32_setter) (QofEntity *, gint32);
718 void (*i64_setter) (QofEntity *, gint64);
719 void (*char_setter) (QofEntity *, gchar);
720 void (*kvp_frame_setter) (QofEntity *, KvpFrame *);
721 void (*reference_setter) (QofEntity *, QofEntity *);
722 void (*collection_setter) (QofEntity *, QofCollection *);
723
724 g_return_if_fail (rule != NULL);
725 g_return_if_fail (mergeData != NULL);
726 g_return_if_fail (mergeData->targetBook != NULL);
727 g_return_if_fail ((rule->mergeResult != MERGE_NEW)
728 || (rule->mergeResult != MERGE_UPDATE));
729 /* create a new object for MERGE_NEW */
730 /* The new object takes the GUID from the import to retain an absolute match */
731 if (rule->mergeResult == MERGE_NEW)
732 {
733 inst =
734 (QofInstance *) qof_object_new_instance (rule->importEnt->
735 e_type, mergeData->targetBook);
736 g_return_if_fail (inst != NULL);
737 rule->targetEnt = &inst->entity;
738 qof_entity_set_guid (rule->targetEnt,
739 qof_entity_get_guid (rule->importEnt));
740 }
741 /* currentRule->targetEnt is now set,
742 1. by an absolute GUID match or
743 2. by best_matchEnt and difference or
744 3. by MERGE_NEW.
745 */
746 while (rule->mergeParam != NULL)
747 {
748 registered_type = FALSE;
749 g_return_if_fail (rule->mergeParam->data);
750 cm_param = rule->mergeParam->data;
751 rule->mergeType = cm_param->param_type;
752 if (safe_strcmp (rule->mergeType, QOF_TYPE_STRING) == 0)
753 {
754 cm_string = cm_param->param_getfcn (rule->importEnt, cm_param);
755 string_setter =
756 (void (*)(QofEntity *,
757 const gchar *)) cm_param->param_setfcn;
758 if (string_setter != NULL)
759 string_setter (rule->targetEnt, cm_string);
760 registered_type = TRUE;
761 }
762 if (safe_strcmp (rule->mergeType, QOF_TYPE_TIME) == 0)
763 {
764 QofTime *(*time_getter) (QofEntity *, QofParam *);
765
766 time_getter =
767 (QofTime* (*)(QofEntity *, QofParam *))cm_param->param_getfcn;
768 cm_qt = qof_time_copy (
769 time_getter (rule->importEnt, cm_param));
770 time_setter =
771 (void (*)(QofEntity *, QofTime *))
772 cm_param->param_setfcn;
773 if ((time_setter != NULL) && (qof_time_is_valid (cm_qt)))
774 time_setter (rule->targetEnt, cm_qt);
775 registered_type = TRUE;
776 }
777 #ifndef QOF_DISABLE_DEPRECATED
778 if (safe_strcmp (rule->mergeType, QOF_TYPE_DATE) == 0)
779 {
780 Timespec cm_date, (*date_getter) (QofEntity *, QofParam *);
781 void (*date_setter) (QofEntity *, Timespec);
782
783 date_getter =
784 (Timespec (*)(QofEntity *, QofParam *)) cm_param->
785 param_getfcn;
786 cm_date = date_getter (rule->importEnt, cm_param);
787 date_setter =
788 (void (*)(QofEntity *, Timespec)) cm_param->param_setfcn;
789 if (date_setter != NULL)
790 date_setter (rule->targetEnt, cm_date);
791 registered_type = TRUE;
792 }
793 #endif
794 if ((safe_strcmp (rule->mergeType, QOF_TYPE_NUMERIC) == 0) ||
795 (safe_strcmp (rule->mergeType, QOF_TYPE_DEBCRED) == 0))
796 {
797 numeric_getter =
798 (QofNumeric (*)(QofEntity *, QofParam *)) cm_param->
799 param_getfcn;
800 cm_numeric = numeric_getter (rule->importEnt, cm_param);
801 numeric_setter =
802 (void (*)(QofEntity *,
803 QofNumeric)) cm_param->param_setfcn;
804 if (numeric_setter != NULL)
805 numeric_setter (rule->targetEnt, cm_numeric);
806 registered_type = TRUE;
807 }
808 if (safe_strcmp (rule->mergeType, QOF_TYPE_GUID) == 0)
809 {
810 cm_guid = cm_param->param_getfcn (rule->importEnt, cm_param);
811 guid_setter =
812 (void (*)(QofEntity *,
813 const GUID *)) cm_param->param_setfcn;
814 if (guid_setter != NULL)
815 guid_setter (rule->targetEnt, cm_guid);
816 registered_type = TRUE;
817 }
818 if (safe_strcmp (rule->mergeType, QOF_TYPE_INT32) == 0)
819 {
820 int32_getter =
821 (gint32 (*)(QofEntity *,
822 QofParam *)) cm_param->param_getfcn;
823 cm_i32 = int32_getter (rule->importEnt, cm_param);
824 i32_setter =
825 (void (*)(QofEntity *, gint32)) cm_param->param_setfcn;
826 if (i32_setter != NULL)
827 i32_setter (rule->targetEnt, cm_i32);
828 registered_type = TRUE;
829 }
830 if (safe_strcmp (rule->mergeType, QOF_TYPE_INT64) == 0)
831 {
832 int64_getter =
833 (gint64 (*)(QofEntity *,
834 QofParam *)) cm_param->param_getfcn;
835 cm_i64 = int64_getter (rule->importEnt, cm_param);
836 i64_setter =
837 (void (*)(QofEntity *, gint64)) cm_param->param_setfcn;
838 if (i64_setter != NULL)
839 i64_setter (rule->targetEnt, cm_i64);
840 registered_type = TRUE;
841 }
842 if (safe_strcmp (rule->mergeType, QOF_TYPE_DOUBLE) == 0)
843 {
844 double_getter =
845 (double (*)(QofEntity *,
846 QofParam *)) cm_param->param_getfcn;
847 cm_double = double_getter (rule->importEnt, cm_param);
848 double_setter =
849 (void (*)(QofEntity *, double)) cm_param->param_setfcn;
850 if (double_setter != NULL)
851 double_setter (rule->targetEnt, cm_double);
852 registered_type = TRUE;
853 }
854 if (safe_strcmp (rule->mergeType, QOF_TYPE_BOOLEAN) == 0)
855 {
856 boolean_getter =
857 (gboolean (*)(QofEntity *, QofParam *)) cm_param->
858 param_getfcn;
859 cm_boolean = boolean_getter (rule->importEnt, cm_param);
860 boolean_setter =
861 (void (*)(QofEntity *, gboolean)) cm_param->param_setfcn;
862 if (boolean_setter != NULL)
863 boolean_setter (rule->targetEnt, cm_boolean);
864 registered_type = TRUE;
865 }
866 if (safe_strcmp (rule->mergeType, QOF_TYPE_KVP) == 0)
867 {
868 cm_kvp =
869 kvp_frame_copy (cm_param->
870 param_getfcn (rule->importEnt, cm_param));
871 kvp_frame_setter =
872 (void (*)(QofEntity *, KvpFrame *)) cm_param->param_setfcn;
873 if (kvp_frame_setter != NULL)
874 kvp_frame_setter (rule->targetEnt, cm_kvp);
875 registered_type = TRUE;
876 }
877 if (safe_strcmp (rule->mergeType, QOF_TYPE_CHAR) == 0)
878 {
879 char_getter =
880 (gchar (*)(QofEntity *,
881 QofParam *)) cm_param->param_getfcn;
882 cm_char = char_getter (rule->importEnt, cm_param);
883 char_setter =
884 (void (*)(QofEntity *, gchar)) cm_param->param_setfcn;
885 if (char_setter != NULL)
886 char_setter (rule->targetEnt, cm_char);
887 registered_type = TRUE;
888 }
889 if (safe_strcmp (rule->mergeType, QOF_TYPE_COLLECT) == 0)
890 {
891 cm_coll = cm_param->param_getfcn (rule->importEnt, cm_param);
892 collection_setter =
893 (void (*)(QofEntity *, QofCollection *)) cm_param->
894 param_setfcn;
895 if (collection_setter != NULL)
896 collection_setter (rule->targetEnt, cm_coll);
897 registered_type = TRUE;
898 }
899 if (safe_strcmp (rule->mergeType, QOF_TYPE_CHOICE) == 0)
900 {
901 referenceEnt =
902 cm_param->param_getfcn (rule->importEnt, cm_param);
903 reference_setter =
904 (void (*)(QofEntity *,
905 QofEntity *)) cm_param->param_setfcn;
906 if (reference_setter != NULL)
907 reference_setter (rule->targetEnt, referenceEnt);
908 registered_type = TRUE;
909 }
910 if (registered_type == FALSE)
911 {
912 referenceEnt =
913 cm_param->param_getfcn (rule->importEnt, cm_param);
914 if (referenceEnt)
915 {
916 reference_setter =
917 (void (*)(QofEntity *, QofEntity *)) cm_param->
918 param_setfcn;
919 if (reference_setter != NULL)
920 {
921 reference_setter (rule->targetEnt, referenceEnt);
922 }
923 }
924 }
925 rule->mergeParam = g_slist_next (rule->mergeParam);
926 }
927 }
928
929 /* ================================================================ */
930 /* API functions. */
931
932 QofBookMergeData *
933 qof_book_merge_init (QofBook * importBook, QofBook * targetBook)
934 {
935 QofBookMergeData *mergeData;
936 QofBookMergeRule *currentRule;
937 GList *check;
938
939 g_return_val_if_fail ((importBook != NULL)
940 && (targetBook != NULL), NULL);
941 mergeData = g_new0 (QofBookMergeData, 1);
942 mergeData->abort = FALSE;
943 mergeData->mergeList = NULL;
944 mergeData->targetList = NULL;
945 mergeData->mergeBook = importBook;
946 mergeData->targetBook = targetBook;
947 mergeData->mergeObjectParams = NULL;
948 mergeData->orphan_list = NULL;
949 mergeData->target_table =
950 g_hash_table_new (g_direct_hash, qof_book_merge_rule_cmp);
951 currentRule = g_new0 (QofBookMergeRule, 1);
952 mergeData->currentRule = currentRule;
953 qof_object_foreach_type (qof_book_merge_foreach_type, mergeData);
954 g_return_val_if_fail (mergeData->mergeObjectParams, NULL);
955 if (mergeData->orphan_list != NULL)
956 qof_book_merge_match_orphans (mergeData);
957 for (check = mergeData->mergeList; check != NULL; check = check->next)
958 {
959 currentRule = check->data;
960 if (currentRule->mergeResult == MERGE_INVALID)
961 {
962 mergeData->abort = TRUE;
963 return (NULL);
964 }
965 }
966 return mergeData;
967 }
968
969 void
970 qof_book_merge_abort (QofBookMergeData * mergeData)
971 {
972 QofBookMergeRule *currentRule;
973
974 g_return_if_fail (mergeData != NULL);
975 while (mergeData->mergeList != NULL)
976 {
977 currentRule = mergeData->mergeList->data;
978 g_slist_free (currentRule->linkedEntList);
979 g_slist_free (currentRule->mergeParam);
980 g_free (mergeData->mergeList->data);
981 if (currentRule)
982 {
983 g_slist_free (currentRule->linkedEntList);
984 g_slist_free (currentRule->mergeParam);
985 g_free (currentRule);
986 }
987 mergeData->mergeList = g_list_next (mergeData->mergeList);
988 }
989 g_list_free (mergeData->mergeList);
990 g_slist_free (mergeData->mergeObjectParams);
991 g_slist_free (mergeData->targetList);
992 if (mergeData->orphan_list != NULL)
993 g_slist_free (mergeData->orphan_list);
994 g_hash_table_destroy (mergeData->target_table);
995 g_free (mergeData);
996 }
997
998 /* The QOF_TYPE_DATE output format from
999 qof_book_merge_param_as_string has been changed to QSF_XSD_TIME,
1000 a UTC formatted timestring: 2005-01-01T10:55:23Z
1001 If you change QOF_UTC_DATE_FORMAT, change
1002 backend/file/qsf-xml.c : qsf_entity_foreach to
1003 reformat to QSF_XSD_TIME or the QSF XML will
1004 FAIL the schema validation and QSF exports will become invalid.
1005
1006 The QOF_TYPE_BOOLEAN is lowercase for the same reason.
1007 */
1009 gchar *
1010 qof_book_merge_param_as_string (QofParam * qtparam, QofEntity * qtEnt)
1011 {
1012 gchar *param_string;
1013 gchar param_sa[GUID_ENCODING_LENGTH + 1];
1014 QofType paramType;
1015 const GUID *param_guid;
1016 QofTime *param_qt;
1017 QofNumeric param_numeric, (*numeric_getter) (QofEntity *, QofParam *);
1018 gdouble param_double, (*double_getter) (QofEntity *, QofParam *);
1019 gboolean param_boolean, (*boolean_getter) (QofEntity *, QofParam *);
1020 gint32 param_i32, (*int32_getter) (QofEntity *, QofParam *);
1021 gint64 param_i64, (*int64_getter) (QofEntity *, QofParam *);
1022 gchar param_char, (*char_getter) (QofEntity *, QofParam *);
1023
1024 param_string = NULL;
1025 paramType = qtparam->param_type;
1026 if (safe_strcmp (paramType, QOF_TYPE_STRING) == 0)
1027 {
1028 param_string = qtparam->param_getfcn (qtEnt, qtparam);
1029 if (param_string == NULL)
1030 param_string = "";
1031 return param_string;
1032 }
1033 if (safe_strcmp (paramType, QOF_TYPE_TIME) == 0)
1034 {
1035 QofDate *qd;
1036
1037 param_qt = qof_time_copy (
1038 qtparam->param_getfcn (qtEnt, qtparam));
1039 if (!param_qt)
1040 return NULL;
1041 qd = qof_date_from_qtime (param_qt);
1042 param_string = qof_date_print (qd, QOF_DATE_FORMAT_UTC);
1043 qof_date_free (qd);
1044 qof_time_free (param_qt);
1045 return param_string;
1046 }
1047 #ifndef QOF_DISABLE_DEPRECATED
1048 if (safe_strcmp (paramType, QOF_TYPE_DATE) == 0)
1049 {
1050 Timespec param_ts, (*date_getter) (QofEntity *, QofParam *);
1051 time_t param_t;
1052 gchar param_date[QOF_DATE_STRING_LENGTH];
1053
1054 date_getter =
1055 (Timespec (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
1056 param_ts = date_getter (qtEnt, qtparam);
1057 param_t = timespecToTime_t (param_ts);
1058 strftime (param_date, QOF_DATE_STRING_LENGTH, QOF_UTC_DATE_FORMAT,
1059 gmtime (&param_t));
1060 param_string = g_strdup (param_date);
1061 return param_string;
1062 }
1063 #endif
1064 if ((safe_strcmp (paramType, QOF_TYPE_NUMERIC) == 0) ||
1065 (safe_strcmp (paramType, QOF_TYPE_DEBCRED) == 0))
1066 {
1067 numeric_getter =
1068 (QofNumeric (*)(QofEntity *,
1069 QofParam *)) qtparam->param_getfcn;
1070 param_numeric = numeric_getter (qtEnt, qtparam);
1071 param_string = g_strdup (qof_numeric_to_string (param_numeric));
1072 return param_string;
1073 }
1074 if (safe_strcmp (paramType, QOF_TYPE_GUID) == 0)
1075 {
1076 param_guid = qtparam->param_getfcn (qtEnt, qtparam);
1077 guid_to_string_buff (param_guid, param_sa);
1078 param_string = g_strdup (param_sa);
1079 return param_string;
1080 }
1081 if (safe_strcmp (paramType, QOF_TYPE_INT32) == 0)
1082 {
1083 int32_getter =
1084 (gint32 (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
1085 param_i32 = int32_getter (qtEnt, qtparam);
1086 param_string = g_strdup_printf ("%d", param_i32);
1087 return param_string;
1088 }
1089 if (safe_strcmp (paramType, QOF_TYPE_INT64) == 0)
1090 {
1091 int64_getter =
1092 (gint64 (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
1093 param_i64 = int64_getter (qtEnt, qtparam);
1094 param_string = g_strdup_printf ("%" G_GINT64_FORMAT, param_i64);
1095 return param_string;
1096 }
1097 if (safe_strcmp (paramType, QOF_TYPE_DOUBLE) == 0)
1098 {
1099 double_getter =
1100 (double (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
1101 param_double = double_getter (qtEnt, qtparam);
1102 param_string = g_strdup_printf ("%f", param_double);
1103 return param_string;
1104 }
1105 if (safe_strcmp (paramType, QOF_TYPE_BOOLEAN) == 0)
1106 {
1107 boolean_getter =
1108 (gboolean (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
1109 param_boolean = boolean_getter (qtEnt, qtparam);
1110 /* Boolean values need to be lowercase for QSF validation. */
1111 if (param_boolean == TRUE)
1112 param_string = g_strdup ("true");
1113 else
1114 param_string = g_strdup ("false");
1115 return param_string;
1116 }
1117 /* "kvp" contains repeating values, cannot be a single string for the frame. */
1118 if (safe_strcmp (paramType, QOF_TYPE_KVP) == 0)
1119 return param_string;
1120 if (safe_strcmp (paramType, QOF_TYPE_CHAR) == 0)
1121 {
1122 char_getter =
1123 (gchar (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
1124 param_char = char_getter (qtEnt, qtparam);
1125 param_string = g_strdup_printf ("%c", param_char);
1126 return param_string;
1127 }
1128 return NULL;
1129 }
1130
1131 QofBookMergeData *
1132 qof_book_merge_update_result (QofBookMergeData * mergeData,
1133 QofBookMergeResult tag)
1134 {
1135 QofBookMergeRule *resolved;
1136
1137 g_return_val_if_fail ((mergeData != NULL), NULL);
1138 g_return_val_if_fail ((tag > 0), NULL);
1139 g_return_val_if_fail ((tag != MERGE_REPORT), NULL);
1140 resolved = mergeData->currentRule;
1141 g_return_val_if_fail ((resolved != NULL), NULL);
1142 if ((resolved->mergeAbsolute == TRUE) && (tag == MERGE_DUPLICATE))
1143 tag = MERGE_ABSOLUTE;
1144 if ((resolved->mergeAbsolute == TRUE) && (tag == MERGE_NEW))
1145 tag = MERGE_UPDATE;
1146 if ((resolved->mergeAbsolute == FALSE) && (tag == MERGE_ABSOLUTE))
1147 tag = MERGE_DUPLICATE;
1148 if ((resolved->mergeResult == MERGE_NEW) && (tag == MERGE_UPDATE))
1149 tag = MERGE_NEW;
1150 if (resolved->updated == FALSE)
1151 resolved->mergeResult = tag;
1152 resolved->updated = TRUE;
1153 if (tag >= MERGE_INVALID)
1154 {
1155 mergeData->abort = TRUE;
1156 mergeData->currentRule = resolved;
1157 return NULL;
1158 }
1159 mergeData->currentRule = resolved;
1160 return mergeData;
1161 }
1162
1163 gint
1164 qof_book_merge_commit (QofBookMergeData * mergeData)
1165 {
1166 QofBookMergeRule *currentRule;
1167 GList *check, *node;
1168
1169 g_return_val_if_fail (mergeData != NULL, -1);
1170 g_return_val_if_fail (mergeData->mergeList != NULL, -1);
1171 g_return_val_if_fail (mergeData->targetBook != NULL, -1);
1172 if (mergeData->abort == TRUE)
1173 return -1;
1174 check = g_list_copy (mergeData->mergeList);
1175 g_return_val_if_fail (check != NULL, -1);
1176 for (node = check; node != NULL; node = node->next)
1177 {
1178 currentRule = node->data;
1179 if (currentRule->mergeResult == MERGE_INVALID)
1180 {
1181 qof_book_merge_abort (mergeData);
1182 g_list_free (check);
1183 return (-2);
1184 }
1185 if (currentRule->mergeResult == MERGE_REPORT)
1186 {
1187 g_list_free (check);
1188 return 1;
1189 }
1190 }
1191 g_list_free (check);
1192 qof_book_merge_commit_foreach (qof_book_merge_commit_rule_loop,
1193 MERGE_NEW, mergeData);
1194 qof_book_merge_commit_foreach (qof_book_merge_commit_rule_loop,
1195 MERGE_UPDATE, mergeData);
1196 /* Placeholder for QofObject merge_helper_cb - all objects
1197 and all parameters set */
1198 while (mergeData->mergeList != NULL)
1199 {
1200 currentRule = mergeData->mergeList->data;
1201 g_slist_free (currentRule->mergeParam);
1202 g_slist_free (currentRule->linkedEntList);
1203 mergeData->mergeList = g_list_next (mergeData->mergeList);
1204 }
1205 g_list_free (mergeData->mergeList);
1206 g_slist_free (mergeData->mergeObjectParams);
1207 g_slist_free (mergeData->targetList);
1208 if (mergeData->orphan_list != NULL)
1209 g_slist_free (mergeData->orphan_list);
1210 g_hash_table_destroy (mergeData->target_table);
1211 g_free (mergeData);
1212 return 0;
1213 }
1214
1215 void
1216 qof_book_merge_rule_foreach (QofBookMergeData * mergeData,
1217 QofBookMergeRuleForeachCB cb, QofBookMergeResult mergeResult)
1218 {
1219 struct QofBookMergeRuleIterate qiter;
1220 QofBookMergeRule *currentRule;
1221 GList *matching_rules, *node;
1222
1223 g_return_if_fail (cb != NULL);
1224 g_return_if_fail (mergeData != NULL);
1225 currentRule = mergeData->currentRule;
1226 g_return_if_fail (mergeResult > 0);
1227 g_return_if_fail (mergeResult != MERGE_INVALID);
1228 g_return_if_fail (mergeData->abort == FALSE);
1229 qiter.fcn = cb;
1230 qiter.data = mergeData;
1231 matching_rules = NULL;
1232 for (node = mergeData->mergeList; node != NULL; node = node->next)
1233 {
1234 currentRule = node->data;
1235 if (currentRule->mergeResult == mergeResult)
1236 matching_rules = g_list_prepend (matching_rules,
1237 currentRule);
1238 }
1239 qiter.remainder = g_list_length (matching_rules);
1240 g_list_foreach (matching_rules, qof_book_merge_rule_cb, &qiter);
1241 g_list_free (matching_rules);
1242 }
1243
1244 /* ============================================================== */
QofBookMergeRule::mergeLabel
const gchar * mergeLabel
Definition: qofbookmerge.h:176
QOF_TYPE_DATE
#define QOF_TYPE_DATE
Definition: deprecated.h:336
QOF_DATE_FORMAT_UTC
#define QOF_DATE_FORMAT_UTC
QOF UTC format, xsd:date compatible. QOF_UTC_DATE_FORMAT "%Y-%m-%dT%H:%M:%SZ".
Definition: qofdate.h:277
qof_object_new_instance
gpointer qof_object_new_instance(QofIdTypeConst type_name, QofBook *book)
Definition: qofobject.c:42
qof_collection_compare
gint qof_collection_compare(QofCollection *target, QofCollection *merge)
Compare two secondary collections.
Definition: qofid.c:293
QofBookMergeData::mergeList
GList * mergeList
Definition: qofbookmerge.h:213
QOF_TYPE_COLLECT
#define QOF_TYPE_COLLECT
Definition: qofclass.h:93
QofBookMergeData::mergeBook
QofBook * mergeBook
Definition: qofbookmerge.h:217
QofIdType
const gchar * QofIdType
Definition: qofid.h:81
QofInstance_s
Definition: qofinstance-p.h:35
qof_numeric_compare
gint qof_numeric_compare(QofNumeric a, QofNumeric b)
Definition: qofnumeric.c:174
qof_book_get_collection
QofCollection * qof_book_get_collection(QofBook *book, QofIdType entity_type)
Definition: qofbook.c:220
Timespec
struct timespec64 Timespec
QofNumeric
struct _QofNumeric QofNumeric
A rational-number type.
Definition: qofnumeric.h:61
qof_date_free
void qof_date_free(QofDate *date)
Definition: qofdate.c:608
qof_class_param_foreach
void qof_class_param_foreach(QofIdTypeConst obj_name, QofParamForeachCB cb, gpointer user_data)
Definition: qofclass.c:267
_QofParam
Definition: qofclass.h:195
QOF_UTC_DATE_FORMAT
#define QOF_UTC_DATE_FORMAT
UTC date format string.
Definition: qofdate.h:244
qof_date_from_qtime
QofDate * qof_date_from_qtime(const QofTime *qt)
Definition: qofdate.c:850
qof_book_merge_commit
gint qof_book_merge_commit(QofBookMergeData *mergeData)
Commits the import data to the target book.
Definition: qofbookmerge.c:1164
guid_malloc
GUID * guid_malloc(void)
Definition: guid.c:100
QofEntity_s
Definition: qofid.h:147
qof_object_foreach
void qof_object_foreach(QofIdTypeConst type_name, QofBook *book, QofEntityForeachCB cb, gpointer user_data)
Definition: qofobject.c:174
MERGE_INVALID
Definition: qofbookmerge.h:136
kvp_frame_compare
gint kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb)
Definition: kvpframe.c:1757
kvp_frame_copy
KvpFrame * kvp_frame_copy(const KvpFrame *frame)
Definition: kvpframe.c:156
KvpFrame
struct _KvpFrame KvpFrame
Definition: kvpframe.h:74
qof_date_print
gchar * qof_date_print(const QofDate *date, QofDateFormat df)
Convert a QofDate to a timestamp according to the specified date format.
Definition: qofdate.c:547
qof_time_copy
QofTime * qof_time_copy(const QofTime *qt)
Create a copy of a QofTime.
Definition: qoftime.c:223
timespec64
Definition: qoftime.h:97
qof_book_merge_param_as_string
gchar * qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt)
provides easy string access to parameter data for dialogue use
Definition: qofbookmerge.c:1010
MERGE_NEW
Definition: qofbookmerge.h:129
_QofObject::create
gpointer(* create)(QofBook *)
Definition: qofobject.h:80
QofBookMergeRule::mergeType
QofIdType mergeType
Definition: qofbookmerge.h:174
GUID_ENCODING_LENGTH
#define GUID_ENCODING_LENGTH
Definition: guid.h:64
qof_object_get_type_label
const gchar * qof_object_get_type_label(QofIdTypeConst type_name)
Definition: qofobject.c:223
QofBookMergeRule::mergeParam
GSList * mergeParam
Definition: qofbookmerge.h:178
QofDate_s
Full range replacement for struct tm.
Definition: qofdate.h:138
QofBookMergeRule::importEnt
QofEntity * importEnt
Definition: qofbookmerge.h:189
QofCollection
struct QofCollection_s QofCollection
Definition: qofid.h:138
MERGE_DUPLICATE
Definition: qofbookmerge.h:132
QofBookMergeData::target_table
GHashTable * target_table
Definition: qofbookmerge.h:232
qof_collection_lookup_entity
QofEntity * qof_collection_lookup_entity(QofCollection *col, const GUID *guid)
Definition: qofid.c:321
QofBookMergeRule::mergeResult
QofBookMergeResult mergeResult
Definition: qofbookmerge.h:188
QofBookMergeRule::linkedEntList
GSList * linkedEntList
Definition: qofbookmerge.h:179
qof_time_free
void qof_time_free(QofTime *qt)
Free a QofTime when no longer required.
Definition: qoftime.c:56
qof_collection_foreach
void qof_collection_foreach(QofCollection *col, QofEntityForeachCB cb_func, gpointer user_data)
Definition: qofid.c:421
qof_time_cmp
gint qof_time_cmp(const QofTime *ta, const QofTime *tb)
Definition: qoftime.c:165
qof_book_merge_rule_foreach
void qof_book_merge_rule_foreach(QofBookMergeData *mergeData, QofBookMergeRuleForeachCB cb, QofBookMergeResult mergeResult)
Dialogue Control Callback.
Definition: qofbookmerge.c:1216
_QofObject
Definition: qofobject.h:70
_QofNumeric
Definition: qofnumeric.h:52
DEBUG
#define DEBUG(format, args...)
Definition: qoflog.h:208
MERGE_UPDATE
Definition: qofbookmerge.h:134
timespec_cmp
gint timespec_cmp(const Timespec *ta, const Timespec *tb)
Definition: deprecated.c:290
guid_to_string_buff
gchar * guid_to_string_buff(const GUID *guid, gchar *buff)
QofBookMergeRule
One rule per entity, built into a single GList for the entire merge.
Definition: qofbookmerge.h:164
QofBookMergeData::abort
gboolean abort
Definition: qofbookmerge.h:221
MERGE_ABSOLUTE
Definition: qofbookmerge.h:128
QofInstance_s::entity
QofEntity entity
Definition: qofinstance-p.h:38
QofType
const gchar * QofType
Definition: qofclass.h:150
QofBookMergeRule::difference
double difference
Definition: qofbookmerge.h:169
MERGE_UNDEF
Definition: qofbookmerge.h:127
qof_book_merge_init
QofBookMergeData * qof_book_merge_init(QofBook *importBook, QofBook *targetBook)
Initialise the QofBookMerge process.
Definition: qofbookmerge.c:933
_GUID
Definition: guid.h:53
qof_numeric_to_string
gchar * qof_numeric_to_string(QofNumeric n)
Definition: qofnumeric.c:1089
QofBookMergeRule::mergeAbsolute
gboolean mergeAbsolute
Definition: qofbookmerge.h:167
QofBookMergeData::currentRule
QofBookMergeRule * currentRule
Definition: qofbookmerge.h:222
QofBookMergeData::targetBook
QofBook * targetBook
Definition: qofbookmerge.h:219
QofTime
struct QofTime64 QofTime
Use a 64-bit signed int QofTime.
Definition: qoftime.h:120
QofBookMergeResult
QofBookMergeResult
Results of collisions and user resolution.
Definition: qofbookmerge.h:125
qof_entity_get_guid
const GUID * qof_entity_get_guid(QofEntity *ent)
Definition: qofid.c:105
QofBookMergeRuleForeachCB
void(* QofBookMergeRuleForeachCB)(QofBookMergeData *, QofBookMergeRule *, guint)
Definition of the dialogue control callback routine.
Definition: qofbookmerge.h:321
timespecToTime_t
time_t timespecToTime_t(Timespec ts)
Definition: deprecated.c:315
qof_book_merge_abort
void qof_book_merge_abort(QofBookMergeData *mergeData)
Abort the merge and free all memory allocated by the merge.
Definition: qofbookmerge.c:970
QofBookMergeData::mergeObjectParams
GSList * mergeObjectParams
Definition: qofbookmerge.h:211
QofBookMergeRule::updated
gboolean updated
Definition: qofbookmerge.h:171
qof_entity_set_guid
void qof_entity_set_guid(QofEntity *ent, const GUID *guid)
Definition: qofid.c:92
_QofBook
Definition: qofbook-p.h:48
kvp_frame_new
KvpFrame * kvp_frame_new(void)
Definition: kvpframe.c:100
_QofObject::foreach
void(* foreach)(QofCollection *, QofEntityForeachCB, gpointer)
Definition: qofobject.h:105
safe_strcmp
gint safe_strcmp(const gchar *da, const gchar *db)
Definition: qofutil.c:72
qof_book_merge_update_result
QofBookMergeData * qof_book_merge_update_result(QofBookMergeData *mergeData, QofBookMergeResult tag)
called by dialogue callback to set the result of user intervention
Definition: qofbookmerge.c:1132
QofBookMergeData::orphan_list
GSList * orphan_list
Definition: qofbookmerge.h:224
MERGE_REPORT
Definition: qofbookmerge.h:131
QofBookMergeRule::targetEnt
QofEntity * targetEnt
Definition: qofbookmerge.h:190
QOF_TYPE_CHOICE
#define QOF_TYPE_CHOICE
Identify an object as containing a choice.
Definition: qofchoice.h:103
qof_object_foreach_type
void qof_object_foreach_type(QofForeachTypeCB cb, gpointer user_data)
Definition: qofobject.c:140
QofLogModule
const gchar * QofLogModule
Definition: qofid.h:85
QofBookMergeData::targetList
GSList * targetList
Definition: qofbookmerge.h:215
QofBookMergeData
mergeData contains the essential context data for any merge.
Definition: qofbookmerge.h:209