mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
* Neil William's patch to remove static mergeData context.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@10414 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
2005-01-15 Derek Atkins <derek@ihtfp.com>
|
||||
|
||||
* Neil William's patch to remove static mergeData context.
|
||||
|
||||
2005-01-12 Christian Stimming <stimming@tuhh.de>
|
||||
|
||||
* rpm/gnucash.spec.in, rpm/README: Remove obsolete package
|
||||
|
||||
@@ -25,25 +25,35 @@
|
||||
#include "qofid-p.h"
|
||||
static short module = MOD_IMPORT;
|
||||
|
||||
/* all qof_book_merge data is held in mergeData. */
|
||||
static qof_book_mergeData* mergeData = NULL;
|
||||
/* private rule iteration struct */
|
||||
struct qof_book_mergeRuleIterate {
|
||||
qof_book_mergeRuleForeachCB fcn;
|
||||
qof_book_mergeData *data;
|
||||
qof_book_mergeRule *rule;
|
||||
GList *ruleList;
|
||||
guint remainder;
|
||||
};
|
||||
|
||||
/*
|
||||
currentRule is only used when a qof_book_mergeRule is being inspected or
|
||||
tested, not when it is created. This is to avoid the need for g_new()
|
||||
every time a single rule is checked.
|
||||
|
||||
Rules are created and freed separately, via the mergeData GList, mergeList.
|
||||
/* Make string type parameters 3 times more
|
||||
important in the match than default types.
|
||||
i.e. even if two other parameters differ,
|
||||
a string match will still provide a better target
|
||||
than when other types match and the string does not.
|
||||
*/
|
||||
static qof_book_mergeRule* currentRule = NULL;
|
||||
#define DEFAULT_MERGE_WEIGHT 1
|
||||
#define QOF_STRING_WEIGHT 3
|
||||
|
||||
/* ================================================================ */
|
||||
/* API functions. */
|
||||
int
|
||||
|
||||
qof_book_mergeData*
|
||||
qof_book_mergeInit( QofBook *importBook, QofBook *targetBook)
|
||||
{
|
||||
qof_book_mergeData *mergeData;
|
||||
qof_book_mergeRule *currentRule;
|
||||
GList *check;
|
||||
g_return_val_if_fail((importBook != NULL)&&(targetBook != NULL), -1);
|
||||
|
||||
g_return_val_if_fail((importBook != NULL)&&(targetBook != NULL), NULL);
|
||||
mergeData = g_new(qof_book_mergeData, 1);
|
||||
mergeData->abort = FALSE;
|
||||
mergeData->mergeList = NULL;
|
||||
@@ -51,31 +61,44 @@ qof_book_mergeInit( QofBook *importBook, QofBook *targetBook)
|
||||
mergeData->mergeBook = importBook;
|
||||
mergeData->targetBook = targetBook;
|
||||
mergeData->mergeObjectParams = NULL;
|
||||
mergeData->orphan_list = NULL;
|
||||
mergeData->target_table = g_hash_table_new( g_direct_hash, qof_book_merge_rule_cmp);
|
||||
currentRule = g_new(qof_book_mergeRule, 1);
|
||||
qof_object_foreach_type(qof_book_mergeForeachType, NULL);
|
||||
mergeData->currentRule = currentRule;
|
||||
qof_object_foreach_type(qof_book_mergeForeachType, mergeData);
|
||||
g_return_val_if_fail(mergeData->mergeObjectParams, NULL);
|
||||
if(mergeData->orphan_list != NULL) {
|
||||
qof_book_merge_match_orphans(mergeData);
|
||||
}
|
||||
|
||||
check = g_list_copy(mergeData->mergeList);
|
||||
while(check != NULL) {
|
||||
currentRule = check->data;
|
||||
if(currentRule->mergeResult == MERGE_INVALID) {
|
||||
qof_book_merge_abort();
|
||||
return(-1);
|
||||
qof_book_merge_abort(mergeData);
|
||||
return(NULL);
|
||||
}
|
||||
check = g_list_next(check);
|
||||
}
|
||||
g_list_free(check);
|
||||
return 0;
|
||||
return mergeData;
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_merge_abort (void)
|
||||
qof_book_merge_abort (qof_book_mergeData *mergeData)
|
||||
{
|
||||
qof_book_mergeRule *currentRule;
|
||||
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
while(mergeData->mergeList != NULL) {
|
||||
currentRule = mergeData->mergeList->data;
|
||||
g_slist_free(currentRule->linkedEntList);
|
||||
g_slist_free(currentRule->mergeParam);
|
||||
g_free(mergeData->mergeList->data);
|
||||
if(currentRule) {
|
||||
g_slist_free(currentRule->linkedEntList);
|
||||
g_slist_free(currentRule->mergeParam);
|
||||
g_free(currentRule);
|
||||
}
|
||||
while(mergeData->mergeList != NULL) {
|
||||
g_free(mergeData->mergeList->data);
|
||||
mergeData->mergeList = g_list_next(mergeData->mergeList);
|
||||
}
|
||||
while(mergeData->targetList != NULL) {
|
||||
@@ -85,124 +108,151 @@ qof_book_merge_abort (void)
|
||||
g_list_free(mergeData->mergeList);
|
||||
g_slist_free(mergeData->mergeObjectParams);
|
||||
g_slist_free(mergeData->targetList);
|
||||
if(mergeData->orphan_list != NULL) { g_slist_free(mergeData->orphan_list); }
|
||||
g_hash_table_destroy(mergeData->target_table);
|
||||
g_free(mergeData);
|
||||
}
|
||||
|
||||
/* Q: This could be a general usage function:
|
||||
qof_param_as_string(QofParam*, QofEntity*);
|
||||
Useful? Worth transferring to qofclass.c?
|
||||
Need to fix the KVP->string. How?
|
||||
*/
|
||||
char*
|
||||
qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt)
|
||||
{
|
||||
gchar *stringImport;
|
||||
char sa[GUID_ENCODING_LENGTH + 1];
|
||||
KvpFrame *kvpImport;
|
||||
QofType mergeType;
|
||||
const GUID *guidImport;
|
||||
gnc_numeric numericImport, (*numeric_getter) (QofEntity*, QofParam*);
|
||||
Timespec tsImport, (*date_getter) (QofEntity*, QofParam*);
|
||||
double doubleImport, (*double_getter) (QofEntity*, QofParam*);
|
||||
gboolean booleanImport, (*boolean_getter) (QofEntity*, QofParam*);
|
||||
gint32 i32Import, (*int32_getter) (QofEntity*, QofParam*);
|
||||
gint64 i64Import, (*int64_getter) (QofEntity*, QofParam*);
|
||||
gchar *param_string;
|
||||
char param_sa[GUID_ENCODING_LENGTH + 1];
|
||||
KvpFrame *param_kvp;
|
||||
QofType paramType;
|
||||
const GUID *param_guid;
|
||||
gnc_numeric param_numeric, (*numeric_getter) (QofEntity*, QofParam*);
|
||||
Timespec param_ts, (*date_getter) (QofEntity*, QofParam*);
|
||||
double param_double, (*double_getter) (QofEntity*, QofParam*);
|
||||
gboolean param_boolean, (*boolean_getter) (QofEntity*, QofParam*);
|
||||
gint32 param_i32, (*int32_getter) (QofEntity*, QofParam*);
|
||||
gint64 param_i64, (*int64_getter) (QofEntity*, QofParam*);
|
||||
|
||||
stringImport = NULL;
|
||||
mergeType = qtparam->param_type;
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_STRING) == 0) {
|
||||
stringImport = g_strdup(qtparam->param_getfcn(qtEnt,qtparam));
|
||||
if(stringImport == NULL) { stringImport = ""; }
|
||||
return stringImport;
|
||||
param_string = NULL;
|
||||
paramType = qtparam->param_type;
|
||||
if(safe_strcmp(paramType, QOF_TYPE_STRING) == 0) {
|
||||
param_string = g_strdup(qtparam->param_getfcn(qtEnt,qtparam));
|
||||
if(param_string == NULL) { param_string = ""; }
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_DATE) == 0) {
|
||||
if(safe_strcmp(paramType, QOF_TYPE_DATE) == 0) {
|
||||
date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
|
||||
tsImport = date_getter(qtEnt, qtparam);
|
||||
stringImport = g_strdup_printf("%llu", tsImport.tv_sec);
|
||||
return stringImport;
|
||||
param_ts = date_getter(qtEnt, qtparam);
|
||||
param_string = g_strdup_printf("%llu", param_ts.tv_sec);
|
||||
return param_string;
|
||||
}
|
||||
if((safe_strcmp(mergeType, QOF_TYPE_NUMERIC) == 0) ||
|
||||
(safe_strcmp(mergeType, QOF_TYPE_DEBCRED) == 0)) {
|
||||
if((safe_strcmp(paramType, QOF_TYPE_NUMERIC) == 0) ||
|
||||
(safe_strcmp(paramType, QOF_TYPE_DEBCRED) == 0)) {
|
||||
numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
|
||||
numericImport = numeric_getter(qtEnt,qtparam);
|
||||
stringImport = g_strdup(gnc_numeric_to_string(numericImport));
|
||||
return stringImport;
|
||||
param_numeric = numeric_getter(qtEnt,qtparam);
|
||||
param_string = g_strdup(gnc_numeric_to_string(param_numeric));
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_GUID) == 0) {
|
||||
guidImport = qtparam->param_getfcn(qtEnt,qtparam);
|
||||
guid_to_string_buff(guidImport, sa);
|
||||
stringImport = g_strdup(sa);
|
||||
return stringImport;
|
||||
if(safe_strcmp(paramType, QOF_TYPE_GUID) == 0) {
|
||||
param_guid = qtparam->param_getfcn(qtEnt,qtparam);
|
||||
guid_to_string_buff(param_guid, param_sa);
|
||||
param_string = g_strdup(param_sa);
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_INT32) == 0) {
|
||||
if(safe_strcmp(paramType, QOF_TYPE_INT32) == 0) {
|
||||
int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
|
||||
i32Import = int32_getter(qtEnt, qtparam);
|
||||
stringImport = g_strdup_printf("%u", i32Import);
|
||||
return stringImport;
|
||||
param_i32 = int32_getter(qtEnt, qtparam);
|
||||
param_string = g_strdup_printf("%u", param_i32);
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_INT64) == 0) {
|
||||
if(safe_strcmp(paramType, QOF_TYPE_INT64) == 0) {
|
||||
int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
|
||||
i64Import = int64_getter(qtEnt, qtparam);
|
||||
stringImport = g_strdup_printf("%llu", i64Import);
|
||||
return stringImport;
|
||||
param_i64 = int64_getter(qtEnt, qtparam);
|
||||
param_string = g_strdup_printf("%llu", param_i64);
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_DOUBLE) == 0) {
|
||||
if(safe_strcmp(paramType, QOF_TYPE_DOUBLE) == 0) {
|
||||
double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
|
||||
doubleImport = double_getter(qtEnt, qtparam);
|
||||
stringImport = g_strdup_printf("%f", doubleImport);
|
||||
return stringImport;
|
||||
param_double = double_getter(qtEnt, qtparam);
|
||||
param_string = g_strdup_printf("%f", param_double);
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_BOOLEAN) == 0){
|
||||
if(safe_strcmp(paramType, QOF_TYPE_BOOLEAN) == 0){
|
||||
boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
|
||||
booleanImport = boolean_getter(qtEnt, qtparam);
|
||||
if(booleanImport == TRUE) { stringImport = g_strdup("TRUE"); }
|
||||
else { stringImport = g_strdup("FALSE"); }
|
||||
return stringImport;
|
||||
param_boolean = boolean_getter(qtEnt, qtparam);
|
||||
if(param_boolean == TRUE) { param_string = g_strdup("TRUE"); }
|
||||
else { param_string = g_strdup("FALSE"); }
|
||||
return param_string;
|
||||
}
|
||||
/* "kvp" */
|
||||
/* FIXME: how can this be a string??? */
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_KVP) == 0) {
|
||||
kvpImport = kvp_frame_copy(qtparam->param_getfcn(qtEnt,qtparam));
|
||||
if(safe_strcmp(paramType, QOF_TYPE_KVP) == 0) {
|
||||
param_kvp = kvp_frame_copy(qtparam->param_getfcn(qtEnt,qtparam));
|
||||
|
||||
return stringImport;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_CHAR) == 0) {
|
||||
stringImport = g_strdup(qtparam->param_getfcn(qtEnt,qtparam));
|
||||
return stringImport;
|
||||
if(safe_strcmp(paramType, QOF_TYPE_CHAR) == 0) {
|
||||
param_string = g_strdup(qtparam->param_getfcn(qtEnt,qtparam));
|
||||
return param_string;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
qof_book_mergeUpdateResult(qof_book_mergeRule *resolved, qof_book_mergeResult tag)
|
||||
qof_book_mergeData*
|
||||
qof_book_mergeUpdateResult(qof_book_mergeData *mergeData,
|
||||
qof_book_mergeResult tag)
|
||||
{
|
||||
g_return_val_if_fail((resolved != NULL), -1);
|
||||
g_return_val_if_fail((tag > 0), -1);
|
||||
g_return_val_if_fail((tag != MERGE_REPORT), -1);
|
||||
currentRule = resolved;
|
||||
if((currentRule->mergeAbsolute == TRUE)&& (tag == MERGE_DUPLICATE)) { tag = MERGE_ABSOLUTE; }
|
||||
if((currentRule->mergeAbsolute == TRUE)&& (tag == MERGE_NEW)) { tag = MERGE_UPDATE; }
|
||||
if((currentRule->mergeAbsolute == FALSE)&& (tag == MERGE_ABSOLUTE)) { tag = MERGE_DUPLICATE; }
|
||||
if((currentRule->mergeResult == MERGE_NEW)&&(tag == MERGE_UPDATE)) { tag = MERGE_NEW; }
|
||||
if(currentRule->updated == FALSE) { currentRule->mergeResult = tag; }
|
||||
currentRule->updated = TRUE;
|
||||
if(tag == MERGE_INVALID) {
|
||||
mergeData->abort = TRUE;
|
||||
qof_book_merge_abort();
|
||||
qof_book_mergeRule *resolved;
|
||||
|
||||
g_return_val_if_fail((mergeData != NULL), NULL);
|
||||
g_return_val_if_fail((tag > 0), NULL);
|
||||
g_return_val_if_fail((tag != MERGE_REPORT), NULL);
|
||||
resolved = mergeData->currentRule;
|
||||
g_return_val_if_fail((resolved != NULL), NULL);
|
||||
if((resolved->mergeAbsolute == TRUE)&&(tag == MERGE_DUPLICATE))
|
||||
{
|
||||
tag = MERGE_ABSOLUTE;
|
||||
}
|
||||
return 0;
|
||||
if((resolved->mergeAbsolute == TRUE)&&(tag == MERGE_NEW))
|
||||
{
|
||||
tag = MERGE_UPDATE;
|
||||
}
|
||||
if((resolved->mergeAbsolute == FALSE)&& (tag == MERGE_ABSOLUTE))
|
||||
{
|
||||
tag = MERGE_DUPLICATE;
|
||||
}
|
||||
if((resolved->mergeResult == MERGE_NEW)&&(tag == MERGE_UPDATE))
|
||||
{
|
||||
tag = MERGE_NEW;
|
||||
}
|
||||
if(resolved->updated == FALSE) { resolved->mergeResult = tag; }
|
||||
resolved->updated = TRUE;
|
||||
if(tag >= MERGE_INVALID) {
|
||||
mergeData->abort = TRUE;
|
||||
mergeData->currentRule = resolved;
|
||||
return NULL;
|
||||
}
|
||||
mergeData->currentRule = resolved;
|
||||
return mergeData;
|
||||
}
|
||||
|
||||
int
|
||||
qof_book_mergeCommit( void )
|
||||
qof_book_mergeCommit( qof_book_mergeData *mergeData )
|
||||
{
|
||||
qof_book_mergeRule *currentRule;
|
||||
GList *check;
|
||||
|
||||
if(mergeData->abort == TRUE) return -1;
|
||||
g_return_val_if_fail(mergeData != NULL, -1);
|
||||
g_return_val_if_fail(mergeData->mergeList != NULL, -1);
|
||||
currentRule = mergeData->mergeList->data;
|
||||
g_return_val_if_fail(mergeData->targetBook != NULL, -1);
|
||||
if(mergeData->abort == TRUE) return -1;
|
||||
check = g_list_copy(mergeData->mergeList);
|
||||
g_return_val_if_fail(check != NULL, -1);
|
||||
while(check != NULL) {
|
||||
currentRule = check->data;
|
||||
if(currentRule->mergeResult == MERGE_INVALID) {
|
||||
qof_book_merge_abort();
|
||||
return(-1);
|
||||
qof_book_merge_abort(mergeData);
|
||||
return(-2);
|
||||
}
|
||||
if(currentRule->mergeResult == MERGE_REPORT) {
|
||||
g_list_free(check);
|
||||
@@ -210,16 +260,24 @@ qof_book_mergeCommit( void )
|
||||
}
|
||||
check = g_list_next(check);
|
||||
}
|
||||
g_list_free(check);
|
||||
qof_book_mergeCommitForeach( qof_book_mergeCommitRuleLoop, MERGE_NEW);
|
||||
qof_book_mergeCommitForeach( qof_book_mergeCommitRuleLoop, MERGE_UPDATE);
|
||||
qof_book_mergeCommitForeach( qof_book_mergeCommitRuleLoop, MERGE_NEW, mergeData);
|
||||
qof_book_mergeCommitForeach( qof_book_mergeCommitRuleLoop, MERGE_UPDATE, mergeData);
|
||||
/* Placeholder for QofObject merge_helper_cb - all objects and all parameters set */
|
||||
while(mergeData->mergeList != NULL) {
|
||||
g_free(mergeData->mergeList->data);
|
||||
currentRule = mergeData->mergeList->data;
|
||||
g_slist_free(currentRule->mergeParam);
|
||||
g_slist_free(currentRule->linkedEntList);
|
||||
mergeData->mergeList = g_list_next(mergeData->mergeList);
|
||||
}
|
||||
while(mergeData->targetList != NULL) {
|
||||
g_free(mergeData->targetList->data);
|
||||
mergeData->targetList = g_slist_next(mergeData->targetList);
|
||||
}
|
||||
g_list_free(mergeData->mergeList);
|
||||
g_slist_free(mergeData->mergeObjectParams);
|
||||
g_slist_free(mergeData->targetList);
|
||||
if(mergeData->orphan_list != NULL) { g_slist_free(mergeData->orphan_list); }
|
||||
g_hash_table_destroy(mergeData->target_table);
|
||||
g_free(mergeData);
|
||||
return 0;
|
||||
}
|
||||
@@ -227,13 +285,17 @@ qof_book_mergeCommit( void )
|
||||
/* End of API functions. Internal code follows. */
|
||||
/* ==================================================================== */
|
||||
|
||||
void qof_book_mergeRuleForeach( qof_book_mergeRuleForeachCB cb, qof_book_mergeResult mergeResult)
|
||||
void qof_book_mergeRuleForeach( qof_book_mergeData *mergeData,
|
||||
qof_book_mergeRuleForeachCB cb,
|
||||
qof_book_mergeResult mergeResult )
|
||||
{
|
||||
struct qof_book_mergeRuleIterate iter;
|
||||
qof_book_mergeRule *currentRule;
|
||||
GList *matching_rules;
|
||||
|
||||
g_return_if_fail(cb != NULL);
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_if_fail(mergeResult > 0);
|
||||
g_return_if_fail(mergeResult != MERGE_INVALID);
|
||||
g_return_if_fail(mergeData->abort == FALSE);
|
||||
@@ -247,15 +309,14 @@ void qof_book_mergeRuleForeach( qof_book_mergeRuleForeachCB cb, qof_book_mergeRe
|
||||
}
|
||||
iter.ruleList = g_list_next(iter.ruleList);
|
||||
}
|
||||
iter.data = mergeData;
|
||||
iter.remainder = g_list_length(matching_rules);
|
||||
g_list_free(iter.ruleList);
|
||||
g_list_foreach (matching_rules, qof_book_mergeRuleCB, &iter);
|
||||
g_list_free(matching_rules);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
qof_book_mergeUpdateRule(gboolean match)
|
||||
qof_book_mergeRule*
|
||||
qof_book_mergeUpdateRule(qof_book_mergeRule *currentRule, gboolean match, gint weight)
|
||||
{
|
||||
gboolean absolute;
|
||||
|
||||
@@ -268,16 +329,18 @@ qof_book_mergeUpdateRule(gboolean match)
|
||||
currentRule->mergeResult = MERGE_DUPLICATE;
|
||||
}
|
||||
if(!absolute && !match) {
|
||||
currentRule->difference++;
|
||||
currentRule->difference += weight;
|
||||
if(currentRule->mergeResult == MERGE_DUPLICATE) {
|
||||
currentRule->mergeResult = MERGE_REPORT;
|
||||
}
|
||||
}
|
||||
return currentRule;
|
||||
}
|
||||
|
||||
int
|
||||
qof_book_mergeCompare( void )
|
||||
qof_book_mergeCompare( qof_book_mergeData *mergeData )
|
||||
{
|
||||
qof_book_mergeRule *currentRule;
|
||||
gchar *stringImport, *stringTarget, *charImport, *charTarget;
|
||||
QofEntity *mergeEnt, *targetEnt, *referenceEnt;
|
||||
const GUID *guidImport, *guidTarget;
|
||||
@@ -294,13 +357,16 @@ qof_book_mergeCompare( void )
|
||||
gint32 i32Import, i32Target, (*int32_getter) (QofEntity*, QofParam*);
|
||||
gint64 i64Import, i64Target, (*int64_getter) (QofEntity*, QofParam*);
|
||||
|
||||
g_return_val_if_fail((mergeData != NULL)||(currentRule != NULL), -1);
|
||||
g_return_val_if_fail((mergeData != NULL), -1);
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_val_if_fail((currentRule != NULL), -1);
|
||||
absolute = currentRule->mergeAbsolute;
|
||||
mergeEnt = currentRule->importEnt;
|
||||
targetEnt = currentRule->targetEnt;
|
||||
paramList = currentRule->mergeParam;
|
||||
currentRule->difference = 0;
|
||||
currentRule->mergeResult = MERGE_UNDEF;
|
||||
currentRule->linkedEntList = NULL;
|
||||
g_return_val_if_fail((targetEnt)||(mergeEnt)||(paramList), -1);
|
||||
|
||||
kvpImport = kvp_frame_new();
|
||||
@@ -321,7 +387,8 @@ qof_book_mergeCompare( void )
|
||||
if(stringImport == NULL) { stringImport = ""; }
|
||||
if(stringTarget == NULL) { stringTarget = ""; }
|
||||
if(safe_strcmp(stringImport,stringTarget) == 0) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
/* Give special weight to a string match */
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, QOF_STRING_WEIGHT);
|
||||
stringImport = stringTarget = NULL;
|
||||
knowntype= TRUE;
|
||||
}
|
||||
@@ -330,7 +397,7 @@ qof_book_mergeCompare( void )
|
||||
tsImport = date_getter(mergeEnt, qtparam);
|
||||
tsTarget = date_getter(targetEnt, qtparam);
|
||||
if(timespec_cmp(&tsImport, &tsTarget) == 0) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if((safe_strcmp(mergeType, QOF_TYPE_NUMERIC) == 0) ||
|
||||
@@ -339,14 +406,14 @@ qof_book_mergeCompare( void )
|
||||
numericImport = numeric_getter(mergeEnt,qtparam);
|
||||
numericTarget = numeric_getter(targetEnt,qtparam);
|
||||
if(gnc_numeric_compare (numericImport, numericTarget) == 0) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_GUID) == 0) {
|
||||
guidImport = qtparam->param_getfcn(mergeEnt,qtparam);
|
||||
guidTarget = qtparam->param_getfcn(targetEnt,qtparam);
|
||||
if(guid_compare(guidImport, guidTarget) == 0) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_INT32) == 0) {
|
||||
@@ -354,7 +421,7 @@ qof_book_mergeCompare( void )
|
||||
i32Import = int32_getter(mergeEnt, qtparam);
|
||||
i32Target = int32_getter(targetEnt, qtparam);
|
||||
if(i32Target == i32Import) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_INT64) == 0) {
|
||||
@@ -362,7 +429,7 @@ qof_book_mergeCompare( void )
|
||||
i64Import = int64_getter(mergeEnt, qtparam);
|
||||
i64Target = int64_getter(targetEnt, qtparam);
|
||||
if(i64Target == i64Import) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_DOUBLE) == 0) {
|
||||
@@ -370,7 +437,7 @@ qof_book_mergeCompare( void )
|
||||
doubleImport = double_getter(mergeEnt, qtparam);
|
||||
doubleTarget = double_getter(mergeEnt, qtparam);
|
||||
if(doubleImport == doubleTarget) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_BOOLEAN) == 0){
|
||||
@@ -380,58 +447,59 @@ qof_book_mergeCompare( void )
|
||||
if(booleanImport != FALSE && booleanImport != TRUE) { booleanImport = FALSE; }
|
||||
if(booleanTarget != FALSE && booleanTarget != TRUE) { booleanTarget = FALSE; }
|
||||
if(booleanImport == booleanTarget) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_KVP) == 0) {
|
||||
kvpImport = kvp_frame_copy(qtparam->param_getfcn(mergeEnt,qtparam));
|
||||
kvpTarget = kvp_frame_copy(qtparam->param_getfcn(targetEnt,qtparam));
|
||||
if(kvp_frame_compare(kvpImport, kvpTarget) == 0) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
if(safe_strcmp(mergeType, QOF_TYPE_CHAR) == 0) {
|
||||
charImport = qtparam->param_getfcn(mergeEnt,qtparam);
|
||||
charTarget = qtparam->param_getfcn(targetEnt,qtparam);
|
||||
if(charImport == charTarget) { mergeMatch = TRUE; }
|
||||
qof_book_mergeUpdateRule(mergeMatch);
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
knowntype= TRUE;
|
||||
}
|
||||
/* No object should have QofSetterFunc defined for the book, but just to be safe, do nothing. */
|
||||
if(safe_strcmp(mergeType, QOF_ID_BOOK) == 0) { knowntype= TRUE; }
|
||||
/* deal with custom type parameters : */
|
||||
/* using references to other registered QOF objects */
|
||||
/* these references are NOT compared again here, just stored for the commit. */
|
||||
/* deal with custom type parameters :
|
||||
using references to other registered QOF objects. */
|
||||
if(knowntype == FALSE) {
|
||||
referenceEnt = g_new(QofEntity,1);
|
||||
referenceEnt = qtparam->param_getfcn(targetEnt, qtparam);
|
||||
if(referenceEnt != NULL) {
|
||||
if(referenceEnt->e_type != NULL) {
|
||||
if(safe_strcmp(referenceEnt->e_type, mergeType) != 0) {
|
||||
referenceEnt->e_type = NULL;
|
||||
g_free(referenceEnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* add to the rule so that the reference can be picked up in commit */
|
||||
if(referenceEnt) {
|
||||
referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
|
||||
if((referenceEnt != NULL)
|
||||
&&(safe_strcmp(referenceEnt->e_type, mergeType) == 0)) {
|
||||
currentRule->linkedEntList = g_slist_prepend(currentRule->linkedEntList, referenceEnt);
|
||||
/* Compare the mergeEnt reference with targetEnt reference */
|
||||
if(referenceEnt == qtparam->param_getfcn(targetEnt, qtparam)) { mergeMatch = TRUE; }
|
||||
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
|
||||
}
|
||||
}
|
||||
paramList = g_slist_next(paramList);
|
||||
}
|
||||
mergeData->currentRule = currentRule;
|
||||
g_free(kvpImport);
|
||||
g_free(kvpTarget);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_mergeCommitForeach (qof_book_mergeRuleForeachCB cb, qof_book_mergeResult mergeResult )
|
||||
qof_book_mergeCommitForeach (
|
||||
qof_book_mergeRuleForeachCB cb,
|
||||
qof_book_mergeResult mergeResult,
|
||||
qof_book_mergeData *mergeData)
|
||||
{
|
||||
struct qof_book_mergeRuleIterate iter;
|
||||
qof_book_mergeRule *currentRule;
|
||||
GList *subList;
|
||||
|
||||
g_return_if_fail(cb != NULL);
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_if_fail(currentRule != NULL);
|
||||
g_return_if_fail(mergeResult > 0);
|
||||
g_return_if_fail((mergeResult != MERGE_INVALID)||(mergeResult != MERGE_UNDEF)||(mergeResult != MERGE_REPORT));
|
||||
|
||||
@@ -446,29 +514,112 @@ qof_book_mergeCommitForeach (qof_book_mergeRuleForeachCB cb, qof_book_mergeResul
|
||||
iter.ruleList = g_list_next(iter.ruleList);
|
||||
}
|
||||
iter.remainder = g_list_length(subList);
|
||||
iter.data = mergeData;
|
||||
g_list_foreach (subList, qof_book_mergeCommitForeachCB, &iter);
|
||||
}
|
||||
|
||||
void qof_book_mergeCommitForeachCB(gpointer lister, gpointer arg)
|
||||
void qof_book_mergeCommitForeachCB(gpointer rule, gpointer arg)
|
||||
{
|
||||
struct qof_book_mergeRuleIterate *iter;
|
||||
|
||||
iter = arg;
|
||||
iter->fcn ((qof_book_mergeRule*)lister, iter->remainder);
|
||||
g_return_if_fail(arg != NULL);
|
||||
iter = (struct qof_book_mergeRuleIterate*)arg;
|
||||
g_return_if_fail(iter->data != NULL);
|
||||
iter->fcn (iter->data, (qof_book_mergeRule*)rule, iter->remainder);
|
||||
iter->remainder--;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_book_merge_rule_cmp(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
qof_book_mergeRule *ra = (qof_book_mergeRule *) a;
|
||||
qof_book_mergeRule *rb = (qof_book_mergeRule *) b;
|
||||
|
||||
if (ra->difference == rb->difference) { return TRUE; }
|
||||
else return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
qof_book_merge_orphan_check(double difference, qof_book_mergeRule *mergeRule, qof_book_mergeData *mergeData)
|
||||
{
|
||||
/* Called when difference is lower than previous
|
||||
Lookup target to find previous match
|
||||
and re-assign mergeEnt to orphan_list */
|
||||
qof_book_mergeRule *rule;
|
||||
|
||||
g_return_if_fail(mergeRule != NULL);
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
if(g_hash_table_size(mergeData->target_table) == 0) { return; }
|
||||
rule = (qof_book_mergeRule*)g_hash_table_lookup(mergeData->target_table, mergeRule->targetEnt);
|
||||
/* If NULL, no match was found. */
|
||||
if(rule == NULL) { return; }
|
||||
/* Only orphan if this is a better match than already exists. */
|
||||
if(difference >= rule->difference) { return; }
|
||||
rule->targetEnt = NULL;
|
||||
rule->mergeResult = MERGE_UNDEF;
|
||||
mergeData->orphan_list = g_slist_append(mergeData->orphan_list, rule);
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_merge_match_orphans(qof_book_mergeData *mergeData)
|
||||
{
|
||||
GSList *orphans, *targets;
|
||||
qof_book_mergeRule *rule, *currentRule;
|
||||
QofEntity *best_matchEnt;
|
||||
double difference;
|
||||
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_if_fail(currentRule != NULL);
|
||||
/* This routine does NOT copy the orphan list, it
|
||||
is used recursively until empty. */
|
||||
orphans = mergeData->orphan_list;
|
||||
targets = g_slist_copy(mergeData->targetList);
|
||||
while(orphans != NULL) {
|
||||
rule = orphans->data;
|
||||
g_return_if_fail(rule != NULL);
|
||||
difference = g_slist_length(mergeData->mergeObjectParams);
|
||||
mergeData->currentRule = rule;
|
||||
while(targets != NULL) {
|
||||
mergeData->currentRule->targetEnt = targets->data;
|
||||
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
|
||||
if(difference > mergeData->currentRule->difference) {
|
||||
best_matchEnt = currentRule->targetEnt;
|
||||
difference = currentRule->difference;
|
||||
rule = currentRule;
|
||||
mergeData->mergeList = g_list_prepend(mergeData->mergeList,rule);
|
||||
qof_book_merge_orphan_check(difference, rule, mergeData);
|
||||
}
|
||||
targets = g_slist_next(targets);
|
||||
}
|
||||
if(rule->targetEnt == NULL) {
|
||||
rule->mergeResult = MERGE_NEW;
|
||||
rule->difference = 0;
|
||||
mergeData->mergeList = g_list_prepend(mergeData->mergeList,rule);
|
||||
orphans = g_slist_next(orphans);
|
||||
continue;
|
||||
}
|
||||
orphans = g_slist_next(orphans);
|
||||
}
|
||||
g_slist_free(mergeData->orphan_list);
|
||||
g_slist_free(targets);
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_mergeForeach ( QofEntity* mergeEnt, gpointer user_data)
|
||||
{
|
||||
qof_book_mergeRule* mergeRule;
|
||||
qof_book_mergeRule *mergeRule, *currentRule;
|
||||
qof_book_mergeData *mergeData;
|
||||
QofEntity *targetEnt, *best_matchEnt;
|
||||
GUID *g;
|
||||
gint difference;
|
||||
double difference;
|
||||
GSList *c;
|
||||
|
||||
g_return_if_fail(user_data != NULL);
|
||||
mergeData = (qof_book_mergeData*)user_data;
|
||||
g_return_if_fail(mergeEnt != NULL);
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_if_fail(currentRule != NULL);
|
||||
g = guid_malloc();
|
||||
*g = mergeEnt->guid;
|
||||
mergeRule = g_new(qof_book_mergeRule,1);
|
||||
@@ -481,40 +632,53 @@ qof_book_mergeForeach ( QofEntity* mergeEnt, gpointer user_data)
|
||||
mergeRule->mergeLabel = qof_object_get_type_label(mergeEnt->e_type);
|
||||
mergeRule->mergeParam = g_slist_copy(mergeData->mergeObjectParams);
|
||||
mergeRule->linkedEntList = NULL;
|
||||
currentRule = mergeRule;
|
||||
mergeData->currentRule = mergeRule;
|
||||
targetEnt = best_matchEnt = NULL;
|
||||
targetEnt = qof_collection_lookup_entity (
|
||||
qof_book_get_collection (mergeData->targetBook, mergeEnt->e_type), g);
|
||||
if( targetEnt != NULL) {
|
||||
mergeRule->mergeAbsolute = TRUE;
|
||||
mergeRule->targetEnt = targetEnt;
|
||||
g_return_if_fail(qof_book_mergeCompare() != -1);
|
||||
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
|
||||
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
|
||||
mergeData->mergeList = g_list_prepend(mergeData->mergeList,mergeRule);
|
||||
return;
|
||||
}
|
||||
/* no absolute match exists */
|
||||
g_slist_free(mergeData->targetList);
|
||||
mergeData->targetList = NULL;
|
||||
qof_object_foreach_type(qof_book_mergeForeachTypeTarget, NULL);
|
||||
qof_object_foreach_type(qof_book_mergeForeachTypeTarget, mergeData);
|
||||
if(g_slist_length(mergeData->targetList) == 0) {
|
||||
mergeRule->mergeResult = MERGE_NEW;
|
||||
}
|
||||
difference = g_slist_length(mergeRule->mergeParam);
|
||||
c = g_slist_copy(mergeData->targetList);
|
||||
gnc_set_log_level(MOD_IMPORT, GNC_LOG_DEBUG);
|
||||
while(c != NULL) {
|
||||
mergeRule->targetEnt = c->data;
|
||||
currentRule = mergeRule;
|
||||
g_return_if_fail(qof_book_mergeCompare() != -1);
|
||||
/* compare two entities and sum the differences */
|
||||
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
|
||||
if(mergeRule->difference == 0) {
|
||||
/* check if this is a better match than one already assigned */
|
||||
best_matchEnt = mergeRule->targetEnt;
|
||||
mergeRule->mergeResult = MERGE_DUPLICATE;
|
||||
difference = 0;
|
||||
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
|
||||
g_slist_free(c);
|
||||
guid_free(g);
|
||||
/* exact match, return */
|
||||
return;
|
||||
}
|
||||
if(difference > mergeRule->difference) {
|
||||
/* The chosen targetEnt determines the parenting of any child object */
|
||||
/* check if this is a better match than one already assigned */
|
||||
best_matchEnt = mergeRule->targetEnt;
|
||||
difference = mergeRule->difference;
|
||||
/* Use match to lookup the previous entity that matched this targetEnt (if any)
|
||||
and remove targetEnt from the rule for that mergeEnt.
|
||||
Add the previous mergeEnt to orphan_list.
|
||||
*/
|
||||
qof_book_merge_orphan_check(difference, mergeRule, mergeData);
|
||||
}
|
||||
c = g_slist_next(c);
|
||||
}
|
||||
@@ -522,14 +686,18 @@ qof_book_mergeForeach ( QofEntity* mergeEnt, gpointer user_data)
|
||||
if(best_matchEnt != NULL ) {
|
||||
mergeRule->targetEnt = best_matchEnt;
|
||||
mergeRule->difference = difference;
|
||||
/* Set this entity in the target_table in case a better match can be made
|
||||
with the next mergeEnt. */
|
||||
g_hash_table_insert(mergeData->target_table, mergeRule->targetEnt, mergeRule);
|
||||
/* compare again with the best partial match */
|
||||
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
|
||||
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
|
||||
}
|
||||
else {
|
||||
mergeRule->targetEnt = NULL;
|
||||
mergeRule->difference = 0;
|
||||
mergeRule->mergeResult = MERGE_NEW;
|
||||
}
|
||||
if(best_matchEnt != NULL ) {
|
||||
g_return_if_fail(qof_book_mergeCompare() != -1);
|
||||
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
|
||||
}
|
||||
mergeData->mergeList = g_list_prepend(mergeData->mergeList,mergeRule);
|
||||
guid_free(g);
|
||||
@@ -538,48 +706,38 @@ qof_book_mergeForeach ( QofEntity* mergeEnt, gpointer user_data)
|
||||
|
||||
void qof_book_mergeForeachTarget (QofEntity* targetEnt, gpointer user_data)
|
||||
{
|
||||
g_return_if_fail(targetEnt != NULL);
|
||||
|
||||
qof_book_merge_target_check(targetEnt);
|
||||
}
|
||||
|
||||
|
||||
void qof_book_merge_target_check (QofEntity* targetEnt)
|
||||
{
|
||||
GList *checklist;
|
||||
qof_book_mergeRule *destination;
|
||||
const GUID *guid_ent, *guid_dest;
|
||||
gboolean exists;
|
||||
qof_book_mergeData *mergeData;
|
||||
|
||||
exists = FALSE;
|
||||
checklist = NULL;
|
||||
if(mergeData->mergeList == NULL) { return; }
|
||||
guid_ent = qof_entity_get_guid(targetEnt);
|
||||
checklist = g_list_copy(mergeData->mergeList);
|
||||
while(checklist != NULL) {
|
||||
destination = checklist->data;
|
||||
guid_dest = qof_entity_get_guid(destination->targetEnt);
|
||||
if(guid_compare(guid_ent,guid_dest) == 0) { exists = TRUE; }
|
||||
checklist = g_list_next(checklist);
|
||||
}
|
||||
if(exists == FALSE ) {
|
||||
g_return_if_fail(user_data != NULL);
|
||||
mergeData = (qof_book_mergeData*)user_data;
|
||||
g_return_if_fail(targetEnt != NULL);
|
||||
mergeData->targetList = g_slist_prepend(mergeData->targetList,targetEnt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_mergeForeachTypeTarget ( QofObject* merge_obj, gpointer user_data)
|
||||
{
|
||||
qof_book_mergeData *mergeData;
|
||||
qof_book_mergeRule *currentRule;
|
||||
|
||||
g_return_if_fail(user_data != NULL);
|
||||
mergeData = (qof_book_mergeData*)user_data;
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_if_fail(currentRule != NULL);
|
||||
g_return_if_fail(merge_obj != NULL);
|
||||
if(safe_strcmp(merge_obj->e_type, currentRule->importEnt->e_type) == 0) {
|
||||
qof_object_foreach(currentRule->importEnt->e_type, mergeData->targetBook,
|
||||
qof_book_mergeForeachTarget, NULL);
|
||||
qof_book_mergeForeachTarget, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_mergeForeachType ( QofObject* merge_obj, gpointer user_data)
|
||||
{
|
||||
qof_book_mergeData *mergeData;
|
||||
|
||||
g_return_if_fail(user_data != NULL);
|
||||
mergeData = (qof_book_mergeData*)user_data;
|
||||
g_return_if_fail((merge_obj != NULL));
|
||||
/* Skip unsupported objects */
|
||||
if((merge_obj->create == NULL)||(merge_obj->foreach == NULL)){
|
||||
@@ -589,13 +747,17 @@ qof_book_mergeForeachType ( QofObject* merge_obj, gpointer user_data)
|
||||
|
||||
if(mergeData->mergeObjectParams != NULL) g_slist_free(mergeData->mergeObjectParams);
|
||||
mergeData->mergeObjectParams = NULL;
|
||||
qof_class_param_foreach(merge_obj->e_type, qof_book_mergeForeachParam , NULL);
|
||||
qof_object_foreach(merge_obj->e_type, mergeData->mergeBook, qof_book_mergeForeach, NULL);
|
||||
qof_class_param_foreach(merge_obj->e_type, qof_book_mergeForeachParam , mergeData);
|
||||
qof_object_foreach(merge_obj->e_type, mergeData->mergeBook, qof_book_mergeForeach, mergeData);
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_mergeForeachParam( QofParam* param, gpointer user_data)
|
||||
{
|
||||
qof_book_mergeData *mergeData;
|
||||
|
||||
g_return_if_fail(user_data != NULL);
|
||||
mergeData = (qof_book_mergeData*)user_data;
|
||||
g_return_if_fail(param != NULL);
|
||||
if((param->param_getfcn != NULL)&&(param->param_setfcn != NULL)) {
|
||||
mergeData->mergeObjectParams = g_slist_append(mergeData->mergeObjectParams, param);
|
||||
@@ -603,17 +765,46 @@ qof_book_mergeForeachParam( QofParam* param, gpointer user_data)
|
||||
}
|
||||
|
||||
void
|
||||
qof_book_mergeRuleCB(gpointer lister, gpointer arg)
|
||||
qof_book_mergeRuleCB(gpointer rule, gpointer arg)
|
||||
{
|
||||
struct qof_book_mergeRuleIterate *iter;
|
||||
qof_book_mergeData *mergeData;
|
||||
|
||||
g_return_if_fail(arg != NULL);
|
||||
iter = (struct qof_book_mergeRuleIterate*)arg;
|
||||
mergeData = iter->data;
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
g_return_if_fail(mergeData->abort == FALSE);
|
||||
iter = arg;
|
||||
iter->fcn ((qof_book_mergeRule*)lister, iter->remainder);
|
||||
iter->fcn (mergeData, (qof_book_mergeRule*)rule, iter->remainder);
|
||||
iter->data = mergeData;
|
||||
iter->remainder--;
|
||||
}
|
||||
|
||||
void qof_book_mergeCommitRuleLoop(qof_book_mergeRule *rule, guint remainder)
|
||||
static QofEntity*
|
||||
qof_book_mergeLocateReference( QofEntity *ent, qof_book_mergeData *mergeData)
|
||||
{
|
||||
GList *all_rules;
|
||||
qof_book_mergeRule *rule;
|
||||
QofEntity *referenceEnt;
|
||||
|
||||
/* locates the rule referring to this import entity */
|
||||
if(!ent) { return NULL; }
|
||||
g_return_val_if_fail((mergeData != NULL), NULL);
|
||||
all_rules = NULL;
|
||||
referenceEnt = NULL;
|
||||
all_rules = g_list_copy(mergeData->mergeList);
|
||||
while(all_rules != NULL) {
|
||||
rule = all_rules->data;
|
||||
if(rule->importEnt == ent) { referenceEnt = rule->targetEnt; }
|
||||
all_rules = g_list_next(all_rules);
|
||||
}
|
||||
return referenceEnt;
|
||||
}
|
||||
|
||||
void qof_book_mergeCommitRuleLoop(
|
||||
qof_book_mergeData *mergeData,
|
||||
qof_book_mergeRule *rule,
|
||||
guint remainder)
|
||||
{
|
||||
QofInstance *inst;
|
||||
gboolean registered_type;
|
||||
@@ -645,8 +836,9 @@ void qof_book_mergeCommitRuleLoop(qof_book_mergeRule *rule, guint remainder)
|
||||
void (*reference_setter) (QofEntity*, QofEntity*);
|
||||
|
||||
g_return_if_fail(rule != NULL);
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
g_return_if_fail(mergeData->targetBook != NULL);
|
||||
g_return_if_fail((rule->mergeResult != MERGE_NEW)||(rule->mergeResult != MERGE_UPDATE));
|
||||
|
||||
/* create a new object for MERGE_NEW */
|
||||
/* The new object takes the GUID from the import to retain an absolute match */
|
||||
if(rule->mergeResult == MERGE_NEW) {
|
||||
@@ -734,11 +926,20 @@ void qof_book_mergeCommitRuleLoop(qof_book_mergeRule *rule, guint remainder)
|
||||
}
|
||||
if(registered_type == FALSE) {
|
||||
linkage = g_slist_copy(rule->linkedEntList);
|
||||
referenceEnt = NULL;
|
||||
// currentRule = NULL;
|
||||
reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
|
||||
if((linkage == NULL)&&(rule->mergeResult == MERGE_NEW)) {
|
||||
referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
|
||||
reference_setter(rule->targetEnt, qof_book_mergeLocateReference(referenceEnt, mergeData));
|
||||
}
|
||||
while(linkage != NULL) {
|
||||
referenceEnt = linkage->data;
|
||||
if(safe_strcmp(referenceEnt->e_type, rule->mergeType) == 0) {
|
||||
reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
|
||||
if(reference_setter != NULL) { reference_setter(rule->targetEnt, referenceEnt); }
|
||||
if((referenceEnt)
|
||||
&&(referenceEnt->e_type)
|
||||
&&(safe_strcmp(referenceEnt->e_type, rule->mergeType) == 0)) {
|
||||
/* The function behind reference_setter must create objects for any non-QOF references */
|
||||
reference_setter(rule->targetEnt, qof_book_mergeLocateReference(referenceEnt, mergeData));
|
||||
}
|
||||
linkage = g_slist_next(linkage);
|
||||
}
|
||||
|
||||
@@ -61,8 +61,10 @@ Note that this will not set or return an error value. However, g_return is only
|
||||
used for critical errors that arise from programming errors, not for invalid import data
|
||||
which should be cleaned up before creating the import QofBook.
|
||||
|
||||
Only ::qof_book_mergeInit, ::qof_book_mergeUpdateResult and ::qof_book_mergeCommit return
|
||||
any error values to the calling process.
|
||||
Only ::qof_book_mergeUpdateResult and ::qof_book_mergeCommit return
|
||||
any error values to the calling process. ::qof_book_mergeInit returns a
|
||||
pointer to the ::qof_book_mergeData struct - the calling process needs to
|
||||
make sure this is non-NULL to know that the Init has been successful.
|
||||
|
||||
@{ */
|
||||
/**@file qof_book_merge.h
|
||||
@@ -70,7 +72,6 @@ any error values to the calling process.
|
||||
@author Copyright (c) 2004 Neil Williams <linux@codehelp.co.uk>
|
||||
*/
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
#include "qof/gnc-engine-util.h"
|
||||
#include "qofbook.h"
|
||||
@@ -129,34 +130,9 @@ typedef enum {
|
||||
or parameter types or user decided to abort - \b abort */
|
||||
}qof_book_mergeResult;
|
||||
|
||||
/** \brief One rule per entity, built into a single GList for the entire merge
|
||||
|
||||
/** \brief mergeData contains the essential data for any merge.
|
||||
|
||||
Used to dictate what to merge, how to merge it, where to get the new data and
|
||||
where to put the amended data.
|
||||
|
||||
Combines lists of \a ::QofParam, \a ::QofEntity and \a ::qof_book_mergeRule into one struct that
|
||||
can be easily passed between callbacks. Also holds the pointers to the import and target ::QofBook
|
||||
structures.
|
||||
|
||||
- targetList and mergeObjectParams change each time a new object type is set for compare.
|
||||
- mergeList is the complete list of rules for all objects in the import book.
|
||||
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
GSList *mergeObjectParams; /**< GSList of ::QofParam details for each parameter in the current object. */
|
||||
GList *mergeList; /**< GSList of ::qof_book_mergeRule rules for the import data. */
|
||||
GSList *targetList; /**< GSList of ::QofEntity * for each object of this type in the target book */
|
||||
QofBook *mergeBook; /**< pointer to the import book for this merge operation. */
|
||||
QofBook *targetBook; /**< pointer to the target book for this merge operation. */
|
||||
gboolean abort; /**< set to TRUE if MERGE_INVALID is set. */
|
||||
}qof_book_mergeData;
|
||||
|
||||
|
||||
/** \brief One rule per entity, built into a single GSList for the entire merge
|
||||
|
||||
All rules are stored in the GSList qof_book_mergeData::mergeList.
|
||||
All rules are stored in the GList qof_book_mergeData::mergeList.
|
||||
|
||||
If the ::GUID matches it's the always same semantic object,
|
||||
regardless of whether other data fields are changed.
|
||||
@@ -171,12 +147,16 @@ data that is causing a collision. These values must \b NOT be used to set the ta
|
||||
parameter - the function is provided for display purposes only, to make it simple to
|
||||
explain the collision to the user using MERGE_REPORT and the dialog.
|
||||
|
||||
The GHashTable targetTable in qof_book_mergeRule will probably replace the GSList of the
|
||||
same name in mergeData.
|
||||
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* internal counters and reference variables */
|
||||
gboolean mergeAbsolute; /**< Only set if the GUID of the import matches the target */
|
||||
gint difference; /**< used to find best match in a book where no GUID matches */
|
||||
double difference; /**< used to find best match in a book where no GUID matches */
|
||||
gboolean updated; /**< prevent the mergeResult from being overwritten. */
|
||||
/* rule objects set from or by external calls */
|
||||
QofIdType mergeType; /**< type of comparison required for check for collision */
|
||||
@@ -197,6 +177,40 @@ typedef struct
|
||||
}qof_book_mergeRule;
|
||||
|
||||
|
||||
/** \brief mergeData contains the essential context data for any merge.
|
||||
|
||||
Used to dictate what to merge, how to merge it, where to get the new data and
|
||||
where to put the amended data.
|
||||
|
||||
Combines lists of \a ::QofParam, \a ::QofEntity and \a ::qof_book_mergeRule into one struct that
|
||||
can be easily passed between callbacks. Also holds the pointers to the import and target ::QofBook
|
||||
structures.
|
||||
|
||||
- targetList and mergeObjectParams change each time a new object type is set for compare.
|
||||
- mergeList is the complete list of rules for all objects in the import book.
|
||||
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
GSList *mergeObjectParams; /**< GSList of ::QofParam details for each parameter in the current object. */
|
||||
GList *mergeList; /**< GList of all ::qof_book_mergeRule rules for the merge operation. */
|
||||
GSList *targetList; /**< GSList of ::QofEntity * for each object of this type in the target book */
|
||||
QofBook *mergeBook; /**< pointer to the import book for this merge operation. */
|
||||
QofBook *targetBook; /**< pointer to the target book for this merge operation. */
|
||||
gboolean abort; /**< set to TRUE if MERGE_INVALID is set. */
|
||||
qof_book_mergeRule *currentRule; /**< placeholder for the rule currently being tested or applied. */
|
||||
GSList *orphan_list; /**< List of QofEntity's that need to be rematched.
|
||||
|
||||
When one QofEntity has a lower difference to the targetEnt than the previous best_match,
|
||||
the new match takes precedence. This list holds those orphaned entities that are not a good
|
||||
enough match so that these can be rematched later. The ranking is handled using
|
||||
the private qof_entity_rating struct and the GHashTable ::qof_book_mergeData::target_table.
|
||||
*/
|
||||
GHashTable *target_table; /**< The GHashTable to hold the qof_entity_rating values. */
|
||||
|
||||
}qof_book_mergeData;
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/** @name qof_book_merge API */
|
||||
/** @{
|
||||
@@ -206,7 +220,8 @@ typedef struct
|
||||
First function of the qof_book_merge API. Every merge must begin with Init.
|
||||
|
||||
Requires the book to import (::QofBook *) and the book to receive the import, the target book
|
||||
(::QofBook *). \n
|
||||
(::QofBook *). Returns a pointer to ::qof_book_mergeData which must be checked for a
|
||||
NULL before continuing. \n
|
||||
Process:
|
||||
|
||||
-# Invoke the callback ::qof_book_mergeForeachType on every registered object class definition.
|
||||
@@ -225,10 +240,10 @@ Process:
|
||||
-# ::qof_book_mergeCompare sets the ::qof_book_mergeResult of the comparison.
|
||||
-# Inserts the completed rule into qof_book_mergeData::mergeList GSList.
|
||||
|
||||
\return -1 in case of error, otherwise 0.
|
||||
\return NULL in case of error, otherwise a ::qof_book_mergeData* metadata context.
|
||||
|
||||
*/
|
||||
int
|
||||
qof_book_mergeData*
|
||||
qof_book_mergeInit( QofBook *importBook, QofBook *targetBook);
|
||||
|
||||
|
||||
@@ -243,12 +258,13 @@ will cause an error.
|
||||
|
||||
For an example, consider test_rule_loop, declared as:
|
||||
|
||||
<tt>void test_rule_loop(qof_book_mergeRule *rule, guint remainder);\n
|
||||
void test_rule_loop(qof_book_mergeRule *rule, guint remainder) \n
|
||||
<tt>void test_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder);\n
|
||||
void test_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder) \n
|
||||
{\n
|
||||
g_return_if_fail(rule != NULL);\n
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
printf("Rule Result %s", rule->mergeType);\n
|
||||
qof_book_mergeUpdateResult(rule,MERGE_UPDATE);\n
|
||||
qof_book_mergeUpdateResult(mergeData, rule, MERGE_UPDATE);\n
|
||||
}</tt>
|
||||
|
||||
The dialog is free to call ::qof_book_mergeUpdateResult in the loop or at the end
|
||||
@@ -256,6 +272,7 @@ as long as the link between the rule and the result is maintained, e.g. by using
|
||||
GHashTable.
|
||||
\n
|
||||
The parameters are:
|
||||
- data : pointer to the ::qof_book_mergeData metadata context returned by Init.
|
||||
- rule : pointer to the ::qof_book_mergeRule that generated the collision report
|
||||
- remainder : guint value returned from g_slist_length for the number of other
|
||||
rules remaining with the same result. This might be useful for a progress dialog, it might not.
|
||||
@@ -264,20 +281,30 @@ The parameters are:
|
||||
\n
|
||||
|
||||
If the dialog sets \b any rule result to ::MERGE_INVALID, the import will abort when
|
||||
::qof_book_mergeCommit is called. It is the responsibility of the calling
|
||||
function to handle the error code from ::qof_book_mergeCommit, close the dialog
|
||||
and return to GnuCash. The merge routines in these files will already have halted the merge
|
||||
operation and freed any memory allocated to merge structures before returning the error code.
|
||||
::qof_book_mergeCommit is called. It is the responsibility of the calling function to
|
||||
handle the error code from ::qof_book_mergeCommit, close the dialog and return.
|
||||
The merge routines in these files will already have halted the merge operation and
|
||||
freed any memory allocated to merge structures before returning the error code.
|
||||
There is no need for the dialog process to report back to qof_book_merge in this situation.
|
||||
|
||||
*/
|
||||
typedef void (* qof_book_mergeRuleForeachCB)(qof_book_mergeRule*, guint);
|
||||
typedef void (* qof_book_mergeRuleForeachCB)( qof_book_mergeData*, qof_book_mergeRule*, guint);
|
||||
|
||||
/** \brief Dialog Control Callback
|
||||
|
||||
This function is designed to be used to iterate over all rules tagged with a specific
|
||||
::qof_book_mergeResult value.
|
||||
|
||||
@param callback external loop of type qof_book_mergeRuleForeachCB
|
||||
@param mergeResult ::qof_book_mergeResult value to look up.
|
||||
@param mergeData ::qof_book_mergeData merge context.
|
||||
|
||||
\b Note : MERGE_NEW causes a new entity to be created in the target book at Commit
|
||||
which is then assigned as the targetEnt of that rule. If mergeResult == MERGE_NEW,
|
||||
the rules returned by qof_book_mergeRuleForeach will have a NULL set for the targetEnt.
|
||||
This is because Commit has not yet been called and no changes can be made to the target
|
||||
book. The calling process must handle the NULL targetEnt and NOT call any param_getfcn
|
||||
routines for the target entity. The import entity is available for display.
|
||||
|
||||
Uses ::qof_book_get_collection with the qof_book_mergeRule::mergeType object type to
|
||||
return a collection of ::QofEntity entities from either the qof_book_mergeData::mergeBook or
|
||||
qof_book_mergeData::targetBook. Then uses ::qof_collection_lookup_entity to lookup
|
||||
@@ -285,26 +312,18 @@ the qof_book_mergeRule::importEnt and again the qof_book_mergeRule::targetEnt to
|
||||
return the two specific entities.
|
||||
|
||||
*/
|
||||
void qof_book_mergeRuleForeach( qof_book_mergeRuleForeachCB, qof_book_mergeResult);
|
||||
|
||||
|
||||
/** \brief Holds details of each rule as the callbacks iterate over the list.
|
||||
|
||||
*/
|
||||
struct qof_book_mergeRuleIterate {
|
||||
qof_book_mergeRuleForeachCB fcn;
|
||||
qof_book_mergeRule *data;
|
||||
GList *ruleList;
|
||||
guint remainder;
|
||||
};
|
||||
void qof_book_mergeRuleForeach( qof_book_mergeData*,
|
||||
qof_book_mergeRuleForeachCB,
|
||||
qof_book_mergeResult );
|
||||
|
||||
/** \brief provides easy string access to parameter data for dialog use
|
||||
|
||||
<b>Must only be used for display purposes!</b>
|
||||
|
||||
Uses the param_getfcn to retrieve the parameter value as a string, suitable for
|
||||
display in dialogs and user intervention output. Only the parameters used in the merge
|
||||
are available, i.e. parameters where both param_getfcn and param_setfcn are not NULL.
|
||||
display in dialogs and user intervention output. Within a qof_book_merge context,
|
||||
only the parameters used in the merge are available, i.e. parameters where both
|
||||
param_getfcn and param_setfcn are not NULL.
|
||||
|
||||
Note that the object type description (a full text version of the object name) is
|
||||
also available to the dialog as qof_book_mergeRule::mergeLabel.
|
||||
@@ -347,7 +366,8 @@ To ignore entities tagged as:
|
||||
|
||||
To merge entities that are not pre-set to MERGE_NEW, set MERGE_UPDATE.\n
|
||||
Attempting to merge an entity when the pre-set value was MERGE_NEW will
|
||||
force a change back to MERGE_NEW.
|
||||
force a change back to MERGE_NEW because no suitable target exists for the
|
||||
merge.
|
||||
|
||||
To add entities, check mergeAbsolute is FALSE and set MERGE_NEW.\n
|
||||
An entity \b only be added if mergeAbsolute is FALSE. Attempting to
|
||||
@@ -381,10 +401,19 @@ with MERGE_NEW and MERGE_UPDATE results.
|
||||
\n
|
||||
Entities tagged with MERGE_ABSOLUTE and MERGE_DUPLICATE results are ignored.
|
||||
|
||||
The calling process must check the return value and call
|
||||
::qof_book_merge_abort(mergeData) if non-zero.
|
||||
|
||||
@param mergeData the merge context, ::qof_book_mergeData*
|
||||
@param resolved the current rule, ::qof_book_mergeRule*
|
||||
@param tag the result to attempt to set, ::qof_book_mergeResult
|
||||
|
||||
\return -1 if supplied parameters are invalid or NULL, 0 on success.
|
||||
|
||||
*/
|
||||
int qof_book_mergeUpdateResult(qof_book_mergeRule *resolved, qof_book_mergeResult tag);
|
||||
qof_book_mergeData*
|
||||
qof_book_mergeUpdateResult(qof_book_mergeData *mergeData,
|
||||
qof_book_mergeResult tag);
|
||||
|
||||
|
||||
/** \brief Commits the import data to the target book
|
||||
@@ -394,7 +423,7 @@ int qof_book_mergeUpdateResult(qof_book_mergeRule *resolved, qof_book_mergeResul
|
||||
qof_book_mergeCommit will abort the \b entire merge operation if any rule is set to
|
||||
::MERGE_INVALID. It is the responsibility of the calling
|
||||
function to handle the error code from ::qof_book_mergeCommit, close the dialog
|
||||
and return to GnuCash. qof_book_mergeCommit will already have halted the merge
|
||||
and return. qof_book_mergeCommit will already have halted the merge
|
||||
operation and freed any memory allocated to all merge structures before returning the error
|
||||
code. There is no way for the dialog process to report back to qof_book_merge in this situation.
|
||||
|
||||
@@ -404,14 +433,21 @@ to import all entities tagged as ::MERGE_UPDATE or ::MERGE_NEW into the target b
|
||||
<b>This final process cannot be UNDONE!</b>\n
|
||||
\n
|
||||
|
||||
@param mergeData the merge context, ::qof_book_mergeData*
|
||||
|
||||
\return
|
||||
- -1 if no merge has been initialised with ::qof_book_mergeInit or if any rules
|
||||
are tagged as ::MERGE_INVALID,
|
||||
- +1 if some entities are still tagged as \a MERGE_REPORT
|
||||
- 0 on success.
|
||||
- -2 if any rules are tagged as ::MERGE_INVALID
|
||||
- mergeData will have been g_free'd).
|
||||
- note that this will be before any operations are done on the target
|
||||
QofBook.
|
||||
- -1 if mergeData is invalid or no merge has been initialised with
|
||||
::qof_book_mergeInit - the calling process must check the value of mergeData
|
||||
- +1 if some entities are still tagged as \a MERGE_REPORT - use
|
||||
::qof_book_mergeUpdateRule and try again (mergeData is retained).
|
||||
- 0 on success - mergeData will have been freed.
|
||||
*/
|
||||
int
|
||||
qof_book_mergeCommit( void );
|
||||
qof_book_mergeCommit( qof_book_mergeData *mergeData );
|
||||
|
||||
/** \brief Abort the merge and free all memory allocated by the merge
|
||||
|
||||
@@ -421,7 +457,7 @@ causes an immediate abort - the calling process must start again at Init if
|
||||
a new merge is required.
|
||||
*/
|
||||
void
|
||||
qof_book_merge_abort(void);
|
||||
qof_book_merge_abort(qof_book_mergeData *mergeData);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -480,7 +516,7 @@ void qof_book_mergeForeachType (QofObject* merge_obj, gpointer mergeData);
|
||||
\b Note: This function is called by ::qof_book_mergeForeachType in the comparison
|
||||
stage and ::qof_book_mergeCommitRuleLoop in the commit stage. Change with care!
|
||||
*/
|
||||
void qof_book_mergeForeachParam(QofParam* param_name, gpointer user_data);
|
||||
void qof_book_mergeForeachParam(QofParam* param_name, gpointer mergeData);
|
||||
|
||||
/** @} */
|
||||
/** @name Phase 2: Target book */
|
||||
@@ -519,18 +555,44 @@ void qof_book_mergeForeachTypeTarget ( QofObject* merge_obj, gpointer mergeData)
|
||||
*/
|
||||
void qof_book_mergeForeachTarget (QofEntity* mergeEnt, gpointer mergeData);
|
||||
|
||||
/** \brief Omits target entities that have already been matched.
|
||||
/** \brief build the table of target comparisons
|
||||
|
||||
It is possible for two entities in the import book to match a single entity in
|
||||
the target book, resulting in a loss of data during commit.
|
||||
This can get confusing, so bear with me. (!)
|
||||
|
||||
Whilst iterating through the entities in the mergeBook, qof_book_mergeForeach assigns
|
||||
a targetEnt to each mergeEnt (until it runs out of targetEnt or mergeEnt). Each match
|
||||
is made against the one targetEnt that best matches the mergeEnt. Fine so far.
|
||||
|
||||
Any mergeEnt is only ever assigned a targetEnt if the calculated difference between
|
||||
the two is less than the difference between that targetEnt and any previous mergeEnt
|
||||
match.
|
||||
|
||||
The next mergeEnt may be a much better match for that targetEnt and the target_table
|
||||
is designed to solve the issues that result from this conflict. The previous match
|
||||
must be re-assigned because if two mergeEnt's are matched with only one targetEnt,
|
||||
data loss \b WILL follow. Equally, the current mergeEnt must replace the previous
|
||||
one as it is a better match. qof_entity_rating holds the details required to identify
|
||||
the correct mergeEnt to be re-assigned and these mergeEnt entities are therefore
|
||||
orphaned - to be re-matched later.
|
||||
|
||||
Meanwhile, the current mergeEnt is entered into target_table with it's difference and
|
||||
rule data, in case an even better match is found later in the mergeBook.
|
||||
|
||||
Finally, each mergeEnt in the orphan_list is now put through the comparison again.
|
||||
|
||||
qof_book_merge_target_check simply checks the GUID of all existing
|
||||
target entities against the full list of all entities of a suitable type
|
||||
in the ::qof_book_mergeForeachTarget iteration. Possible target entity
|
||||
matches are only added to the qof_book_mergeData::targetList if the GUID
|
||||
does not match.
|
||||
*/
|
||||
void qof_book_merge_target_check (QofEntity* targetEnt);
|
||||
gboolean qof_book_merge_rule_cmp(gconstpointer a, gconstpointer b);
|
||||
|
||||
/** \brief Recursively matches orphaned entities
|
||||
|
||||
Any entities from the import book that were superceded by a better match are
|
||||
retained in the mergeData orphan_list. This function processes each one
|
||||
and finds the best remaining match and adds the rule back to the
|
||||
::qof_book_mergeData::mergeList
|
||||
until ::qof_book_mergeData::orphan_list is empty.
|
||||
|
||||
*/
|
||||
void qof_book_merge_match_orphans(qof_book_mergeData *mergeData);
|
||||
|
||||
/** @} */
|
||||
/** @name Phase 3: User Intervention
|
||||
@@ -559,7 +621,10 @@ void qof_book_mergeRuleCB(gpointer, gpointer);
|
||||
|
||||
It can \b NOT be used for ::MERGE_UNDEF, ::MERGE_INVALID or ::MERGE_REPORT.
|
||||
*/
|
||||
void qof_book_mergeCommitForeach (qof_book_mergeRuleForeachCB cb, qof_book_mergeResult mergeResult );
|
||||
void qof_book_mergeCommitForeach (
|
||||
qof_book_mergeRuleForeachCB cb,
|
||||
qof_book_mergeResult mergeResult,
|
||||
qof_book_mergeData *mergeData );
|
||||
|
||||
/** \brief Iterates over the rules and declares the number of rules left to commit
|
||||
|
||||
@@ -586,7 +651,9 @@ void qof_book_mergeCommitForeachCB(gpointer, gpointer);
|
||||
|
||||
*/
|
||||
|
||||
void qof_book_mergeCommitRuleLoop(qof_book_mergeRule *rule, guint remainder);
|
||||
void qof_book_mergeCommitRuleLoop(qof_book_mergeData *mergeData,
|
||||
qof_book_mergeRule *rule,
|
||||
guint remainder);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -616,10 +683,14 @@ that contains the parameter and mergeResult.
|
||||
|
||||
*/
|
||||
int
|
||||
qof_book_mergeCompare( void );
|
||||
qof_book_mergeCompare( qof_book_mergeData* );
|
||||
|
||||
/** \brief Makes the decisions about how matches and conflicts are tagged.
|
||||
|
||||
Paramater Type Weighting is used via the gint argument. This is used to give
|
||||
priority to string matches over boolean or numerical matches. Higher values
|
||||
of weight decrease the likelihood of that entity being the best match.
|
||||
|
||||
New rules start at:
|
||||
- ::MERGE_ABSOLUTE\n
|
||||
(GUID's match; first parameter matches) OR
|
||||
@@ -638,17 +709,26 @@ If subsequent parameters in the same object FAIL a match:
|
||||
(GUID does not match and some parameters now DO match)
|
||||
|
||||
<b>Comparisons without a GUID match.</b>
|
||||
Only sets a failed match if ALL objects fail to match.
|
||||
when absolute is FALSE, all suitable target objects are compared.
|
||||
mergeResult is not set until all targets checked.
|
||||
Only sets a failed match if ALL parameters fail to match.
|
||||
When absolute is FALSE, all suitable target objects are compared.
|
||||
|
||||
Identifies the closest match using a difference rank. This avoids
|
||||
using non-generic tests for object similarities. difference has a
|
||||
maximum value of the total number of comparable parameters and the
|
||||
value closest to zero is used. In the case of a tie, it is
|
||||
currently first-come-first-served. FIXME!
|
||||
using non-generic tests for object similarities. The
|
||||
value closest to zero is used.
|
||||
|
||||
qof_book_merge use sa high value of weight to make a good match
|
||||
more important and make it more likely that the chosen target will
|
||||
have matching values for the types with the highest weight.
|
||||
|
||||
@param currentRule - the ::qof_book_mergeRule to update.
|
||||
@param mergeMatch - whether the two entities match or not
|
||||
@param weight - Parameter Type Weighting.
|
||||
|
||||
@return returns the qof_book_mergeRule.
|
||||
|
||||
*/
|
||||
void qof_book_mergeUpdateRule( gboolean match);
|
||||
qof_book_mergeRule*
|
||||
qof_book_mergeUpdateRule( qof_book_mergeRule *currentRule, gboolean mergeMatch, gint weight);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#define OBJ_MINOR "tiny"
|
||||
#define OBJ_ACTIVE "ofcourse"
|
||||
|
||||
static void test_rule_loop (qof_book_mergeRule*, guint);
|
||||
static void test_rule_loop (qof_book_mergeData*, qof_book_mergeRule*, guint);
|
||||
static void test_merge (void);
|
||||
gboolean myobjRegister (void);
|
||||
|
||||
@@ -170,10 +170,6 @@ Timespec
|
||||
obj_getDate(myobj *g)
|
||||
{
|
||||
Timespec ts;
|
||||
|
||||
ts.tv_nsec = 0;
|
||||
ts.tv_sec = 0;
|
||||
|
||||
if(!g) return ts;
|
||||
ts = g->date;
|
||||
return ts;
|
||||
@@ -269,6 +265,7 @@ test_merge (void)
|
||||
gint64 minor;
|
||||
gchar *import_init, *target_init;
|
||||
gnc_numeric obj_amount;
|
||||
qof_book_mergeData *mergeData;
|
||||
|
||||
target = qof_book_new();
|
||||
import = qof_book_new();
|
||||
@@ -350,21 +347,21 @@ test_merge (void)
|
||||
obj_setMinor(target_obj, minor);
|
||||
obj_setDate(target_obj, tc );
|
||||
|
||||
result = qof_book_mergeInit(import, target);
|
||||
do_test ( result != -1, "FATAL: Merge could not be initialised!\t aborting . . ");
|
||||
g_return_if_fail(result != -1);
|
||||
qof_book_mergeRuleForeach(test_rule_loop, MERGE_REPORT);
|
||||
qof_book_mergeRuleForeach(test_rule_loop, MERGE_UPDATE);
|
||||
qof_book_mergeRuleForeach(test_rule_loop, MERGE_NEW);
|
||||
mergeData = qof_book_mergeInit(import, target);
|
||||
do_test ( mergeData != NULL, "FATAL: Merge could not be initialised!\t aborting . . ");
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_REPORT);
|
||||
qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_UPDATE);
|
||||
qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_NEW);
|
||||
/* reserved calls - test only */
|
||||
qof_book_mergeRuleForeach(test_rule_loop, MERGE_ABSOLUTE);
|
||||
qof_book_mergeRuleForeach(test_rule_loop, MERGE_DUPLICATE);
|
||||
qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_ABSOLUTE);
|
||||
qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_DUPLICATE);
|
||||
|
||||
/* import should not be in the target - pass if import_init fails match with target */
|
||||
do_test (((safe_strcmp(obj_getName(import_obj),obj_getName(target_obj))) != 0), "Init value test #1");
|
||||
|
||||
/* a good commit returns zero */
|
||||
do_test (qof_book_mergeCommit() == 0, "Commit failed");
|
||||
do_test (qof_book_mergeCommit(mergeData) == 0, "Commit failed");
|
||||
|
||||
/* import should be in the target - pass if import_init matches target */
|
||||
do_test (((safe_strcmp(import_init,obj_getName(target_obj))) == 0), "Merged value test #1");
|
||||
@@ -396,7 +393,7 @@ test_merge (void)
|
||||
}
|
||||
|
||||
static void
|
||||
test_rule_loop (qof_book_mergeRule *rule, guint remainder)
|
||||
test_rule_loop (qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder)
|
||||
{
|
||||
GSList *testing;
|
||||
QofParam *eachParam;
|
||||
@@ -467,14 +464,13 @@ test_rule_loop (qof_book_mergeRule *rule, guint remainder)
|
||||
} // end param loop
|
||||
/* set each rule dependent on the user involvement response above. */
|
||||
/* test routine just sets all MERGE_REPORT to MERGE_UPDATE */
|
||||
qof_book_mergeUpdateResult(rule,MERGE_UPDATE);
|
||||
mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_UPDATE);
|
||||
do_test ((rule->mergeResult != MERGE_REPORT), "update result fail");
|
||||
}
|
||||
|
||||
static void
|
||||
main_helper (void *closure, int argc, char **argv)
|
||||
{
|
||||
// gnc_module_load("gnucash/engine", 0);
|
||||
gnc_engine_init(argc, argv);
|
||||
myobjRegister();
|
||||
test_merge();
|
||||
|
||||
@@ -40,19 +40,20 @@
|
||||
#include "Account.h"
|
||||
#include "global-options.h"
|
||||
#include "gnc-trace.h"
|
||||
#include "Group.h"
|
||||
|
||||
static GtkWidget *qof_book_merge_window = NULL;
|
||||
static GtkWidget *druid_hierarchy_window = NULL;
|
||||
static QofSession *previous_session = NULL;
|
||||
static gint count = 0;
|
||||
static qof_book_mergeRule *currentRule = NULL;
|
||||
static QofSession *merge_session = NULL;
|
||||
static QofBook *mergeBook = NULL;
|
||||
static QofBook *targetBook = NULL;
|
||||
static gchar *buffer = "";
|
||||
GtkWidget *druid_hierarchy_window = NULL;
|
||||
QofSession *previous_session = NULL;
|
||||
gint count = 0;
|
||||
qof_book_mergeData *mergeData = NULL;
|
||||
QofSession *merge_session = NULL;
|
||||
QofBook *mergeBook = NULL;
|
||||
QofBook *targetBook = NULL;
|
||||
gchar *buffer = "";
|
||||
|
||||
void collision_rule_loop ( qof_book_mergeRule*, guint );
|
||||
void progress_rule_loop ( qof_book_mergeRule*, guint );
|
||||
void collision_rule_loop ( qof_book_mergeData*, qof_book_mergeRule*, guint );
|
||||
void progress_rule_loop ( qof_book_mergeData*, qof_book_mergeRule*, guint );
|
||||
|
||||
static GtkWidget*
|
||||
merge_get_widget (const char *name)
|
||||
@@ -72,9 +73,8 @@ delete_merge_window (void)
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_merge_destroy_cb (GtkObject *obj, gpointer user_data)
|
||||
qof_book_merge_destroy_cb (GtkObject *obj, gpointer user_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -91,35 +91,45 @@ static void
|
||||
on_MergeUpdate_clicked (GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
qof_book_mergeUpdateResult(currentRule, MERGE_UPDATE);
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_UPDATE);
|
||||
count = 0;
|
||||
qof_book_mergeRuleForeach(collision_rule_loop, MERGE_REPORT);
|
||||
qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
|
||||
}
|
||||
|
||||
static void
|
||||
on_MergeDuplicate_clicked (GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
qof_book_mergeRule *currentRule;
|
||||
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
currentRule = mergeData->currentRule;
|
||||
if(currentRule->mergeAbsolute == FALSE) {
|
||||
qof_book_mergeUpdateResult(currentRule, MERGE_DUPLICATE);
|
||||
mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_DUPLICATE);
|
||||
count = 0;
|
||||
}
|
||||
if(currentRule->mergeAbsolute == TRUE) {
|
||||
qof_book_mergeUpdateResult(currentRule, MERGE_ABSOLUTE);
|
||||
mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_ABSOLUTE);
|
||||
count = 0;
|
||||
}
|
||||
qof_book_mergeRuleForeach(collision_rule_loop, MERGE_REPORT);
|
||||
qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
|
||||
}
|
||||
|
||||
static void
|
||||
on_MergeNew_clicked (GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
qof_book_mergeRule *currentRule;
|
||||
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
currentRule = mergeData->currentRule;
|
||||
g_return_if_fail(currentRule != NULL);
|
||||
if(currentRule->mergeAbsolute == FALSE) {
|
||||
qof_book_mergeUpdateResult(currentRule, MERGE_NEW);
|
||||
mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_NEW);
|
||||
}
|
||||
count = 0;
|
||||
qof_book_mergeRuleForeach(collision_rule_loop, MERGE_REPORT);
|
||||
qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -150,6 +160,7 @@ on_cancel ( GnomeDruid *gnomedruid,
|
||||
gpointer user_data)
|
||||
{
|
||||
gnc_suspend_gui_refresh ();
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
delete_merge_window();
|
||||
qof_session_set_current_session(previous_session);
|
||||
qof_book_destroy(mergeBook);
|
||||
@@ -169,7 +180,7 @@ void reference_parent_cb ( QofEntity* ent, gpointer user_data)
|
||||
{
|
||||
if(!ent) return;
|
||||
if(xaccAccountGetParent((Account*)ent) == NULL) {
|
||||
xaccGroupInsertAccount(xaccGroupGetRoot(xaccGetAccountGroup(gnc_get_current_book())), (Account*)ent);
|
||||
xaccGroupInsertAccount(xaccGroupGetRoot(xaccGetAccountGroup(targetBook)), (Account*)ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,24 +191,21 @@ on_finish (GnomeDruidPage *gnomedruidpage,
|
||||
{
|
||||
gint result;
|
||||
GtkWidget *top;
|
||||
const char *message = _("Error: the Commit operation failed.");
|
||||
const char *message;
|
||||
|
||||
message = _("Error: the Commit operation failed.");
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
gnc_suspend_gui_refresh ();
|
||||
result = qof_book_mergeCommit();
|
||||
result = qof_book_mergeCommit(mergeData);
|
||||
if(result != 0) {
|
||||
top = gtk_widget_get_toplevel (GTK_WIDGET (gnomedruidpage));
|
||||
gnc_error_dialog(top, message);
|
||||
}
|
||||
g_return_if_fail(result == 0);
|
||||
delete_merge_window ();
|
||||
/*
|
||||
Account has a new setparent parameter that takes
|
||||
a QofEntity. Account converts this into an AccountGroup based on
|
||||
the GUID in the reference. This needs improving as child accounts
|
||||
are currently being re-parented to top-level.
|
||||
*/
|
||||
qof_session_set_current_session(previous_session);
|
||||
qof_object_foreach(GNC_ID_ACCOUNT, gnc_get_current_book(), reference_parent_cb, NULL);
|
||||
qof_object_foreach(GNC_ID_ACCOUNT, gnc_get_current_book(), currency_transfer_cb, NULL);
|
||||
qof_object_foreach(GNC_ID_ACCOUNT, targetBook, currency_transfer_cb, NULL);
|
||||
qof_object_foreach(GNC_ID_ACCOUNT, targetBook, reference_parent_cb, NULL);
|
||||
qof_book_destroy(mergeBook);
|
||||
qof_session_end(merge_session);
|
||||
gnc_resume_gui_refresh ();
|
||||
@@ -208,29 +216,27 @@ on_qof_book_merge_prepare (GnomeDruidPage *gnomedruidpage,
|
||||
gpointer arg1,
|
||||
gpointer user_data)
|
||||
{
|
||||
gint result;
|
||||
GtkLabel *progress;
|
||||
|
||||
gnc_suspend_gui_refresh ();
|
||||
progress = GTK_LABEL (merge_get_widget("ResultsBox"));
|
||||
/* blank out old data */
|
||||
gtk_label_set_text(progress, "");
|
||||
result = 0;
|
||||
g_return_if_fail(mergeBook != NULL);
|
||||
g_return_if_fail(targetBook != NULL);
|
||||
result = qof_book_mergeInit(mergeBook, targetBook);
|
||||
g_return_if_fail(result == 0);
|
||||
qof_book_mergeRuleForeach(progress_rule_loop, MERGE_NEW);
|
||||
qof_book_mergeRuleForeach(progress_rule_loop, MERGE_ABSOLUTE);
|
||||
qof_book_mergeRuleForeach(progress_rule_loop, MERGE_DUPLICATE);
|
||||
qof_book_mergeRuleForeach(progress_rule_loop, MERGE_UPDATE);
|
||||
mergeData = qof_book_mergeInit(mergeBook, targetBook);
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
qof_book_mergeRuleForeach(mergeData, progress_rule_loop, MERGE_NEW);
|
||||
qof_book_mergeRuleForeach(mergeData, progress_rule_loop, MERGE_ABSOLUTE);
|
||||
qof_book_mergeRuleForeach(mergeData, progress_rule_loop, MERGE_DUPLICATE);
|
||||
qof_book_mergeRuleForeach(mergeData, progress_rule_loop, MERGE_UPDATE);
|
||||
gtk_label_set_text(progress, buffer);
|
||||
qof_book_mergeRuleForeach(collision_rule_loop, MERGE_REPORT);
|
||||
qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
|
||||
gnc_resume_gui_refresh ();
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
gnc_create_merge_druid (void)
|
||||
gnc_create_merge_druid ( void )
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
GtkWidget *druid;
|
||||
@@ -247,17 +253,19 @@ gnc_create_merge_druid (void)
|
||||
glade_xml_signal_connect(xml, "on_qof_book_merge_next",
|
||||
GTK_SIGNAL_FUNC (on_qof_book_merge_next));
|
||||
|
||||
glade_xml_signal_connect (xml, "on_finish", GTK_SIGNAL_FUNC (on_finish));
|
||||
glade_xml_signal_connect (xml, "on_finish",
|
||||
GTK_SIGNAL_FUNC (on_finish));
|
||||
|
||||
glade_xml_signal_connect (xml, "on_cancel", GTK_SIGNAL_FUNC (on_cancel));
|
||||
glade_xml_signal_connect (xml, "on_cancel",
|
||||
GTK_SIGNAL_FUNC (on_cancel));
|
||||
|
||||
glade_xml_signal_connect(xml, "on_MergeUpdate_clicked",
|
||||
glade_xml_signal_connect (xml, "on_MergeUpdate_clicked",
|
||||
GTK_SIGNAL_FUNC (on_MergeUpdate_clicked));
|
||||
|
||||
glade_xml_signal_connect(xml, "on_MergeDuplicate_clicked",
|
||||
glade_xml_signal_connect (xml, "on_MergeDuplicate_clicked",
|
||||
GTK_SIGNAL_FUNC (on_MergeDuplicate_clicked));
|
||||
|
||||
glade_xml_signal_connect(xml, "on_MergeNew_clicked",
|
||||
glade_xml_signal_connect (xml, "on_MergeNew_clicked",
|
||||
GTK_SIGNAL_FUNC (on_MergeNew_clicked));
|
||||
|
||||
dialog = glade_xml_get_widget (xml, "Merge Druid");
|
||||
@@ -267,18 +275,18 @@ gnc_create_merge_druid (void)
|
||||
gnc_druid_set_colors (GNOME_DRUID (druid));
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT(dialog), "destroy",
|
||||
GTK_SIGNAL_FUNC(gnc_merge_destroy_cb), NULL);
|
||||
GTK_SIGNAL_FUNC(qof_book_merge_destroy_cb), NULL);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
void progress_rule_loop(qof_book_mergeRule *rule, guint remainder)
|
||||
void progress_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder)
|
||||
{
|
||||
GtkLabel *progress;
|
||||
|
||||
g_return_if_fail(mergeData != NULL);
|
||||
progress = GTK_LABEL(merge_get_widget("ResultsBox"));
|
||||
buffer = "";
|
||||
g_return_if_fail(rule != NULL);
|
||||
currentRule = rule;
|
||||
if(rule->mergeResult == MERGE_NEW) {
|
||||
if (remainder == 1) {
|
||||
buffer = g_strconcat(buffer,
|
||||
@@ -341,12 +349,11 @@ void progress_rule_loop(qof_book_mergeRule *rule, guint remainder)
|
||||
g_free(buffer);
|
||||
}
|
||||
|
||||
void collision_rule_loop(qof_book_mergeRule *rule, guint remainder)
|
||||
void collision_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder)
|
||||
{
|
||||
GSList *user_reports;
|
||||
QofParam *one_param;
|
||||
gchar *importstring, *targetstring;
|
||||
gchar *buffer;
|
||||
GtkLabel *output;
|
||||
|
||||
g_return_if_fail(rule != NULL);
|
||||
@@ -355,7 +362,7 @@ void collision_rule_loop(qof_book_mergeRule *rule, guint remainder)
|
||||
if(count > 0) return;
|
||||
gnc_suspend_gui_refresh ();
|
||||
user_reports = rule->mergeParam;
|
||||
currentRule = rule;
|
||||
mergeData->currentRule = rule;
|
||||
output = GTK_LABEL(merge_get_widget("OutPut"));
|
||||
gtk_label_set_text(output, buffer);
|
||||
gtk_widget_show(GTK_WIDGET(output));
|
||||
@@ -386,6 +393,8 @@ void collision_rule_loop(qof_book_mergeRule *rule, guint remainder)
|
||||
gtk_widget_show(GTK_WIDGET(output));
|
||||
gnc_resume_gui_refresh ();
|
||||
g_free(buffer);
|
||||
g_free(importstring);
|
||||
g_free(targetstring);
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
@@ -399,7 +408,6 @@ qof_book_merge_running (void)
|
||||
void
|
||||
gnc_ui_qof_book_merge_druid (void)
|
||||
{
|
||||
|
||||
if (qof_book_merge_window) return;
|
||||
/* QofSession changes to avoid using current book */
|
||||
gnc_engine_suspend_events ();
|
||||
@@ -422,5 +430,4 @@ gnc_ui_qof_book_merge_druid (void)
|
||||
g_return_if_fail(targetBook != NULL);
|
||||
g_return_if_fail(mergeBook != NULL);
|
||||
g_return_if_fail(merge_session != NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user