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