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 {
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 {
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 =
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 =
735  e_type, mergeData->targetBook);
736  g_return_if_fail (inst != NULL);
737  rule->targetEnt = &inst->entity;
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 
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
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 
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
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
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 /* ============================================================== */