qofbookmerge.c

00001 /*********************************************************************
00002  * QofBookMerge.c -- api for QoFBook merge with collision handling   *
00003  * Copyright (C) 2004,2005,2006                                      *
00004  *    Neil Williams <linux@codehelp.co.uk>                           *
00005  *                                                                   *
00006  * This program is free software; you can redistribute it and/or     *
00007  * modify it under the terms of the GNU General Public License as    *
00008  * published by the Free Software Foundation; either version 2 of    *
00009  * the License, or (at your option) any later version.               *
00010  *                                                                   *
00011  * This program is distributed in the hope that it will be useful,   *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     *
00014  * GNU General Public License for more details.                      *
00015  *                                                                   *
00016  * You should have received a copy of the GNU General Public License *
00017  * along with this program; if not, contact:                         *
00018  *                                                                   *
00019  * Free Software Foundation           Voice:  +1-617-542-5942        *
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00022  *                                                                   *
00023  ********************************************************************/
00024 
00025 #include "config.h"
00026 #include <glib.h>
00027 #include "qof.h"
00028 
00029 static QofLogModule log_module = QOF_MOD_MERGE;
00030 
00031 /* private rule iteration struct */
00032 struct QofBookMergeRuleIterate
00033 {
00034     QofBookMergeRuleForeachCB fcn;
00035     QofBookMergeData *data;
00036     QofBookMergeRule *rule;
00037     GList *ruleList;
00038     guint remainder;
00039 };
00040 
00041 /* Make string type parameters 3 times more
00042     important in the match than default types.
00043     i.e. even if two other parameters differ, 
00044     a string match will still provide a better target
00045     than when other types match and the string does not.
00046 */
00047 #define DEFAULT_MERGE_WEIGHT    1
00048 #define QOF_STRING_WEIGHT       3
00049 #define QOF_DATE_STRING_LENGTH  MAX_DATE_LENGTH
00050 
00051 static QofBookMergeRule *
00052 qof_book_merge_update_rule (QofBookMergeRule * currentRule, gboolean match,
00053     gint weight)
00054 {
00055     gboolean absolute;
00056 
00057     absolute = currentRule->mergeAbsolute;
00058     if (absolute && match && currentRule->mergeResult == MERGE_UNDEF)
00059         currentRule->mergeResult = MERGE_ABSOLUTE;
00060     if (absolute && !match)
00061         currentRule->mergeResult = MERGE_UPDATE;
00062     if (!absolute && match && currentRule->mergeResult == MERGE_UNDEF)
00063         currentRule->mergeResult = MERGE_DUPLICATE;
00064     if (!absolute && !match)
00065     {
00066         currentRule->difference += weight;
00067         if (currentRule->mergeResult == MERGE_DUPLICATE)
00068             currentRule->mergeResult = MERGE_REPORT;
00069     }
00070     return currentRule;
00071 }
00072 
00073 struct collect_list_s
00074 {
00075     GSList *linkedEntList;
00076 };
00077 
00078 static void
00079 collect_reference_cb (QofEntity * ent, gpointer user_data)
00080 {
00081     struct collect_list_s *s;
00082 
00083     s = (struct collect_list_s *) user_data;
00084     if (!ent || !s)
00085         return;
00086     s->linkedEntList = g_slist_prepend (s->linkedEntList, ent);
00087 }
00088 
00089 static gint
00090 qof_book_merge_compare (QofBookMergeData * mergeData)
00091 {
00092     QofBookMergeRule *currentRule;
00093     QofCollection *mergeColl, *targetColl;
00094     gchar *stringImport, *stringTarget;
00095     QofEntity *mergeEnt, *targetEnt, *referenceEnt;
00096     const GUID *guidImport, *guidTarget;
00097     QofParam *qtparam;
00098     KvpFrame *kvpImport, *kvpTarget;
00099     QofIdType mergeParamName;
00100     QofType mergeType;
00101     GSList *paramList;
00102     gboolean absolute, mergeError, knowntype, mergeMatch, booleanImport,
00103         booleanTarget, (*boolean_getter) (QofEntity *, QofParam *);
00104     QofNumeric numericImport, numericTarget,
00105         (*numeric_getter) (QofEntity *, QofParam *);
00106     gdouble doubleImport, doubleTarget, (*double_getter) (QofEntity *,
00107         QofParam *);
00108     gint32 i32Import, i32Target, (*int32_getter) (QofEntity *, QofParam *);
00109     gint64 i64Import, i64Target, (*int64_getter) (QofEntity *, QofParam *);
00110     gchar charImport, charTarget, (*char_getter) (QofEntity *, QofParam *);
00111 
00112     g_return_val_if_fail ((mergeData != NULL), -1);
00113     currentRule = mergeData->currentRule;
00114     g_return_val_if_fail ((currentRule != NULL), -1);
00115     absolute = currentRule->mergeAbsolute;
00116     mergeEnt = currentRule->importEnt;
00117     targetEnt = currentRule->targetEnt;
00118     paramList = currentRule->mergeParam;
00119     currentRule->difference = 0;
00120     currentRule->mergeResult = MERGE_UNDEF;
00121     currentRule->linkedEntList = NULL;
00122     g_return_val_if_fail ((targetEnt) || (mergeEnt) || (paramList), -1);
00123     kvpImport = kvp_frame_new ();
00124     kvpTarget = kvp_frame_new ();
00125     mergeError = FALSE;
00126     while (paramList != NULL)
00127     {
00128         mergeMatch = FALSE;
00129         knowntype = FALSE;
00130         qtparam = paramList->data;
00131         mergeParamName = qtparam->param_name;
00132         g_return_val_if_fail (mergeParamName != NULL, -1);
00133         mergeType = qtparam->param_type;
00134         if (safe_strcmp (mergeType, QOF_TYPE_STRING) == 0)
00135         {
00136             stringImport = qtparam->param_getfcn (mergeEnt, qtparam);
00137             stringTarget = qtparam->param_getfcn (targetEnt, qtparam);
00138             /* very strict string matches may need to be relaxed. */
00139             if (stringImport == NULL)
00140                 stringImport = "";
00141             if (stringTarget == NULL)
00142                 stringTarget = "";
00143             if (safe_strcmp (stringImport, stringTarget) == 0)
00144                 mergeMatch = TRUE;
00145             /* Give special weight to a string match */
00146             currentRule = qof_book_merge_update_rule (currentRule,
00147                 mergeMatch, QOF_STRING_WEIGHT);
00148             stringImport = stringTarget = NULL;
00149             knowntype = TRUE;
00150         }
00151         if (safe_strcmp (mergeType, QOF_TYPE_TIME) == 0)
00152         {
00153             QofTime *qtImport, *qtTarget;
00154 
00155             qtImport = qtparam->param_getfcn (mergeEnt, qtparam);
00156             qtTarget = qtparam->param_getfcn (targetEnt, qtparam);
00157             if (qof_time_cmp (qtImport, qtTarget) == 0)
00158                 currentRule = qof_book_merge_update_rule (currentRule,
00159                     mergeMatch, DEFAULT_MERGE_WEIGHT);
00160             knowntype = TRUE;
00161         }
00162 #ifndef QOF_DISABLE_DEPRECATED
00163         if (safe_strcmp (mergeType, QOF_TYPE_DATE) == 0)
00164         {
00165             Timespec tsImport, tsTarget, (*date_getter) (QofEntity *, QofParam *);
00166             date_getter =
00167                 (Timespec (*)(QofEntity *,
00168                     QofParam *)) qtparam->param_getfcn;
00169             tsImport = date_getter (mergeEnt, qtparam);
00170             tsTarget = date_getter (targetEnt, qtparam);
00171             if (timespec_cmp (&tsImport, &tsTarget) == 0)
00172                 mergeMatch = TRUE;
00173             currentRule = qof_book_merge_update_rule (currentRule,
00174                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00175             knowntype = TRUE;
00176         }
00177 #endif
00178         if ((safe_strcmp (mergeType, QOF_TYPE_NUMERIC) == 0) ||
00179             (safe_strcmp (mergeType, QOF_TYPE_DEBCRED) == 0))
00180         {
00181             numeric_getter =
00182                 (QofNumeric (*)(QofEntity *, QofParam *)) qtparam->
00183                 param_getfcn;
00184             numericImport = numeric_getter (mergeEnt, qtparam);
00185             numericTarget = numeric_getter (targetEnt, qtparam);
00186             if (qof_numeric_compare (numericImport, numericTarget) == 0)
00187                 mergeMatch = TRUE;
00188             currentRule = qof_book_merge_update_rule (currentRule,
00189                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00190             knowntype = TRUE;
00191         }
00192         if (safe_strcmp (mergeType, QOF_TYPE_GUID) == 0)
00193         {
00194             guidImport = qtparam->param_getfcn (mergeEnt, qtparam);
00195             guidTarget = qtparam->param_getfcn (targetEnt, qtparam);
00196             if (guid_compare (guidImport, guidTarget) == 0)
00197                 mergeMatch = TRUE;
00198             currentRule = qof_book_merge_update_rule (currentRule,
00199                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00200             knowntype = TRUE;
00201         }
00202         if (safe_strcmp (mergeType, QOF_TYPE_INT32) == 0)
00203         {
00204             int32_getter =
00205                 (gint32 (*)(QofEntity *,
00206                     QofParam *)) qtparam->param_getfcn;
00207             i32Import = int32_getter (mergeEnt, qtparam);
00208             i32Target = int32_getter (targetEnt, qtparam);
00209             if (i32Target == i32Import)
00210                 mergeMatch = TRUE;
00211             currentRule = qof_book_merge_update_rule (currentRule,
00212                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00213             knowntype = TRUE;
00214         }
00215         if (safe_strcmp (mergeType, QOF_TYPE_INT64) == 0)
00216         {
00217             int64_getter =
00218                 (gint64 (*)(QofEntity *,
00219                     QofParam *)) qtparam->param_getfcn;
00220             i64Import = int64_getter (mergeEnt, qtparam);
00221             i64Target = int64_getter (targetEnt, qtparam);
00222             if (i64Target == i64Import)
00223                 mergeMatch = TRUE;
00224             currentRule = qof_book_merge_update_rule (currentRule,
00225                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00226             knowntype = TRUE;
00227         }
00228         if (safe_strcmp (mergeType, QOF_TYPE_DOUBLE) == 0)
00229         {
00230             double_getter =
00231                 (double (*)(QofEntity *,
00232                     QofParam *)) qtparam->param_getfcn;
00233             doubleImport = double_getter (mergeEnt, qtparam);
00234             doubleTarget = double_getter (mergeEnt, qtparam);
00235             if (doubleImport == doubleTarget)
00236                 mergeMatch = TRUE;
00237             currentRule = qof_book_merge_update_rule (currentRule,
00238                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00239             knowntype = TRUE;
00240         }
00241         if (safe_strcmp (mergeType, QOF_TYPE_BOOLEAN) == 0)
00242         {
00243             boolean_getter =
00244                 (gboolean (*)(QofEntity *,
00245                     QofParam *)) qtparam->param_getfcn;
00246             booleanImport = boolean_getter (mergeEnt, qtparam);
00247             booleanTarget = boolean_getter (targetEnt, qtparam);
00248             if (booleanImport != FALSE && booleanImport != TRUE)
00249                 booleanImport = FALSE;
00250             if (booleanTarget != FALSE && booleanTarget != TRUE)
00251                 booleanTarget = FALSE;
00252             if (booleanImport == booleanTarget)
00253                 mergeMatch = TRUE;
00254             currentRule = qof_book_merge_update_rule (currentRule,
00255                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00256             knowntype = TRUE;
00257         }
00258         if (safe_strcmp (mergeType, QOF_TYPE_KVP) == 0)
00259         {
00260             kvpImport =
00261                 kvp_frame_copy (qtparam->param_getfcn (mergeEnt, qtparam));
00262             kvpTarget =
00263                 kvp_frame_copy (qtparam->param_getfcn (targetEnt,
00264                     qtparam));
00265             if (kvp_frame_compare (kvpImport, kvpTarget) == 0)
00266                 mergeMatch = TRUE;
00267             currentRule = qof_book_merge_update_rule (currentRule,
00268                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00269             knowntype = TRUE;
00270         }
00271         if (safe_strcmp (mergeType, QOF_TYPE_CHAR) == 0)
00272         {
00273             char_getter =
00274                 (gchar (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
00275             charImport = char_getter (mergeEnt, qtparam);
00276             charTarget = char_getter (targetEnt, qtparam);
00277             if (charImport == charTarget)
00278                 mergeMatch = TRUE;
00279             currentRule = qof_book_merge_update_rule (currentRule,
00280                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00281             knowntype = TRUE;
00282         }
00283         /* No object should have QofSetterFunc defined for the book,
00284            but just to be safe, do nothing. */
00285         if (safe_strcmp (mergeType, QOF_ID_BOOK) == 0)
00286             knowntype = TRUE;
00287         if (safe_strcmp (mergeType, QOF_TYPE_COLLECT) == 0)
00288         {
00289             struct collect_list_s s;
00290             s.linkedEntList = NULL;
00291             mergeColl = qtparam->param_getfcn (mergeEnt, qtparam);
00292             targetColl = qtparam->param_getfcn (targetEnt, qtparam);
00293             s.linkedEntList = g_slist_copy (currentRule->linkedEntList);
00294             qof_collection_foreach (mergeColl, collect_reference_cb, &s);
00295             currentRule->linkedEntList = g_slist_copy (s.linkedEntList);
00296             if (0 == qof_collection_compare (mergeColl, targetColl))
00297                 mergeMatch = TRUE;
00298             currentRule = qof_book_merge_update_rule (currentRule,
00299                 mergeMatch, DEFAULT_MERGE_WEIGHT);
00300             knowntype = TRUE;
00301         }
00302         if (safe_strcmp (mergeType, QOF_TYPE_CHOICE) == 0)
00303         {
00304             referenceEnt = qtparam->param_getfcn (mergeEnt, qtparam);
00305             currentRule->linkedEntList =
00306                 g_slist_prepend (currentRule->linkedEntList, referenceEnt);
00307             if (referenceEnt == qtparam->param_getfcn (targetEnt, qtparam))
00308                 mergeMatch = TRUE;
00309             knowntype = TRUE;
00310         }
00311         if (knowntype == FALSE)
00312         {
00313             referenceEnt = qtparam->param_getfcn (mergeEnt, qtparam);
00314             if ((referenceEnt != NULL)
00315                 && (safe_strcmp (referenceEnt->e_type, mergeType) == 0))
00316             {
00317                 currentRule->linkedEntList =
00318                     g_slist_prepend (currentRule->linkedEntList,
00319                     referenceEnt);
00320                 if (referenceEnt ==
00321                     qtparam->param_getfcn (targetEnt, qtparam))
00322                     mergeMatch = TRUE;
00323                 currentRule = qof_book_merge_update_rule (currentRule,
00324                     mergeMatch, DEFAULT_MERGE_WEIGHT);
00325             }
00326         }
00327         paramList = g_slist_next (paramList);
00328     }
00329     mergeData->currentRule = currentRule;
00330     g_free (kvpImport);
00331     g_free (kvpTarget);
00332     return 0;
00333 }
00334 
00335 static void
00336 qof_book_merge_commit_foreach_cb (gpointer rule, gpointer arg)
00337 {
00338     struct QofBookMergeRuleIterate *qiter;
00339 
00340     g_return_if_fail (arg != NULL);
00341     qiter = (struct QofBookMergeRuleIterate *) arg;
00342     g_return_if_fail (qiter->data != NULL);
00343     qiter->fcn (qiter->data, (QofBookMergeRule *) rule, 
00344         qiter->remainder);
00345     qiter->remainder--;
00346 }
00347 
00348 static void
00349 qof_book_merge_commit_foreach (QofBookMergeRuleForeachCB cb,
00350     QofBookMergeResult mergeResult, QofBookMergeData * mergeData)
00351 {
00352     struct QofBookMergeRuleIterate qiter;
00353     QofBookMergeRule *currentRule;
00354     GList *subList, *node;
00355 
00356     g_return_if_fail (cb != NULL);
00357     g_return_if_fail (mergeData != NULL);
00358     currentRule = mergeData->currentRule;
00359     g_return_if_fail (currentRule != NULL);
00360     g_return_if_fail (mergeResult > 0);
00361     g_return_if_fail ((mergeResult != MERGE_INVALID) || 
00362         (mergeResult != MERGE_UNDEF) || 
00363         (mergeResult != MERGE_REPORT));
00364 
00365     qiter.fcn = cb;
00366     subList = NULL;
00367     qiter.ruleList = NULL;
00368     for (node = mergeData->mergeList; node != NULL; node = node->next)
00369     {
00370         currentRule = node->data;
00371         if (currentRule->mergeResult == mergeResult)
00372             subList = g_list_prepend (subList, currentRule);
00373     }
00374     qiter.remainder = g_list_length (subList);
00375     qiter.data = mergeData;
00376     g_list_foreach (subList, qof_book_merge_commit_foreach_cb, &qiter);
00377 }
00378 
00379 /* build the table of target comparisons
00380 
00381 This can get confusing, so bear with me. (!)
00382 
00383 Whilst iterating through the entities in the mergeBook, qof_book_mergeForeach assigns
00384 a targetEnt to each mergeEnt (until it runs out of targetEnt or mergeEnt). Each match
00385 is made against the one targetEnt that best matches the mergeEnt. Fine so far.
00386 
00387 Any mergeEnt is only ever assigned a targetEnt if the calculated difference between
00388 the two is less than the difference between that targetEnt and any previous mergeEnt
00389 match.
00390 
00391 The next mergeEnt may be a much better match for that targetEnt and the target_table
00392 is designed to solve the issues that result from this conflict. The previous match
00393 must be re-assigned because if two mergeEnt's are matched with only one targetEnt,
00394 data loss \b WILL follow. Equally, the current mergeEnt must replace the previous
00395 one as it is a better match. qof_entity_rating holds the details required to identify
00396 the correct mergeEnt to be re-assigned and these mergeEnt entities are therefore
00397 orphaned - to be re-matched later.
00398 
00399 Meanwhile, the current mergeEnt is entered into target_table with it's difference and
00400 rule data, in case an even better match is found later in the mergeBook.
00401 
00402 Finally, each mergeEnt in the orphan_list is now put through the comparison again.
00403 
00404 */
00405 static gboolean
00406 qof_book_merge_rule_cmp (gconstpointer a, gconstpointer b)
00407 {
00408     QofBookMergeRule *ra = (QofBookMergeRule *) a;
00409     QofBookMergeRule *rb = (QofBookMergeRule *) b;
00410     if (ra->difference == rb->difference)
00411         return TRUE;
00412     else
00413         return FALSE;
00414 }
00415 
00416 static void
00417 qof_book_merge_orphan_check (double difference,
00418     QofBookMergeRule * mergeRule, QofBookMergeData * mergeData)
00419 {
00420     /* Called when difference is lower than previous
00421        Lookup target to find previous match
00422        and re-assign mergeEnt to orphan_list */
00423     QofBookMergeRule *rule;
00424 
00425     g_return_if_fail (mergeRule != NULL);
00426     g_return_if_fail (mergeData != NULL);
00427     if (g_hash_table_size (mergeData->target_table) == 0)
00428         return;
00429     rule =
00430         (QofBookMergeRule *) g_hash_table_lookup (mergeData->target_table,
00431         mergeRule->targetEnt);
00432     /* If NULL, no match was found. */
00433     if (rule == NULL)
00434         return;
00435     /* Only orphan if this is a better match than already exists. */
00436     if (difference >= rule->difference)
00437         return;
00438     rule->targetEnt = NULL;
00439     rule->mergeResult = MERGE_UNDEF;
00440     mergeData->orphan_list = g_slist_append (mergeData->orphan_list, rule);
00441 }
00442 
00443 static void
00444 qof_book_merge_match_orphans (QofBookMergeData * mergeData)
00445 {
00446     GSList *orphans, *targets;
00447     QofBookMergeRule *rule, *currentRule;
00448     QofEntity *best_matchEnt;
00449     double difference;
00450 
00451     g_return_if_fail (mergeData != NULL);
00452     currentRule = mergeData->currentRule;
00453     g_return_if_fail (currentRule != NULL);
00454     /* This routine does NOT copy the orphan list, it
00455        is used recursively until empty. */
00456     orphans = mergeData->orphan_list;
00457     targets = g_slist_copy (mergeData->targetList);
00458     while (orphans != NULL)
00459     {
00460         rule = orphans->data;
00461         g_return_if_fail (rule != NULL);
00462         difference = g_slist_length (mergeData->mergeObjectParams);
00463         if (rule->targetEnt == NULL)
00464         {
00465             rule->mergeResult = MERGE_NEW;
00466             rule->difference = 0;
00467             mergeData->mergeList =
00468                 g_list_prepend (mergeData->mergeList, rule);
00469             orphans = g_slist_next (orphans);
00470             continue;
00471         }
00472         mergeData->currentRule = rule;
00473         g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
00474         if (difference > mergeData->currentRule->difference)
00475         {
00476             best_matchEnt = currentRule->targetEnt;
00477             difference = currentRule->difference;
00478             rule = currentRule;
00479             mergeData->mergeList =
00480                 g_list_prepend (mergeData->mergeList, rule);
00481             qof_book_merge_orphan_check (difference, rule, mergeData);
00482         }
00483         orphans = g_slist_next (orphans);
00484     }
00485     g_slist_free (mergeData->orphan_list);
00486     g_slist_free (targets);
00487 }
00488 
00489 static void
00490 qof_book_merge_foreach_target (QofEntity * targetEnt, gpointer user_data)
00491 {
00492     QofBookMergeData *mergeData;
00493 
00494     g_return_if_fail (user_data != NULL);
00495     mergeData = (QofBookMergeData *) user_data;
00496     g_return_if_fail (targetEnt != NULL);
00497     mergeData->targetList =
00498         g_slist_prepend (mergeData->targetList, targetEnt);
00499 }
00500 
00501 static void
00502 qof_book_merge_foreach_type_target (QofObject * merge_obj,
00503     gpointer user_data)
00504 {
00505     QofBookMergeData *mergeData;
00506     QofBookMergeRule *currentRule;
00507 
00508     g_return_if_fail (user_data != NULL);
00509     mergeData = (QofBookMergeData *) user_data;
00510     currentRule = mergeData->currentRule;
00511     g_return_if_fail (currentRule != NULL);
00512     g_return_if_fail (merge_obj != NULL);
00513     if (safe_strcmp (merge_obj->e_type,
00514             currentRule->importEnt->e_type) == 0)
00515     {
00516         qof_object_foreach (currentRule->importEnt->e_type,
00517             mergeData->targetBook,
00518             qof_book_merge_foreach_target, user_data);
00519     }
00520 }
00521 
00522 static void
00523 qof_book_merge_foreach (QofEntity * mergeEnt, gpointer user_data)
00524 {
00525     QofBookMergeRule *mergeRule, *currentRule;
00526     QofBookMergeData *mergeData;
00527     QofEntity *targetEnt, *best_matchEnt;
00528     GUID *g;
00529     double difference;
00530     GSList *c;
00531 
00532     g_return_if_fail (user_data != NULL);
00533     mergeData = (QofBookMergeData *) user_data;
00534     g_return_if_fail (mergeEnt != NULL);
00535     currentRule = mergeData->currentRule;
00536     g_return_if_fail (currentRule != NULL);
00537     g = guid_malloc ();
00538     *g = mergeEnt->guid;
00539     mergeRule = g_new0 (QofBookMergeRule, 1);
00540     mergeRule->importEnt = mergeEnt;
00541     mergeRule->difference = difference = 0;
00542     mergeRule->mergeAbsolute = FALSE;
00543     mergeRule->mergeResult = MERGE_UNDEF;
00544     mergeRule->updated = FALSE;
00545     mergeRule->mergeType = mergeEnt->e_type;
00546     mergeRule->mergeLabel = qof_object_get_type_label (mergeEnt->e_type);
00547     mergeRule->mergeParam = g_slist_copy (mergeData->mergeObjectParams);
00548     mergeRule->linkedEntList = NULL;
00549     mergeData->currentRule = mergeRule;
00550     targetEnt = best_matchEnt = NULL;
00551     targetEnt =
00552         qof_collection_lookup_entity (qof_book_get_collection
00553         (mergeData->targetBook, mergeEnt->e_type), g);
00554     if (targetEnt != NULL)
00555     {
00556         mergeRule->mergeAbsolute = TRUE;
00557         mergeRule->targetEnt = targetEnt;
00558         g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
00559         mergeRule->linkedEntList =
00560             g_slist_copy (currentRule->linkedEntList);
00561         mergeData->mergeList =
00562             g_list_prepend (mergeData->mergeList, mergeRule);
00563         return;
00564     }
00565     /* no absolute match exists */
00566     g_slist_free (mergeData->targetList);
00567     mergeData->targetList = NULL;
00568     qof_object_foreach_type (qof_book_merge_foreach_type_target,
00569         mergeData);
00570     if (g_slist_length (mergeData->targetList) == 0)
00571         mergeRule->mergeResult = MERGE_NEW;
00572     difference = g_slist_length (mergeRule->mergeParam);
00573     c = g_slist_copy (mergeData->targetList);
00574     while (c != NULL)
00575     {
00576         mergeRule->targetEnt = c->data;
00577         currentRule = mergeRule;
00578         /* compare two entities and sum the differences */
00579         g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
00580         if (mergeRule->difference == 0)
00581         {
00582             /* check if this is a better match than one already assigned */
00583             best_matchEnt = mergeRule->targetEnt;
00584             mergeRule->mergeResult = MERGE_DUPLICATE;
00585             difference = 0;
00586             mergeRule->linkedEntList =
00587                 g_slist_copy (currentRule->linkedEntList);
00588             g_slist_free (c);
00589             guid_free (g);
00590             /* exact match, return */
00591             return;
00592         }
00593         if (difference > mergeRule->difference)
00594         {
00595             /* The chosen targetEnt determines the parenting of any child object */
00596             /* check if this is a better match than one already assigned */
00597             best_matchEnt = mergeRule->targetEnt;
00598             difference = mergeRule->difference;
00599             /* Use match to lookup the previous entity that matched this targetEnt (if any)
00600                and remove targetEnt from the rule for that mergeEnt.
00601                Add the previous mergeEnt to orphan_list.
00602              */
00603             qof_book_merge_orphan_check (difference, mergeRule, mergeData);
00604         }
00605         c = g_slist_next (c);
00606     }
00607     g_slist_free (c);
00608     if (best_matchEnt != NULL)
00609     {
00610         mergeRule->targetEnt = best_matchEnt;
00611         mergeRule->difference = difference;
00612         /* Set this entity in the target_table in case a better match can be made
00613            with the next mergeEnt. */
00614         g_hash_table_insert (mergeData->target_table, mergeRule->targetEnt,
00615             mergeRule);
00616         /* compare again with the best partial match */
00617         g_return_if_fail (qof_book_merge_compare (mergeData) != -1);
00618         mergeRule->linkedEntList =
00619             g_slist_copy (currentRule->linkedEntList);
00620     }
00621     else
00622     {
00623         mergeRule->targetEnt = NULL;
00624         mergeRule->difference = 0;
00625         mergeRule->mergeResult = MERGE_NEW;
00626         mergeRule->linkedEntList =
00627             g_slist_copy (currentRule->linkedEntList);
00628     }
00629     mergeData->mergeList =
00630         g_list_prepend (mergeData->mergeList, mergeRule);
00631     guid_free (g);
00632     /* return to qof_book_merge_init */
00633 }
00634 
00635 static void
00636 qof_book_merge_foreach_param (QofParam * param, gpointer user_data)
00637 {
00638     QofBookMergeData *mergeData;
00639 
00640     g_return_if_fail (user_data != NULL);
00641     mergeData = (QofBookMergeData *) user_data;
00642     g_return_if_fail (param != NULL);
00643     if ((param->param_getfcn != NULL) && (param->param_setfcn != NULL))
00644     {
00645         mergeData->mergeObjectParams =
00646             g_slist_append (mergeData->mergeObjectParams, param);
00647     }
00648 }
00649 
00650 static void
00651 qof_book_merge_foreach_type (QofObject * merge_obj, gpointer user_data)
00652 {
00653     QofBookMergeData *mergeData;
00654 
00655     g_return_if_fail (user_data != NULL);
00656     mergeData = (QofBookMergeData *) user_data;
00657     g_return_if_fail ((merge_obj != NULL));
00658     /* Skip unsupported objects */
00659     if ((merge_obj->create == NULL) || (merge_obj->foreach == NULL))
00660     {
00661         DEBUG (" merge_obj QOF support failed %s", merge_obj->e_type);
00662         return;
00663     }
00664     if (mergeData->mergeObjectParams != NULL)
00665         g_slist_free (mergeData->mergeObjectParams);
00666     mergeData->mergeObjectParams = NULL;
00667     qof_class_param_foreach (merge_obj->e_type,
00668         qof_book_merge_foreach_param, mergeData);
00669     qof_object_foreach (merge_obj->e_type, mergeData->mergeBook,
00670         qof_book_merge_foreach, mergeData);
00671 }
00672 
00673 static void
00674 qof_book_merge_rule_cb (gpointer rule, gpointer arg)
00675 {
00676     struct QofBookMergeRuleIterate *qiter;
00677     QofBookMergeData *mergeData;
00678 
00679     g_return_if_fail (arg != NULL);
00680     qiter = (struct QofBookMergeRuleIterate *) arg;
00681     mergeData = qiter->data;
00682     g_return_if_fail (mergeData != NULL);
00683     g_return_if_fail (mergeData->abort == FALSE);
00684     qiter->fcn (mergeData, (QofBookMergeRule *) rule, qiter->remainder);
00685     qiter->data = mergeData;
00686     qiter->remainder--;
00687 }
00688 
00689 static void
00690 qof_book_merge_commit_rule_loop (QofBookMergeData * mergeData,
00691     QofBookMergeRule * rule, guint remainder __attribute__ ((unused)))
00692 {
00693     QofInstance *inst;
00694     gboolean registered_type;
00695     QofEntity *referenceEnt;
00696     /* cm_ prefix used for variables that hold the data to commit */
00697     QofCollection *cm_coll;
00698     QofParam *cm_param;
00699     gchar *cm_string;
00700     const GUID *cm_guid;
00701     KvpFrame *cm_kvp;
00702     QofTime *cm_qt;
00703     /* function pointers and variables for parameter getters that don't use pointers normally */
00704     QofNumeric cm_numeric, (*numeric_getter) (QofEntity *, QofParam *);
00705     gdouble cm_double, (*double_getter) (QofEntity *, QofParam *);
00706     gboolean cm_boolean, (*boolean_getter) (QofEntity *, QofParam *);
00707     gint32 cm_i32, (*int32_getter) (QofEntity *, QofParam *);
00708     gint64 cm_i64, (*int64_getter) (QofEntity *, QofParam *);
00709     gchar cm_char, (*char_getter) (QofEntity *, QofParam *);
00710     /* function pointers to the parameter setters */
00711     void (*string_setter) (QofEntity *, const gchar *);
00712     void (*time_setter) (QofEntity *, QofTime *);
00713     void (*numeric_setter) (QofEntity *, QofNumeric);
00714     void (*guid_setter) (QofEntity *, const GUID *);
00715     void (*double_setter) (QofEntity *, double);
00716     void (*boolean_setter) (QofEntity *, gboolean);
00717     void (*i32_setter) (QofEntity *, gint32);
00718     void (*i64_setter) (QofEntity *, gint64);
00719     void (*char_setter) (QofEntity *, gchar);
00720     void (*kvp_frame_setter) (QofEntity *, KvpFrame *);
00721     void (*reference_setter) (QofEntity *, QofEntity *);
00722     void (*collection_setter) (QofEntity *, QofCollection *);
00723 
00724     g_return_if_fail (rule != NULL);
00725     g_return_if_fail (mergeData != NULL);
00726     g_return_if_fail (mergeData->targetBook != NULL);
00727     g_return_if_fail ((rule->mergeResult != MERGE_NEW)
00728         || (rule->mergeResult != MERGE_UPDATE));
00729     /* create a new object for MERGE_NEW */
00730     /* The new object takes the GUID from the import to retain an absolute match */
00731     if (rule->mergeResult == MERGE_NEW)
00732     {
00733         inst =
00734             (QofInstance *) qof_object_new_instance (rule->importEnt->
00735             e_type, mergeData->targetBook);
00736         g_return_if_fail (inst != NULL);
00737         rule->targetEnt = &inst->entity;
00738         qof_entity_set_guid (rule->targetEnt,
00739             qof_entity_get_guid (rule->importEnt));
00740     }
00741     /* currentRule->targetEnt is now set,
00742        1. by an absolute GUID match or
00743        2. by best_matchEnt and difference or
00744        3. by MERGE_NEW.
00745      */
00746     while (rule->mergeParam != NULL)
00747     {
00748         registered_type = FALSE;
00749         g_return_if_fail (rule->mergeParam->data);
00750         cm_param = rule->mergeParam->data;
00751         rule->mergeType = cm_param->param_type;
00752         if (safe_strcmp (rule->mergeType, QOF_TYPE_STRING) == 0)
00753         {
00754             cm_string = cm_param->param_getfcn (rule->importEnt, cm_param);
00755             string_setter =
00756                 (void (*)(QofEntity *,
00757                     const gchar *)) cm_param->param_setfcn;
00758             if (string_setter != NULL)
00759                 string_setter (rule->targetEnt, cm_string);
00760             registered_type = TRUE;
00761         }
00762         if (safe_strcmp (rule->mergeType, QOF_TYPE_TIME) == 0)
00763         {
00764             QofTime *(*time_getter) (QofEntity *, QofParam *);
00765 
00766             time_getter =
00767                 (QofTime* (*)(QofEntity *, QofParam *))cm_param->param_getfcn;
00768             cm_qt = qof_time_copy (
00769                 time_getter (rule->importEnt, cm_param));
00770             time_setter = 
00771                 (void (*)(QofEntity *, QofTime *))
00772                 cm_param->param_setfcn;
00773             if ((time_setter != NULL) && (qof_time_is_valid (cm_qt)))
00774                 time_setter (rule->targetEnt, cm_qt);
00775             registered_type = TRUE;
00776         }
00777 #ifndef QOF_DISABLE_DEPRECATED
00778         if (safe_strcmp (rule->mergeType, QOF_TYPE_DATE) == 0)
00779         {
00780             Timespec cm_date, (*date_getter) (QofEntity *, QofParam *);
00781             void (*date_setter) (QofEntity *, Timespec);
00782 
00783             date_getter =
00784                 (Timespec (*)(QofEntity *, QofParam *)) cm_param->
00785                 param_getfcn;
00786             cm_date = date_getter (rule->importEnt, cm_param);
00787             date_setter =
00788                 (void (*)(QofEntity *, Timespec)) cm_param->param_setfcn;
00789             if (date_setter != NULL)
00790                 date_setter (rule->targetEnt, cm_date);
00791             registered_type = TRUE;
00792         }
00793 #endif
00794         if ((safe_strcmp (rule->mergeType, QOF_TYPE_NUMERIC) == 0) ||
00795             (safe_strcmp (rule->mergeType, QOF_TYPE_DEBCRED) == 0))
00796         {
00797             numeric_getter =
00798                 (QofNumeric (*)(QofEntity *, QofParam *)) cm_param->
00799                 param_getfcn;
00800             cm_numeric = numeric_getter (rule->importEnt, cm_param);
00801             numeric_setter =
00802                 (void (*)(QofEntity *,
00803                     QofNumeric)) cm_param->param_setfcn;
00804             if (numeric_setter != NULL)
00805                 numeric_setter (rule->targetEnt, cm_numeric);
00806             registered_type = TRUE;
00807         }
00808         if (safe_strcmp (rule->mergeType, QOF_TYPE_GUID) == 0)
00809         {
00810             cm_guid = cm_param->param_getfcn (rule->importEnt, cm_param);
00811             guid_setter =
00812                 (void (*)(QofEntity *,
00813                     const GUID *)) cm_param->param_setfcn;
00814             if (guid_setter != NULL)
00815                 guid_setter (rule->targetEnt, cm_guid);
00816             registered_type = TRUE;
00817         }
00818         if (safe_strcmp (rule->mergeType, QOF_TYPE_INT32) == 0)
00819         {
00820             int32_getter =
00821                 (gint32 (*)(QofEntity *,
00822                     QofParam *)) cm_param->param_getfcn;
00823             cm_i32 = int32_getter (rule->importEnt, cm_param);
00824             i32_setter =
00825                 (void (*)(QofEntity *, gint32)) cm_param->param_setfcn;
00826             if (i32_setter != NULL)
00827                 i32_setter (rule->targetEnt, cm_i32);
00828             registered_type = TRUE;
00829         }
00830         if (safe_strcmp (rule->mergeType, QOF_TYPE_INT64) == 0)
00831         {
00832             int64_getter =
00833                 (gint64 (*)(QofEntity *,
00834                     QofParam *)) cm_param->param_getfcn;
00835             cm_i64 = int64_getter (rule->importEnt, cm_param);
00836             i64_setter =
00837                 (void (*)(QofEntity *, gint64)) cm_param->param_setfcn;
00838             if (i64_setter != NULL)
00839                 i64_setter (rule->targetEnt, cm_i64);
00840             registered_type = TRUE;
00841         }
00842         if (safe_strcmp (rule->mergeType, QOF_TYPE_DOUBLE) == 0)
00843         {
00844             double_getter =
00845                 (double (*)(QofEntity *,
00846                     QofParam *)) cm_param->param_getfcn;
00847             cm_double = double_getter (rule->importEnt, cm_param);
00848             double_setter =
00849                 (void (*)(QofEntity *, double)) cm_param->param_setfcn;
00850             if (double_setter != NULL)
00851                 double_setter (rule->targetEnt, cm_double);
00852             registered_type = TRUE;
00853         }
00854         if (safe_strcmp (rule->mergeType, QOF_TYPE_BOOLEAN) == 0)
00855         {
00856             boolean_getter =
00857                 (gboolean (*)(QofEntity *, QofParam *)) cm_param->
00858                 param_getfcn;
00859             cm_boolean = boolean_getter (rule->importEnt, cm_param);
00860             boolean_setter =
00861                 (void (*)(QofEntity *, gboolean)) cm_param->param_setfcn;
00862             if (boolean_setter != NULL)
00863                 boolean_setter (rule->targetEnt, cm_boolean);
00864             registered_type = TRUE;
00865         }
00866         if (safe_strcmp (rule->mergeType, QOF_TYPE_KVP) == 0)
00867         {
00868             cm_kvp =
00869                 kvp_frame_copy (cm_param->
00870                 param_getfcn (rule->importEnt, cm_param));
00871             kvp_frame_setter =
00872                 (void (*)(QofEntity *, KvpFrame *)) cm_param->param_setfcn;
00873             if (kvp_frame_setter != NULL)
00874                 kvp_frame_setter (rule->targetEnt, cm_kvp);
00875             registered_type = TRUE;
00876         }
00877         if (safe_strcmp (rule->mergeType, QOF_TYPE_CHAR) == 0)
00878         {
00879             char_getter =
00880                 (gchar (*)(QofEntity *,
00881                     QofParam *)) cm_param->param_getfcn;
00882             cm_char = char_getter (rule->importEnt, cm_param);
00883             char_setter =
00884                 (void (*)(QofEntity *, gchar)) cm_param->param_setfcn;
00885             if (char_setter != NULL)
00886                 char_setter (rule->targetEnt, cm_char);
00887             registered_type = TRUE;
00888         }
00889         if (safe_strcmp (rule->mergeType, QOF_TYPE_COLLECT) == 0)
00890         {
00891             cm_coll = cm_param->param_getfcn (rule->importEnt, cm_param);
00892             collection_setter =
00893                 (void (*)(QofEntity *, QofCollection *)) cm_param->
00894                 param_setfcn;
00895             if (collection_setter != NULL)
00896                 collection_setter (rule->targetEnt, cm_coll);
00897             registered_type = TRUE;
00898         }
00899         if (safe_strcmp (rule->mergeType, QOF_TYPE_CHOICE) == 0)
00900         {
00901             referenceEnt =
00902                 cm_param->param_getfcn (rule->importEnt, cm_param);
00903             reference_setter =
00904                 (void (*)(QofEntity *,
00905                     QofEntity *)) cm_param->param_setfcn;
00906             if (reference_setter != NULL)
00907                 reference_setter (rule->targetEnt, referenceEnt);
00908             registered_type = TRUE;
00909         }
00910         if (registered_type == FALSE)
00911         {
00912             referenceEnt =
00913                 cm_param->param_getfcn (rule->importEnt, cm_param);
00914             if (referenceEnt)
00915             {
00916                 reference_setter =
00917                     (void (*)(QofEntity *, QofEntity *)) cm_param->
00918                     param_setfcn;
00919                 if (reference_setter != NULL)
00920                 {
00921                     reference_setter (rule->targetEnt, referenceEnt);
00922                 }
00923             }
00924         }
00925         rule->mergeParam = g_slist_next (rule->mergeParam);
00926     }
00927 }
00928 
00929 /* ================================================================ */
00930 /* API functions. */
00931 
00932 QofBookMergeData *
00933 qof_book_merge_init (QofBook * importBook, QofBook * targetBook)
00934 {
00935     QofBookMergeData *mergeData;
00936     QofBookMergeRule *currentRule;
00937     GList *check;
00938 
00939     g_return_val_if_fail ((importBook != NULL)
00940         && (targetBook != NULL), NULL);
00941     mergeData = g_new0 (QofBookMergeData, 1);
00942     mergeData->abort = FALSE;
00943     mergeData->mergeList = NULL;
00944     mergeData->targetList = NULL;
00945     mergeData->mergeBook = importBook;
00946     mergeData->targetBook = targetBook;
00947     mergeData->mergeObjectParams = NULL;
00948     mergeData->orphan_list = NULL;
00949     mergeData->target_table =
00950         g_hash_table_new (g_direct_hash, qof_book_merge_rule_cmp);
00951     currentRule = g_new0 (QofBookMergeRule, 1);
00952     mergeData->currentRule = currentRule;
00953     qof_object_foreach_type (qof_book_merge_foreach_type, mergeData);
00954     g_return_val_if_fail (mergeData->mergeObjectParams, NULL);
00955     if (mergeData->orphan_list != NULL)
00956         qof_book_merge_match_orphans (mergeData);
00957     for (check = mergeData->mergeList; check != NULL; check = check->next)
00958     {
00959         currentRule = check->data;
00960         if (currentRule->mergeResult == MERGE_INVALID)
00961         {
00962             mergeData->abort = TRUE;
00963             return (NULL);
00964         }
00965     }
00966     return mergeData;
00967 }
00968 
00969 void
00970 qof_book_merge_abort (QofBookMergeData * mergeData)
00971 {
00972     QofBookMergeRule *currentRule;
00973 
00974     g_return_if_fail (mergeData != NULL);
00975     while (mergeData->mergeList != NULL)
00976     {
00977         currentRule = mergeData->mergeList->data;
00978         g_slist_free (currentRule->linkedEntList);
00979         g_slist_free (currentRule->mergeParam);
00980         g_free (mergeData->mergeList->data);
00981         if (currentRule)
00982         {
00983             g_slist_free (currentRule->linkedEntList);
00984             g_slist_free (currentRule->mergeParam);
00985             g_free (currentRule);
00986         }
00987         mergeData->mergeList = g_list_next (mergeData->mergeList);
00988     }
00989     g_list_free (mergeData->mergeList);
00990     g_slist_free (mergeData->mergeObjectParams);
00991     g_slist_free (mergeData->targetList);
00992     if (mergeData->orphan_list != NULL)
00993         g_slist_free (mergeData->orphan_list);
00994     g_hash_table_destroy (mergeData->target_table);
00995     g_free (mergeData);
00996 }
00997 
00998 /* The QOF_TYPE_DATE output format from
00999 qof_book_merge_param_as_string has been changed to QSF_XSD_TIME,
01000 a UTC formatted timestring: 2005-01-01T10:55:23Z
01001 If you change QOF_UTC_DATE_FORMAT, change 
01002 backend/file/qsf-xml.c : qsf_entity_foreach to
01003 reformat to QSF_XSD_TIME or the QSF XML will
01004 FAIL the schema validation and QSF exports will become invalid.
01005 
01006 The QOF_TYPE_BOOLEAN is lowercase for the same reason.
01007 */
01009 gchar *
01010 qof_book_merge_param_as_string (QofParam * qtparam, QofEntity * qtEnt)
01011 {
01012     gchar *param_string;
01013     gchar param_sa[GUID_ENCODING_LENGTH + 1];
01014     QofType paramType;
01015     const GUID *param_guid;
01016     QofTime *param_qt;
01017     QofNumeric param_numeric, (*numeric_getter) (QofEntity *, QofParam *);
01018     gdouble param_double, (*double_getter) (QofEntity *, QofParam *);
01019     gboolean param_boolean, (*boolean_getter) (QofEntity *, QofParam *);
01020     gint32 param_i32, (*int32_getter) (QofEntity *, QofParam *);
01021     gint64 param_i64, (*int64_getter) (QofEntity *, QofParam *);
01022     gchar param_char, (*char_getter) (QofEntity *, QofParam *);
01023 
01024     param_string = NULL;
01025     paramType = qtparam->param_type;
01026     if (safe_strcmp (paramType, QOF_TYPE_STRING) == 0)
01027     {
01028         param_string = qtparam->param_getfcn (qtEnt, qtparam);
01029         if (param_string == NULL)
01030             param_string = "";
01031         return param_string;
01032     }
01033     if (safe_strcmp (paramType, QOF_TYPE_TIME) == 0)
01034     {
01035         QofDate *qd;
01036 
01037         param_qt = qof_time_copy (
01038             qtparam->param_getfcn (qtEnt, qtparam));
01039         if (!param_qt)
01040             return NULL;
01041         qd = qof_date_from_qtime (param_qt);
01042         param_string = qof_date_print (qd, QOF_DATE_FORMAT_UTC);
01043         qof_date_free (qd);
01044         qof_time_free (param_qt);
01045         return param_string;
01046     }
01047 #ifndef QOF_DISABLE_DEPRECATED
01048     if (safe_strcmp (paramType, QOF_TYPE_DATE) == 0)
01049     {
01050         Timespec param_ts, (*date_getter) (QofEntity *, QofParam *);
01051         time_t param_t;
01052         gchar param_date[QOF_DATE_STRING_LENGTH];
01053 
01054         date_getter =
01055             (Timespec (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
01056         param_ts = date_getter (qtEnt, qtparam);
01057         param_t = timespecToTime_t (param_ts);
01058         strftime (param_date, QOF_DATE_STRING_LENGTH, QOF_UTC_DATE_FORMAT,
01059             gmtime (&param_t));
01060         param_string = g_strdup (param_date);
01061         return param_string;
01062     }
01063 #endif
01064     if ((safe_strcmp (paramType, QOF_TYPE_NUMERIC) == 0) ||
01065         (safe_strcmp (paramType, QOF_TYPE_DEBCRED) == 0))
01066     {
01067         numeric_getter =
01068             (QofNumeric (*)(QofEntity *,
01069                 QofParam *)) qtparam->param_getfcn;
01070         param_numeric = numeric_getter (qtEnt, qtparam);
01071         param_string = g_strdup (qof_numeric_to_string (param_numeric));
01072         return param_string;
01073     }
01074     if (safe_strcmp (paramType, QOF_TYPE_GUID) == 0)
01075     {
01076         param_guid = qtparam->param_getfcn (qtEnt, qtparam);
01077         guid_to_string_buff (param_guid, param_sa);
01078         param_string = g_strdup (param_sa);
01079         return param_string;
01080     }
01081     if (safe_strcmp (paramType, QOF_TYPE_INT32) == 0)
01082     {
01083         int32_getter =
01084             (gint32 (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
01085         param_i32 = int32_getter (qtEnt, qtparam);
01086         param_string = g_strdup_printf ("%d", param_i32);
01087         return param_string;
01088     }
01089     if (safe_strcmp (paramType, QOF_TYPE_INT64) == 0)
01090     {
01091         int64_getter =
01092             (gint64 (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
01093         param_i64 = int64_getter (qtEnt, qtparam);
01094         param_string = g_strdup_printf ("%" G_GINT64_FORMAT, param_i64);
01095         return param_string;
01096     }
01097     if (safe_strcmp (paramType, QOF_TYPE_DOUBLE) == 0)
01098     {
01099         double_getter =
01100             (double (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
01101         param_double = double_getter (qtEnt, qtparam);
01102         param_string = g_strdup_printf ("%f", param_double);
01103         return param_string;
01104     }
01105     if (safe_strcmp (paramType, QOF_TYPE_BOOLEAN) == 0)
01106     {
01107         boolean_getter =
01108             (gboolean (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
01109         param_boolean = boolean_getter (qtEnt, qtparam);
01110         /* Boolean values need to be lowercase for QSF validation. */
01111         if (param_boolean == TRUE)
01112             param_string = g_strdup ("true");
01113         else
01114             param_string = g_strdup ("false");
01115         return param_string;
01116     }
01117     /* "kvp" contains repeating values, cannot be a single string for the frame. */
01118     if (safe_strcmp (paramType, QOF_TYPE_KVP) == 0)
01119         return param_string;
01120     if (safe_strcmp (paramType, QOF_TYPE_CHAR) == 0)
01121     {
01122         char_getter =
01123             (gchar (*)(QofEntity *, QofParam *)) qtparam->param_getfcn;
01124         param_char = char_getter (qtEnt, qtparam);
01125         param_string = g_strdup_printf ("%c", param_char);
01126         return param_string;
01127     }
01128     return NULL;
01129 }
01130 
01131 QofBookMergeData *
01132 qof_book_merge_update_result (QofBookMergeData * mergeData,
01133     QofBookMergeResult tag)
01134 {
01135     QofBookMergeRule *resolved;
01136 
01137     g_return_val_if_fail ((mergeData != NULL), NULL);
01138     g_return_val_if_fail ((tag > 0), NULL);
01139     g_return_val_if_fail ((tag != MERGE_REPORT), NULL);
01140     resolved = mergeData->currentRule;
01141     g_return_val_if_fail ((resolved != NULL), NULL);
01142     if ((resolved->mergeAbsolute == TRUE) && (tag == MERGE_DUPLICATE))
01143         tag = MERGE_ABSOLUTE;
01144     if ((resolved->mergeAbsolute == TRUE) && (tag == MERGE_NEW))
01145         tag = MERGE_UPDATE;
01146     if ((resolved->mergeAbsolute == FALSE) && (tag == MERGE_ABSOLUTE))
01147         tag = MERGE_DUPLICATE;
01148     if ((resolved->mergeResult == MERGE_NEW) && (tag == MERGE_UPDATE))
01149         tag = MERGE_NEW;
01150     if (resolved->updated == FALSE)
01151         resolved->mergeResult = tag;
01152     resolved->updated = TRUE;
01153     if (tag >= MERGE_INVALID)
01154     {
01155         mergeData->abort = TRUE;
01156         mergeData->currentRule = resolved;
01157         return NULL;
01158     }
01159     mergeData->currentRule = resolved;
01160     return mergeData;
01161 }
01162 
01163 gint
01164 qof_book_merge_commit (QofBookMergeData * mergeData)
01165 {
01166     QofBookMergeRule *currentRule;
01167     GList *check, *node;
01168 
01169     g_return_val_if_fail (mergeData != NULL, -1);
01170     g_return_val_if_fail (mergeData->mergeList != NULL, -1);
01171     g_return_val_if_fail (mergeData->targetBook != NULL, -1);
01172     if (mergeData->abort == TRUE)
01173         return -1;
01174     check = g_list_copy (mergeData->mergeList);
01175     g_return_val_if_fail (check != NULL, -1);
01176     for (node = check; node != NULL; node = node->next)
01177     {
01178         currentRule = node->data;
01179         if (currentRule->mergeResult == MERGE_INVALID)
01180         {
01181             qof_book_merge_abort (mergeData);
01182             g_list_free (check);
01183             return (-2);
01184         }
01185         if (currentRule->mergeResult == MERGE_REPORT)
01186         {
01187             g_list_free (check);
01188             return 1;
01189         }
01190     }
01191     g_list_free (check);
01192     qof_book_merge_commit_foreach (qof_book_merge_commit_rule_loop,
01193         MERGE_NEW, mergeData);
01194     qof_book_merge_commit_foreach (qof_book_merge_commit_rule_loop,
01195         MERGE_UPDATE, mergeData);
01196     /* Placeholder for QofObject merge_helper_cb - all objects
01197        and all parameters set */
01198     while (mergeData->mergeList != NULL)
01199     {
01200         currentRule = mergeData->mergeList->data;
01201         g_slist_free (currentRule->mergeParam);
01202         g_slist_free (currentRule->linkedEntList);
01203         mergeData->mergeList = g_list_next (mergeData->mergeList);
01204     }
01205     g_list_free (mergeData->mergeList);
01206     g_slist_free (mergeData->mergeObjectParams);
01207     g_slist_free (mergeData->targetList);
01208     if (mergeData->orphan_list != NULL)
01209         g_slist_free (mergeData->orphan_list);
01210     g_hash_table_destroy (mergeData->target_table);
01211     g_free (mergeData);
01212     return 0;
01213 }
01214 
01215 void
01216 qof_book_merge_rule_foreach (QofBookMergeData * mergeData,
01217     QofBookMergeRuleForeachCB cb, QofBookMergeResult mergeResult)
01218 {
01219     struct QofBookMergeRuleIterate qiter;
01220     QofBookMergeRule *currentRule;
01221     GList *matching_rules, *node;
01222 
01223     g_return_if_fail (cb != NULL);
01224     g_return_if_fail (mergeData != NULL);
01225     currentRule = mergeData->currentRule;
01226     g_return_if_fail (mergeResult > 0);
01227     g_return_if_fail (mergeResult != MERGE_INVALID);
01228     g_return_if_fail (mergeData->abort == FALSE);
01229     qiter.fcn = cb;
01230     qiter.data = mergeData;
01231     matching_rules = NULL;
01232     for (node = mergeData->mergeList; node != NULL; node = node->next)
01233     {
01234         currentRule = node->data;
01235         if (currentRule->mergeResult == mergeResult)
01236             matching_rules = g_list_prepend (matching_rules, 
01237                 currentRule);
01238     }
01239     qiter.remainder = g_list_length (matching_rules);
01240     g_list_foreach (matching_rules, qof_book_merge_rule_cb, &qiter);
01241     g_list_free (matching_rules);
01242 }
01243 
01244 /* ============================================================== */

Generated on Thu Jan 31 22:50:25 2008 for QOF by  doxygen 1.5.4