[Gnucash-changes] r13567 - gnucash/trunk - Move qof_book_merge to
new naming convention and deprecate old code
Neil Williams
codehelp at cvs.gnucash.org
Thu Mar 9 12:03:03 EST 2006
Author: codehelp
Date: 2006-03-09 12:03:01 -0500 (Thu, 09 Mar 2006)
New Revision: 13567
Trac: http://svn.gnucash.org/trac/changeset/13567
Added:
gnucash/trunk/lib/libqof/qof/qofbookmerge.c
gnucash/trunk/lib/libqof/qof/qofbookmerge.h
Removed:
gnucash/trunk/lib/libqof/qof/qof_book_merge.c
gnucash/trunk/lib/libqof/qof/qof_book_merge.h
Modified:
gnucash/trunk/ChangeLog
gnucash/trunk/lib/libqof/qof/Makefile.am
gnucash/trunk/lib/libqof/qof/deprecated.c
gnucash/trunk/lib/libqof/qof/deprecated.h
gnucash/trunk/lib/libqof/qof/qof.h
gnucash/trunk/src/engine/test/test-book-merge.c
gnucash/trunk/src/gnome/druid-merge.c
Log:
Move qof_book_merge to new naming convention and deprecate old code
Modified: gnucash/trunk/ChangeLog
===================================================================
--- gnucash/trunk/ChangeLog 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/ChangeLog 2006-03-09 17:03:01 UTC (rev 13567)
@@ -1,4 +1,34 @@
2006-03-09 Neil Williams <linux at codehelp.co.uk>
+
+ * src/gnome/druid-merge.c : Use new function names.
+ * src/engine/test/test-book-merge.c : Use new function names.
+
+ * lib/libqof/qof/qof_book_merge.c : Removed.
+ * lib/libqof/qof/qof_book_merge.h : Removed.
+ * lib/libqof/qof/qofbookmerge.c : Replacement file.
+ * lib/libqof/qof/qofbookmerge.h : Replacement file.
+ * lib/libqof/qof/qof.h : Use new filename.
+ * lib/libqof/qof/deprecated.c : Deprecate old function
+ names from qof_book_merge in favour of new from qofbookmerge.
+ * lib/libqof/qof/deprecated.h : Added deprecated function
+ declaration.s
+ * lib/libqof/qof/Makefile.am : New files.
+
+2006-03-09 Neil Williams <linux at codehelp.co.uk>
+
+ * lib/libqof/qof/qofquery-deserial.c : Update status; this
+ file will not exist in libqof2
+
+2006-03-09 Neil Williams <linux at codehelp.co.uk>
+
+ * lib/libqof/qof/qofevent.h : Doxygen tweak.
+
+2006-03-09 Neil Williams <linux at codehelp.co.uk>
+
+ * lib/libqof/backend/file/qsf-backend.c : Remove deprecated
+ glib function g_strncasecmp in favour of g_ascii_strncasecmp
+
+2006-03-09 Neil Williams <linux at codehelp.co.uk>
* src/optional/xsl : New directory for optional XSL
* src/optional/xsl/vcard-gnccustomer.pl : Perl script
to create QSF from VCards
Modified: gnucash/trunk/lib/libqof/qof/Makefile.am
===================================================================
--- gnucash/trunk/lib/libqof/qof/Makefile.am 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/lib/libqof/qof/Makefile.am 2006-03-09 17:03:01 UTC (rev 13567)
@@ -29,7 +29,7 @@
qofquerycore.c \
qofreference.c \
qofsession.c \
- qof_book_merge.c
+ qofbookmerge.c
qofincludedir = ${pkgincludedir}
@@ -62,7 +62,7 @@
qofreference.h \
qofsession.h \
qofsql.h \
- qof_book_merge.h
+ qofbookmerge.h
nodist_qofinclude_HEADERS = \
qofla-dir.h
Modified: gnucash/trunk/lib/libqof/qof/deprecated.c
===================================================================
--- gnucash/trunk/lib/libqof/qof/deprecated.c 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/lib/libqof/qof/deprecated.c 2006-03-09 17:03:01 UTC (rev 13567)
@@ -79,3 +79,28 @@
{
qof_event_gen(entity, event_type, NULL);
}
+QofBookMergeData*
+qof_book_mergeInit(QofBook *importBook, QofBook *targetBook)
+{
+ return qof_book_merge_init(importBook, targetBook);
+}
+QofBookMergeData*
+qof_book_mergeUpdateResult(QofBookMergeData *mergeData,
+ QofBookMergeResult tag)
+{
+ return qof_book_merge_update_result(mergeData, tag);
+}
+gint
+qof_book_mergeCommit(QofBookMergeData *mergeData )
+{
+ return qof_book_merge_commit(mergeData);
+}
+void
+qof_book_mergeRuleForeach(QofBookMergeData *mergeData,
+ QofBookMergeRuleForeachCB cb,
+ QofBookMergeResult mergeResult )
+{
+ qof_book_merge_rule_foreach(mergeData, cb, mergeResult);
+}
+
+/* ==================================================================== */
Modified: gnucash/trunk/lib/libqof/qof/deprecated.h
===================================================================
--- gnucash/trunk/lib/libqof/qof/deprecated.h 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/lib/libqof/qof/deprecated.h 2006-03-09 17:03:01 UTC (rev 13567)
@@ -125,5 +125,25 @@
/** \deprecated use qof_event_generate instead. */
void gnc_engine_generate_event (const GUID *guid, QofIdType e_type,
GNCEngineEventType event_type);
+/** \deprecated use QofBookMergeResult instead. */
+#define qof_book_mergeResult QofBookMergeResult
+/** \deprecated use QofBookMergeRule instead. */
+#define qof_book_mergeRule QofBookMergeRule
+/** \deprecated use QofBookMergeData instead. */
+#define qof_book_mergeData QofBookMergeData
+/** \deprecated use qof_book_merge_init instead. */
+QofBookMergeData* qof_book_mergeInit( QofBook *importBook, QofBook *targetBook);
+/** \deprecated use QofBookMergeRuleForeachCB instead. */
+typedef void (* qof_book_mergeRuleForeachCB)(QofBookMergeData*, QofBookMergeRule*, guint);
+/** \deprecated use qof_book_merge_rule_foreach instead. */
+void qof_book_mergeRuleForeach(QofBookMergeData* mergeData,
+ QofBookMergeRuleForeachCB callback ,
+ QofBookMergeResult mergeResult);
+/** \deprecated use qof_book_merge_update_result instead. */
+QofBookMergeData*
+qof_book_mergeUpdateResult(QofBookMergeData *mergeData, QofBookMergeResult tag);
+/** \deprecated use qof_book_merge_commit instead. */
+gint
+qof_book_mergeCommit(QofBookMergeData *mergeData );
#endif /* _DEPRECATED_H */
Modified: gnucash/trunk/lib/libqof/qof/qof.h
===================================================================
--- gnucash/trunk/lib/libqof/qof/qof.h 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/lib/libqof/qof/qof.h 2006-03-09 17:03:01 UTC (rev 13567)
@@ -97,7 +97,7 @@
#include "qofsession.h"
#include "qofsql.h"
#include "qofchoice.h"
-#include "qof_book_merge.h"
+#include "qofbookmerge.h"
#include "qof-be-utils.h"
#include "qofreference.h"
#include "qofla-dir.h"
Deleted: gnucash/trunk/lib/libqof/qof/qof_book_merge.c
===================================================================
--- gnucash/trunk/lib/libqof/qof/qof_book_merge.c 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/lib/libqof/qof/qof_book_merge.c 2006-03-09 17:03:01 UTC (rev 13567)
@@ -1,996 +0,0 @@
-/*********************************************************************
- * qof_book_merge.c -- api for QoFBook merge with collision handling *
- * Copyright (C) 2004-2005 Neil Williams <linux at codehelp.co.uk> *
- * *
- * This program is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU General Public License as *
- * published by the Free Software Foundation; either version 2 of *
- * the License, or (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, contact: *
- * *
- * Free Software Foundation Voice: +1-617-542-5942 *
- * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
- * Boston, MA 02110-1301, USA gnu at gnu.org *
- * *
- ********************************************************************/
-
-#include "config.h"
-#include "qof.h"
-
-static QofLogModule log_module = QOF_MOD_MERGE;
-
-/* private rule iteration struct */
-struct qof_book_mergeRuleIterate {
- qof_book_mergeRuleForeachCB fcn;
- qof_book_mergeData *data;
- qof_book_mergeRule *rule;
- GList *ruleList;
- guint remainder;
-};
-
-/* 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.
-*/
-#define DEFAULT_MERGE_WEIGHT 1
-#define QOF_STRING_WEIGHT 3
-#define QOF_DATE_STRING_LENGTH MAX_DATE_LENGTH
-
-static qof_book_mergeRule*
-qof_book_mergeUpdateRule(qof_book_mergeRule *currentRule, gboolean match, gint weight)
-{
- gboolean absolute;
-
- absolute = currentRule->mergeAbsolute;
- if(absolute && match && currentRule->mergeResult == MERGE_UNDEF) {
- currentRule->mergeResult = MERGE_ABSOLUTE;
- }
- if(absolute && !match) { currentRule->mergeResult = MERGE_UPDATE; }
- if(!absolute && match &¤tRule->mergeResult == MERGE_UNDEF) {
- currentRule->mergeResult = MERGE_DUPLICATE;
- }
- if(!absolute && !match) {
- currentRule->difference += weight;
- if(currentRule->mergeResult == MERGE_DUPLICATE) {
- currentRule->mergeResult = MERGE_REPORT;
- }
- }
- return currentRule;
-}
-
-struct collect_list_s
-{
- GSList *linkedEntList;
-};
-
-static void
-collect_reference_cb (QofEntity *ent, gpointer user_data)
-{
- struct collect_list_s *s;
-
- s = (struct collect_list_s*)user_data;
- if(!ent || !s) { return; }
- s->linkedEntList = g_slist_prepend(s->linkedEntList, ent);
-}
-
-static int
-qof_book_mergeCompare( qof_book_mergeData *mergeData )
-{
- qof_book_mergeRule *currentRule;
- QofCollection *mergeColl, *targetColl;
- gchar *stringImport, *stringTarget;
- QofEntity *mergeEnt, *targetEnt, *referenceEnt;
- const GUID *guidImport, *guidTarget;
- QofParam *qtparam;
- KvpFrame *kvpImport, *kvpTarget;
- QofIdType mergeParamName;
- QofType mergeType;
- GSList *paramList;
- gboolean absolute, mergeError, knowntype, mergeMatch, booleanImport, booleanTarget,
- (*boolean_getter) (QofEntity*, QofParam*);
- Timespec tsImport, tsTarget, (*date_getter) (QofEntity*, QofParam*);
- gnc_numeric numericImport, numericTarget, (*numeric_getter) (QofEntity*, QofParam*);
- double doubleImport, doubleTarget, (*double_getter) (QofEntity*, QofParam*);
- gint32 i32Import, i32Target, (*int32_getter) (QofEntity*, QofParam*);
- gint64 i64Import, i64Target, (*int64_getter) (QofEntity*, QofParam*);
- gchar charImport, charTarget, (*char_getter) (QofEntity*, QofParam*);
-
- 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();
- kvpTarget = kvp_frame_new();
- mergeError = FALSE;
- while(paramList != NULL) {
- mergeMatch = FALSE;
- knowntype = FALSE;
- qtparam = paramList->data;
- mergeParamName = qtparam->param_name;
- g_return_val_if_fail(mergeParamName != NULL, -1);
- mergeType = qtparam->param_type;
- if(safe_strcmp(mergeType, QOF_TYPE_STRING) == 0) {
- stringImport = qtparam->param_getfcn(mergeEnt,qtparam);
- stringTarget = qtparam->param_getfcn(targetEnt,qtparam);
- /* very strict string matches may need to be relaxed. */
- if(stringImport == NULL) { stringImport = ""; }
- if(stringTarget == NULL) { stringTarget = ""; }
- if(safe_strcmp(stringImport,stringTarget) == 0) { mergeMatch = TRUE; }
- /* Give special weight to a string match */
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, QOF_STRING_WEIGHT);
- stringImport = stringTarget = NULL;
- knowntype= TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_DATE) == 0) {
- date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
- tsImport = date_getter(mergeEnt, qtparam);
- tsTarget = date_getter(targetEnt, qtparam);
- if(timespec_cmp(&tsImport, &tsTarget) == 0) { mergeMatch = TRUE; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype= TRUE;
- }
- if((safe_strcmp(mergeType, QOF_TYPE_NUMERIC) == 0) ||
- (safe_strcmp(mergeType, QOF_TYPE_DEBCRED) == 0)) {
- numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- numericImport = numeric_getter(mergeEnt,qtparam);
- numericTarget = numeric_getter(targetEnt,qtparam);
- if(gnc_numeric_compare (numericImport, numericTarget) == 0) { mergeMatch = TRUE; }
- 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; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype= TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_INT32) == 0) {
- int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- i32Import = int32_getter(mergeEnt, qtparam);
- i32Target = int32_getter(targetEnt, qtparam);
- if(i32Target == i32Import) { mergeMatch = TRUE; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype= TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_INT64) == 0) {
- int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- i64Import = int64_getter(mergeEnt, qtparam);
- i64Target = int64_getter(targetEnt, qtparam);
- if(i64Target == i64Import) { mergeMatch = TRUE; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype= TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_DOUBLE) == 0) {
- double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- doubleImport = double_getter(mergeEnt, qtparam);
- doubleTarget = double_getter(mergeEnt, qtparam);
- if(doubleImport == doubleTarget) { mergeMatch = TRUE; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype= TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_BOOLEAN) == 0){
- boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- booleanImport = boolean_getter(mergeEnt, qtparam);
- booleanTarget = boolean_getter(targetEnt, qtparam);
- if(booleanImport != FALSE && booleanImport != TRUE) { booleanImport = FALSE; }
- if(booleanTarget != FALSE && booleanTarget != TRUE) { booleanTarget = FALSE; }
- if(booleanImport == booleanTarget) { mergeMatch = TRUE; }
- 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; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype= TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_CHAR) == 0) {
- char_getter = (gchar (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- charImport = char_getter(mergeEnt, qtparam);
- charTarget = char_getter(targetEnt, qtparam);
- if(charImport == charTarget) { mergeMatch = TRUE; }
- 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; }
- if(safe_strcmp(mergeType, QOF_TYPE_COLLECT) == 0) {
- struct collect_list_s s;
- s.linkedEntList = NULL;
- mergeColl = qtparam->param_getfcn(mergeEnt, qtparam);
- targetColl = qtparam->param_getfcn(targetEnt, qtparam);
- s.linkedEntList = g_slist_copy(currentRule->linkedEntList);
- qof_collection_foreach(mergeColl, collect_reference_cb, &s);
- currentRule->linkedEntList = g_slist_copy(s.linkedEntList);
- if(0 == qof_collection_compare(mergeColl, targetColl)) { mergeMatch = TRUE; }
- currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
- knowntype = TRUE;
- }
- if(safe_strcmp(mergeType, QOF_TYPE_CHOICE) ==0) {
- referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
- currentRule->linkedEntList = g_slist_prepend(currentRule->linkedEntList, referenceEnt);
- if(referenceEnt == qtparam->param_getfcn(targetEnt, qtparam)) { mergeMatch = TRUE; }
- knowntype = TRUE;
- }
- if(knowntype == FALSE) {
- referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
- if((referenceEnt != NULL)
- &&(safe_strcmp(referenceEnt->e_type, mergeType) == 0)) {
- currentRule->linkedEntList = g_slist_prepend(currentRule->linkedEntList, referenceEnt);
- 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;
-}
-
-static void
-qof_book_mergeCommitForeachCB(gpointer rule, gpointer arg)
-{
- struct qof_book_mergeRuleIterate *iter;
-
- 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--;
-}
-
-static void
-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));
-
- iter.fcn = cb;
- subList = NULL;
- iter.ruleList = g_list_copy(mergeData->mergeList);
- while(iter.ruleList!=NULL) {
- currentRule = iter.ruleList->data;
- if(currentRule->mergeResult == mergeResult) {
- subList = g_list_prepend(subList, currentRule);
- }
- iter.ruleList = g_list_next(iter.ruleList);
- }
- iter.remainder = g_list_length(subList);
- iter.data = mergeData;
- g_list_foreach (subList, qof_book_mergeCommitForeachCB, &iter);
-}
-
-/* build the table of target comparisons
-
-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.
-
-*/
-static 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);
-}
-
-static 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);
- 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;
- }
- mergeData->currentRule = rule;
- 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);
- }
- orphans = g_slist_next(orphans);
- }
- g_slist_free(mergeData->orphan_list);
- g_slist_free(targets);
-}
-
-static void
-qof_book_mergeForeachTarget (QofEntity* targetEnt, gpointer user_data)
-{
- qof_book_mergeData *mergeData;
-
- 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);
-}
-
-static 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, user_data);
- }
-}
-
-static void
-qof_book_mergeForeach ( QofEntity* mergeEnt, gpointer user_data)
-{
- qof_book_mergeRule *mergeRule, *currentRule;
- qof_book_mergeData *mergeData;
- QofEntity *targetEnt, *best_matchEnt;
- GUID *g;
- 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);
- mergeRule->importEnt = mergeEnt;
- mergeRule->difference = difference = 0;
- mergeRule->mergeAbsolute = FALSE;
- mergeRule->mergeResult = MERGE_UNDEF;
- mergeRule->updated = FALSE;
- mergeRule->mergeType = mergeEnt->e_type;
- mergeRule->mergeLabel = qof_object_get_type_label(mergeEnt->e_type);
- mergeRule->mergeParam = g_slist_copy(mergeData->mergeObjectParams);
- mergeRule->linkedEntList = NULL;
- 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(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, mergeData);
- if(g_slist_length(mergeData->targetList) == 0) {
- mergeRule->mergeResult = MERGE_NEW;
- }
- difference = g_slist_length(mergeRule->mergeParam);
- c = g_slist_copy(mergeData->targetList);
- while(c != NULL) {
- mergeRule->targetEnt = c->data;
- currentRule = mergeRule;
- /* 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);
- }
- g_slist_free(c);
- 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;
- mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
- }
- mergeData->mergeList = g_list_prepend(mergeData->mergeList,mergeRule);
- guid_free(g);
- /* return to qof_book_mergeInit */
-}
-
-static 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);
- }
-}
-
-static 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)){
- DEBUG (" merge_obj QOF support failed %s", merge_obj->e_type);
- return;
- }
- if(mergeData->mergeObjectParams != NULL) g_slist_free(mergeData->mergeObjectParams);
- mergeData->mergeObjectParams = 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);
-}
-
-static void
-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->fcn (mergeData, (qof_book_mergeRule*)rule, iter->remainder);
- iter->data = mergeData;
- iter->remainder--;
-}
-
-static void
-qof_book_mergeCommitRuleLoop(
- qof_book_mergeData *mergeData,
- qof_book_mergeRule *rule,
- guint remainder)
-{
- QofInstance *inst;
- gboolean registered_type;
- QofEntity *referenceEnt;
- /* cm_ prefix used for variables that hold the data to commit */
- QofCollection *cm_coll;
- QofParam *cm_param;
- gchar *cm_string;
- const GUID *cm_guid;
- KvpFrame *cm_kvp;
- /* function pointers and variables for parameter getters that don't use pointers normally */
- gnc_numeric cm_numeric, (*numeric_getter) (QofEntity*, QofParam*);
- double cm_double, (*double_getter) (QofEntity*, QofParam*);
- gboolean cm_boolean, (*boolean_getter) (QofEntity*, QofParam*);
- gint32 cm_i32, (*int32_getter) (QofEntity*, QofParam*);
- gint64 cm_i64, (*int64_getter) (QofEntity*, QofParam*);
- Timespec cm_date, (*date_getter) (QofEntity*, QofParam*);
- gchar cm_char, (*char_getter) (QofEntity*, QofParam*);
- /* function pointers to the parameter setters */
- void (*string_setter) (QofEntity*, const gchar*);
- void (*date_setter) (QofEntity*, Timespec);
- void (*numeric_setter) (QofEntity*, gnc_numeric);
- void (*guid_setter) (QofEntity*, const GUID*);
- void (*double_setter) (QofEntity*, double);
- void (*boolean_setter) (QofEntity*, gboolean);
- void (*i32_setter) (QofEntity*, gint32);
- void (*i64_setter) (QofEntity*, gint64);
- void (*char_setter) (QofEntity*, gchar);
- void (*kvp_frame_setter) (QofEntity*, KvpFrame*);
- void (*reference_setter) (QofEntity*, QofEntity*);
- void (*collection_setter)(QofEntity*, QofCollection*);
-
- 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) {
- inst = (QofInstance*)qof_object_new_instance(rule->importEnt->e_type, mergeData->targetBook);
- g_return_if_fail(inst != NULL);
- rule->targetEnt = &inst->entity;
- qof_entity_set_guid(rule->targetEnt, qof_entity_get_guid(rule->importEnt));
- }
- /* currentRule->targetEnt is now set,
- 1. by an absolute GUID match or
- 2. by best_matchEnt and difference or
- 3. by MERGE_NEW.
- */
- while(rule->mergeParam != NULL) {
- registered_type = FALSE;
- g_return_if_fail(rule->mergeParam->data);
- cm_param = rule->mergeParam->data;
- rule->mergeType = cm_param->param_type;
- if(safe_strcmp(rule->mergeType, QOF_TYPE_STRING) == 0) {
- cm_string = cm_param->param_getfcn(rule->importEnt, cm_param);
- string_setter = (void(*)(QofEntity*, const gchar*))cm_param->param_setfcn;
- if(string_setter != NULL) { string_setter(rule->targetEnt, cm_string); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_DATE) == 0) {
- date_getter = (Timespec (*)(QofEntity*, QofParam*))cm_param->param_getfcn;
- cm_date = date_getter(rule->importEnt, cm_param);
- date_setter = (void(*)(QofEntity*, Timespec))cm_param->param_setfcn;
- if(date_setter != NULL) { date_setter(rule->targetEnt, cm_date); }
- registered_type = TRUE;
- }
- if((safe_strcmp(rule->mergeType, QOF_TYPE_NUMERIC) == 0) ||
- (safe_strcmp(rule->mergeType, QOF_TYPE_DEBCRED) == 0)) {
- numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*))cm_param->param_getfcn;
- cm_numeric = numeric_getter(rule->importEnt, cm_param);
- numeric_setter = (void(*)(QofEntity*, gnc_numeric))cm_param->param_setfcn;
- if(numeric_setter != NULL) { numeric_setter(rule->targetEnt, cm_numeric); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_GUID) == 0) {
- cm_guid = cm_param->param_getfcn(rule->importEnt, cm_param);
- guid_setter = (void(*)(QofEntity*, const GUID*))cm_param->param_setfcn;
- if(guid_setter != NULL) { guid_setter(rule->targetEnt, cm_guid); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_INT32) == 0) {
- int32_getter = (gint32 (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
- cm_i32 = int32_getter(rule->importEnt, cm_param);
- i32_setter = (void(*)(QofEntity*, gint32))cm_param->param_setfcn;
- if(i32_setter != NULL) { i32_setter(rule->targetEnt, cm_i32); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_INT64) == 0) {
- int64_getter = (gint64 (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
- cm_i64 = int64_getter(rule->importEnt, cm_param);
- i64_setter = (void(*)(QofEntity*, gint64))cm_param->param_setfcn;
- if(i64_setter != NULL) { i64_setter(rule->targetEnt, cm_i64); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_DOUBLE) == 0) {
- double_getter = (double (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
- cm_double = double_getter(rule->importEnt, cm_param);
- double_setter = (void(*)(QofEntity*, double))cm_param->param_setfcn;
- if(double_setter != NULL) { double_setter(rule->targetEnt, cm_double); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_BOOLEAN) == 0){
- boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
- cm_boolean = boolean_getter(rule->importEnt, cm_param);
- boolean_setter = (void(*)(QofEntity*, gboolean))cm_param->param_setfcn;
- if(boolean_setter != NULL) { boolean_setter(rule->targetEnt, cm_boolean); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_KVP) == 0) {
- cm_kvp = kvp_frame_copy(cm_param->param_getfcn(rule->importEnt,cm_param));
- kvp_frame_setter = (void(*)(QofEntity*, KvpFrame*))cm_param->param_setfcn;
- if(kvp_frame_setter != NULL) { kvp_frame_setter(rule->targetEnt, cm_kvp); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_CHAR) == 0) {
- char_getter = (gchar (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
- cm_char = char_getter(rule->importEnt,cm_param);
- char_setter = (void(*)(QofEntity*, gchar))cm_param->param_setfcn;
- if(char_setter != NULL) { char_setter(rule->targetEnt, cm_char); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_COLLECT) == 0) {
- cm_coll = cm_param->param_getfcn(rule->importEnt, cm_param);
- collection_setter = (void(*)(QofEntity*, QofCollection*))cm_param->param_setfcn;
- if(collection_setter != NULL) { collection_setter(rule->targetEnt, cm_coll); }
- registered_type = TRUE;
- }
- if(safe_strcmp(rule->mergeType, QOF_TYPE_CHOICE) == 0) {
- referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
- reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
- if(reference_setter != NULL)
- {
- reference_setter(rule->targetEnt, referenceEnt);
- }
- registered_type = TRUE;
- }
- if(registered_type == FALSE) {
- referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
- if(referenceEnt) {
- reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
- if(reference_setter != NULL)
- {
- reference_setter(rule->targetEnt, referenceEnt);
- }
- }
- }
- rule->mergeParam = g_slist_next(rule->mergeParam);
- }
-}
-/* ================================================================ */
-/* API functions. */
-
-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), NULL);
- mergeData = g_new(qof_book_mergeData, 1);
- mergeData->abort = FALSE;
- mergeData->mergeList = NULL;
- mergeData->targetList = NULL;
- 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);
- 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) {
- mergeData->abort = TRUE;
- return(NULL);
- }
- check = g_list_next(check);
- }
- g_list_free(check);
- return mergeData;
-}
-
-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);
- }
- mergeData->mergeList = g_list_next(mergeData->mergeList);
- }
- 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);
-}
-
-/* The QOF_TYPE_DATE output format from
-qof_book_merge_param_as_string has been changed to QSF_XSD_TIME,
-a UTC formatted timestring: 2005-01-01T10:55:23Z
-If you change QOF_UTC_DATE_FORMAT, change
-backend/file/qsf-xml.c : qsf_entity_foreach to
-reformat to QSF_XSD_TIME or the QSF XML will
-FAIL the schema validation and QSF exports will become invalid.
-
-The QOF_TYPE_BOOLEAN is lowercase for the same reason.
-
-\todo deprecate and replace with
-gchar* qof_instance_param_as_string(const QofParam*, QofInstance*);
-and then add
-gchar* qof_class_get_param_as_string(QofIdTypeConst, QofInstance*); ?
-*/
-gchar*
-qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt)
-{
- gchar *param_string, param_date[QOF_DATE_STRING_LENGTH];
- gchar param_sa[GUID_ENCODING_LENGTH + 1];
- QofType paramType;
- const GUID *param_guid;
- time_t param_t;
- 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*);
- gchar param_char, (*char_getter) (QofEntity*, QofParam*);
-
- 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(paramType, QOF_TYPE_DATE) == 0) {
- date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
- param_ts = date_getter(qtEnt, qtparam);
- param_t = timespecToTime_t(param_ts);
- strftime(param_date, QOF_DATE_STRING_LENGTH, QOF_UTC_DATE_FORMAT, gmtime(¶m_t));
- param_string = g_strdup(param_date);
- return param_string;
- }
- if((safe_strcmp(paramType, QOF_TYPE_NUMERIC) == 0) ||
- (safe_strcmp(paramType, QOF_TYPE_DEBCRED) == 0)) {
- numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- param_numeric = numeric_getter(qtEnt,qtparam);
- param_string = g_strdup(gnc_numeric_to_string(param_numeric));
- return param_string;
- }
- 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(paramType, QOF_TYPE_INT32) == 0) {
- int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- param_i32 = int32_getter(qtEnt, qtparam);
- param_string = g_strdup_printf("%d", param_i32);
- return param_string;
- }
- if(safe_strcmp(paramType, QOF_TYPE_INT64) == 0) {
- int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- param_i64 = int64_getter(qtEnt, qtparam);
- param_string = g_strdup_printf("%" G_GINT64_FORMAT, param_i64);
- return param_string;
- }
- if(safe_strcmp(paramType, QOF_TYPE_DOUBLE) == 0) {
- double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- param_double = double_getter(qtEnt, qtparam);
- param_string = g_strdup_printf("%f", param_double);
- return param_string;
- }
- if(safe_strcmp(paramType, QOF_TYPE_BOOLEAN) == 0){
- boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- param_boolean = boolean_getter(qtEnt, qtparam);
- /* Boolean values need to be lowercase for QSF validation. */
- if(param_boolean == TRUE) { param_string = g_strdup("true"); }
- else { param_string = g_strdup("false"); }
- return param_string;
- }
- /* "kvp" contains repeating values, cannot be a single string for the frame. */
- if(safe_strcmp(paramType, QOF_TYPE_KVP) == 0) { return param_string; }
- if(safe_strcmp(paramType, QOF_TYPE_CHAR) == 0) {
- char_getter = (gchar (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
- param_char = char_getter(qtEnt, qtparam);
- param_string = g_strdup_printf("%c", param_char);
- return param_string;
- }
- return NULL;
-}
-
-qof_book_mergeData*
-qof_book_mergeUpdateResult(qof_book_mergeData *mergeData,
- qof_book_mergeResult tag)
-{
- 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;
- }
- 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( qof_book_mergeData *mergeData )
-{
- qof_book_mergeRule *currentRule;
- GList *check;
-
- g_return_val_if_fail(mergeData != NULL, -1);
- g_return_val_if_fail(mergeData->mergeList != NULL, -1);
- 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(mergeData);
- return(-2);
- }
- if(currentRule->mergeResult == MERGE_REPORT) {
- g_list_free(check);
- return 1;
- }
- check = g_list_next(check);
- }
- 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) {
- currentRule = mergeData->mergeList->data;
- g_slist_free(currentRule->mergeParam);
- g_slist_free(currentRule->linkedEntList);
- mergeData->mergeList = g_list_next(mergeData->mergeList);
- }
- 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;
-}
-
-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);
- iter.fcn = cb;
- iter.data = mergeData;
- matching_rules = NULL;
- iter.ruleList = g_list_copy(mergeData->mergeList);
- while(iter.ruleList!=NULL) {
- currentRule = iter.ruleList->data;
- if(currentRule->mergeResult == mergeResult) {
- matching_rules = g_list_prepend(matching_rules, currentRule);
- }
- iter.ruleList = g_list_next(iter.ruleList);
- }
- iter.remainder = g_list_length(matching_rules);
- g_list_foreach (matching_rules, qof_book_mergeRuleCB, &iter);
- g_list_free(matching_rules);
-}
-
-/* End of file. */
-/* ==================================================================== */
Deleted: gnucash/trunk/lib/libqof/qof/qof_book_merge.h
===================================================================
--- gnucash/trunk/lib/libqof/qof/qof_book_merge.h 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/lib/libqof/qof/qof_book_merge.h 2006-03-09 17:03:01 UTC (rev 13567)
@@ -1,462 +0,0 @@
-/*********************************************************************
- * qof_book_merge.h -- api for QofBook merge with collision handling *
- * Copyright (C) 2004 Neil Williams <linux at codehelp.co.uk> *
- * *
- * This program is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU General Public License as *
- * published by the Free Software Foundation; either version 2 of *
- * the License, or (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, contact: *
- * *
- * Free Software Foundation Voice: +1-617-542-5942 *
- * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
- * Boston, MA 02110-1301, USA gnu at gnu.org *
- * *
- ********************************************************************/
-
-#ifndef QOFBOOKMERGE_H
-#define QOFBOOKMERGE_H
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#define QOF_MOD_MERGE "qof-merge"
-
-/** @addtogroup BookMerge
-
-<b>Collision handling principles.</b>\n
-\n
- -# Always check for a ::GUID first and compare. qof_book_merge only accepts valid ::QofBook
- data and therefore ALL objects in the import book will include valid GUID's.
- -# If the original import data did not contain a GUID (e.g. an external non-GnuCash source)
- the GUID values will have been created during the import and will not match any existing
- GUID's in the target book so objects that do not have a GUID match cannot be assumed to
- be ::MERGE_NEW - parameter values must be checked.
- -# If import contains data from closed books, store the data from the closed
- books in the current book as active. i.e. re-open the books.
-
-- If a GUID match exists, set qof_book_mergeRule::mergeAbsolute to \a TRUE.
- -# If ALL parameters in the import object match the target object with the same \a GUID,
- set ::qof_book_mergeResult to \a MERGE_ABSOLUTE.
- -# If any parameters differ, set ::MERGE_UPDATE.
-- If the import object \a GUID does not match an existing object,
-mergeAbsolute is unchanged from the default \a FALSE
-The parameter values of the object are compared to other objects of the same
-type in the target book.
- -# If the same data exists in the target book with a different GUID, the object
- is tagged as DUPLICATE.
- -# If the data has changed, the object is tagged as REPORT.
- -# If the data does not match, the object is tagged as NEW
-
-More information is at http://code.neil.williamsleesmill.me.uk/
-
-Each foreach function uses g_return_if_fail checks to protect the target book. If
-any essential data is missing, the loop returns without changing the target book.
-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_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.
-
-(to be renamed qofbookmerge.h in libqof2)
- @{
-*/
-/** @file qof_book_merge.h
- @brief API for merging two \c QofBook structures with collision handling
- @author Copyright (c) 2004-2005 Neil Williams <linux at codehelp.co.uk>
-*/
-
-#include <glib.h>
-#include "gnc-engine-util.h"
-#include "qofbook.h"
-#include "qofclass.h"
-#include "qofobject.h"
-#include "qofinstance.h"
-#include "qoflog.h"
-
-/** \brief Results of collisions and user resolution.
-
-All rules are initialised as ::MERGE_UNDEF.
-Once the comparison is complete, each object within the import will be
-updated.
-
-::MERGE_ABSOLUTE, ::MERGE_NEW, ::MERGE_DUPLICATE and ::MERGE_UPDATE can be reported
-to the user along with all ::MERGE_REPORT objects for confirmation.
-It may be useful later to allow \a MERGE_ABSOLUTE, \a MERGE_NEW, \a MERGE_DUPLICATE and
-\a MERGE_UPDATE to not be reported, if the user sets a preferences option
-for each result. (Always accept new items: Y/N default NO, ignores all
-MERGE_NEW if set to Y etc.) This option would not require any changes
-to qof_book_merge.
-
-\a MERGE_NEW, \a MERGE_DUPLICATE and \a MERGE_UPDATE are only actioned after
-conflicts are resolved by the user using a dialog and all \a MERGE_REPORT objects are
-re-assigned to one of MERGE_NEW, MERGE_DUPLICATE or MERGE_UPDATE. There is no automatic
-merge, even if no entities are tagged as MERGE_REPORT, the calling process must still
-check for REPORT items using ::qof_book_mergeRuleForeach and call ::qof_book_mergeCommit.
-
-\a MERGE_INVALID data should be rare and allows for user-abort - the imported file/source
- may be corrupted and the prescence of invalid data should raise concerns that
- the rest of the data may be corrupted, damaged or otherwise altered. If any entity is
- tagged as MERGE_INVALID, the merge operation will abort and leave the target book
- completely unchanged.
-
-\a MERGE_ABSOLUTE is only used for a complete match. The import object contains
-the same data in the same parameters with no omissions or amendments. If any data is missing,
-amended or added, the data is labelled \a MERGE_UPDATE.
-
- Every piece of data has a corresponding result. Only when the count of items labelled
- \a MERGE_REPORT is equal to zero are \a MERGE_NEW and \a MERGE_UPDATE
- items added to the existing book.\n \a MERGE_DUPLICATE items are silently ignored.
- Aborting the dialog/process (by the user or in a program crash) at any point before the
- final commit leaves the existing book completely untouched.
-*/
-typedef enum {
- MERGE_UNDEF, /**< default value before comparison is made. */
- MERGE_ABSOLUTE, /**< GUID exact match, no new data - \b ignore */
- MERGE_NEW, /**< import object does \b not exist in the target book - \b add */
- MERGE_REPORT, /**< import object needs user intervention - \b report */
- MERGE_DUPLICATE, /**< import object with different GUID exactly matches existing GUID - \b ignore */
- MERGE_UPDATE, /**< import object matches an existing entity but includes new or
- modified parameter data - \b update */
- MERGE_INVALID /**< import object didn't match registered object 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
-
-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.
-\n
-The boolean value mergeAbsolute defaults to \c FALSE
-
-NOTE 1: if mergeAbsolute == \c TRUE, ::qof_book_mergeResult will still be set to
-::MERGE_UPDATE if parameters within this entity have been modified.
-
-NOTE 2: ::qof_book_merge_param_as_string returns \b string representations of the parameter
-data that is causing a collision. These values must \b NOT be used to set the target
-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 */
- 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 */
- const gchar* mergeLabel; /**< Descriptive label for the object type, useful for the
- user intervention dialog. */
- GSList *mergeParam; /**< list of usable parameters for the object type */
- GSList *linkedEntList; /**< list of complex data types included in this object.
-
- linkedEntList contains an ::QofEntity reference to any parameter that is not
- one of the core QOF_TYPE data types. This entity must be already registered with QOF
- and the results of the comparison for the linked entity will modulate the mergeResult
- of this object. e.g. if an invoice is the same value but for a different customer,
- the invoice will be set to MERGE_REPORT and the customer as MERGE_NEW.
- */
- qof_book_mergeResult mergeResult; /**< result of comparison with main ::QofBook */
- QofEntity *importEnt; /**< pointer to the current entity in the import book. */
- QofEntity *targetEnt; /**< pointer to the corresponding entity in the target book, if any. */
-}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
- @{
-*/
-/** \brief Initialise the qof_book_merge process
-
- 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 *). 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.
- -# Callback obtains the registered parameter list for each object type. This provides run time
- access to all registered objects and all object parameters without any changes to
- qof_book_merge - no registered object or parameter is omitted from any merge operation.
- -# Use ::qof_object_foreach to invoke the callback ::qof_book_mergeForeach, one object at a time
- on every instance stored in mergeBook. This is the first point where real data from the import
- book is accessed.
- -# qof_book_mergeForeach obtains the ::GUID for the object from the import book and runs the first
- check on the original book, checking for any exact GUID match. With the full parameter list,
- the rules for this object can be created. If there is a GUID match, the data in each parameter
- of the import object is compared with the same semantic object in the original book. If there is
- no GUID in the import object or no GUID match with the original book, the original book is
- searched to find a parameter match - checking for a ::MERGE_DUPLICATE result.
- -# ::qof_book_mergeCompare sets the ::qof_book_mergeResult of the comparison.
- -# Inserts the completed rule into qof_book_mergeData::mergeList GSList.
-
-\return NULL in case of error, otherwise a ::qof_book_mergeData* metadata context.
-
-*/
-qof_book_mergeData*
-qof_book_mergeInit( QofBook *importBook, QofBook *targetBook);
-
-
-/** \brief Definition of the dialog control callback routine
-
-All ::MERGE_REPORT rules must be offered for user intervention using this template.\n
-Commit will fail if any rules are still tagged as \a MERGE_REPORT.
-
-Calling processes are free to also offer MERGE_NEW, MERGE_UPDATE, MERGE_DUPLICATE and
-MERGE_ABSOLUTE for user intervention. Attempting to query MERGE_INVALID rules
-will cause an error.
-
-For an example, consider test_rule_loop, declared as:
-
-<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(mergeData, rule, MERGE_UPDATE);\n
-}</tt>
-
-The dialog is free to call ::qof_book_mergeUpdateResult in the loop or at the end
-as long as the link between the rule and the result is maintained, e.g. by using a
-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.
- When updating MERGE_REPORT, remainder must equal zero before calling
- ::qof_book_mergeCommit or the import will abort.
-\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.
-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_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.
-
- at param callback external loop of type qof_book_mergeRuleForeachCB
- at param mergeResult ::qof_book_mergeResult value to look up.
- at 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
-the qof_book_mergeRule::importEnt and again the qof_book_mergeRule::targetEnt to
-return the two specific entities.
-
-*/
-void qof_book_mergeRuleForeach( qof_book_mergeData* mergeData,
- qof_book_mergeRuleForeachCB callback ,
- qof_book_mergeResult mergeResult);
-
-/** \brief provides easy string access to parameter data for dialog use
-
-Uses the param_getfcn to retrieve the parameter value as a string, suitable for
-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.
-
-This allows the dialog to display the description of the object and all parameter data.
-
-*/
-gchar* qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt);
-
-/** \brief called by dialog callback to set the result of user intervention
-
-Set \b any rule result to ::MERGE_INVALID to abort the import when
-::qof_book_mergeCommit is called, without changing the target book.
-
-The calling process should make it absolutely clear that a merge operation
-\b cannot be undone and that a backup copy should always be available
-\b before a merge is initialised.
-
-Recommended method: Only offer three options to the user per rule:
-
--# Allow import data to be merged into target data
- - change MERGE_REPORT to MERGE_UPDATE
--# Allow import data without an exact match to be
- added as new
- - change MERGE_REPORT to MERGE_NEW \b IF mergeAbsolute = FALSE
--# Ignore import data and leave target data unchanged
- - change MERGE_REPORT to MERGE_ABSOLUTE or MERGE_DUPLICATE
-
-Handle the required result changes in code: Check the value of
-qof_book_mergeRule::mergeAbsolute and use these principles:
-
-To ignore entities tagged as:
-- MERGE_REPORT, you must check the value of mergeAbsolute.
- - if mergeAbsolute is TRUE, change MERGE_REPORT to MERGE_ABSOLUTE
- - if mergeAbsolute is FALSE, change MERGE_REPORT to MERGE_DUPLICATE
-- MERGE_NEW, set MERGE_DUPLICATE.
-- MERGE_UPDATE, you must check the value of mergeAbsolute.
- - if mergeAbsolute is TRUE, change MERGE_UPDATE to MERGE_ABSOLUTE
- - if mergeAbsolute is FALSE, change MERGE_UPDATE to MERGE_DUPLICATE
-
-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 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
-add an entity when mergeAbsolute is TRUE will always force a MERGE_UPDATE.
-
-It is not possible to update the same rule more than once.
-
--# \b MERGE_NEW is reserved for new objects and is only pre-set if
-all parameters, including GUID, have already failed to match any
-relevant object. ::qof_book_mergeCommit will create new
-entities for all rules tagged as MERGE_NEW.
- - if mergeAbsolute is TRUE and the user wants to import the
- data, requests to set MERGE_NEW will be forced to MERGE_UPDATE
- because an entity with that GUID already exists in the target book.
- - if MERGE_NEW is pre-set, requests to change to MERGE_UPDATE will be
- ignored because a new entity is needed.
--# \b MERGE_UPDATE is reserved for existing objects - ::qof_book_mergeCommit
-will require a matching entity to update and will force a change to back to
-MERGE_NEW if none is known to exist, using the principle above.
--# \b MERGE_INVALID will cause an abort of the merge process.
--# \b MERGE_UNDEF and \b MERGE_REPORT cannot be set - the entity result will be unchanged.
--# \b MERGE_DUPLICATE and \b MERGE_ABSOLUTE are handled identically but are semantically
- different - qof_book_mergeRule::mergeAbsolute is used to dictate which to set:
- - if mergeAbsolute is TRUE but MERGE_DUPLICATE is requested,
- force a change to MERGE_ABSOLUTE.
- - if mergeAbsolute is FALSE but MERGE_ABSOLUTE is requested,
- force a change to MERGE_DUPLICATE.
-
-::qof_book_mergeCommit only commits entities tagged
-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.
-
- at param mergeData the merge context, ::qof_book_mergeData*
- at param tag the result to attempt to set, ::qof_book_mergeResult
-
-\return -1 if supplied parameters are invalid or NULL, 0 on success.
-
-*/
-qof_book_mergeData*
-qof_book_mergeUpdateResult(qof_book_mergeData *mergeData, qof_book_mergeResult tag);
-
-
-/** \brief Commits the import data to the target book
-
- The last function in the API and the final part of any qof_book_merge operation.
-
-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. 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.
-
-qof_book_mergeCommit checks for any entities still tagged as ::MERGE_REPORT and then proceeds
-to import all entities tagged as ::MERGE_UPDATE or ::MERGE_NEW into the target book.
-\n
-<b>This final process cannot be UNDONE!</b>\n
-\n
-
- at param mergeData the merge context, ::qof_book_mergeData*
-
-\return
- - -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( qof_book_mergeData *mergeData );
-
-/** \brief Abort the merge and free all memory allocated by the merge
-
-Sometimes, setting ::MERGE_INVALID is insufficient: e.g. if the user aborts the
-merge from outside the functions dealing with the merge ruleset. This function
-causes an immediate abort - the calling process must start again at Init if
-a new merge is required.
-*/
-void
-qof_book_merge_abort(qof_book_mergeData *mergeData);
-
-#endif // QOFBOOKMERGE_H
-/** @} */
-/** @} */
Copied: gnucash/trunk/lib/libqof/qof/qofbookmerge.c (from rev 13555, gnucash/trunk/lib/libqof/qof/qof_book_merge.c)
===================================================================
--- gnucash/trunk/lib/libqof/qof/qof_book_merge.c 2006-03-09 13:03:29 UTC (rev 13555)
+++ gnucash/trunk/lib/libqof/qof/qofbookmerge.c 2006-03-09 17:03:01 UTC (rev 13567)
@@ -0,0 +1,1019 @@
+/*********************************************************************
+ * QofBookMerge.c -- api for QoFBook merge with collision handling *
+ * Copyright (C) 2004-2005 Neil Williams <linux at codehelp.co.uk> *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+ * *
+ ********************************************************************/
+
+#include "qof.h"
+
+static QofLogModule log_module = QOF_MOD_MERGE;
+
+/* private rule iteration struct */
+struct QofBookMergeRuleIterate {
+ QofBookMergeRuleForeachCB fcn;
+ QofBookMergeData *data;
+ QofBookMergeRule *rule;
+ GList *ruleList;
+ guint remainder;
+};
+
+/* 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.
+*/
+#define DEFAULT_MERGE_WEIGHT 1
+#define QOF_STRING_WEIGHT 3
+#define QOF_DATE_STRING_LENGTH MAX_DATE_LENGTH
+
+static QofBookMergeRule*
+qof_book_merge_update_rule(QofBookMergeRule *currentRule, gboolean match, gint weight)
+{
+ gboolean absolute;
+
+ absolute = currentRule->mergeAbsolute;
+ if(absolute && match && currentRule->mergeResult == MERGE_UNDEF) {
+ currentRule->mergeResult = MERGE_ABSOLUTE;
+ }
+ if(absolute && !match) { currentRule->mergeResult = MERGE_UPDATE; }
+ if(!absolute && match &¤tRule->mergeResult == MERGE_UNDEF) {
+ currentRule->mergeResult = MERGE_DUPLICATE;
+ }
+ if(!absolute && !match) {
+ currentRule->difference += weight;
+ if(currentRule->mergeResult == MERGE_DUPLICATE) {
+ currentRule->mergeResult = MERGE_REPORT;
+ }
+ }
+ return currentRule;
+}
+
+struct collect_list_s
+{
+ GSList *linkedEntList;
+};
+
+static void
+collect_reference_cb (QofEntity *ent, gpointer user_data)
+{
+ struct collect_list_s *s;
+
+ s = (struct collect_list_s*)user_data;
+ if(!ent || !s) { return; }
+ s->linkedEntList = g_slist_prepend(s->linkedEntList, ent);
+}
+
+static int
+qof_book_merge_compare(QofBookMergeData *mergeData )
+{
+ QofBookMergeRule *currentRule;
+ QofCollection *mergeColl, *targetColl;
+ gchar *stringImport, *stringTarget;
+ QofEntity *mergeEnt, *targetEnt, *referenceEnt;
+ const GUID *guidImport, *guidTarget;
+ QofParam *qtparam;
+ KvpFrame *kvpImport, *kvpTarget;
+ QofIdType mergeParamName;
+ QofType mergeType;
+ GSList *paramList;
+ gboolean absolute, mergeError, knowntype, mergeMatch, booleanImport, booleanTarget,
+ (*boolean_getter) (QofEntity*, QofParam*);
+ Timespec tsImport, tsTarget, (*date_getter) (QofEntity*, QofParam*);
+ gnc_numeric numericImport, numericTarget, (*numeric_getter) (QofEntity*, QofParam*);
+ double doubleImport, doubleTarget, (*double_getter) (QofEntity*, QofParam*);
+ gint32 i32Import, i32Target, (*int32_getter) (QofEntity*, QofParam*);
+ gint64 i64Import, i64Target, (*int64_getter) (QofEntity*, QofParam*);
+ gchar charImport, charTarget, (*char_getter) (QofEntity*, QofParam*);
+
+ 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();
+ kvpTarget = kvp_frame_new();
+ mergeError = FALSE;
+ while(paramList != NULL) {
+ mergeMatch = FALSE;
+ knowntype = FALSE;
+ qtparam = paramList->data;
+ mergeParamName = qtparam->param_name;
+ g_return_val_if_fail(mergeParamName != NULL, -1);
+ mergeType = qtparam->param_type;
+ if(safe_strcmp(mergeType, QOF_TYPE_STRING) == 0) {
+ stringImport = qtparam->param_getfcn(mergeEnt,qtparam);
+ stringTarget = qtparam->param_getfcn(targetEnt,qtparam);
+ /* very strict string matches may need to be relaxed. */
+ if(stringImport == NULL) { stringImport = ""; }
+ if(stringTarget == NULL) { stringTarget = ""; }
+ if(safe_strcmp(stringImport,stringTarget) == 0) { mergeMatch = TRUE; }
+ /* Give special weight to a string match */
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, QOF_STRING_WEIGHT);
+ stringImport = stringTarget = NULL;
+ knowntype= TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_DATE) == 0) {
+ date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
+ tsImport = date_getter(mergeEnt, qtparam);
+ tsTarget = date_getter(targetEnt, qtparam);
+ if(timespec_cmp(&tsImport, &tsTarget) == 0) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype= TRUE;
+ }
+ if((safe_strcmp(mergeType, QOF_TYPE_NUMERIC) == 0) ||
+ (safe_strcmp(mergeType, QOF_TYPE_DEBCRED) == 0)) {
+ numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ numericImport = numeric_getter(mergeEnt,qtparam);
+ numericTarget = numeric_getter(targetEnt,qtparam);
+ if(gnc_numeric_compare (numericImport, numericTarget) == 0) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(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; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype= TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_INT32) == 0) {
+ int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ i32Import = int32_getter(mergeEnt, qtparam);
+ i32Target = int32_getter(targetEnt, qtparam);
+ if(i32Target == i32Import) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype= TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_INT64) == 0) {
+ int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ i64Import = int64_getter(mergeEnt, qtparam);
+ i64Target = int64_getter(targetEnt, qtparam);
+ if(i64Target == i64Import) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype= TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_DOUBLE) == 0) {
+ double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ doubleImport = double_getter(mergeEnt, qtparam);
+ doubleTarget = double_getter(mergeEnt, qtparam);
+ if(doubleImport == doubleTarget) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype= TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_BOOLEAN) == 0){
+ boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ booleanImport = boolean_getter(mergeEnt, qtparam);
+ booleanTarget = boolean_getter(targetEnt, qtparam);
+ if(booleanImport != FALSE && booleanImport != TRUE) { booleanImport = FALSE; }
+ if(booleanTarget != FALSE && booleanTarget != TRUE) { booleanTarget = FALSE; }
+ if(booleanImport == booleanTarget) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(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; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype= TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_CHAR) == 0) {
+ char_getter = (gchar (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ charImport = char_getter(mergeEnt, qtparam);
+ charTarget = char_getter(targetEnt, qtparam);
+ if(charImport == charTarget) { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(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; }
+ if(safe_strcmp(mergeType, QOF_TYPE_COLLECT) == 0) {
+ struct collect_list_s s;
+ s.linkedEntList = NULL;
+ mergeColl = qtparam->param_getfcn(mergeEnt, qtparam);
+ targetColl = qtparam->param_getfcn(targetEnt, qtparam);
+ s.linkedEntList = g_slist_copy(currentRule->linkedEntList);
+ qof_collection_foreach(mergeColl, collect_reference_cb, &s);
+ currentRule->linkedEntList = g_slist_copy(s.linkedEntList);
+ if(0 == qof_collection_compare(mergeColl, targetColl))
+ { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ knowntype = TRUE;
+ }
+ if(safe_strcmp(mergeType, QOF_TYPE_CHOICE) ==0) {
+ referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
+ currentRule->linkedEntList =
+ g_slist_prepend(currentRule->linkedEntList, referenceEnt);
+ if(referenceEnt == qtparam->param_getfcn(targetEnt, qtparam))
+ { mergeMatch = TRUE; }
+ knowntype = TRUE;
+ }
+ if(knowntype == FALSE) {
+ referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
+ if((referenceEnt != NULL)
+ &&(safe_strcmp(referenceEnt->e_type, mergeType) == 0)) {
+ currentRule->linkedEntList =
+ g_slist_prepend(currentRule->linkedEntList, referenceEnt);
+ if(referenceEnt == qtparam->param_getfcn(targetEnt, qtparam))
+ { mergeMatch = TRUE; }
+ currentRule = qof_book_merge_update_rule(currentRule,
+ mergeMatch, DEFAULT_MERGE_WEIGHT);
+ }
+ }
+ paramList = g_slist_next(paramList);
+ }
+ mergeData->currentRule = currentRule;
+ g_free(kvpImport);
+ g_free(kvpTarget);
+ return 0;
+}
+
+static void
+qof_book_merge_commit_foreach_cb(gpointer rule, gpointer arg)
+{
+ struct QofBookMergeRuleIterate *iter;
+
+ g_return_if_fail(arg != NULL);
+ iter = (struct QofBookMergeRuleIterate*)arg;
+ g_return_if_fail(iter->data != NULL);
+ iter->fcn (iter->data, (QofBookMergeRule*)rule, iter->remainder);
+ iter->remainder--;
+}
+
+static void
+qof_book_merge_commit_foreach (
+ QofBookMergeRuleForeachCB cb,
+ QofBookMergeResult mergeResult,
+ QofBookMergeData *mergeData)
+{
+ struct QofBookMergeRuleIterate iter;
+ QofBookMergeRule *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));
+
+ iter.fcn = cb;
+ subList = NULL;
+ iter.ruleList = g_list_copy(mergeData->mergeList);
+ while(iter.ruleList!=NULL) {
+ currentRule = iter.ruleList->data;
+ if(currentRule->mergeResult == mergeResult) {
+ subList = g_list_prepend(subList, currentRule);
+ }
+ iter.ruleList = g_list_next(iter.ruleList);
+ }
+ iter.remainder = g_list_length(subList);
+ iter.data = mergeData;
+ g_list_foreach (subList, qof_book_merge_commit_foreach_cb, &iter);
+}
+
+/* build the table of target comparisons
+
+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.
+
+*/
+static gboolean
+qof_book_merge_rule_cmp(gconstpointer a, gconstpointer b)
+{
+ QofBookMergeRule *ra = (QofBookMergeRule *) a;
+ QofBookMergeRule *rb = (QofBookMergeRule *) b;
+ if (ra->difference == rb->difference) { return TRUE; }
+ else return FALSE;
+}
+
+static void
+qof_book_merge_orphan_check(double difference, QofBookMergeRule *mergeRule,
+ QofBookMergeData *mergeData)
+{
+ /* Called when difference is lower than previous
+ Lookup target to find previous match
+ and re-assign mergeEnt to orphan_list */
+ QofBookMergeRule *rule;
+
+ g_return_if_fail(mergeRule != NULL);
+ g_return_if_fail(mergeData != NULL);
+ if(g_hash_table_size(mergeData->target_table) == 0) { return; }
+ rule = (QofBookMergeRule*)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);
+}
+
+static void
+qof_book_merge_match_orphans(QofBookMergeData *mergeData)
+{
+ GSList *orphans, *targets;
+ QofBookMergeRule *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);
+ 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;
+ }
+ mergeData->currentRule = rule;
+ g_return_if_fail(qof_book_merge_compare(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);
+ }
+ orphans = g_slist_next(orphans);
+ }
+ g_slist_free(mergeData->orphan_list);
+ g_slist_free(targets);
+}
+
+static void
+qof_book_merge_foreach_target (QofEntity* targetEnt, gpointer user_data)
+{
+ QofBookMergeData *mergeData;
+
+ 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);
+}
+
+static void
+qof_book_merge_foreach_type_target ( QofObject* merge_obj, gpointer user_data)
+{
+ QofBookMergeData *mergeData;
+ QofBookMergeRule *currentRule;
+
+ g_return_if_fail(user_data != NULL);
+ mergeData = (QofBookMergeData*)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_merge_foreach_target, user_data);
+ }
+}
+
+static void
+qof_book_merge_foreach ( QofEntity* mergeEnt, gpointer user_data)
+{
+ QofBookMergeRule *mergeRule, *currentRule;
+ QofBookMergeData *mergeData;
+ QofEntity *targetEnt, *best_matchEnt;
+ GUID *g;
+ double difference;
+ GSList *c;
+
+ g_return_if_fail(user_data != NULL);
+ mergeData = (QofBookMergeData*)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(QofBookMergeRule,1);
+ mergeRule->importEnt = mergeEnt;
+ mergeRule->difference = difference = 0;
+ mergeRule->mergeAbsolute = FALSE;
+ mergeRule->mergeResult = MERGE_UNDEF;
+ mergeRule->updated = FALSE;
+ mergeRule->mergeType = mergeEnt->e_type;
+ mergeRule->mergeLabel = qof_object_get_type_label(mergeEnt->e_type);
+ mergeRule->mergeParam = g_slist_copy(mergeData->mergeObjectParams);
+ mergeRule->linkedEntList = NULL;
+ 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_merge_compare(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_merge_foreach_type_target, mergeData);
+ if(g_slist_length(mergeData->targetList) == 0) {
+ mergeRule->mergeResult = MERGE_NEW;
+ }
+ difference = g_slist_length(mergeRule->mergeParam);
+ c = g_slist_copy(mergeData->targetList);
+ while(c != NULL) {
+ mergeRule->targetEnt = c->data;
+ currentRule = mergeRule;
+ /* compare two entities and sum the differences */
+ g_return_if_fail(qof_book_merge_compare(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);
+ }
+ g_slist_free(c);
+ 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_merge_compare(mergeData) != -1);
+ mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
+ }
+ else {
+ mergeRule->targetEnt = NULL;
+ mergeRule->difference = 0;
+ mergeRule->mergeResult = MERGE_NEW;
+ mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
+ }
+ mergeData->mergeList = g_list_prepend(mergeData->mergeList,mergeRule);
+ guid_free(g);
+ /* return to qof_book_merge_init */
+}
+
+static void
+qof_book_merge_foreach_param( QofParam* param, gpointer user_data)
+{
+ QofBookMergeData *mergeData;
+
+ g_return_if_fail(user_data != NULL);
+ mergeData = (QofBookMergeData*)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);
+ }
+}
+
+static void
+qof_book_merge_foreach_type ( QofObject* merge_obj, gpointer user_data)
+{
+ QofBookMergeData *mergeData;
+
+ g_return_if_fail(user_data != NULL);
+ mergeData = (QofBookMergeData*)user_data;
+ g_return_if_fail((merge_obj != NULL));
+ /* Skip unsupported objects */
+ if((merge_obj->create == NULL)||(merge_obj->foreach == NULL)){
+ DEBUG (" merge_obj QOF support failed %s", merge_obj->e_type);
+ return;
+ }
+ if(mergeData->mergeObjectParams != NULL) g_slist_free(mergeData->mergeObjectParams);
+ mergeData->mergeObjectParams = NULL;
+ qof_class_param_foreach(merge_obj->e_type, qof_book_merge_foreach_param , mergeData);
+ qof_object_foreach(merge_obj->e_type, mergeData->mergeBook,
+ qof_book_merge_foreach, mergeData);
+}
+
+static void
+qof_book_merge_rule_cb(gpointer rule, gpointer arg)
+{
+ struct QofBookMergeRuleIterate *iter;
+ QofBookMergeData *mergeData;
+
+ g_return_if_fail(arg != NULL);
+ iter = (struct QofBookMergeRuleIterate*)arg;
+ mergeData = iter->data;
+ g_return_if_fail(mergeData != NULL);
+ g_return_if_fail(mergeData->abort == FALSE);
+ iter->fcn (mergeData, (QofBookMergeRule*)rule, iter->remainder);
+ iter->data = mergeData;
+ iter->remainder--;
+}
+
+static void
+qof_book_merge_commit_rule_loop(
+ QofBookMergeData *mergeData,
+ QofBookMergeRule *rule,
+ guint remainder)
+{
+ QofInstance *inst;
+ gboolean registered_type;
+ QofEntity *referenceEnt;
+ /* cm_ prefix used for variables that hold the data to commit */
+ QofCollection *cm_coll;
+ QofParam *cm_param;
+ gchar *cm_string;
+ const GUID *cm_guid;
+ KvpFrame *cm_kvp;
+ /* function pointers and variables for parameter getters that don't use pointers normally */
+ gnc_numeric cm_numeric, (*numeric_getter) (QofEntity*, QofParam*);
+ double cm_double, (*double_getter) (QofEntity*, QofParam*);
+ gboolean cm_boolean, (*boolean_getter) (QofEntity*, QofParam*);
+ gint32 cm_i32, (*int32_getter) (QofEntity*, QofParam*);
+ gint64 cm_i64, (*int64_getter) (QofEntity*, QofParam*);
+ Timespec cm_date, (*date_getter) (QofEntity*, QofParam*);
+ gchar cm_char, (*char_getter) (QofEntity*, QofParam*);
+ /* function pointers to the parameter setters */
+ void (*string_setter) (QofEntity*, const gchar*);
+ void (*date_setter) (QofEntity*, Timespec);
+ void (*numeric_setter) (QofEntity*, gnc_numeric);
+ void (*guid_setter) (QofEntity*, const GUID*);
+ void (*double_setter) (QofEntity*, double);
+ void (*boolean_setter) (QofEntity*, gboolean);
+ void (*i32_setter) (QofEntity*, gint32);
+ void (*i64_setter) (QofEntity*, gint64);
+ void (*char_setter) (QofEntity*, gchar);
+ void (*kvp_frame_setter) (QofEntity*, KvpFrame*);
+ void (*reference_setter) (QofEntity*, QofEntity*);
+ void (*collection_setter)(QofEntity*, QofCollection*);
+
+ 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) {
+ inst = (QofInstance*)qof_object_new_instance(rule->importEnt->e_type, mergeData->targetBook);
+ g_return_if_fail(inst != NULL);
+ rule->targetEnt = &inst->entity;
+ qof_entity_set_guid(rule->targetEnt, qof_entity_get_guid(rule->importEnt));
+ }
+ /* currentRule->targetEnt is now set,
+ 1. by an absolute GUID match or
+ 2. by best_matchEnt and difference or
+ 3. by MERGE_NEW.
+ */
+ while(rule->mergeParam != NULL) {
+ registered_type = FALSE;
+ g_return_if_fail(rule->mergeParam->data);
+ cm_param = rule->mergeParam->data;
+ rule->mergeType = cm_param->param_type;
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_STRING) == 0) {
+ cm_string = cm_param->param_getfcn(rule->importEnt, cm_param);
+ string_setter = (void(*)(QofEntity*, const gchar*))cm_param->param_setfcn;
+ if(string_setter != NULL) { string_setter(rule->targetEnt, cm_string); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_DATE) == 0) {
+ date_getter = (Timespec (*)(QofEntity*, QofParam*))cm_param->param_getfcn;
+ cm_date = date_getter(rule->importEnt, cm_param);
+ date_setter = (void(*)(QofEntity*, Timespec))cm_param->param_setfcn;
+ if(date_setter != NULL) { date_setter(rule->targetEnt, cm_date); }
+ registered_type = TRUE;
+ }
+ if((safe_strcmp(rule->mergeType, QOF_TYPE_NUMERIC) == 0) ||
+ (safe_strcmp(rule->mergeType, QOF_TYPE_DEBCRED) == 0)) {
+ numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*))cm_param->param_getfcn;
+ cm_numeric = numeric_getter(rule->importEnt, cm_param);
+ numeric_setter = (void(*)(QofEntity*, gnc_numeric))cm_param->param_setfcn;
+ if(numeric_setter != NULL) { numeric_setter(rule->targetEnt, cm_numeric); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_GUID) == 0) {
+ cm_guid = cm_param->param_getfcn(rule->importEnt, cm_param);
+ guid_setter = (void(*)(QofEntity*, const GUID*))cm_param->param_setfcn;
+ if(guid_setter != NULL) { guid_setter(rule->targetEnt, cm_guid); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_INT32) == 0) {
+ int32_getter = (gint32 (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
+ cm_i32 = int32_getter(rule->importEnt, cm_param);
+ i32_setter = (void(*)(QofEntity*, gint32))cm_param->param_setfcn;
+ if(i32_setter != NULL) { i32_setter(rule->targetEnt, cm_i32); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_INT64) == 0) {
+ int64_getter = (gint64 (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
+ cm_i64 = int64_getter(rule->importEnt, cm_param);
+ i64_setter = (void(*)(QofEntity*, gint64))cm_param->param_setfcn;
+ if(i64_setter != NULL) { i64_setter(rule->targetEnt, cm_i64); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_DOUBLE) == 0) {
+ double_getter = (double (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
+ cm_double = double_getter(rule->importEnt, cm_param);
+ double_setter = (void(*)(QofEntity*, double))cm_param->param_setfcn;
+ if(double_setter != NULL) { double_setter(rule->targetEnt, cm_double); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_BOOLEAN) == 0){
+ boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
+ cm_boolean = boolean_getter(rule->importEnt, cm_param);
+ boolean_setter = (void(*)(QofEntity*, gboolean))cm_param->param_setfcn;
+ if(boolean_setter != NULL) { boolean_setter(rule->targetEnt, cm_boolean); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_KVP) == 0) {
+ cm_kvp = kvp_frame_copy(cm_param->param_getfcn(rule->importEnt,cm_param));
+ kvp_frame_setter = (void(*)(QofEntity*, KvpFrame*))cm_param->param_setfcn;
+ if(kvp_frame_setter != NULL) { kvp_frame_setter(rule->targetEnt, cm_kvp); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_CHAR) == 0) {
+ char_getter = (gchar (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
+ cm_char = char_getter(rule->importEnt,cm_param);
+ char_setter = (void(*)(QofEntity*, gchar))cm_param->param_setfcn;
+ if(char_setter != NULL) { char_setter(rule->targetEnt, cm_char); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_COLLECT) == 0) {
+ cm_coll = cm_param->param_getfcn(rule->importEnt, cm_param);
+ collection_setter = (void(*)(QofEntity*, QofCollection*))cm_param->param_setfcn;
+ if(collection_setter != NULL) { collection_setter(rule->targetEnt, cm_coll); }
+ registered_type = TRUE;
+ }
+ if(safe_strcmp(rule->mergeType, QOF_TYPE_CHOICE) == 0) {
+ referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
+ reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
+ if(reference_setter != NULL)
+ {
+ reference_setter(rule->targetEnt, referenceEnt);
+ }
+ registered_type = TRUE;
+ }
+ if(registered_type == FALSE) {
+ referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
+ if(referenceEnt) {
+ reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
+ if(reference_setter != NULL)
+ {
+ reference_setter(rule->targetEnt, referenceEnt);
+ }
+ }
+ }
+ rule->mergeParam = g_slist_next(rule->mergeParam);
+ }
+}
+/* ================================================================ */
+/* API functions. */
+
+QofBookMergeData*
+qof_book_merge_init( QofBook *importBook, QofBook *targetBook)
+{
+ QofBookMergeData *mergeData;
+ QofBookMergeRule *currentRule;
+ GList *check;
+
+ g_return_val_if_fail((importBook != NULL)&&(targetBook != NULL), NULL);
+ mergeData = g_new(QofBookMergeData, 1);
+ mergeData->abort = FALSE;
+ mergeData->mergeList = NULL;
+ mergeData->targetList = NULL;
+ 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(QofBookMergeRule, 1);
+ mergeData->currentRule = currentRule;
+ qof_object_foreach_type(qof_book_merge_foreach_type, 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) {
+ mergeData->abort = TRUE;
+ return(NULL);
+ }
+ check = g_list_next(check);
+ }
+ g_list_free(check);
+ return mergeData;
+}
+
+void
+qof_book_merge_abort (QofBookMergeData *mergeData)
+{
+ QofBookMergeRule *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);
+ }
+ mergeData->mergeList = g_list_next(mergeData->mergeList);
+ }
+ 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);
+}
+
+/* The QOF_TYPE_DATE output format from
+qof_book_merge_param_as_string has been changed to QSF_XSD_TIME,
+a UTC formatted timestring: 2005-01-01T10:55:23Z
+If you change QOF_UTC_DATE_FORMAT, change
+backend/file/qsf-xml.c : qsf_entity_foreach to
+reformat to QSF_XSD_TIME or the QSF XML will
+FAIL the schema validation and QSF exports will become invalid.
+
+The QOF_TYPE_BOOLEAN is lowercase for the same reason.
+
+\todo deprecate and replace with
+gchar* qof_instance_param_as_string(const QofParam*, QofInstance*);
+and then add
+gchar* qof_class_get_param_as_string(QofIdTypeConst, QofInstance*); ?
+*/
+gchar*
+qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt)
+{
+ gchar *param_string, param_date[QOF_DATE_STRING_LENGTH];
+ gchar param_sa[GUID_ENCODING_LENGTH + 1];
+ QofType paramType;
+ const GUID *param_guid;
+ time_t param_t;
+ 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*);
+ gchar param_char, (*char_getter) (QofEntity*, QofParam*);
+
+ 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(paramType, QOF_TYPE_DATE) == 0) {
+ date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
+ param_ts = date_getter(qtEnt, qtparam);
+ param_t = timespecToTime_t(param_ts);
+ strftime(param_date, QOF_DATE_STRING_LENGTH, QOF_UTC_DATE_FORMAT, gmtime(¶m_t));
+ param_string = g_strdup(param_date);
+ return param_string;
+ }
+ if((safe_strcmp(paramType, QOF_TYPE_NUMERIC) == 0) ||
+ (safe_strcmp(paramType, QOF_TYPE_DEBCRED) == 0)) {
+ numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ param_numeric = numeric_getter(qtEnt,qtparam);
+ param_string = g_strdup(gnc_numeric_to_string(param_numeric));
+ return param_string;
+ }
+ 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(paramType, QOF_TYPE_INT32) == 0) {
+ int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ param_i32 = int32_getter(qtEnt, qtparam);
+ param_string = g_strdup_printf("%d", param_i32);
+ return param_string;
+ }
+ if(safe_strcmp(paramType, QOF_TYPE_INT64) == 0) {
+ int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ param_i64 = int64_getter(qtEnt, qtparam);
+ param_string = g_strdup_printf("%" G_GINT64_FORMAT, param_i64);
+ return param_string;
+ }
+ if(safe_strcmp(paramType, QOF_TYPE_DOUBLE) == 0) {
+ double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ param_double = double_getter(qtEnt, qtparam);
+ param_string = g_strdup_printf("%f", param_double);
+ return param_string;
+ }
+ if(safe_strcmp(paramType, QOF_TYPE_BOOLEAN) == 0){
+ boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ param_boolean = boolean_getter(qtEnt, qtparam);
+ /* Boolean values need to be lowercase for QSF validation. */
+ if(param_boolean == TRUE) { param_string = g_strdup("true"); }
+ else { param_string = g_strdup("false"); }
+ return param_string;
+ }
+ /* "kvp" contains repeating values, cannot be a single string for the frame. */
+ if(safe_strcmp(paramType, QOF_TYPE_KVP) == 0) { return param_string; }
+ if(safe_strcmp(paramType, QOF_TYPE_CHAR) == 0) {
+ char_getter = (gchar (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
+ param_char = char_getter(qtEnt, qtparam);
+ param_string = g_strdup_printf("%c", param_char);
+ return param_string;
+ }
+ return NULL;
+}
+
+QofBookMergeData*
+qof_book_merge_update_result(QofBookMergeData *mergeData,
+ QofBookMergeResult tag)
+{
+ QofBookMergeRule *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;
+ }
+ 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;
+}
+
+gint
+qof_book_merge_commit(QofBookMergeData *mergeData )
+{
+ QofBookMergeRule *currentRule;
+ GList *check;
+
+ g_return_val_if_fail(mergeData != NULL, -1);
+ g_return_val_if_fail(mergeData->mergeList != NULL, -1);
+ 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(mergeData);
+ return(-2);
+ }
+ if(currentRule->mergeResult == MERGE_REPORT) {
+ g_list_free(check);
+ return 1;
+ }
+ check = g_list_next(check);
+ }
+ qof_book_merge_commit_foreach(qof_book_merge_commit_rule_loop,
+ MERGE_NEW, mergeData);
+ qof_book_merge_commit_foreach(qof_book_merge_commit_rule_loop,
+ MERGE_UPDATE, mergeData);
+ /* Placeholder for QofObject merge_helper_cb - all objects
+ and all parameters set */
+ while(mergeData->mergeList != NULL) {
+ currentRule = mergeData->mergeList->data;
+ g_slist_free(currentRule->mergeParam);
+ g_slist_free(currentRule->linkedEntList);
+ mergeData->mergeList = g_list_next(mergeData->mergeList);
+ }
+ 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;
+}
+
+void
+qof_book_merge_rule_foreach(QofBookMergeData *mergeData,
+ QofBookMergeRuleForeachCB cb,
+ QofBookMergeResult mergeResult )
+{
+ struct QofBookMergeRuleIterate iter;
+ QofBookMergeRule *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);
+ iter.fcn = cb;
+ iter.data = mergeData;
+ matching_rules = NULL;
+ iter.ruleList = g_list_copy(mergeData->mergeList);
+ while(iter.ruleList!=NULL) {
+ currentRule = iter.ruleList->data;
+ if(currentRule->mergeResult == mergeResult) {
+ matching_rules = g_list_prepend(matching_rules, currentRule);
+ }
+ iter.ruleList = g_list_next(iter.ruleList);
+ }
+ iter.remainder = g_list_length(matching_rules);
+ g_list_foreach (matching_rules, qof_book_merge_rule_cb, &iter);
+ g_list_free(matching_rules);
+}
+
+/* End of file. */
+/* ==================================================================== */
Copied: gnucash/trunk/lib/libqof/qof/qofbookmerge.h (from rev 13555, gnucash/trunk/lib/libqof/qof/qof_book_merge.h)
===================================================================
--- gnucash/trunk/lib/libqof/qof/qof_book_merge.h 2006-03-09 13:03:29 UTC (rev 13555)
+++ gnucash/trunk/lib/libqof/qof/qofbookmerge.h 2006-03-09 17:03:01 UTC (rev 13567)
@@ -0,0 +1,498 @@
+/*********************************************************************
+ * qofbookmerge.h -- api for QofBook merge with collision handling *
+ * Copyright (C) 2004 Neil Williams <linux at codehelp.co.uk> *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+ * *
+ ********************************************************************/
+
+#ifndef QOFBOOKMERGE_H
+#define QOFBOOKMERGE_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#define QOF_MOD_MERGE "qof-merge"
+
+/** @addtogroup BookMerge
+
+<b>Collision handling principles.</b>\n
+\n
+ -# Always check for a ::GUID first and compare. qofbookmerge only accepts
+ valid ::QofBook data and therefore ALL objects in the import book will
+ include valid GUID's.
+ -# If the original import data did not contain a GUID (e.g. an external
+ non-GnuCash source) the GUID values will have been created during the
+ import and will not match any existing GUID's in the target book so objects
+ that do not have a GUID match cannot be assumed to be ::MERGE_NEW - parameter
+ values must be checked.
+ -# If import contains data from closed books, store the data from the closed
+ books in the current book as active. i.e. re-open the books.
+
+- If a GUID match exists, set qof_book_merge_rule::mergeAbsolute to \a TRUE.
+ -# If ALL parameters in the import object match the target object with
+ the same \a GUID,
+ set ::qof_book_merge_result to \a MERGE_ABSOLUTE.
+ -# If any parameters differ, set ::MERGE_UPDATE.
+- If the import object \a GUID does not match an existing object,
+mergeAbsolute is unchanged from the default \a FALSE
+The parameter values of the object are compared to other objects of the same
+type in the target book.
+ -# If the same data exists in the target book with a different GUID, the object
+ is tagged as DUPLICATE.
+ -# If the data has changed, the object is tagged as REPORT.
+ -# If the data does not match, the object is tagged as NEW
+
+More information is at http://code.neil.williamsleesmill.me.uk/
+
+Each foreach function uses g_return_if_fail checks to protect the target book.
+If any essential data is missing, the loop returns without changing the target
+book. 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_merge_update_result and ::qof_book_merge_commit return
+any error values to the calling process. ::qof_book_merge_init returns a
+pointer to the ::QofBookMergeData struct - the calling process needs to
+make sure this is non-NULL to know that the Init has been successful.
+
+ @{
+*/
+/** @file qofbookmerge.h
+ @brief API for merging two \c QofBook structures with collision handling
+ @author Copyright (c) 2004-2005 Neil Williams <linux at codehelp.co.uk>
+*/
+
+#include <glib.h>
+#include "gnc-engine-util.h"
+#include "qofbook.h"
+#include "qofclass.h"
+#include "qofobject.h"
+#include "qofinstance.h"
+#include "qoflog.h"
+
+/** \brief Results of collisions and user resolution.
+
+All rules are initialised as ::MERGE_UNDEF.
+Once the comparison is complete, each object within the import will be
+updated.
+
+::MERGE_ABSOLUTE, ::MERGE_NEW, ::MERGE_DUPLICATE and ::MERGE_UPDATE can be
+reported to the user along with all ::MERGE_REPORT objects for confirmation.
+It may be useful later to allow \a MERGE_ABSOLUTE, \a MERGE_NEW,
+\a MERGE_DUPLICATE and \a MERGE_UPDATE to not be reported, if the user sets a
+preferences option for each result. (Always accept new items: Y/N default NO,
+ignores all MERGE_NEW if set to Y etc.) This option would not require any
+changes to qofbookmerge.
+
+\a MERGE_NEW, \a MERGE_DUPLICATE and \a MERGE_UPDATE are only actioned after
+conflicts are resolved by the user using a dialog and all \a MERGE_REPORT
+objects are re-assigned to one of MERGE_NEW, MERGE_DUPLICATE or MERGE_UPDATE.
+There is no automatic merge, even if no entities are tagged as MERGE_REPORT,
+the calling process must still check for REPORT items using
+::qof_book_merge_rule_foreach and call ::qof_book_merge_commit.
+
+\a MERGE_INVALID data should be rare and allows for user-abort - the imported
+file/source may be corrupted and the prescence of invalid data should raise
+concerns that the rest of the data may be corrupted, damaged or otherwise
+altered. If any entity is tagged as MERGE_INVALID, the merge operation will
+abort and leave the target book completely unchanged.
+
+\a MERGE_ABSOLUTE is only used for a complete match. The import object contains
+the same data in the same parameters with no omissions or amendments. If any
+data is missing, amended or added, the data is labelled \a MERGE_UPDATE.
+
+ Every piece of data has a corresponding result. Only when the count of items
+ labelled \a MERGE_REPORT is equal to zero are \a MERGE_NEW and \a MERGE_UPDATE
+ items added to the existing book.\n \a MERGE_DUPLICATE items are silently
+ ignored. Aborting the dialogue/process (by the user or in a program crash) at
+ any point before the final commit leaves the existing book completely untouched.
+*/
+typedef enum {
+ MERGE_UNDEF, /**< default value before comparison is made. */
+ MERGE_ABSOLUTE, /**< GUID exact match, no new data - \b ignore */
+ MERGE_NEW, /**< import object does \b not exist in the target
+ book - \b add */
+ MERGE_REPORT, /**< import object needs user intervention - \b report */
+ MERGE_DUPLICATE, /**< import object with different GUID exactly matches
+ existing GUID - \b ignore */
+ MERGE_UPDATE, /**< import object matches an existing entity but includes
+ new or modified parameter data - \b update */
+ MERGE_INVALID /**< import object didn't match registered object or
+ parameter types or user decided to abort - \b abort */
+}QofBookMergeResult;
+
+/** \brief One rule per entity, built into a single GList for the entire merge
+
+All rules are stored in the GList QofBookMergeData::mergeList.
+
+If the ::GUID matches it's the always same semantic object,
+regardless of whether other data fields are changed.
+\n
+The boolean value mergeAbsolute defaults to \c FALSE
+
+NOTE 1: if mergeAbsolute == \c TRUE, ::QofBookMergeResult will still be set
+to ::MERGE_UPDATE if parameters within this entity have been modified.
+
+NOTE 2: ::qof_book_merge_param_as_string returns \b string representations of
+the parameter data that is causing a collision. These values must \b NOT be
+used to set the target 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 dialogue.
+
+The GHashTable targetTable in QofBookMergeRule 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 */
+ 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 */
+ const gchar* mergeLabel; /**< Descriptive label for the object type,
+ useful for the user intervention dialogue. */
+ GSList *mergeParam; /**< list of usable parameters for the object type */
+ GSList *linkedEntList; /**< list of complex data types included in this object.
+
+ linkedEntList contains an ::QofEntity reference to any parameter that is not
+ one of the core QOF_TYPE data types. This entity must be already
+ registered with QOF and the results of the comparison for the linked entity
+ will modulate the mergeResult of this object. e.g. if an invoice is the
+ same value but for a different customer, the invoice will be set to
+ MERGE_REPORT and the customer as MERGE_NEW.
+ */
+ QofBookMergeResult mergeResult; /**< result of comparison with main ::QofBook */
+ QofEntity *importEnt; /**< pointer to the current entity in the import book. */
+ QofEntity *targetEnt; /**< pointer to the corresponding entity in the
+ target book, if any. */
+}QofBookMergeRule;
+
+
+/** \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 ::QofBookMergeRule
+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. */
+ QofBookMergeRule *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 QofEntityRating
+ struct and the GHashTable ::QofBookMergeData::target_table.
+ */
+ GHashTable *target_table; /**< The GHashTable to hold the
+ QofEntityRating values. */
+
+}QofBookMergeData;
+
+
+/* ======================================================================== */
+/** @name qof_book_merge API
+ @{
+*/
+/** \brief Initialise the QofBookMerge process
+
+ First function of the QofBookMerge API. Every merge must begin with init.
+
+ Requires the book to import (::QofBook *) and the book to receive the
+ import, the target book (::QofBook *). Returns a pointer to
+ ::QofBookMergeData which must be checked for a NULL before continuing. \n
+Process:
+
+ -# Invoke the callback ::qof_book_merge_foreach_type on every registered
+ object class definition.
+ -# Callback obtains the registered parameter list for each object type.
+ This provides run time access to all registered objects and all object
+ parameters without any changes to QofBookMerge - no registered object or
+ parameter is omitted from any merge operation.
+ -# Use ::qof_object_foreach to invoke the callback ::qof_book_merge_foreach,
+ one object at a time on every instance stored in mergeBook. This is the
+ first point where real data from the import book is accessed.
+ -# qof_book_merge_foreach obtains the ::GUID for the object from the import
+ book and runs the first check on the original book, checking for any exact
+ GUID match. With the full parameter list, the rules for this object can be
+ created. If there is a GUID match, the data in each parameter of the import
+ object is compared with the same semantic object in the original book. If
+ there is no GUID in the import object or no GUID match with the original
+ book, the original book is searched to find a parameter match - checking
+ for a ::MERGE_DUPLICATE result.
+ -# ::qof_book_merge_compare sets the ::QofBookMergeResult of the comparison.
+ -# Inserts the completed rule into QofBookMergeData::mergeList GSList.
+
+\return NULL in case of error, otherwise a ::QofBookMergeData* metadata context.
+
+*/
+QofBookMergeData*
+qof_book_merge_init( QofBook *importBook, QofBook *targetBook);
+
+
+/** \brief Definition of the dialogue control callback routine
+
+All ::MERGE_REPORT rules must be offered for user intervention using this
+template.\n
+Commit will fail if any rules are still tagged as \a MERGE_REPORT.
+
+Calling processes are free to also offer MERGE_NEW, MERGE_UPDATE,
+MERGE_DUPLICATE and MERGE_ABSOLUTE for user intervention. Attempting to query
+MERGE_INVALID rules will cause an error.
+
+For an example, consider test_rule_loop, declared as:
+
+<tt>void test_rule_loop(QofBookMergeData *mergeData, QofBookMergeRule *rule, guint remainder);\n
+void test_rule_loop(QofBookMergeData *mergeData, QofBookMergeRule *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_merge_update_result(mergeData, rule, MERGE_UPDATE);\n
+}</tt>
+
+The dialogue is free to call ::qof_book_merge_update_result in the loop or at the end
+as long as the link between the rule and the result is maintained, e.g. by using a
+GHashTable.
+\n
+The parameters are:
+ - data : pointer to the ::QofBookMergeData metadata context returned by init.
+ - rule : pointer to the ::QofBookMergeRule 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 dialogue, it might not. When updating MERGE_REPORT,
+ remainder must equal zero before calling ::qof_book_merge_commit or
+ the import will abort.
+\n
+
+If the dialogue sets \b any rule result to ::MERGE_INVALID, the import will
+abort when ::qof_book_merge_commit is called. It is the responsibility of
+the calling function to handle the error code from ::qof_book_merge_commit,
+close the dialogue 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 dialogue
+process to report back to QofBookMerge in this situation.
+*/
+typedef void (* QofBookMergeRuleForeachCB)( QofBookMergeData*, QofBookMergeRule*, guint);
+
+/** \brief Dialogue Control Callback
+
+This function is designed to be used to iterate over all rules tagged with a
+specific ::QofBookMergeResult value.
+
+ at param callback external loop of type QofBookMergeRuleForeachCB
+ at param mergeResult ::QofBookMergeResult value to look up.
+ at param mergeData ::QofBookMergeData 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_merge_rule_foreach
+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 QofBookMergeRule::mergeType object
+type to return a collection of ::QofEntity entities from either the
+QofBookMergeData::mergeBook or QofBookMergeData::targetBook. Then
+uses ::qof_collection_lookup_entity to lookup the QofBookMergeRule::importEnt
+and again the qof_book_mergeRule::targetEnt to return the two specific entities.
+
+*/
+void qof_book_merge_rule_foreach( QofBookMergeData* mergeData,
+ QofBookMergeRuleForeachCB callback ,
+ QofBookMergeResult mergeResult);
+
+/** \brief provides easy string access to parameter data for dialogue use
+
+Uses the param_getfcn to retrieve the parameter value as a string, suitable for
+display in dialogues and user intervention output. Within a QofBookMerge 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 dialogue as QofBookMergeRule::mergeLabel.
+
+This allows the dialog to display the description of the object and all
+parameter data.
+
+*/
+gchar* qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt);
+
+/** \brief called by dialogue callback to set the result of user intervention
+
+Set \b any rule result to ::MERGE_INVALID to abort the import when
+::qof_book_merge_commit is called, without changing the target book.
+
+The calling process should make it absolutely clear that a merge operation
+\b cannot be undone and that a backup copy should always be available
+\b before a merge is initialised.
+
+Recommended method: Only offer three options to the user per rule:
+
+-# Allow import data to be merged into target data
+ - change MERGE_REPORT to MERGE_UPDATE
+-# Allow import data without an exact match to be
+ added as new
+ - change MERGE_REPORT to MERGE_NEW \b IF mergeAbsolute = FALSE
+-# Ignore import data and leave target data unchanged
+ - change MERGE_REPORT to MERGE_ABSOLUTE or MERGE_DUPLICATE
+
+Handle the required result changes in code: Check the value of
+qof_book_mergeRule::mergeAbsolute and use these principles:
+
+To ignore entities tagged as:
+- MERGE_REPORT, you must check the value of mergeAbsolute.
+ - if mergeAbsolute is TRUE, change MERGE_REPORT to MERGE_ABSOLUTE
+ - if mergeAbsolute is FALSE, change MERGE_REPORT to MERGE_DUPLICATE
+- MERGE_NEW, set MERGE_DUPLICATE.
+- MERGE_UPDATE, you must check the value of mergeAbsolute.
+ - if mergeAbsolute is TRUE, change MERGE_UPDATE to MERGE_ABSOLUTE
+ - if mergeAbsolute is FALSE, change MERGE_UPDATE to MERGE_DUPLICATE
+
+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 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
+add an entity when mergeAbsolute is TRUE will always force a MERGE_UPDATE.
+
+It is not possible to update the same rule more than once.
+
+-# \b MERGE_NEW is reserved for new objects and is only pre-set if
+all parameters, including GUID, have already failed to match any
+relevant object. ::qof_book_mergeCommit will create new
+entities for all rules tagged as MERGE_NEW.
+ - if mergeAbsolute is TRUE and the user wants to import the
+ data, requests to set MERGE_NEW will be forced to MERGE_UPDATE
+ because an entity with that GUID already exists in the target book.
+ - if MERGE_NEW is pre-set, requests to change to MERGE_UPDATE will be
+ ignored because a new entity is needed.
+-# \b MERGE_UPDATE is reserved for existing objects - ::qof_book_mergeCommit
+will require a matching entity to update and will force a change to back to
+MERGE_NEW if none is known to exist, using the principle above.
+-# \b MERGE_INVALID will cause an abort of the merge process.
+-# \b MERGE_UNDEF and \b MERGE_REPORT cannot be set - the entity result will
+be unchanged.
+-# \b MERGE_DUPLICATE and \b MERGE_ABSOLUTE are handled identically but are
+ semantically different - QofBookMergeRule::mergeAbsolute is used to
+ dictate which to set:
+ - if mergeAbsolute is TRUE but MERGE_DUPLICATE is requested,
+ force a change to MERGE_ABSOLUTE.
+ - if mergeAbsolute is FALSE but MERGE_ABSOLUTE is requested,
+ force a change to MERGE_DUPLICATE.
+
+::qof_book_merge_commit only commits entities tagged
+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.
+
+ at param mergeData the merge context, ::QofBookMergeData*
+ at param tag the result to attempt to set, ::QofBookMergeResult
+
+\return -1 if supplied parameters are invalid or NULL, 0 on success.
+
+*/
+QofBookMergeData*
+qof_book_merge_update_result(QofBookMergeData *mergeData, QofBookMergeResult tag);
+
+/** \brief Commits the import data to the target book
+
+ The last function in the API and the final part of any QofBookMerge operation.
+
+qof_book_merge_commit 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
+dialogue and return. qof_book_merge_commit 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 dialogue process to report
+back to qof_book_merge in this situation.
+
+qof_book_merge_commit checks for any entities still tagged as
+::MERGE_REPORT and then proceeds to import all entities tagged as
+::MERGE_UPDATE or ::MERGE_NEW into the target book.
+\n
+<b>This final process cannot be UNDONE!</b>\n
+\n
+
+ at param mergeData the merge context, ::QofBookMergeData*
+
+\return
+ - -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_merge_init - the calling process must check the value of
+ mergeData
+ - +1 if some entities are still tagged as \a MERGE_REPORT - use
+ ::qof_book_merge_update_rule and try again (mergeData is retained).
+ - 0 on success - mergeData will have been freed.
+*/
+gint
+qof_book_merge_commit(QofBookMergeData *mergeData );
+
+/** \brief Abort the merge and free all memory allocated by the merge
+
+Sometimes, setting ::MERGE_INVALID is insufficient: e.g. if the user aborts the
+merge from outside the functions dealing with the merge ruleset. This function
+causes an immediate abort - the calling process must start again at Init if
+a new merge is required.
+*/
+void
+qof_book_merge_abort(QofBookMergeData *mergeData);
+
+#endif // QOFBOOKMERGE_H
+/** @} */
+/** @} */
Modified: gnucash/trunk/src/engine/test/test-book-merge.c
===================================================================
--- gnucash/trunk/src/engine/test/test-book-merge.c 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/src/engine/test/test-book-merge.c 2006-03-09 17:03:01 UTC (rev 13567)
@@ -41,7 +41,7 @@
#define OBJ_MINOR "tiny"
#define OBJ_ACTIVE "ofcourse"
-static void test_rule_loop (qof_book_mergeData*, qof_book_mergeRule*, guint);
+static void test_rule_loop (QofBookMergeData*, QofBookMergeRule*, guint);
static void test_merge (void);
gboolean myobjRegister (void);
@@ -50,7 +50,7 @@
{
QofInstance inst;
char *Name;
- gnc_numeric Amount;
+ gnc_numeric Amount;
const GUID *obj_guid;
Timespec date;
double discount; /* cheap pun, I know. */
@@ -261,7 +261,7 @@
gint64 minor;
gchar *import_init, *target_init;
gnc_numeric obj_amount;
- qof_book_mergeData *mergeData;
+ QofBookMergeData *mergeData;
target = qof_book_new();
import = qof_book_new();
@@ -343,21 +343,21 @@
obj_setMinor(target_obj, minor);
obj_setDate(target_obj, tc );
- mergeData = qof_book_mergeInit(import, target);
+ mergeData = qof_book_merge_init(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);
+ qof_book_merge_rule_foreach(mergeData, test_rule_loop, MERGE_REPORT);
+ qof_book_merge_rule_foreach(mergeData, test_rule_loop, MERGE_UPDATE);
+ qof_book_merge_rule_foreach(mergeData, test_rule_loop, MERGE_NEW);
/* reserved calls - test only */
- qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_ABSOLUTE);
- qof_book_mergeRuleForeach(mergeData, test_rule_loop, MERGE_DUPLICATE);
+ qof_book_merge_rule_foreach(mergeData, test_rule_loop, MERGE_ABSOLUTE);
+ qof_book_merge_rule_foreach(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(mergeData) == 0, "Commit failed");
+ do_test (qof_book_merge_commit(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");
@@ -389,7 +389,7 @@
}
static void
-test_rule_loop (qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder)
+test_rule_loop (QofBookMergeData *mergeData, QofBookMergeRule *rule, guint remainder)
{
GSList *testing;
QofParam *eachParam;
@@ -461,17 +461,17 @@
} // end param loop
/* set each rule dependent on the user involvement response above. */
/* test routine just sets all MERGE_REPORT to MERGE_UPDATE */
- mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_UPDATE);
+ mergeData = qof_book_merge_update_result(mergeData, MERGE_UPDATE);
do_test ((rule->mergeResult != MERGE_REPORT), "update result fail");
}
int
main (int argc, char **argv)
{
- gnc_engine_init(argc, argv);
+ qof_init();
myobjRegister();
test_merge();
print_test_results();
- exit(get_rv());
+ qof_close();
return 0;
}
Modified: gnucash/trunk/src/gnome/druid-merge.c
===================================================================
--- gnucash/trunk/src/gnome/druid-merge.c 2006-03-09 16:54:44 UTC (rev 13566)
+++ gnucash/trunk/src/gnome/druid-merge.c 2006-03-09 17:03:01 UTC (rev 13567)
@@ -44,21 +44,21 @@
static GtkWidget *qsf_import_merge_window = NULL;
QofSession *previous_session = NULL;
gint count = 0;
-qof_book_mergeData *mergeData = NULL;
+QofBookMergeData *mergeData = NULL;
QofSession *merge_session = NULL;
QofBook *mergeBook = NULL;
QofBook *targetBook = NULL;
static QofLogModule log_module = GNC_QSF_IMPORT;
-void collision_rule_loop ( qof_book_mergeData*, qof_book_mergeRule*, guint );
+void collision_rule_loop (QofBookMergeData*, QofBookMergeRule*, guint );
static GtkWidget*
merge_get_widget (const char *name)
{
- if (!qsf_import_merge_window) return NULL;
+ if (!qsf_import_merge_window) return NULL;
- return gnc_glade_lookup_widget (qsf_import_merge_window, name);
+ return gnc_glade_lookup_widget (qsf_import_merge_window, name);
}
static void
@@ -93,51 +93,51 @@
g_return_if_fail(mergeData != NULL);
ENTER (" ");
- mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_UPDATE);
+ mergeData = qof_book_merge_update_result(mergeData, MERGE_UPDATE);
count = 0;
- qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
- if(count == 0)
- {
- output = GTK_LABEL(merge_get_widget("OutPut"));
- gtk_label_set_text(output,_("No conflicts to be resolved."));
- gtk_widget_show(GTK_WIDGET(output));
- }
- LEAVE (" ");
+ qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
+ if(count == 0)
+ {
+ output = GTK_LABEL(merge_get_widget("OutPut"));
+ gtk_label_set_text(output,_("No conflicts to be resolved."));
+ gtk_widget_show(GTK_WIDGET(output));
+ }
+ LEAVE (" ");
}
static void
on_MergeDuplicate_clicked (GtkButton *button,
gpointer user_data)
{
- qof_book_mergeRule *currentRule;
+ QofBookMergeRule *currentRule;
GtkLabel *output;
g_return_if_fail(mergeData != NULL);
ENTER (" ");
currentRule = mergeData->currentRule;
if(currentRule->mergeAbsolute == FALSE) {
- mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_DUPLICATE);
+ mergeData = qof_book_merge_update_result(mergeData, MERGE_DUPLICATE);
count = 0;
}
if(currentRule->mergeAbsolute == TRUE) {
- mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_ABSOLUTE);
+ mergeData = qof_book_merge_update_result(mergeData, MERGE_ABSOLUTE);
count = 0;
}
- qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
- if(count == 0)
- {
- output = GTK_LABEL(merge_get_widget("OutPut"));
- gtk_label_set_text(output,_("No conflicts to be resolved."));
- gtk_widget_show(GTK_WIDGET(output));
- }
- LEAVE (" ");
+ qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
+ if(count == 0)
+ {
+ output = GTK_LABEL(merge_get_widget("OutPut"));
+ gtk_label_set_text(output,_("No conflicts to be resolved."));
+ gtk_widget_show(GTK_WIDGET(output));
+ }
+ LEAVE (" ");
}
static void
on_MergeNew_clicked (GtkButton *button,
gpointer user_data)
{
- qof_book_mergeRule *currentRule;
+ QofBookMergeRule *currentRule;
GtkLabel *output;
g_return_if_fail(mergeData != NULL);
@@ -145,17 +145,17 @@
g_return_if_fail(currentRule != NULL);
ENTER (" ");
if(currentRule->mergeAbsolute == FALSE) {
- mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_NEW);
+ mergeData = qof_book_merge_update_result(mergeData, MERGE_NEW);
}
count = 0;
- qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
- if(count == 0)
- {
- output = GTK_LABEL(merge_get_widget("OutPut"));
- gtk_label_set_text(output,_("No conflicts to be resolved."));
- gtk_widget_show(GTK_WIDGET(output));
- }
- LEAVE (" ");
+ qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
+ if(count == 0)
+ {
+ output = GTK_LABEL(merge_get_widget("OutPut"));
+ gtk_label_set_text(output,_("No conflicts to be resolved."));
+ gtk_widget_show(GTK_WIDGET(output));
+ }
+ LEAVE (" ");
}
static gboolean
@@ -166,15 +166,15 @@
GtkWidget *top;
gchar *message;
- if(count > 0)
- {
- message = g_strdup_printf(_("Error: Please resolve all %d "
- "conflicts before trying to commit the data."), count);
+ if(count > 0)
+ {
+ message = g_strdup_printf(_("Error: Please resolve all %d "
+ "conflicts before trying to commit the data."), count);
top = gtk_widget_get_toplevel (GTK_WIDGET (gnomedruidpage));
- gnc_error_dialog(top, message);
- g_free(message);
- return TRUE;
- }
+ gnc_error_dialog(top, message);
+ g_free(message);
+ return TRUE;
+ }
return FALSE;
}
@@ -218,11 +218,11 @@
ENTER (" ");
g_return_if_fail(mergeData != NULL);
gnc_suspend_gui_refresh ();
- result = qof_book_mergeCommit(mergeData);
+ result = qof_book_merge_commit(mergeData);
if(result != 0) {
- message = _("Error: the Commit operation failed.");
+ message = _("Error: the Commit operation failed.");
top = gtk_widget_get_toplevel (GTK_WIDGET (gnomedruidpage));
- gnc_error_dialog(top, message);
+ gnc_error_dialog(top, message);
}
g_return_if_fail(result == 0);
delete_merge_window ();
@@ -247,16 +247,16 @@
/* blank out old data */
gtk_label_set_text(progress, "");
g_return_if_fail(mergeBook || targetBook);
- mergeData = qof_book_mergeInit(mergeBook, targetBook);
+ mergeData = qof_book_merge_init(mergeBook, targetBook);
g_return_if_fail(mergeData != NULL);
- count = 0;
- qof_book_mergeRuleForeach(mergeData, collision_rule_loop, MERGE_REPORT);
- if(count == 0)
- {
- output = GTK_LABEL(merge_get_widget("OutPut"));
- gtk_label_set_text(output,_("No conflicts to be resolved."));
- gtk_widget_show(GTK_WIDGET(output));
- }
+ count = 0;
+ qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
+ if(count == 0)
+ {
+ output = GTK_LABEL(merge_get_widget("OutPut"));
+ gtk_label_set_text(output,_("No conflicts to be resolved."));
+ gtk_widget_show(GTK_WIDGET(output));
+ }
gnc_resume_gui_refresh ();
LEAVE (" ");
}
@@ -264,8 +264,8 @@
static GtkWidget *
gnc_create_import_druid ( void )
{
- GtkWidget *dialog, *druid, *start_page;
- GladeXML *xml;
+ GtkWidget *dialog, *druid, *start_page;
+ GladeXML *xml;
xml = gnc_glade_xml_new ("merge.glade", "Merge Druid");
@@ -274,7 +274,7 @@
gnc_druid_set_colors (GNOME_DRUID (druid));
start_page = glade_xml_get_widget (xml, "start_page");
gtk_widget_show (start_page);
- gtk_widget_show (glade_xml_get_widget (xml, "MergeDruidFinishPage"));
+ gtk_widget_show (glade_xml_get_widget (xml, "MergeDruidFinishPage"));
glade_xml_signal_connect(xml, "on_start_page_next",
G_CALLBACK (on_import_start_page_next));
@@ -305,7 +305,7 @@
return dialog;
}
-void collision_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule,
+void collision_rule_loop(QofBookMergeData *mergeData, QofBookMergeRule *rule,
guint remainder)
{
GSList *user_reports;
@@ -367,7 +367,7 @@
g_free(buffer);
g_free(importstring);
g_free(targetstring);
- LEAVE (" ");
+ LEAVE (" ");
}
void
More information about the gnucash-changes
mailing list