qofbookmerge.c

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

Generated on Fri May 12 17:57:20 2006 for QOF by  doxygen 1.4.4