qofbookmerge.h File Reference


Detailed Description

API for merging two QofBook structures with collision handling.

Author:
Copyright (c) 2004-2005 Neil Williams <linux@codehelp.co.uk>

Definition in file qofbookmerge.h.

#include "qofutil.h"
#include "qofbook.h"
#include "qofclass.h"
#include "qofobject.h"
#include "qofinstance.h"
#include "qoflog.h"

Go to the source code of this file.

Data Structures

struct  QofBookMergeRule
 One rule per entity, built into a single GList for the entire merge. More...
struct  QofBookMergeData
 mergeData contains the essential context data for any merge. More...

qof_book_merge API

typedef void(* QofBookMergeRuleForeachCB )(QofBookMergeData *, QofBookMergeRule *, guint)
 Definition of the dialogue control callback routine.
QofBookMergeDataqof_book_merge_init (QofBook *importBook, QofBook *targetBook)
 Initialise the QofBookMerge process.
void qof_book_merge_rule_foreach (QofBookMergeData *mergeData, QofBookMergeRuleForeachCB callback, QofBookMergeResult mergeResult)
 Dialogue Control Callback.
gchar * qof_book_merge_param_as_string (QofParam *qtparam, QofEntity *qtEnt)
 provides easy string access to parameter data for dialogue use
QofBookMergeDataqof_book_merge_update_result (QofBookMergeData *mergeData, QofBookMergeResult tag)
 called by dialogue callback to set the result of user intervention
gint qof_book_merge_commit (QofBookMergeData *mergeData)
 Commits the import data to the target book.
void qof_book_merge_abort (QofBookMergeData *mergeData)
 Abort the merge and free all memory allocated by the merge.

Defines

#define QOF_MOD_MERGE   "qof-merge"

Enumerations

enum  QofBookMergeResult {
  MERGE_UNDEF, MERGE_ABSOLUTE, MERGE_NEW, MERGE_REPORT,
  MERGE_DUPLICATE, MERGE_UPDATE, MERGE_INVALID
}
 Results of collisions and user resolution. More...


Typedef Documentation

typedef void(* QofBookMergeRuleForeachCB)(QofBookMergeData *, QofBookMergeRule *, guint)
 

Definition of the dialogue control callback routine.

All MERGE_REPORT rules must be offered for user intervention using this template.
Commit will fail if any rules are still tagged as 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:

void test_rule_loop(QofBookMergeData *mergeData, QofBookMergeRule *rule, guint remainder);
void test_rule_loop(QofBookMergeData *mergeData, QofBookMergeRule *rule, guint remainder)
{
g_return_if_fail(rule != NULL);
g_return_if_fail(mergeData != NULL); printf("Rule Result %s", rule->mergeType);
qof_book_merge_update_result(mergeData, rule, MERGE_UPDATE);
}

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.
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.

If the dialogue sets 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.

Definition at line 321 of file qofbookmerge.h.


Function Documentation

void qof_book_merge_abort QofBookMergeData mergeData  ) 
 

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.

Definition at line 837 of file qofbookmerge.c.

00841                                                           { 
00842                         param_string = g_strdup(qtparam->param_getfcn(qtEnt,qtparam));
00843                         if(param_string == NULL) { param_string = ""; }
00844                         return param_string;
00845                 }
00846                 if(safe_strcmp(paramType, QOF_TYPE_DATE) == 0) { 
00847                         date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
00848                         param_ts = date_getter(qtEnt, qtparam);
00849                         param_t = timespecToTime_t(param_ts);
00850                         strftime(param_date, QOF_DATE_STRING_LENGTH, QOF_UTC_DATE_FORMAT, gmtime(&param_t));
00851                         param_string = g_strdup(param_date);
00852                         return param_string;
00853                 }
00854                 if((safe_strcmp(paramType, QOF_TYPE_NUMERIC) == 0)  ||
00855                 (safe_strcmp(paramType, QOF_TYPE_DEBCRED) == 0)) { 
00856                         numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
00857                         param_numeric = numeric_getter(qtEnt,qtparam);
00858                         param_string = g_strdup(gnc_numeric_to_string(param_numeric));
00859                         return param_string;
00860                 }

gint qof_book_merge_commit QofBookMergeData mergeData  ) 
 

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 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.
This final process cannot be UNDONE!

Parameters:
mergeData the merge context, QofBookMergeData*
Returns:
  • -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 MERGE_REPORT - use qof_book_merge_update_rule and try again (mergeData is retained).
  • 0 on success - mergeData will have been freed.

Definition at line 997 of file qofbookmerge.c.

