00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <glib.h>
00025 #include "qof.h"
00026
00027 static QofLogModule log_module = QOF_MOD_MERGE;
00028
00029
00030 struct QofBookMergeRuleIterate {
00031 QofBookMergeRuleForeachCB fcn;
00032 QofBookMergeData *data;
00033 QofBookMergeRule *rule;
00034 GList *ruleList;
00035 guint remainder;
00036 };
00037
00038
00039
00040
00041
00042
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 &¤tRule->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
00132 if(stringImport == NULL) { stringImport = ""; }
00133 if(stringTarget == NULL) { stringTarget = ""; }
00134 if(safe_strcmp(stringImport,stringTarget) == 0) { mergeMatch = TRUE; }
00135
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
00224
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
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
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
00353
00354
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
00363 if(rule == NULL) { return; }
00364
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
00383
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
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
00492 g_return_if_fail(qof_book_merge_compare(mergeData) != -1);
00493 if(mergeRule->difference == 0) {
00494
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
00502 return;
00503 }
00504 if(difference > mergeRule->difference) {
00505
00506
00507 best_matchEnt = mergeRule->targetEnt;
00508 difference = mergeRule->difference;
00509
00510
00511
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
00522
00523 g_hash_table_insert(mergeData->target_table, mergeRule->targetEnt, mergeRule);
00524
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
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
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
00598 QofCollection *cm_coll;
00599 QofParam *cm_param;
00600 gchar *cm_string;
00601 const GUID *cm_guid;
00602 KvpFrame *cm_kvp;
00603
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
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
00630
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
00638
00639
00640
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
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
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
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(¶m_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
00889 if(param_boolean == TRUE) { param_string = g_strdup("true"); }
00890 else { param_string = g_strdup("false"); }
00891 return param_string;
00892 }
00893
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
00973
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
01020