01008         {
01009                 currentRule = node->data;
01010                 if(currentRule->mergeResult == mergeResult) {
01011                         matching_rules = g_list_prepend(matching_rules, currentRule);
01012                 }
01013         }
01014         iter.remainder = g_list_length(matching_rules);
01015         g_list_foreach (matching_rules, qof_book_merge_rule_cb, &iter);
01016         g_list_free(matching_rules);
01017 }
01018 
01019 /* End of file. */
01020 /* ==================================================================== */
01021 /* ==================================================================== */

QofBookMergeData* qof_book_merge_init QofBook importBook,
QofBook targetBook
 

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.
Process:

  1. Invoke the callback qof_book_merge_foreach_type on every registered object class definition.
  2. 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.
  3. 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.
  4. 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.
  5. qof_book_merge_compare sets the QofBookMergeResult of the comparison.
  6. Inserts the completed rule into QofBookMergeData::mergeList GSList.

Returns:
NULL in case of error, otherwise a QofBookMergeData* metadata context.

Definition at line 801 of file qofbookmerge.c.

00803                                            { g_slist_free(mergeData->orphan_list); }
00804         g_hash_table_destroy(mergeData->target_table);
00805         g_free(mergeData);
00806 }
00807 
00808 /* The QOF_TYPE_DATE output format from
00809 qof_book_merge_param_as_string has been changed to QSF_XSD_TIME,
00810 a UTC formatted timestring: 2005-01-01T10:55:23Z
00811 If you change QOF_UTC_DATE_FORMAT, change 
00812 backend/file/qsf-xml.c : qsf_entity_foreach to
00813 reformat to QSF_XSD_TIME or the QSF XML will
00814 FAIL the schema validation and QSF exports will become invalid.
00815 
00816 The QOF_TYPE_BOOLEAN is lowercase for the same reason.
00817 
00818 \todo deprecate and replace with
00819 gchar* qof_instance_param_as_string(const QofParam*, QofInstance*);
00820 and then add
00821 gchar* qof_class_get_param_as_string(QofIdTypeConst, QofInstance*); ?
00822 */
00823 gchar*
00824 qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt)
00825 {
00826         gchar       *param_string, param_date[QOF_DATE_STRING_LENGTH];
00827         gchar       param_sa[GUID_ENCODING_LENGTH + 1];
00828         QofType     paramType;
00829         const GUID *param_guid;
00830         time_t      param_t;
00831         gnc_numeric param_numeric,  (*numeric_getter) (QofEntity*, QofParam*);
00832         Timespec    param_ts,       (*date_getter)    (QofEntity*, QofParam*);
00833         double      param_double,   (*double_getter)  (QofEntity*, QofParam*);
00834         gboolean    param_boolean,  (*boolean_getter) (QofEntity*, QofParam*);

gchar* qof_book_merge_param_as_string QofParam qtparam,
QofEntity qtEnt
 

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.

Definition at line 878 of file qofbookmerge.c.

00879                                                                  { 
00880                         double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
00881                         param_double = double_getter(qtEnt, qtparam);
00882                         param_string = g_strdup_printf("%f", param_double);
00883                         return param_string;
00884                 }
00885                 if(safe_strcmp(paramType, QOF_TYPE_BOOLEAN) == 0){ 
00886                         boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
00887                         param_boolean = boolean_getter(qtEnt, qtparam);
00888                         /* Boolean values need to be lowercase for QSF validation. */
00889                         if(param_boolean == TRUE) { param_string = g_strdup("true"); }
00890                         else { param_string = g_strdup("false"); }
00891                         return param_string;
00892                 }
00893                 /* "kvp" contains repeating values, cannot be a single string for the frame. */
00894                 if(safe_strcmp(paramType, QOF_TYPE_KVP) == 0) { return param_string; }
00895                 if(safe_strcmp(paramType, QOF_TYPE_CHAR) == 0) { 
00896                         char_getter = (gchar (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
00897                         param_char = char_getter(qtEnt, qtparam);
00898                         param_string = g_strdup_printf("%c", param_char);
00899                         return param_string;
00900                 }
00901         return NULL;
00902 }
00903 
00904 QofBookMergeData*
00905 qof_book_merge_update_result(QofBookMergeData *mergeData,
00906                                                 QofBookMergeResult tag)
00907 {
00908         QofBookMergeRule *resolved;
00909 
00910         g_return_val_if_fail((mergeData != NULL), NULL);
00911         g_return_val_if_fail((tag > 0), NULL);
00912         g_return_val_if_fail((tag != MERGE_REPORT), NULL);
00913         resolved = mergeData->currentRule;
00914         g_return_val_if_fail((resolved != NULL), NULL);
00915         if((resolved->mergeAbsolute == TRUE)&&(tag == MERGE_DUPLICATE))
00916         { 
00917                 tag = MERGE_ABSOLUTE; 
00918         }
00919         if((resolved->mergeAbsolute == TRUE)&&(tag == MERGE_NEW))
00920         {
00921                 tag = MERGE_UPDATE; 
00922         }
00923         if((resolved->mergeAbsolute == FALSE)&& (tag == MERGE_ABSOLUTE))
00924         { 
00925                 tag = MERGE_DUPLICATE; 
00926         }
00927         if((resolved->mergeResult == MERGE_NEW)&&(tag == MERGE_UPDATE)) 
00928         { 
00929                 tag = MERGE_NEW; 
00930         }
00931         if(resolved->updated == FALSE) { resolved->mergeResult = tag; }
00932         resolved->updated = TRUE;
00933         if(tag >= MERGE_INVALID) { 
00934                 mergeData->abort = TRUE;
00935                 mergeData->currentRule = resolved;
00936                 return NULL; 
00937         }
00938         mergeData->currentRule = resolved;
00939         return mergeData;
00940 }
00941 
00942 gint
00943 qof_book_merge_commit(QofBookMergeData *mergeData )
00944 {
00945         QofBookMergeRule *currentRule;
00946         GList *check, *node;
00947 
00948         g_return_val_if_fail(mergeData != NULL, -1);
00949         g_return_val_if_fail(mergeData->mergeList != NULL, -1);
00950         g_return_val_if_fail(mergeData->targetBook != NULL, -1);
00951         if(mergeData->abort == TRUE) return -1;
00952         check = g_list_copy(mergeData->mergeList);
00953         g_return_val_if_fail(check != NULL, -1);
00954         for (node = check; node != NULL; node = node->next)
00955         {
00956                 currentRule = node->data;

void qof_book_merge_rule_foreach QofBookMergeData mergeData,
QofBookMergeRuleForeachCB  callback,
QofBookMergeResult  mergeResult
 

Dialogue Control Callback.

This function is designed to be used to iterate over all rules tagged with a specific QofBookMergeResult value.

Parameters:
callback external loop of type QofBookMergeRuleForeachCB
mergeResult QofBookMergeResult value to look up.
mergeData QofBookMergeData merge context.
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.

Definition at line 1044 of file qofbookmerge.c.

QofBookMergeData* qof_book_merge_update_result QofBookMergeData mergeData,
QofBookMergeResult  tag
 

called by dialogue callback to set the result of user intervention

Set 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 cannot be undone and that a backup copy should always be available before a merge is initialised.

Recommended method: Only offer three options to the user per rule:

  1. Allow import data to be merged into target data
    • change MERGE_REPORT to MERGE_UPDATE
  2. Allow import data without an exact match to be added as new
    • change MERGE_REPORT to MERGE_NEW IF mergeAbsolute = FALSE
  3. 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.
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.
An entity 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.

  1. 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.
  2. 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.
  3. MERGE_INVALID will cause an abort of the merge process.
  4. MERGE_UNDEF and MERGE_REPORT cannot be set - the entity result will be unchanged.
  5. MERGE_DUPLICATE and 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.
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.

Parameters:
mergeData the merge context, QofBookMergeData*
tag the result to attempt to set, QofBookMergeResult
Returns:
-1 if supplied parameters are invalid or NULL, 0 on success.

Definition at line 959 of file qofbookmerge.c.

00962                                                              {
00963                         g_list_free(check);
00964                         return 1;
00965                 }
00966         }
00967         g_list_free(check);
00968         qof_book_merge_commit_foreach(qof_book_merge_commit_rule_loop, 
00969         MERGE_NEW, mergeData);
00970         qof_book_merge_commit_foreach(qof_book_merge_commit_rule_loop, 
00971         MERGE_UPDATE, mergeData);
00972         /* Placeholder for QofObject merge_helper_cb - all objects
00973         and all parameters set */
00974         while(mergeData->mergeList != NULL) {
00975                 currentRule = mergeData->mergeList->data;
00976                 g_slist_free(currentRule->mergeParam);
00977                 g_slist_free(currentRule->linkedEntList);
00978                 mergeData->mergeList = g_list_next(mergeData->mergeList);
00979         }
00980         g_list_free(mergeData->mergeList);
00981         g_slist_free(mergeData->mergeObjectParams);
00982         g_slist_free(mergeData->targetList);
00983         if(mergeData->orphan_list != NULL) { g_slist_free(mergeData->orphan_list); }
00984         g_hash_table_destroy(mergeData->target_table);
00985         g_free(mergeData);
00986         return 0;
00987 }
00988 
00989 void 
00990 qof_book_merge_rule_foreach(QofBookMergeData *mergeData, 
00991                                                         QofBookMergeRuleForeachCB cb, 
00992                                                         QofBookMergeResult mergeResult )
00993 {
00994         struct QofBookMergeRuleIterate iter;


Generated on Fri May 12 18:00:34 2006 for QOF by  doxygen 1.4.4