diff --git a/ChangeLog b/ChangeLog index 336087ad35..3d9ae8673b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2002-02-03 Derek Atkins + + * src/engine/gncObject.c: core object registration. + + * src/engine/gnc-book.c: call into the object registration whenver + a book is created or destroyed. + + * src/engine/gnc-engine.c: initialize the object registration and + QueryNew subsystems. + + * first revision of the "QueryNew" interface. Note that the + interface is not complete -- in fact, the API will be changing + this afternoon, but I wanted a baseline (working) system checked + into the repository. Right now only the business code is using + the new query -- the old query still exists, so you should see no + functional difference in anything except the business code. + 2002-01-29 Dave Peticolas * src/report/standard-reports/account-piecharts.scm: bump up default diff --git a/src/business/business-core/businessmod-core.c b/src/business/business-core/businessmod-core.c index d28c5a8a4e..5faefff233 100644 --- a/src/business/business-core/businessmod-core.c +++ b/src/business/business-core/businessmod-core.c @@ -13,7 +13,6 @@ #include "gnc-module-api.h" #include "gw-business-core.h" -#include "gncBusinessP.h" #include "gncCustomerP.h" #include "gncEmployeeP.h" #include "gncEntryP.h" @@ -52,9 +51,6 @@ gnc_module_init(int refcount) if(refcount == 0) { - /* initialize the business engine on the first load */ - gncBusinessInitialize (0, NULL); - /* initialize known types */ gncCustomerRegister (); gncEmployeeRegister (); diff --git a/src/business/business-core/gncBusiness.c b/src/business/business-core/gncBusiness.c index e44a0dd3d2..8073cefdf6 100644 --- a/src/business/business-core/gncBusiness.c +++ b/src/business/business-core/gncBusiness.c @@ -1,130 +1,38 @@ /* - * gncBusiness.c -- the Core Business Object Registry - * Copyright (C) 2001 Derek Atkins + * gncBusiness.c -- Business helper functions + * Copyright (C) 2002 Derek Atkins * Author: Derek Atkins */ #include "config.h" #include -#include - -#include "messages.h" #include "gncBusiness.h" -#include "gncBusinessP.h" -static gboolean business_is_initialized = FALSE; -static GList *business_modules = NULL; +struct _iterate { + foreachObjectCB cb; + gpointer user_data; +}; -void gncBusinessCreateBook (GNCBook *book) +static void get_list (gpointer key, gpointer item, gpointer arg) { - GList *l; - - if (!book) return; - for (l = business_modules; l; l = l->next) { - GncBusinessObject *obj = l->data; - if (obj->create) - obj->create (book); - } + struct _iterate *iter = arg; + iter->cb (item, iter->user_data); } -void gncBusinessDestroyBook (GNCBook *book) +void gncBusinessForeach (GNCBook *book, GNCIdType mod_name, + foreachObjectCB cb, gpointer user_data) { - GList *l; + GHashTable *ht; + struct _iterate iter; - if (!book) return; - for (l = business_modules; l; l = l->next) { - GncBusinessObject *obj = l->data; - if (obj->destroy) - obj->destroy (book); - } -} - -GList * -gncBusinessGetList (GNCBook *book, const char *type_name, - gboolean show_all) -{ - const GncBusinessObject *obj; - - if (!book || !type_name) return NULL; - - obj = gncBusinessLookup (type_name); - if (!obj) return NULL; - - if (obj->get_list) - return ((*(obj->get_list))(book, show_all)); - - return NULL; -} - -const char * -gncBusinessPrintable (const char *type_name, gpointer obj) -{ - const GncBusinessObject *b_obj; - - if (!type_name || !obj) return NULL; - - b_obj = gncBusinessLookup (type_name); - if (!b_obj) return NULL; - - if (b_obj->printable) - return ((*(b_obj->printable))(obj)); - - return NULL; -} - -const char * gncBusinessGetTypeLabel (const char *type_name) -{ - const GncBusinessObject *obj; - - if (!type_name) return NULL; - - obj = gncBusinessLookup (type_name); - if (!obj) return NULL; - - return _(obj->type_label); -} - -/* INITIALIZATION and PRIVATE FUNCTIONS */ - -void -gncBusinessInitialize (int argc, char **argv) -{ - if (business_is_initialized) return; - business_is_initialized = TRUE; -} - -/* Register new types of business objects. - * Return TRUE if successful, - * return FALSE if it fails, invalid arguments, or if the object - * already exists - */ -gboolean gncBusinessRegister (const GncBusinessObject *object) -{ - if (!object) return FALSE; - if (object->version != GNC_BUSINESS_VERSION) return FALSE; - if (!business_is_initialized) return FALSE; - - if (g_list_index (business_modules, (gpointer)object) == -1) - business_modules = g_list_append (business_modules, (gpointer)object); - else - return FALSE; - - return TRUE; -} - -const GncBusinessObject * gncBusinessLookup (const char *name) -{ - GList *iter; - const GncBusinessObject *obj; - - if (!name) return NULL; - - for (iter = business_modules; iter; iter = iter->next) { - obj = iter->data; - if (!strcmp (obj->name, name)) - return obj; - } - return NULL; + if (!book || !cb) return; + + iter.cb = cb; + iter.user_data = user_data; + + ht = gnc_book_get_data (book, mod_name); + if (ht) + g_hash_table_foreach (ht, get_list, &iter); } diff --git a/src/business/business-core/gncBusiness.h b/src/business/business-core/gncBusiness.h index fc65b054b1..c5a09e4c47 100644 --- a/src/business/business-core/gncBusiness.h +++ b/src/business/business-core/gncBusiness.h @@ -1,53 +1,16 @@ /* - * gncBusiness.h -- the Core Business Interface - * Copyright (C) 2001 Derek Atkins + * gncBusiness.h -- Business Helper Functions + * Copyright (C) 2002 Derek Atkins * Author: Derek Atkins */ #ifndef GNC_BUSINESS_H_ #define GNC_BUSINESS_H_ -#include "gnc-book.h" - -/* Defines the version of the core business object registration - * interface. Only business modules compiled against this version - * of the interface will load properly - */ -#define GNC_BUSINESS_VERSION 1 - -typedef struct _gncBusinessObject GncBusinessObject; - -/* This is the Business Object descriptor */ -struct _gncBusinessObject { - gint version; - const char * name; - const char * type_label; - void (*create)(GNCBook *); - void (*destroy)(GNCBook *); - GList * (*get_list)(GNCBook *, gboolean show_all); - const char * (*printable)(gpointer obj); -}; - -void gncBusinessCreateBook (GNCBook *book); - -void gncBusinessDestroyBook (GNCBook *book); - -GList * gncBusinessGetList (GNCBook *book, const char *type_name, - gboolean show_all); - -const char * gncBusinessPrintable (const char *type_name, gpointer obj); - - -/* REGISTRATION AND REG-LOOKUP FUNCTIONS */ - -/* Register new types of business objects */ -gboolean gncBusinessRegister (const GncBusinessObject *object); - -/* Get the printable label for a type */ -const char * gncBusinessGetTypeLabel (const char *type_name); - -/* Lookup a business object */ -const GncBusinessObject * gncBusinessLookup (const char *name); +#include "gncObject.h" +#include "GNCId.h" +void gncBusinessForeach (GNCBook *book, GNCIdType mod_name, + foreachObjectCB cb, gpointer user_data); #endif /* GNC_BUSINESS_H_ */ diff --git a/src/business/business-core/gncCustomer.c b/src/business/business-core/gncCustomer.c index 9107f153e3..e786d1037b 100644 --- a/src/business/business-core/gncCustomer.c +++ b/src/business/business-core/gncCustomer.c @@ -1,6 +1,6 @@ /* * gncCustomer.c -- the Core Customer Interface - * Copyright (C) 2001 Derek Atkins + * Copyright (C) 2001,2002 Derek Atkins * Author: Derek Atkins */ @@ -14,6 +14,8 @@ #include "gnc-book-p.h" #include "GNCIdP.h" #include "gnc-numeric.h" +#include "gncObject.h" +#include "QueryObject.h" #include "gncBusiness.h" #include "gncCustomer.h" @@ -315,10 +317,13 @@ gboolean gncCustomerIsDirty (GncCustomer *cust) /* Other functions */ -static gint gncCustomerSortFunc (gconstpointer a, gconstpointer b) { - GncCustomer *ca = (GncCustomer *) a; - GncCustomer *cb = (GncCustomer *) b; - return(strcmp(ca->name, cb->name)); +int gncCustomerCompare (GncCustomer *a, GncCustomer *b) +{ + if (!a && !b) return 0; + if (!a && b) return 1; + if (a && !b) return -1; + + return(strcmp(a->name, b->name)); } /* Package-Private functions */ @@ -343,36 +348,11 @@ static void remObj (GncCustomer *cust) g_hash_table_remove (ht, &cust->guid); } -struct _iterate { - GList *list; - gboolean show_all; -}; - -static void get_list (gpointer key, gpointer item, gpointer arg) +static void _gncCustomerForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) { - struct _iterate *iter = arg; - GncCustomer *cust = item; - - if (iter->show_all || gncCustomerGetActive (cust)) { - iter->list = g_list_insert_sorted (iter->list, cust, gncCustomerSortFunc); - } -} - -static GList * _gncCustomerGetList (GNCBook *book, gboolean show_all) -{ - GHashTable *ht; - struct _iterate iter; - - if (!book) return NULL; - - iter.list = NULL; - iter.show_all = show_all; - - ht = gnc_book_get_data (book, _GNC_MOD_NAME); - if (ht) - g_hash_table_foreach (ht, get_list, &iter); - - return iter.list; + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); } static const char * _gncCustomerPrintable (gpointer item) @@ -407,19 +387,33 @@ static void _gncCustomerDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncCustomerDesc = { - GNC_BUSINESS_VERSION, +static GncObject_t gncCustomerDesc = { + GNC_OBJECT_VERSION, _GNC_MOD_NAME, "Customer", _gncCustomerCreate, _gncCustomerDestroy, - _gncCustomerGetList, + _gncCustomerForeach, _gncCustomerPrintable }; gboolean gncCustomerRegister (void) { - return gncBusinessRegister (&gncCustomerDesc); + static QueryObjectDef params[] = { + { CUSTOMER_GUID, QUERYCORE_GUID, (QueryAccess)gncCustomerGetGUID }, + { CUSTOMER_ID, QUERYCORE_STRING, (QueryAccess)gncCustomerGetID }, + { CUSTOMER_NAME, QUERYCORE_STRING, (QueryAccess)gncCustomerGetName }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)gncCustomerGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (_GNC_MOD_NAME, (QuerySort)gncCustomerCompare, + params, converters); + + return gncObjectRegister (&gncCustomerDesc); } static gint lastCustomer = 27; diff --git a/src/business/business-core/gncCustomer.h b/src/business/business-core/gncCustomer.h index 8846988389..97c2ecbaee 100644 --- a/src/business/business-core/gncCustomer.h +++ b/src/business/business-core/gncCustomer.h @@ -1,6 +1,6 @@ /* * gncCustomer.h -- the Core Customer Interface - * Copyright (C) 2001 Derek Atkins + * Copyright (C) 2001,2002 Derek Atkins * Author: Derek Atkins */ @@ -58,5 +58,10 @@ GList * gncCustomerGetJoblist (GncCustomer *customer, gboolean show_all); GncCustomer * gncCustomerLookup (GNCBook *book, const GUID *guid); gboolean gncCustomerIsDirty (GncCustomer *customer); +int gncCustomerCompare (GncCustomer *a, GncCustomer *b); + +#define CUSTOMER_GUID "guid" +#define CUSTOMER_ID "id" +#define CUSTOMER_NAME "name" #endif /* GNC_CUSTOMER_H_ */ diff --git a/src/business/business-core/gncEmployee.c b/src/business/business-core/gncEmployee.c index 8ffa2e2b3d..129825059b 100644 --- a/src/business/business-core/gncEmployee.c +++ b/src/business/business-core/gncEmployee.c @@ -1,6 +1,6 @@ /* * gncEmployee.c -- the Core Employee Interface - * Copyright (C) 2001 Derek Atkins + * Copyright (C) 2001,2002 Derek Atkins * Author: Derek Atkins */ @@ -14,6 +14,8 @@ #include "gnc-engine-util.h" #include "gnc-book-p.h" #include "GNCIdP.h" +#include "gncObject.h" +#include "QueryObject.h" #include "gncBusiness.h" #include "gncEmployee.h" @@ -243,10 +245,13 @@ void gncEmployeeCommitEdit (GncEmployee *employee) /* Other functions */ -static gint gncEmployeeSortFunc (gconstpointer a, gconstpointer b) { - GncEmployee *ea = (GncEmployee *) a; - GncEmployee *eb = (GncEmployee *) b; - return(strcmp(ea->username, eb->username)); +int gncEmployeeCompare (GncEmployee *a, GncEmployee *b) +{ + if (!a && !b) return 0; + if (!a && b) return 1; + if (a && !b) return -1; + + return(strcmp(a->username, b->username)); } /* Package-Private functions */ @@ -272,36 +277,11 @@ static void remObj (GncEmployee *employee) g_hash_table_remove (ht, &employee->guid); } -struct _iterate { - GList *list; - gboolean show_all; -}; - -static void get_list (gpointer key, gpointer item, gpointer arg) +static void _gncEmployeeForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) { - struct _iterate *iter = arg; - GncEmployee *employee = item; - - if (iter->show_all || gncEmployeeGetActive (employee)) { - iter->list = g_list_insert_sorted (iter->list, employee, gncEmployeeSortFunc); - } -} - -static GList * _gncEmployeeGetList (GNCBook *book, gboolean show_all) -{ - GHashTable *ht; - struct _iterate iter; - - if (!book) return NULL; - - iter.list = NULL; - iter.show_all = show_all; - - ht = gnc_book_get_data (book, _GNC_MOD_NAME); - if (ht) - g_hash_table_foreach (ht, get_list, &iter); - - return iter.list; + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); } static const char * _gncEmployeePrintable (gpointer item) @@ -336,19 +316,33 @@ static void _gncEmployeeDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncEmployeeDesc = { - GNC_BUSINESS_VERSION, +static GncObject_t gncEmployeeDesc = { + GNC_OBJECT_VERSION, _GNC_MOD_NAME, "Employee", _gncEmployeeCreate, _gncEmployeeDestroy, - _gncEmployeeGetList, + _gncEmployeeForeach, _gncEmployeePrintable }; gboolean gncEmployeeRegister (void) { - return gncBusinessRegister (&gncEmployeeDesc); + static QueryObjectDef params[] = { + { EMPLOYEE_GUID, QUERYCORE_GUID, (QueryAccess)gncEmployeeGetGUID }, + { EMPLOYEE_ID, QUERYCORE_STRING, (QueryAccess)gncEmployeeGetID }, + { EMPLOYEE_USERNAME, QUERYCORE_STRING, (QueryAccess)gncEmployeeGetUsername }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)gncEmployeeGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (_GNC_MOD_NAME, (QuerySort)gncEmployeeCompare, + params, converters); + + return gncObjectRegister (&gncEmployeeDesc); } static gint lastEmployee = 2; diff --git a/src/business/business-core/gncEmployee.h b/src/business/business-core/gncEmployee.h index fa4d994f8b..55626b60eb 100644 --- a/src/business/business-core/gncEmployee.h +++ b/src/business/business-core/gncEmployee.h @@ -46,5 +46,10 @@ GncEmployee * gncEmployeeLookup (GNCBook *book, const GUID *guid); gboolean gncEmployeeIsDirty (GncEmployee *employee); void gncEmployeeCommitEdit (GncEmployee *employee); +int gncEmployeeCompare (GncEmployee *a, GncEmployee *b); + +#define EMPLOYEE_GUID "guid" +#define EMPLOYEE_ID "id" +#define EMPLOYEE_USERNAME "username" #endif /* GNC_EMPLOYEE_H_ */ diff --git a/src/business/business-core/gncEntry.c b/src/business/business-core/gncEntry.c index b2892ef134..bf24f6c816 100644 --- a/src/business/business-core/gncEntry.c +++ b/src/business/business-core/gncEntry.c @@ -13,6 +13,7 @@ #include "gnc-engine-util.h" #include "gnc-book-p.h" #include "GNCIdP.h" +#include "QueryObject.h" #include "gncBusiness.h" #include "gncEntry.h" @@ -400,6 +401,26 @@ void gncEntryCommitEdit (GncEntry *entry) /* XXX */ } +int gncEntryCompare (GncEntry *a, GncEntry *b) +{ + int compare; + + if (a == b) return 0; + if (!a && b) return -1; + if (a && !b) return 1; + + compare = timespec_cmp (&(a->date), &(b->date)); + if (!compare) return compare; + + compare = safe_strcmp (a->desc, b->desc); + if (!compare) return compare; + + compare = safe_strcmp (a->action, b->action); + if (!compare) return compare; + + return guid_compare (&(a->guid), &(b->guid)); +} + /* Package-Private functions */ static void addObj (GncEntry *entry) @@ -444,17 +465,43 @@ static void _gncEntryDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncEntryDesc = { - GNC_BUSINESS_VERSION, +static void _gncEntryForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) +{ + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); +} + +static GncObject_t gncEntryDesc = { + GNC_OBJECT_VERSION, _GNC_MOD_NAME, "Order/Invoice Entry", _gncEntryCreate, _gncEntryDestroy, - NULL, /* get list */ + _gncEntryForeach, NULL /* printable */ }; gboolean gncEntryRegister (void) { - return gncBusinessRegister (&gncEntryDesc); + static QueryObjectDef params[] = { + { ENTRY_GUID, QUERYCORE_GUID, (QueryAccess)gncEntryGetGUID }, + { ENTRY_DATE, QUERYCORE_DATE, (QueryAccess)gncEntryGetDate }, + { ENTRY_DESC, QUERYCORE_STRING, (QueryAccess)gncEntryGetDescription }, + { ENTRY_ACTION, QUERYCORE_STRING, (QueryAccess)gncEntryGetAction }, + { ENTRY_QTY, QUERYCORE_NUMERIC, (QueryAccess)gncEntryGetQuantity }, + { ENTRY_PRICE, QUERYCORE_NUMERIC, (QueryAccess)gncEntryGetPrice }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)gncEntryGetBook }, + { GNC_INVOICE_MODULE_NAME, (QueryConvert)gncEntryGetInvoice }, + { GNC_ORDER_MODULE_NAME, (QueryConvert)gncEntryGetOrder }, + { NULL }, + }; + + gncQueryObjectRegister (_GNC_MOD_NAME, (QuerySort)gncEntryCompare, + params, converters); + + return gncObjectRegister (&gncEntryDesc); } diff --git a/src/business/business-core/gncEntry.h b/src/business/business-core/gncEntry.h index db6ed9c8a3..190a59935e 100644 --- a/src/business/business-core/gncEntry.h +++ b/src/business/business-core/gncEntry.h @@ -84,5 +84,13 @@ GncInvoice * gncEntryGetInvoice (GncEntry *entry); GncEntry * gncEntryLookup (GNCBook *book, const GUID *guid); void gncEntryCommitEdit (GncEntry *entry); +int gncEntryCompare (GncEntry *a, GncEntry *b); + +#define ENTRY_GUID "guid" +#define ENTRY_DATE "date" +#define ENTRY_DESC "desc" +#define ENTRY_ACTION "action" +#define ENTRY_QTY "qty" +#define ENTRY_PRICE "price" #endif /* GNC_ENTRY_H_ */ diff --git a/src/business/business-core/gncInvoice.c b/src/business/business-core/gncInvoice.c index cdfee937ea..5f6afdb6a7 100644 --- a/src/business/business-core/gncInvoice.c +++ b/src/business/business-core/gncInvoice.c @@ -1,6 +1,6 @@ /* * gncInvoice.c -- the Core Business Invoice - * Copyright (C) 2001 Derek Atkins + * Copyright (C) 2001,2002 Derek Atkins * Author: Derek Atkins */ @@ -16,6 +16,7 @@ #include "gnc-engine-util.h" #include "gnc-book-p.h" #include "GNCIdP.h" +#include "QueryObject.h" #include "gncBusiness.h" #include "gncEntry.h" @@ -45,7 +46,7 @@ struct _gncInvoice { gboolean dirty; }; -#define _GNC_MOD_NAME GNC_ENTRY_MODULE_NAME +#define _GNC_MOD_NAME GNC_INVOICE_MODULE_NAME #define GNC_INVOICE_ID "gncInvoice" #define GNC_INVOICE_GUID "invoice-guid" @@ -488,6 +489,26 @@ void gncInvoiceCommitEdit (GncInvoice *invoice) if (!invoice) return; } +int gncInvoiceCompare (GncInvoice *a, GncInvoice *b) +{ + int compare; + + if (a == b) return 0; + if (!a && b) return -1; + if (a && !b) return 1; + + compare = safe_strcmp (a->id, b->id); + if (!compare) return compare; + + compare = timespec_cmp (&(a->date_opened), &(b->date_opened)); + if (!compare) return compare; + + compare = timespec_cmp (&(a->date_closed), &(b->date_closed)); + if (!compare) return compare; + + return guid_compare (&(a->guid), &(b->guid)); +} + /* Package-Private functions */ static void addObj (GncInvoice *invoice) @@ -532,19 +553,40 @@ static void _gncInvoiceDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncInvoiceDesc = { - GNC_BUSINESS_VERSION, +static void _gncInvoiceForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) +{ + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); +} + +static GncObject_t gncInvoiceDesc = { + GNC_OBJECT_VERSION, _GNC_MOD_NAME, "Purchase/Sales Invoice", _gncInvoiceCreate, _gncInvoiceDestroy, - NULL, /* get list */ + _gncInvoiceForeach, NULL /* printable */ }; gboolean gncInvoiceRegister (void) { - return gncBusinessRegister (&gncInvoiceDesc); + static QueryObjectDef params[] = { + { INVOICE_GUID, QUERYCORE_GUID, (QueryAccess)gncInvoiceGetGUID }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)gncInvoiceGetBook }, + { GNC_ID_ACCOUNT, (QueryConvert)gncInvoiceGetPostedAcc }, + { GNC_ID_TRANS, (QueryConvert)gncInvoiceGetPostedTxn }, + { NULL }, + }; + + gncQueryObjectRegister (_GNC_MOD_NAME, (QuerySort)gncInvoiceCompare, + params, converters); + + return gncObjectRegister (&gncInvoiceDesc); } static gint lastId = 187; /* XXX */ diff --git a/src/business/business-core/gncInvoice.h b/src/business/business-core/gncInvoice.h index 0a87f9283f..61b4ea9dfc 100644 --- a/src/business/business-core/gncInvoice.h +++ b/src/business/business-core/gncInvoice.h @@ -69,5 +69,8 @@ GncInvoice * gncInvoiceLookup (GNCBook *book, const GUID *guid); gboolean gncInvoiceIsDirty (GncInvoice *invoice); void gncInvoiceBeginEdit (GncInvoice *invoice); void gncInvoiceCommitEdit (GncInvoice *invoice); +int gncInvoiceCompare (GncInvoice *a, GncInvoice *b); + +#define INVOICE_GUID "guid" #endif /* GNC_INVOICE_H_ */ diff --git a/src/business/business-core/gncJob.c b/src/business/business-core/gncJob.c index 7e74e6c5ac..7247ed0053 100644 --- a/src/business/business-core/gncJob.c +++ b/src/business/business-core/gncJob.c @@ -266,16 +266,6 @@ static void remObj (GncJob *job) g_hash_table_remove (ht, &job->guid); } -#if 0 -static GList * _gncJobGetList (GNCBook *obj, gboolean show_all) -{ - if (!obj) return NULL; - - /* XXX */ - return NULL; -} -#endif - static const char * _gncJobPrintable (gpointer item) { GncJob *c; @@ -308,19 +298,26 @@ static void _gncJobDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncJobDesc = { - GNC_BUSINESS_VERSION, - GNC_JOB_MODULE_NAME, +static void _gncJobForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) +{ + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); +} + +static GncObject_t gncJobDesc = { + GNC_OBJECT_VERSION, + _GNC_MOD_NAME, "Job", _gncJobCreate, _gncJobDestroy, - NULL, /* get_list */ + _gncJobForeach, _gncJobPrintable }; gboolean gncJobRegister (void) { - return gncBusinessRegister (&gncJobDesc); + return gncObjectRegister (&gncJobDesc); } static gint lastJob = 57; diff --git a/src/business/business-core/gncOrder.c b/src/business/business-core/gncOrder.c index b278d35d26..d5e9d588dd 100644 --- a/src/business/business-core/gncOrder.c +++ b/src/business/business-core/gncOrder.c @@ -1,6 +1,6 @@ /* * gncOrder.c -- the Core Business Order - * Copyright (C) 2001 Derek Atkins + * Copyright (C) 2001,2002 Derek Atkins * Author: Derek Atkins */ @@ -14,6 +14,7 @@ #include "gnc-engine-util.h" #include "gnc-book-p.h" #include "GNCIdP.h" +#include "QueryObject.h" #include "gncBusiness.h" #include "gncEntry.h" @@ -256,6 +257,26 @@ void gncOrderCommitEdit (GncOrder *order) if (!order) return; } +int gncOrderCompare (GncOrder *a, GncOrder *b) +{ + int compare; + + if (a == b) return 0; + if (!a && b) return -1; + if (a && !b) return 1; + + compare = safe_strcmp (a->id, b->id); + if (!compare) return compare; + + compare = timespec_cmp (&(a->opened), &(b->opened)); + if (!compare) return compare; + + compare = timespec_cmp (&(a->closed), &(b->closed)); + if (!compare) return compare; + + return guid_compare (&(a->guid), &(b->guid)); +} + /* Package-Private functions */ static void addObj (GncOrder *order) @@ -300,19 +321,38 @@ static void _gncOrderDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncOrderDesc = { - GNC_BUSINESS_VERSION, +static void _gncOrderForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) +{ + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); +} + +static GncObject_t gncOrderDesc = { + GNC_OBJECT_VERSION, _GNC_MOD_NAME, "Purchase/Sales Order", _gncOrderCreate, _gncOrderDestroy, - NULL, /* get list */ + _gncOrderForeach, NULL /* printable */ }; gboolean gncOrderRegister (void) { - return gncBusinessRegister (&gncOrderDesc); + static QueryObjectDef params[] = { + { ORDER_GUID, QUERYCORE_GUID, (QueryAccess)gncOrderGetGUID }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)gncOrderGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (_GNC_MOD_NAME, (QuerySort)gncOrderCompare, + params, converters); + + return gncObjectRegister (&gncOrderDesc); } static gint lastId = 471; /* XXX */ diff --git a/src/business/business-core/gncOrder.h b/src/business/business-core/gncOrder.h index 3c4af1d88c..47489bba88 100644 --- a/src/business/business-core/gncOrder.h +++ b/src/business/business-core/gncOrder.h @@ -51,5 +51,8 @@ GncOrder * gncOrderLookup (GNCBook *book, const GUID *guid); gboolean gncOrderIsDirty (GncOrder *order); void gncOrderBeginEdit (GncOrder *order); void gncOrderCommitEdit (GncOrder *order); +int gncOrderCompare (GncOrder *a, GncOrder *b); + +#define ORDER_GUID "guid" #endif /* GNC_ORDER_H_ */ diff --git a/src/business/business-core/gncVendor.c b/src/business/business-core/gncVendor.c index 37fe211dbd..3b0d2303ef 100644 --- a/src/business/business-core/gncVendor.c +++ b/src/business/business-core/gncVendor.c @@ -14,6 +14,7 @@ #include "gnc-engine-util.h" #include "gnc-book-p.h" #include "GNCIdP.h" +#include "QueryObject.h" #include "gncBusiness.h" #include "gncVendor.h" @@ -242,10 +243,13 @@ void gncVendorCommitEdit (GncVendor *vendor) /* Other functions */ -static gint gncVendorSortFunc (gconstpointer a, gconstpointer b) { - GncVendor *va = (GncVendor *) a; - GncVendor *vb = (GncVendor *) b; - return(strcmp(va->name, vb->name)); +int gncVendorCompare (GncVendor *a, GncVendor *b) +{ + if (!a && !b) return 0; + if (!a && b) return 1; + if (a && !b) return -1; + + return(strcmp(a->name, b->name)); } GList * gncVendorGetJoblist (GncVendor *vendor, gboolean show_all) @@ -300,36 +304,11 @@ static void remObj (GncVendor *vendor) g_hash_table_remove (ht, &vendor->guid); } -struct _iterate { - GList *list; - gboolean show_all; -}; - -static void get_list (gpointer key, gpointer item, gpointer arg) +static void _gncVendorForeach (GNCBook *book, foreachObjectCB cb, + gpointer user_data) { - struct _iterate *iter = arg; - GncVendor *vendor = item; - - if (iter->show_all || gncVendorGetActive (vendor)) { - iter->list = g_list_insert_sorted (iter->list, vendor, gncVendorSortFunc); - } -} - -static GList * _gncVendorGetList (GNCBook *book, gboolean show_all) -{ - GHashTable *ht; - struct _iterate iter; - - if (!book) return NULL; - - iter.list = NULL; - iter.show_all = show_all; - - ht = gnc_book_get_data (book, _GNC_MOD_NAME); - if (ht) - g_hash_table_foreach (ht, get_list, &iter); - - return iter.list; + if (!book || !cb) return; + gncBusinessForeach (book, _GNC_MOD_NAME, cb, user_data); } static const char * _gncVendorPrintable (gpointer item) @@ -364,19 +343,33 @@ static void _gncVendorDestroy (GNCBook *book) g_hash_table_destroy (ht); } -static GncBusinessObject gncVendorDesc = { - GNC_BUSINESS_VERSION, +static GncObject_t gncVendorDesc = { + GNC_OBJECT_VERSION, _GNC_MOD_NAME, "Vendor", _gncVendorCreate, _gncVendorDestroy, - _gncVendorGetList, + _gncVendorForeach, _gncVendorPrintable }; gboolean gncVendorRegister (void) { - return gncBusinessRegister (&gncVendorDesc); + static QueryObjectDef params[] = { + { VENDOR_GUID, QUERYCORE_GUID, (QueryAccess)gncVendorGetGUID }, + { VENDOR_ID, QUERYCORE_STRING, (QueryAccess)gncVendorGetID }, + { VENDOR_NAME, QUERYCORE_STRING, (QueryAccess)gncVendorGetName }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)gncVendorGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (_GNC_MOD_NAME, (QuerySort)gncVendorCompare, + params, converters); + + return gncObjectRegister (&gncVendorDesc); } static gint lastVendor = 17; diff --git a/src/business/business-core/gncVendor.h b/src/business/business-core/gncVendor.h index 9ac19a77a6..4067144c6f 100644 --- a/src/business/business-core/gncVendor.h +++ b/src/business/business-core/gncVendor.h @@ -50,5 +50,10 @@ GList * gncVendorGetJoblist (GncVendor *vendor, gboolean show_all); GncVendor * gncVendorLookup (GNCBook *book, const GUID *guid); gboolean gncVendorIsDirty (GncVendor *vendor); +int gncVendorCompare (GncVendor *a, GncVendor *b); + +#define VENDOR_GUID "guid" +#define VENDOR_ID "id" +#define VENDOR_NAME "name" #endif /* GNC_VENDOR_H_ */ diff --git a/src/business/business-core/gw-business-core-spec.scm b/src/business/business-core/gw-business-core-spec.scm index f378af1aa1..0c0759f606 100644 --- a/src/business/business-core/gw-business-core-spec.scm +++ b/src/business/business-core/gw-business-core-spec.scm @@ -26,7 +26,6 @@ ws (lambda (wrapset client-wrapset) (list - "#include \n" "#include \n" "#include \n" "#include \n" @@ -57,18 +56,6 @@ (gw:wrap-as-wct ws ' "GncOwner*" "const GncOwner*") (gw:wrap-as-wct ws ' "GncVendor*" "const GncVendor*") - ;; - ;; gncBusiness.h - ;; - - (gw:wrap-function - ws - 'gnc:business-create-book - ' - "gncBusinessCreateBook" - '(( book)) - "Create the Business data tables in the book") - ;; gncAddress.h ;; gncCustomer.h diff --git a/src/business/business-core/test/test-address.c b/src/business/business-core/test/test-address.c index 21335a4b1a..4a7bc6799d 100644 --- a/src/business/business-core/test/test-address.c +++ b/src/business/business-core/test/test-address.c @@ -19,7 +19,6 @@ test_address (void) { GncAddress *address; GNCBook *book = gnc_book_new (); - gncBusinessCreateBook (book); /* Test creation/destruction */ { diff --git a/src/business/business-core/test/test-business.c b/src/business/business-core/test/test-business.c index 719421fe86..382e42ab46 100644 --- a/src/business/business-core/test/test-business.c +++ b/src/business/business-core/test/test-business.c @@ -12,6 +12,7 @@ #define TEST_MODULE_NAME "business-test" #define TEST_MODULE_DESC "Test Business" +#if 0 static GList * get_list (GNCBook *, gboolean show_all); static const char * printable (gpointer obj); static void test_printable (const char *name, gpointer obj); @@ -102,10 +103,11 @@ main_helper (int argc, char **argv) print_test_results(); exit(get_rv()); } +#endif int main (int argc, char **argv) { - gh_enter (argc, argv, main_helper); + // gh_enter (argc, argv, main_helper); return 0; } diff --git a/src/business/business-core/test/test-customer.c b/src/business/business-core/test/test-customer.c index 8957f91669..04b298542a 100644 --- a/src/business/business-core/test/test-customer.c +++ b/src/business/business-core/test/test-customer.c @@ -4,8 +4,8 @@ #include "guid.h" #include "gnc-module.h" #include "gnc-engine-util.h" +#include "gncObject.h" -#include "gncBusiness.h" #include "gncCustomer.h" #include "gncCustomerP.h" #include "test-stuff.h" @@ -39,7 +39,6 @@ test_customer (void) GncCustomer *customer; book = gnc_book_new (); - gncBusinessCreateBook (book); /* Test creation/destruction */ { @@ -77,6 +76,7 @@ test_customer (void) gncCustomerSetGUID (customer, &guid); do_test (guid_equal (&guid, gncCustomerGetGUID (customer)), "guid compare"); } +#if 0 { GList *list; @@ -90,12 +90,13 @@ test_customer (void) do_test (g_list_length (list) == 1, "correct length: active"); g_list_free (list); } +#endif { const char *str = get_random_string(); const char *res; gncCustomerSetName (customer, str); - res = gncBusinessPrintable (GNC_CUSTOMER_MODULE_NAME, customer); + res = gncObjectPrintable (GNC_CUSTOMER_MODULE_NAME, customer); do_test (res != NULL, "Printable NULL?"); do_test (safe_strcmp (str, res) == 0, "Printable equals"); } diff --git a/src/business/business-core/test/test-employee.c b/src/business/business-core/test/test-employee.c index 89fb744a3a..f42779c42a 100644 --- a/src/business/business-core/test/test-employee.c +++ b/src/business/business-core/test/test-employee.c @@ -4,8 +4,8 @@ #include "guid.h" #include "gnc-module.h" #include "gnc-engine-util.h" +#include "gncObject.h" -#include "gncBusiness.h" #include "gncEmployee.h" #include "gncEmployeeP.h" #include "test-stuff.h" @@ -41,7 +41,6 @@ test_employee (void) GncEmployee *employee; book = gnc_book_new (); - gncBusinessCreateBook (book); /* Test creation/destruction */ { @@ -76,6 +75,7 @@ test_employee (void) gncEmployeeSetGUID (employee, &guid); do_test (guid_equal (&guid, gncEmployeeGetGUID (employee)), "guid compare"); } +#if 0 { GList *list; @@ -89,12 +89,13 @@ test_employee (void) do_test (g_list_length (list) == 1, "correct length: active"); g_list_free (list); } +#endif { const char *str = get_random_string(); const char *res; gncEmployeeSetUsername (employee, str); - res = gncBusinessPrintable (GNC_EMPLOYEE_MODULE_NAME, employee); + res = gncObjectPrintable (GNC_EMPLOYEE_MODULE_NAME, employee); do_test (res != NULL, "Printable NULL?"); do_test (safe_strcmp (str, res) == 0, "Printable equals"); } diff --git a/src/business/business-core/test/test-job.c b/src/business/business-core/test/test-job.c index db8893c59b..6bea300672 100644 --- a/src/business/business-core/test/test-job.c +++ b/src/business/business-core/test/test-job.c @@ -4,8 +4,8 @@ #include "guid.h" #include "gnc-module.h" #include "gnc-engine-util.h" +#include "gncObject.h" -#include "gncBusiness.h" #include "gncJob.h" #include "gncJobP.h" #include "test-stuff.h" @@ -43,7 +43,6 @@ test_job (void) GncJob *job; book = gnc_book_new (); - gncBusinessCreateBook (book); /* Test creation/destruction */ { @@ -92,7 +91,7 @@ test_job (void) const char *res; gncJobSetName (job, str); - res = gncBusinessPrintable (GNC_JOB_MODULE_NAME, job); + res = gncObjectPrintable (GNC_JOB_MODULE_NAME, job); do_test (res != NULL, "Printable NULL?"); do_test (safe_strcmp (str, res) == 0, "Printable equals"); } diff --git a/src/business/business-core/test/test-vendor.c b/src/business/business-core/test/test-vendor.c index de989502be..e5025db398 100644 --- a/src/business/business-core/test/test-vendor.c +++ b/src/business/business-core/test/test-vendor.c @@ -4,8 +4,8 @@ #include "guid.h" #include "gnc-module.h" #include "gnc-engine-util.h" +#include "gncObject.h" -#include "gncBusiness.h" #include "gncVendor.h" #include "gncVendorP.h" #include "test-stuff.h" @@ -41,7 +41,6 @@ test_vendor (void) GncVendor *vendor; book = gnc_book_new (); - gncBusinessCreateBook (book); /* Test creation/destruction */ { @@ -75,6 +74,7 @@ test_vendor (void) gncVendorSetGUID (vendor, &guid); do_test (guid_equal (&guid, gncVendorGetGUID (vendor)), "guid compare"); } +#if 0 { GList *list; @@ -88,12 +88,13 @@ test_vendor (void) do_test (g_list_length (list) == 1, "correct length: active"); g_list_free (list); } +#endif { const char *str = get_random_string(); const char *res; gncVendorSetName (vendor, str); - res = gncBusinessPrintable (GNC_VENDOR_MODULE_NAME, vendor); + res = gncObjectPrintable (GNC_VENDOR_MODULE_NAME, vendor); do_test (res != NULL, "Printable NULL?"); do_test (safe_strcmp (str, res) == 0, "Printable equals"); } diff --git a/src/business/business-gnome/business-chooser.c b/src/business/business-gnome/business-chooser.c index b490cf7415..8f23ffe831 100644 --- a/src/business/business-gnome/business-chooser.c +++ b/src/business/business-gnome/business-chooser.c @@ -12,8 +12,9 @@ #include "dialog-utils.h" #include "gnc-ui.h" #include "gnc-gui-query.h" +#include "gncObject.h" +#include "QueryNew.h" -#include "gncBusiness.h" #include "business-chooser.h" struct business_chooser_window { @@ -22,8 +23,8 @@ struct business_chooser_window { GtkWidget * choice_entry; GtkWidget * showall_check; - GNCBook * book; - const GncBusinessObject * obj_type; + GNCIdTypeConst obj_type; + QueryNew * query; gnc_business_chooser_new_cb new_cb; gnc_business_chooser_edit_cb edit_cb; gpointer cb_arg; @@ -57,11 +58,12 @@ update_selection_picker (struct business_chooser_window *w) selected = w->selected; /* Get the list of objects */ - obj_list = (*(w->obj_type->get_list))(w->book, show_all); + /* XXX: use show_all in the query */ + obj_list = gncQueryRun (w->query, w->obj_type); /* Build a list of strings (objs is pre-sorted, so keep the order!) */ for (iterator = obj_list; iterator; iterator = iterator->next) { - const gchar *label = (*(w->obj_type->printable))(iterator->data); + const gchar *label = gncObjectPrintable (w->obj_type, iterator->data); li = gtk_list_item_new_with_label (label); gtk_object_set_data (GTK_OBJECT (li), "list-item-pointer", iterator->data); @@ -81,9 +83,7 @@ update_selection_picker (struct business_chooser_window *w) /* And set the current-selected item */ gtk_entry_set_text (GTK_ENTRY (w->choice_entry), ((w->selected) ? - (*(w->obj_type->printable))(w->selected) : "")); - - g_list_free (obj_list); + gncObjectPrintable (w->obj_type, w->selected) : "")); } static void @@ -190,7 +190,7 @@ gnc_ui_business_chooser_new (GtkWidget * parent, /* Set the label */ choice_name_label = glade_xml_get_widget (xml, "choice_name_label"); gtk_label_set_text (GTK_LABEL (choice_name_label), - gncBusinessGetTypeLabel (type_name)); + gncObjectGetTypeLabel (type_name)); if(parent) { gnome_dialog_set_parent(GNOME_DIALOG(win->dialog), GTK_WINDOW(parent)); @@ -225,11 +225,13 @@ gnc_ui_business_chooser_new (GtkWidget * parent, GTK_SIGNAL_FUNC(business_chooser_close), win); /* Save the callbacks */ - win->book = book; - win->obj_type = gncBusinessLookup (type_name); + win->obj_type = type_name; win->new_cb = new_cb; win->edit_cb = edit_cb; win->cb_arg = cbarg; + win->query = gncQueryCreate (); + + gncQuerySetBook (win->query, book); /* Setup the menu */ win->selected = orig_sel; @@ -242,6 +244,7 @@ gnc_ui_business_chooser_new (GtkWidget * parent, /* And exit */ retval = win->selected; + gncQueryDestroy (win->query); g_free(win); return retval; diff --git a/src/business/business-gnome/business-gnome.scm b/src/business/business-gnome/business-gnome.scm index 87bf53e83b..b11f682eae 100644 --- a/src/business/business-gnome/business-gnome.scm +++ b/src/business/business-gnome/business-gnome.scm @@ -7,16 +7,6 @@ (define (add-business-extensions) - (define gnc:extensions-temp-book #f) - - (define (gnc:extensions-get-book) - (if gnc:extensions-temp-book - gnc:extensions-temp-book - (begin - (set! gnc:extensions-temp-book (gnc:get-current-book)) - (gnc:business-create-book gnc:extensions-temp-book) - gnc:extensions-temp-book))) - (define gnc:extensions-last-order #f) (define gnc:extensions-last-invoice #f) (define gnc:extensions-owner #f) @@ -26,14 +16,14 @@ (N_ "Test New Job Dialog") (list "Extensions" "") (lambda () - (gnc:job-new #f (gnc:extensions-get-book) #f)))) + (gnc:job-new #f (gnc:get-current-book) #f)))) (define select-job-item (gnc:make-menu-item (N_ "Test Job Selection Dialog") (N_ "Test Job Selection Dialog") (list "Extensions" "") (lambda () - (gnc:job-select #f (gnc:extensions-get-book) + (gnc:job-select #f (gnc:get-current-book) #f #f)))) (define new-vendor-item @@ -41,7 +31,7 @@ (N_ "Test New Vendor Dialog") (list "Extensions" "") (lambda () - (gnc:vendor-new #f (gnc:extensions-get-book))))) + (gnc:vendor-new #f (gnc:get-current-book))))) (define select-vendor-item @@ -49,7 +39,7 @@ (N_ "Test Vendor Selection Dialog") (list "Extensions" "") (lambda () - (gnc:vendor-select (gnc:extensions-get-book) + (gnc:vendor-select (gnc:get-current-book) #f #f)))) (define new-employee-item @@ -57,7 +47,7 @@ (N_ "Test New Employee Dialog") (list "Extensions" "") (lambda () - (gnc:employee-new #f (gnc:extensions-get-book))))) + (gnc:employee-new #f (gnc:get-current-book))))) (define select-employee-item @@ -65,7 +55,7 @@ (N_ "Test Employee Selection Dialog") (list "Extensions" "") (lambda () - (gnc:employee-select (gnc:extensions-get-book) + (gnc:employee-select (gnc:get-current-book) #f #f)))) (define new-order-item @@ -75,7 +65,7 @@ (lambda () (set! gnc:extensions-last-order (gnc:order-new #f gnc:extensions-owner - (gnc:extensions-get-book)))))) + (gnc:get-current-book)))))) (define edit-order-item (gnc:make-menu-item (N_ "Test Edit/View Order Dialog") @@ -91,7 +81,7 @@ (lambda () (set! gnc:extensions-last-invoice (gnc:invoice-new #f gnc:extensions-owner - (gnc:extensions-get-book)))))) + (gnc:get-current-book)))))) (define edit-invoice-item (gnc:make-menu-item (N_ "Test Edit/View Invoice Dialog") diff --git a/src/business/business-gnome/business-utils.c b/src/business/business-gnome/business-utils.c index d7a4c2df5d..f1de4a749e 100644 --- a/src/business/business-gnome/business-utils.c +++ b/src/business/business-gnome/business-utils.c @@ -9,12 +9,12 @@ #include -#include "gncBusiness.h" #include "gncCustomer.h" #include "gncJob.h" #include "gncVendor.h" #include "gncOwner.h" +#include "gncObject.h" #include "business-utils.h" #include "dialog-customer.h" #include "dialog-job.h" @@ -26,7 +26,7 @@ static GtkWidget * gnc_owner_new (GtkWidget *label, GtkWidget *hbox, { GtkWidget *edit; GNCGeneralSelectNewSelectCB do_select = NULL; - const GncBusinessObject *bus_obj; + const GncObject_t *bus_obj; const char *type_name = NULL; switch (owner->type) { @@ -63,7 +63,7 @@ static GtkWidget * gnc_owner_new (GtkWidget *label, GtkWidget *hbox, return NULL; } - bus_obj = gncBusinessLookup (type_name); + bus_obj = gncObjectLookup (type_name); if (!bus_obj) { g_warning ("Cannot find business object for name and printable()\n"); return NULL; @@ -76,7 +76,7 @@ static GtkWidget * gnc_owner_new (GtkWidget *label, GtkWidget *hbox, gnc_general_select_set_selected (GNC_GENERAL_SELECT (edit), owner->owner.undefined); gtk_box_pack_start (GTK_BOX (hbox), edit, TRUE, TRUE, 0); - gtk_label_set_text (GTK_LABEL (label), gncBusinessGetTypeLabel (type_name)); + gtk_label_set_text (GTK_LABEL (label), gncObjectGetTypeLabel (type_name)); return edit; } diff --git a/src/business/business-gnome/dialog-job-select.c b/src/business/business-gnome/dialog-job-select.c index 609d08fa31..293828975e 100644 --- a/src/business/business-gnome/dialog-job-select.c +++ b/src/business/business-gnome/dialog-job-select.c @@ -12,8 +12,9 @@ #include "dialog-utils.h" #include "gnc-ui.h" #include "gnc-gui-query.h" +#include "gncObject.h" +#include "QueryNew.h" -#include "gncBusiness.h" #include "gncJob.h" #include "dialog-job-select.h" #include "dialog-job.h" @@ -31,7 +32,8 @@ struct select_job_window { GncJob * job; GncOwner owner; - const GncBusinessObject *job_type, *owner_type; + QueryNew * query; + GNCIdTypeConst owner_type; }; @@ -68,7 +70,8 @@ update_job_select_picker (struct select_job_window *w) /* Build a list of strings (objs is pre-sorted, so keep the order!) */ for (iterator = objs; iterator; iterator = iterator->next) { - const gchar *label = (*(w->job_type->printable))(iterator->data); + const gchar *label = gncObjectPrintable (GNC_JOB_MODULE_NAME, + iterator->data); li = gtk_list_item_new_with_label (label); gtk_object_set_data (GTK_OBJECT (li), "item-list-pointer", iterator->data); @@ -93,7 +96,7 @@ update_job_select_picker (struct select_job_window *w) { const char * label; if (w->job) - label = (*(w->job_type->printable))(w->job); + label = gncObjectPrintable (GNC_JOB_MODULE_NAME, w->job); else label = ""; @@ -119,11 +122,12 @@ update_customer_select_picker (struct select_job_window *w) gncOwnerCopy (&(w->owner), &saved_owner); /* Get the list of objects */ - custs = (*(w->owner_type->get_list))(w->book, show_all); + /* XXX: use show_all in the query */ + custs = gncQueryRun (w->query, w->owner_type); /* Build a list of strings (objs is pre-sorted, so keep the order!) */ for (iterator = custs; iterator; iterator = iterator->next) { - const gchar *label = (*(w->owner_type->printable))(iterator->data); + const gchar *label = gncObjectPrintable (w->owner_type, iterator->data); li = gtk_list_item_new_with_label (label); gtk_object_set_data (GTK_OBJECT (li), "item-list-pointer", iterator->data); @@ -152,14 +156,12 @@ update_customer_select_picker (struct select_job_window *w) const char * label; if (w->owner.owner.undefined) - label = (*(w->owner_type->printable))(w->owner.owner.undefined); + label = gncObjectPrintable (w->owner_type, w->owner.owner.undefined); else label = ""; gtk_entry_set_text (GTK_ENTRY (w->customer_entry), label); } - - g_list_free (custs); } static void @@ -297,15 +299,16 @@ gnc_ui_select_job_new (GtkWidget * parent, GNCBook *book, g_warning ("Cannot handle this owner type"); return NULL; } - win->owner_type = gncBusinessLookup (type_name); - win->job_type = gncBusinessLookup (GNC_JOB_MODULE_NAME); + win->owner_type = type_name; + win->query = gncQueryCreate (); + gncQuerySetBook (win->query, book); xml = gnc_glade_xml_new ("job.glade", "Job Selector Dialog"); owner_label = glade_xml_get_widget (xml, "owner_label"); gtk_label_set_text (GTK_LABEL (owner_label), - gncBusinessGetTypeLabel (type_name)); + gncObjectGetTypeLabel (type_name)); /* Grab the widgets */ @@ -369,6 +372,7 @@ gnc_ui_select_job_new (GtkWidget * parent, GNCBook *book, /* exit */ retval = win->job; + gncQueryDestroy (win->query); g_free(win); return retval; diff --git a/src/business/business-ledger/gncEntryLedger.c b/src/business/business-ledger/gncEntryLedger.c index 66e8cee4bc..5b44dd66b0 100644 --- a/src/business/business-ledger/gncEntryLedger.c +++ b/src/business/business-ledger/gncEntryLedger.c @@ -241,6 +241,7 @@ void gnc_entry_ledger_destroy (GncEntryLedger *ledger) gnc_entry_ledger_clear_blank_entry (ledger); gnc_entry_ledger_display_fini (ledger); gnc_table_destroy (ledger->table); + gncQueryDestroy (ledger->query); g_free (ledger); } @@ -255,14 +256,46 @@ void gnc_entry_ledger_set_default_order (GncEntryLedger *ledger, { if (!ledger) return; ledger->order = order; + + if (!ledger->query && order) { + ledger->query = gncQueryCreate (); + gncQuerySetBook (ledger->query, gncOrderGetBook (order)); + gncQueryAddGUIDMatch (ledger->query, QUERY_AND, + GNC_ORDER_MODULE_NAME, ORDER_GUID, + gncOrderGetGUID (order)); + } gnc_entry_ledger_display_refresh (ledger); } void gnc_entry_ledger_set_default_invoice (GncEntryLedger *ledger, - GncInvoice *invoice) + GncInvoice *invoice) { if (!ledger) return; ledger->invoice = invoice; + + if (!ledger->query && invoice) { + ledger->query = gncQueryCreate (); + gncQuerySetBook (ledger->query, gncInvoiceGetBook (invoice)); + + /* Match: + * Entry's Invoice == this invoice || + * ( Entry's Invoice == NULL && + * Entry's Order's real-parent == Invoice's parent ) + * + * Note that the "second" term is only for Editable invoices, and + * only when we've already got an 'owner'. + */ + gncQueryAddGUIDMatch (ledger->query, QUERY_AND, + GNC_INVOICE_MODULE_NAME, INVOICE_GUID, + gncInvoiceGetGUID (invoice)); + + /* Note that this is a bogus search -- it will find all entries that + * exist (including "blank" entries) + */ + gncQueryAddGUIDMatch (ledger->query, QUERY_OR, + GNC_INVOICE_MODULE_NAME, INVOICE_GUID, NULL); + + } gnc_entry_ledger_display_refresh (ledger); } diff --git a/src/business/business-ledger/gncEntryLedgerDisplay.c b/src/business/business-ledger/gncEntryLedgerDisplay.c index 77124c9e2b..5b8289898f 100644 --- a/src/business/business-ledger/gncEntryLedgerDisplay.c +++ b/src/business/business-ledger/gncEntryLedgerDisplay.c @@ -23,13 +23,10 @@ static GList * gnc_entry_ledger_get_entries (GncEntryLedger *ledger) { - if (ledger->order) - return (gncOrderGetEntries (ledger->order)); + if (ledger->query) + return gncQueryRun (ledger->query, GNC_ENTRY_MODULE_NAME); - if (ledger->invoice) - return (gncInvoiceGetEntries (ledger->invoice)); - - // g_warning ("no invoice; no order. Who am I?"); + // g_warning ("No query to run?"); return NULL; } diff --git a/src/business/business-ledger/gncEntryLedgerP.h b/src/business/business-ledger/gncEntryLedgerP.h index 086ca6acb3..473d64eb03 100644 --- a/src/business/business-ledger/gncEntryLedgerP.h +++ b/src/business/business-ledger/gncEntryLedgerP.h @@ -10,6 +10,7 @@ #include "guid.h" #include "gnc-book.h" #include "table-allgui.h" +#include "QueryNew.h" #include "gncEntryLedger.h" struct GncEntryLedger_s { @@ -28,6 +29,8 @@ struct GncEntryLedger_s { Table * table; GncOrder * order; GncInvoice * invoice; + QueryNew * query; + GncEntryLedgerType type; }; diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index f26a442c46..52594e552a 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -13,6 +13,9 @@ libgncmod_engine_la_SOURCES = \ Group.c \ Period.c \ Query.c \ + QueryNew.c \ + QueryCore.c \ + QueryObject.c \ SchedXaction.c \ SX-ttinfo.c \ Scrub.c \ @@ -30,6 +33,7 @@ libgncmod_engine_la_SOURCES = \ gnc-session.c \ gncmod-engine.c \ guid.c \ + gncObject.c \ kvp_frame.c \ kvp-util.c \ md5.c \ @@ -47,6 +51,9 @@ gncinclude_HEADERS = \ SchedXaction.h \ SX-ttinfo.h \ Query.h \ + QueryNew.h \ + QueryObject.h \ + QueryCore.h \ Scrub.h \ TransLog.h \ Transaction.h \ @@ -63,6 +70,7 @@ gncinclude_HEADERS = \ gnc-pricedb.h \ gnc-session.h \ guid.h \ + gncObject.h \ kvp_frame.h \ kvp-scm.h \ messages.h @@ -74,6 +82,9 @@ noinst_HEADERS = \ GNCIdP.h \ GroupP.h \ QueryP.h \ + QueryNewP.h \ + QueryObjectP.h \ + QueryCoreP.h \ SchedXactionP.h \ SX-ttinfo.h \ TransactionP.h \ @@ -81,6 +92,7 @@ noinst_HEADERS = \ gnc-event-p.h \ gnc-pricedb-p.h \ gnc-session-p.h \ + gncObjectP.h \ kvp-util-p.h \ md5.h diff --git a/src/engine/QueryCore.c b/src/engine/QueryCore.c new file mode 100644 index 0000000000..4b71121896 --- /dev/null +++ b/src/engine/QueryCore.c @@ -0,0 +1,917 @@ +/* + * QueryCore.c -- provide core Query data types + * Copyright (C) 2002 Derek Atkins + * + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-engine-util.h" +#include "QueryCoreP.h" +#include "QueryNew.h" + +static short module = MOD_QUERY; + +/* Core Type Predicate definitions */ +typedef struct { + QueryPredDataDef pd; + string_match_t options; + gboolean is_regex; + char * matchstring; + regex_t compiled; +} query_string_def, *query_string_t; +typedef const char * (*query_string_getter) (gpointer); +static const char * query_string_type = QUERYCORE_STRING; + +typedef struct { + QueryPredDataDef pd; + date_match_t options; + Timespec date; +} query_date_def, *query_date_t; +typedef Timespec (*query_date_getter) (gpointer); +static const char * query_date_type = QUERYCORE_DATE; + +typedef struct { + QueryPredDataDef pd; + numeric_match_t options; + gnc_numeric amount; +} query_numeric_def, *query_numeric_t; +typedef gnc_numeric (*query_numeric_getter) (gpointer); +static const char * query_numeric_type = QUERYCORE_NUMERIC; + +typedef struct { + QueryPredDataDef pd; + guid_match_t options; + GList * guids; +} query_guid_def, *query_guid_t; +typedef const GUID * (*query_guid_getter) (gpointer); +static const char * query_guid_type = QUERYCORE_GUID; + +typedef struct { + QueryPredDataDef pd; + gint64 val; +} query_int64_def, *query_int64_t; +typedef gint64 (*query_int64_getter) (gpointer); +static const char * query_int64_type = QUERYCORE_INT64; + +typedef struct { + QueryPredDataDef pd; + double val; +} query_double_def, *query_double_t; +typedef double (*query_double_getter) (gpointer); +static const char * query_double_type = QUERYCORE_DOUBLE; + +typedef struct { + QueryPredDataDef pd; + gboolean val; +} query_boolean_def, *query_boolean_t; +typedef gboolean (*query_boolean_getter) (gpointer); +static const char * query_boolean_type = QUERYCORE_BOOLEAN; + +typedef struct { + QueryPredDataDef pd; + char_match_t options; + char * char_list; +} query_char_def, *query_char_t; +typedef char (*query_char_getter) (gpointer); +static const char * query_char_type = QUERYCORE_CHAR; + +typedef struct { + QueryPredDataDef pd; + GSList * path; + kvp_value * value; +} query_kvp_def, *query_kvp_t; +typedef kvp_frame * (*query_kvp_getter) (gpointer); +static const char * query_kvp_type = QUERYCORE_KVP; + +static gboolean initialized = FALSE; +static GHashTable *predTable = NULL; +static GHashTable *cmpTable = NULL; +static GHashTable *copyTable = NULL; +static GHashTable *freeTable = NULL; + +#define COMPARE_ERROR -3 +#define PREDICATE_ERROR -2 +#define VERIFY_PDATA(str) { \ + g_return_if_fail (pd != NULL); \ + g_return_if_fail (pd->type_name == str || \ + !safe_strcmp (str, pd->type_name)); \ +} +#define VERIFY_PDATA_R(str) { \ + g_return_val_if_fail (pd != NULL, NULL); \ + g_return_val_if_fail (pd->type_name == str || \ + !safe_strcmp (str, pd->type_name), \ + NULL); \ +} +#define VERIFY_PREDICATE(str) { \ + g_return_val_if_fail (get_fcn != NULL, PREDICATE_ERROR); \ + g_return_val_if_fail (pd != NULL, PREDICATE_ERROR); \ + g_return_val_if_fail (pd->type_name == str || \ + !safe_strcmp (str, pd->type_name), \ + PREDICATE_ERROR); \ +} + +/********************************************************************/ +/* TYPE-HANDLING FUNCTIONS */ + +/* QUERYCORE_STRING */ + +static int string_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + query_string_t pdata = (query_string_t) pd; + const char *s; + + VERIFY_PREDICATE (query_string_type); + + s = ((query_string_getter)get_fcn) (object); + + if (pdata->is_regex) { + regmatch_t match; + if (!regexec (&pdata->compiled, s, 1, &match, 0)) + return 1; + else + return 0; + } + + if (pdata->options == STRING_MATCH_CASEINSENSITIVE) { + if (strcasestr (s, pdata->matchstring)) return 1; + return 0; + } + + if (strstr (s, pdata->matchstring)) return 1; + return 0; +} + +static int string_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + const char *s1, *s2; + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + + s1 = ((query_string_getter)get_fcn) (a); + s2 = ((query_string_getter)get_fcn) (b); + + if (options == STRING_MATCH_CASEINSENSITIVE) + return safe_strcasecmp (s1, s2); + + return safe_strcmp (s1, s2); +} + +static void string_free_pdata (QueryPredData_t pd) +{ + query_string_t pdata = (query_string_t) pd; + + VERIFY_PDATA (query_string_type); + + if (pdata->is_regex) + regfree (&pdata->compiled); + else + g_free (pdata->matchstring); + + g_free (pdata); +} + +static QueryPredData_t string_copy_predicate (QueryPredData_t pd) +{ + query_string_t pdata = (query_string_t) pd; + + VERIFY_PDATA_R (query_string_type); + + return gncQueryStringPredicate (pdata->matchstring, pdata->options, + pdata->is_regex); +} + +QueryPredData_t gncQueryStringPredicate (char *str, string_match_t options, + gboolean is_regex) +{ + query_string_t pdata; + + g_return_val_if_fail (str, NULL); + g_return_val_if_fail (*str != '\0', NULL); + + pdata = g_new0 (query_string_def, 1); + pdata->pd.type_name = query_string_type; + pdata->options = options; + pdata->matchstring = g_strdup (str); + + if (is_regex) { + int flags = REG_EXTENDED; + if (options == STRING_MATCH_CASEINSENSITIVE) + flags |= REG_ICASE; + + regcomp(&pdata->compiled, str, flags); + pdata->is_regex = TRUE; + } + + return ((QueryPredData_t)pdata); +} + +/* QUERYCORE_DATE */ + +static int date_compare (Timespec ta, Timespec tb, date_match_t options) +{ + if (options == DATE_MATCH_ROUNDED) { + ta = timespecCanonicalDayTime (ta); + tb = timespecCanonicalDayTime (tb); + } + + if (ta.tv_sec < tb.tv_sec) + return -1; + if (ta.tv_sec > tb.tv_sec) + return 1; + + if (ta.tv_nsec < tb.tv_nsec) + return -1; + if (ta.tv_nsec > tb.tv_nsec) + return 1; + + return 0; +} + +static int date_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + query_date_t pdata = (query_date_t)pd; + Timespec objtime; + int compare; + + VERIFY_PREDICATE (query_date_type); + + objtime = ((query_date_getter)get_fcn) (object); + compare = date_compare (objtime, pdata->date, pdata->options); + + switch (how) { + case COMPARE_LT: + return (compare < 0); + case COMPARE_LTE: + return (compare <= 0); + case COMPARE_EQUAL: + return (compare == 0); + case COMPARE_GT: + return (compare > 0); + case COMPARE_GTE: + return (compare >= 0); + default: + PWARN ("bad match type: %d", how); + return 0; + } +} + +static int date_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + Timespec ta, tb; + + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + + ta = ((query_date_getter)get_fcn) (a); + tb = ((query_date_getter)get_fcn) (b); + + return date_compare (ta, tb, options); +} + +static void date_free_pdata (QueryPredData_t pd) +{ + query_date_t pdata = (query_date_t)pd; + + VERIFY_PDATA (query_date_type); + + g_free (pdata); +} + +static QueryPredData_t date_copy_predicate (QueryPredData_t pd) +{ + query_date_t pdata = (query_date_t)pd; + + VERIFY_PDATA_R (query_date_type); + + return gncQueryDatePredicate (pdata->options, pdata->date); +} + +QueryPredData_t gncQueryDatePredicate (date_match_t options, Timespec date) +{ + query_date_t pdata; + + pdata = g_new0 (query_date_def, 1); + pdata->pd.type_name = query_date_type; + pdata->options = options; + pdata->date = date; + return ((QueryPredData_t)pdata); +} + +/* QUERYCORE_NUMERIC */ + +static int numeric_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + query_numeric_t pdata = (query_numeric_t)pd; + gnc_numeric obj_val; + int compare; + + VERIFY_PREDICATE (query_numeric_type); + + obj_val = ((query_numeric_getter)get_fcn) (object); + + switch (pdata->options) { + case NUMERIC_MATCH_NEG_ONLY: + if (!gnc_numeric_negative_p (obj_val)) return 0; + break; + case NUMERIC_MATCH_POS_ONLY: + if (!gnc_numeric_positive_p (obj_val)) return 0; + break; + default: + break; + } + + if (how == COMPARE_EQUAL) { + gnc_numeric cmp_val = gnc_numeric_create (1, 10000); + compare = + (gnc_numeric_compare (gnc_numeric_sub (gnc_numeric_abs (obj_val), + gnc_numeric_abs (pdata->amount), + 100000, GNC_RND_ROUND), + cmp_val) < 0); + } else + compare = gnc_numeric_compare (gnc_numeric_abs (obj_val), pdata->amount); + + switch (how) { + case COMPARE_LT: + return (compare < 0); + case COMPARE_LTE: + return (compare <= 0); + case COMPARE_EQUAL: + return compare; + case COMPARE_GT: + return (compare > 0); + case COMPARE_GTE: + return (compare >= 0); + default: + PWARN ("bad match type: %d", how); + return 0; + } +} + +static int numeric_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + gnc_numeric va, vb; + + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + + va = ((query_numeric_getter)get_fcn) (a); + vb = ((query_numeric_getter)get_fcn) (b); + + return gnc_numeric_compare (va, vb); +} + +static void numeric_free_pdata (QueryPredData_t pd) +{ + query_numeric_t pdata = (query_numeric_t)pd; + VERIFY_PDATA (query_numeric_type); + g_free (pdata); +} + +static QueryPredData_t numeric_copy_predicate (QueryPredData_t pd) +{ + query_numeric_t pdata = (query_numeric_t)pd; + VERIFY_PDATA_R (query_numeric_type); + return gncQueryNumericPredicate (pdata->options, pdata->amount); +} + +QueryPredData_t gncQueryNumericPredicate (numeric_match_t options, + gnc_numeric value) +{ + query_numeric_t pdata; + pdata = g_new0 (query_numeric_def, 1); + pdata->pd.type_name = query_numeric_type; + pdata->options = options; + pdata->amount = value; + return ((QueryPredData_t)pdata); +} + +/* QUERYCORE_GUID */ + +static int guid_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + query_guid_t pdata = (query_guid_t)pd; + GList *node; + const GUID *guid; + + VERIFY_PREDICATE (query_guid_type); + + /* See if the guid is in the list */ + guid = ((query_guid_getter)get_fcn) (object); + for (node = pdata->guids; node; node = node->next) { + if (guid_equal (node->data, guid)) + break; + } + + switch (pdata->options) { + case GUID_MATCH_ANY: + return (node != NULL); + break; + case GUID_MATCH_NONE: + return (node == NULL); + break; + case GUID_MATCH_NULL: + return (guid == NULL); + break; + default: + PWARN ("bad match type"); + return 0; + } +} + +static void guid_free_pdata (QueryPredData_t pd) +{ + query_guid_t pdata = (query_guid_t)pd; + VERIFY_PDATA (query_guid_type); + g_list_free (pdata->guids); + g_free (pdata); +} + +static QueryPredData_t guid_copy_predicate (QueryPredData_t pd) +{ + query_guid_t pdata = (query_guid_t)pd; + VERIFY_PDATA_R (query_guid_type); + return gncQueryGUIDPredicate (pdata->options, pdata->guids); +} + +QueryPredData_t gncQueryGUIDPredicate (guid_match_t options, GList *guids) +{ + query_guid_t pdata; + + pdata = g_new0 (query_guid_def, 1); + pdata->pd.type_name = query_guid_type; + pdata->options = options; + pdata->guids = g_list_copy (guids); + return ((QueryPredData_t)pdata); +} + +/* QUERYCORE_INT64 */ + +static int int64_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + gint64 val; + query_int64_t pdata = (query_int64_t)pd; + + VERIFY_PREDICATE (query_int64_type); + + val = ((query_int64_getter)get_fcn) (object); + + switch (how) { + case COMPARE_LT: + return (val < pdata->val); + case COMPARE_LTE: + return (val <= pdata->val); + case COMPARE_EQUAL: + return (val == pdata->val); + case COMPARE_GT: + return (val > pdata->val); + case COMPARE_GTE: + return (val >= pdata->val); + default: + PWARN ("bad match type: %d", how); + return 0; + } +} + +static int int64_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + gint64 v1, v2; + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + + v1 = ((query_int64_getter)get_fcn)(a); + v2 = ((query_int64_getter)get_fcn)(b); + + if (v1 < v2) return -1; + if (v1 > v2) return 1; + return 0; +} + +static void int64_free_pdata (QueryPredData_t pd) +{ + query_int64_t pdata = (query_int64_t)pd; + VERIFY_PDATA (query_int64_type); + g_free (pdata); +} + +static QueryPredData_t int64_copy_predicate (QueryPredData_t pd) +{ + query_int64_t pdata = (query_int64_t)pd; + VERIFY_PDATA_R (query_int64_type); + return gncQueryInt64Predicate (pdata->val); +} + +QueryPredData_t gncQueryInt64Predicate (gint64 val) +{ + query_int64_t pdata = g_new0 (query_int64_def, 1); + pdata->pd.type_name = query_int64_type; + pdata->val = val; + return ((QueryPredData_t)pdata); +} + +/* QUERYCORE_DOUBLE */ + +static int double_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + double val; + query_double_t pdata = (query_double_t)pd; + + VERIFY_PREDICATE (query_double_type); + + val = ((query_double_getter)get_fcn) (object); + + switch (how) { + case COMPARE_LT: + return (val < pdata->val); + case COMPARE_LTE: + return (val <= pdata->val); + case COMPARE_EQUAL: + return (val == pdata->val); + case COMPARE_GT: + return (val > pdata->val); + case COMPARE_GTE: + return (val >= pdata->val); + default: + PWARN ("bad match type: %d", how); + return 0; + } +} + +static int double_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + double v1, v2; + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + + v1 = ((query_double_getter)get_fcn) (a); + v2 = ((query_double_getter)get_fcn) (b); + + if (v1 < v2) return -1; + if (v1 > v2) return 1; + return 0; +} + +static void double_free_pdata (QueryPredData_t pd) +{ + query_double_t pdata = (query_double_t)pd; + VERIFY_PDATA (query_double_type); + g_free (pdata); +} + +static QueryPredData_t double_copy_predicate (QueryPredData_t pd) +{ + query_double_t pdata = (query_double_t)pd; + VERIFY_PDATA_R (query_double_type); + return gncQueryDoublePredicate (pdata->val); +} + +QueryPredData_t gncQueryDoublePredicate (double val) +{ + query_double_t pdata = g_new0 (query_double_def, 1); + pdata->pd.type_name = query_double_type; + pdata->val = val; + return ((QueryPredData_t)pdata); +} + + +/* QUERYCORE_BOOLEAN */ + +static int boolean_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + gboolean val; + query_boolean_t pdata = (query_boolean_t)pd; + + VERIFY_PREDICATE (query_boolean_type); + + val = ((query_boolean_getter)get_fcn) (object); + + return (val == pdata->val); +} + +static int boolean_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + gboolean va, vb; + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + va = ((query_boolean_getter)get_fcn) (a); + vb = ((query_boolean_getter)get_fcn) (b); + if (!va && vb) return -1; + if (va && !vb) return 1; + return 0; +} + +static void boolean_free_pdata (QueryPredData_t pd) +{ + query_boolean_t pdata = (query_boolean_t)pd; + VERIFY_PDATA (query_boolean_type); + g_free (pdata); +} + +static QueryPredData_t boolean_copy_predicate (QueryPredData_t pd) +{ + query_boolean_t pdata = (query_boolean_t)pd; + VERIFY_PDATA_R (query_boolean_type); + return gncQueryBooleanPredicate (pdata->val); +} + +QueryPredData_t gncQueryBooleanPredicate (gboolean val) +{ + query_boolean_t pdata = g_new0 (query_boolean_def, 1); + pdata->pd.type_name = query_boolean_type; + pdata->val = val; + return ((QueryPredData_t)pdata); +} + +/* QUERYCORE_CHAR */ + +static int char_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + char c; + query_char_t pdata = (query_char_t)pd; + + VERIFY_PREDICATE (query_char_type); + + c = ((query_char_getter)get_fcn) (object); + + switch (pdata->options) { + case CHAR_MATCH_ANY: + if (strchr (pdata->char_list, c)) return 1; + return 0; + case CHAR_MATCH_NONE: + if (!strchr (pdata->char_list, c)) return 1; + return 0; + default: + PWARN ("bad match type"); + return 0; + } +} + +static int char_compare_func (gpointer a, gpointer b, gint options, + QueryAccess get_fcn) +{ + char va, vb; + g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR); + va = ((query_char_getter)get_fcn)(a); + vb = ((query_char_getter)get_fcn)(b); + return (va-vb); +} + +static void char_free_pdata (QueryPredData_t pd) +{ + query_char_t pdata = (query_char_t)pd; + VERIFY_PDATA (query_char_type); + g_free (pdata->char_list); + g_free (pdata); +} + +static QueryPredData_t char_copy_predicate (QueryPredData_t pd) +{ + query_char_t pdata = (query_char_t)pd; + VERIFY_PDATA_R (query_char_type); + return gncQueryCharPredicate (pdata->options, pdata->char_list); +} + +QueryPredData_t gncQueryCharPredicate (char_match_t options, const char *chars) +{ + query_char_t pdata; + g_return_val_if_fail (chars, NULL); + pdata = g_new0 (query_char_def, 1); + pdata->pd.type_name = query_char_type; + pdata->options = options; + pdata->char_list = g_strdup (chars); + return ((QueryPredData_t)pdata); +} + + +/* QUERYCORE_KVP */ + +static int kvp_match_predicate (gpointer object, QueryAccess get_fcn, + query_compare_t how, QueryPredData_t pd) +{ + int compare; + kvp_frame *kvp; + kvp_value *value; + query_kvp_t pdata = (query_kvp_t)pd; + + VERIFY_PREDICATE (query_kvp_type); + + kvp = ((query_kvp_getter)get_fcn) (object); + if (!kvp) + return 0; + + value = kvp_frame_get_slot_path_gslist (kvp, pdata->path); + if (!value) + return 0; + + if (kvp_value_get_type (value) != kvp_value_get_type (pdata->value)) + return 0; + + compare = kvp_value_compare (value, pdata->value); + + switch (how) + { + case COMPARE_LT: + return (compare < 0); + case COMPARE_LTE: + return (compare <= 0); + case COMPARE_EQUAL: + return (compare == 0); + case COMPARE_GTE: + return (compare >= 0); + case COMPARE_GT: + return (compare > 0); + default: + PWARN ("bad match type: %d", how); + return 0; + } +} + +static void kvp_free_pdata (QueryPredData_t pd) +{ + query_kvp_t pdata = (query_kvp_t)pd; + GSList *node; + + VERIFY_PDATA (query_kvp_type); + kvp_value_delete (pdata->value); + for (node = pdata->path; node; node = node->next) { + g_free (node->data); + node->data = NULL; + } + g_slist_free (pdata->path); + g_free (pdata); +} + +static QueryPredData_t kvp_copy_predicate (QueryPredData_t pd) +{ + query_kvp_t pdata = (query_kvp_t)pd; + VERIFY_PDATA_R (query_kvp_type); + return gncQueryKVPPredicate (pdata->path, pdata->value); +} + +QueryPredData_t gncQueryKVPPredicate (GSList *path, const kvp_value *value) +{ + query_kvp_t pdata; + GSList *node; + + g_return_val_if_fail (path && value, NULL); + + pdata = g_new0 (query_kvp_def, 1); + pdata->pd.type_name = query_kvp_type; + pdata->value = kvp_value_copy (value); + pdata->path = g_slist_copy (path); + for (node = pdata->path; node; node = node->next) + node->data = g_strdup (node->data); + + return ((QueryPredData_t)pdata); +} + +/* initialization */ + +static void init_tables (void) +{ + int i; + struct { + char const *name; + QueryPredicate pred; + QueryCompare comp; + QueryPredicateCopy copy; + QueryPredDataFree pd_free; + } knownTypes[] = { + { QUERYCORE_STRING, string_match_predicate, string_compare_func, + string_copy_predicate, string_free_pdata }, + { QUERYCORE_DATE, date_match_predicate, date_compare_func, + date_copy_predicate, date_free_pdata }, + { QUERYCORE_NUMERIC, numeric_match_predicate, numeric_compare_func, + numeric_copy_predicate, numeric_free_pdata }, + { QUERYCORE_GUID, guid_match_predicate, NULL, + guid_copy_predicate, guid_free_pdata }, + { QUERYCORE_INT64, int64_match_predicate, int64_compare_func, + int64_copy_predicate, int64_free_pdata }, + { QUERYCORE_DOUBLE, double_match_predicate, double_compare_func, + double_copy_predicate, double_free_pdata }, + { QUERYCORE_BOOLEAN, boolean_match_predicate, boolean_compare_func, + boolean_copy_predicate, boolean_free_pdata }, + { QUERYCORE_CHAR, char_match_predicate, char_compare_func, + char_copy_predicate, char_free_pdata }, + { QUERYCORE_KVP, kvp_match_predicate, NULL, kvp_copy_predicate, + kvp_free_pdata }, + }; + + /* Register the known data types */ + for (i = 0; i < (sizeof(knownTypes)/sizeof(*knownTypes)); i++) { + gncQueryRegisterCoreObject (knownTypes[i].name, + knownTypes[i].pred, + knownTypes[i].comp, + knownTypes[i].copy, + knownTypes[i].pd_free); + } +} + +/********************************************************************/ +/* PUBLISHED API FUNCTIONS */ + +void gncQueryRegisterCoreObject (char const *core_name, + QueryPredicate pred, + QueryCompare comp, + QueryPredicateCopy copy, + QueryPredDataFree pd_free) +{ + g_return_if_fail (core_name); + g_return_if_fail (*core_name != '\0'); + + if (pred) + g_hash_table_insert (predTable, (char *)core_name, pred); + + if (comp) + g_hash_table_insert (cmpTable, (char *)core_name, comp); + + if (copy) + g_hash_table_insert (copyTable, (char *)core_name, copy); + + if (pd_free) + g_hash_table_insert (freeTable, (char *)core_name, pd_free); +} + +void gncQueryCoreInit (void) +{ + /* Only let us initialize once */ + if (initialized) return; + initialized = TRUE; + + /* Create the tables */ + predTable = g_hash_table_new (g_str_hash, g_str_equal); + cmpTable = g_hash_table_new (g_str_hash, g_str_equal); + copyTable = g_hash_table_new (g_str_hash, g_str_equal); + freeTable = g_hash_table_new (g_str_hash, g_str_equal); + + init_tables (); +} + +void gncQueryCoreShutdown (void) +{ + if (!initialized) return; + initialized = FALSE; + + g_hash_table_destroy (predTable); + g_hash_table_destroy (cmpTable); + g_hash_table_destroy (copyTable); + g_hash_table_destroy (freeTable); +} + +QueryPredicate gncQueryCoreGetPredicate (char const *type) +{ + g_return_val_if_fail (type, NULL); + return g_hash_table_lookup (predTable, type); +} + +QueryCompare gncQueryCoreGetCompare (char const *type) +{ + g_return_val_if_fail (type, NULL); + return g_hash_table_lookup (cmpTable, type); +} + +QueryPredicateCopy gncQueryCoreGetCopy (char const *type) +{ + g_return_val_if_fail (type, NULL); + return g_hash_table_lookup (copyTable, type); +} + +QueryPredDataFree gncQueryCoreGetPredFree (char const *type) +{ + g_return_val_if_fail (type, NULL); + return g_hash_table_lookup (freeTable, type); +} + +void gncQueryCorePredicateFree (QueryPredData_t pdata) +{ + QueryPredDataFree free_fcn; + + g_return_if_fail (pdata); + g_return_if_fail (pdata->type_name); + + free_fcn = gncQueryCoreGetPredFree (pdata->type_name); + free_fcn (pdata); +} + +QueryPredData_t gncQueryCorePredicateCopy (QueryPredData_t pdata) +{ + QueryPredicateCopy copy; + + g_return_val_if_fail (pdata, NULL); + g_return_val_if_fail (pdata->type_name, NULL); + + copy = gncQueryCoreGetCopy (pdata->type_name); + return (copy (pdata)); +} diff --git a/src/engine/QueryCore.h b/src/engine/QueryCore.h new file mode 100644 index 0000000000..bfb9c20a33 --- /dev/null +++ b/src/engine/QueryCore.h @@ -0,0 +1,89 @@ +/* + * QueryCore.h -- API for providing core Query data types + * Copyright (C) 2002 Derek Atkins + * + */ + +#ifndef GNC_QUERYCORE_H +#define GNC_QUERYCORE_H + +#include "QueryObject.h" /* for QueryAccess */ +#include "QueryNew.h" /* for QueryPredData_t */ +#include "gnc-numeric.h" +#include "date.h" +#include "kvp_frame.h" + +#include + +/* Head of Predicate Data structures. All PData must start like this. */ +typedef struct query_pred_data { + const char * type_name; +} QueryPredDataDef; + +/* + * An arbitrary Query Predicate. Given the gnucash object and the + * particular parameter get-function (obtained from the registry by + * the Query internals), compare the object's parameter to the + * predicate data + */ +typedef int (*QueryPredicate) (gpointer object, + QueryAccess get_fcn, + query_compare_t how, + QueryPredData_t pdata); + +/* A callback for how to destroy a query predicate's pdata */ +typedef void (*QueryPredDataFree) (QueryPredData_t pdata); + +/* A callback to copy a query's predicate data */ +typedef QueryPredData_t (*QueryPredicateCopy) (QueryPredData_t pdata); + +/* A callback for how to compare two (same-type) objects based on a + * common get_fcn (parameter member), using the provided comparrison + * options (which are the type-specific options). + */ +typedef int (*QueryCompare) (gpointer a, gpointer b, + gint compare_options, + QueryAccess get_fcn); + + +/* This function registers a new Core Object with the QueryNew + * subsystem. It maps the "core_name" object to the given + * query_predicate and predicate_data_free functions. + */ +void gncQueryRegisterCoreObject (char const *type_name, + QueryPredicate pred, + QueryCompare comp, + QueryPredicateCopy copy, + QueryPredDataFree pd_free); + + +/* An example: + * + * gncQueryRegisterCoreObject (QUERYCORE_STRING, string_match_predicate, + * string_compare_fcn, string_free_pdata); + */ + + +/* XXX: Define the core data type predicate_data structures here? */ + +/* Copy a predicate */ +QueryPredData_t gncQueryCorePredicateCopy (QueryPredData_t pdata); + +/* Destroy a type */ +void gncQueryCorePredicateFree (QueryPredData_t pdata); + +/* Core Data Type Predicates */ +QueryPredData_t gncQueryStringPredicate (char *str, string_match_t options, + gboolean is_regex); +QueryPredData_t gncQueryDatePredicate (date_match_t options, Timespec date); +QueryPredData_t gncQueryNumericPredicate (numeric_match_t options, + gnc_numeric value); +QueryPredData_t gncQueryGUIDPredicate (guid_match_t options, GList *guids); +QueryPredData_t gncQueryInt64Predicate (gint64 val); +QueryPredData_t gncQueryDoublePredicate (double val); +QueryPredData_t gncQueryBooleanPredicate (gboolean val); +QueryPredData_t gncQueryCharPredicate (char_match_t options, + const char *chars); +QueryPredData_t gncQueryKVPPredicate (GSList *path, const kvp_value *value); + +#endif /* GNC_QUERYCORE_H */ diff --git a/src/engine/QueryCoreP.h b/src/engine/QueryCoreP.h new file mode 100644 index 0000000000..8e2cce4005 --- /dev/null +++ b/src/engine/QueryCoreP.h @@ -0,0 +1,22 @@ +/* + * QueryCoreP.h -- Internal API for providing core Query data types + * Copyright (C) 2002 Derek Atkins + * + */ + +#ifndef GNC_QUERYCOREP_H +#define GNC_QUERYCOREP_H + +#include "QueryCore.h" + +/* Initalize the Query Core registry and install the default type handlers */ +void gncQueryCoreInit(void); +void gncQueryCoreShutdown (void); + +/* Lookup functions */ +QueryPredicate gncQueryCoreGetPredicate (char const *type); +QueryCompare gncQueryCoreGetCompare (char const *type); +QueryPredicateCopy gncQueryCoreGetCopy (char const *type); +QueryPredDataFree gncQueryCoreGetPredFree (char const *type); + +#endif /* GNC_QUERYCOREP_H */ diff --git a/src/engine/QueryNew.c b/src/engine/QueryNew.c new file mode 100644 index 0000000000..4419411f68 --- /dev/null +++ b/src/engine/QueryNew.c @@ -0,0 +1,836 @@ +/* + * QueryNew.c -- API for finding Gnucash objects + * Copyright (C) 2002 Derek Atkins + * + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-engine-util.h" +#include "gnc-book-p.h" +#include "gncObject.h" +//#include "BackendP.h" + +#include "QueryObjectP.h" +#include "QueryCoreP.h" +#include "QueryNew.h" +#include "QueryNewP.h" + +static short module = MOD_QUERY; + +typedef struct { + GNCIdType obj_name; + const char * param_name; + QueryPredData_t pdata; + query_compare_t how; + gboolean invert; + + /* These values are filled in during "compilation" of the query + * term, based upon the obj_name, param_name, and searched-for + * object type. If conv_fcn is NULL, then we don't know how to + * convert types. + */ + QueryConvert conv_fcn; + QueryAccess get_fcn; + QueryPredicate pred_fcn; +} QueryNewTerm; + +typedef struct { + GNCIdType obj_name; + const char * param_name; + gint options; + gboolean increasing; + gboolean use_default; + + /* These values are filled in during "compilation" of the query + * term, based upon the obj_name, param_name, and searched-for + * object type. If conv_fcn is NULL, then we don't know how to + * convert types. + */ + QueryConvert conv_fcn; + QueryAccess get_fcn; + QueryCompare comp_fcn; +} QuerySort_t; + +/* The QUERY structure */ +struct querynew_s { + /* terms is a list of the OR-terms in a sum-of-products + * logical expression. */ + GList * terms; + + /* sorting and chopping is independent of the search filter */ + + QuerySort_t primary_sort; + QuerySort_t secondary_sort; + QuerySort_t tertiary_sort; + QuerySort defaultSort; + + /* The maximum number of results to return */ + int max_results; + + /* list of books that will be participating in the query */ + GList * books; + + /* cache the results so we don't have to run the whole search + * again until it's really necessary */ + int changed; + GNCIdType last_run_type; + + GList * results; +}; + +typedef struct query_cb { + QueryNew * query; + GList * list; + int count; +} query_cb_t; + +/* initial_term will be owned by the new Query */ +static void query_init (QueryNew *q, QueryNewTerm *initial_term) +{ + GList * or = NULL; + GList *and = NULL; + + if (initial_term) { + or = g_list_alloc (); + and = g_list_alloc (); + and->data = initial_term; + or->data = and; + } + + if(q->terms) + gncQueryClear (q); + + g_list_free (q->results); + g_list_free (q->books); + + memset (q, 0, sizeof (*q)); + + q->terms = or; + q->changed = 1; + q->max_results = -1; + + q->primary_sort.obj_name = QUERY_DEFAULT_SORT; + q->primary_sort.increasing = TRUE; + q->secondary_sort.increasing = TRUE; + q->tertiary_sort.increasing = TRUE; +} + +static void swap_terms (QueryNew *q1, QueryNew *q2) +{ + GList *g; + + if (!q1 || !q2) return; + + g = q1->terms; + q1->terms = q2->terms; + q2->terms = g; + + q1->changed = 1; + q2->changed = 1; +} + +static void free_query_term (QueryNewTerm *qt) +{ + if (!qt) return; + + gncQueryCorePredicateFree (qt->pdata); + g_free (qt); +} + +static QueryNewTerm * copy_query_term (QueryNewTerm *qt) +{ + QueryNewTerm *new_qt; + if (!qt) return NULL; + + new_qt = g_new0 (QueryNewTerm, 1); + memcpy (new_qt, qt, sizeof(QueryNewTerm)); + new_qt->pdata = gncQueryCorePredicateCopy (qt->pdata); + return new_qt; +} + +static GList * copy_and_terms (GList *and_terms) +{ + GList *and = NULL; + GList *cur_and; + + for(cur_and = and_terms; cur_and; cur_and = cur_and->next) + { + and = g_list_prepend(and, copy_query_term (cur_and->data)); + } + + return g_list_reverse(and); +} + +static GList * +copy_or_terms(GList * or_terms) +{ + GList * or = NULL; + GList * cur_or; + + for(cur_or = or_terms; cur_or; cur_or = cur_or->next) + { + or = g_list_prepend(or, copy_and_terms(cur_or->data)); + } + + return g_list_reverse(or); +} + +static void free_members (QueryNew *q) +{ + GList * cur_or; + + if (q == NULL) return; + + for(cur_or = q->terms; cur_or; cur_or = cur_or->next) + { + GList * cur_and; + + for(cur_and = cur_or->data; cur_and; cur_and = cur_and->next) + { + free_query_term(cur_and->data); + cur_and->data = NULL; + } + + g_list_free(cur_or->data); + cur_or->data = NULL; + } + + g_list_free(q->terms); + q->terms = NULL; + + g_list_free(q->books); + q->books = NULL; + + g_list_free(q->results); + q->results = NULL; +} + +static int cmp_func (QuerySort_t *sort, QuerySort default_sort, + gconstpointer a, gconstpointer b) +{ + gpointer conva, convb; + + g_return_val_if_fail (sort, 0); + g_return_val_if_fail (default_sort, 0); + + /* See if this is a default sort */ + if (sort->use_default) { + if (default_sort) + return default_sort ((gpointer)a, (gpointer)b); + return 0; + } + + /* If no converter, consider them equal */ + if (!sort->conv_fcn) return 0; + + /* no compare function, consider them equal */ + if (!sort->comp_fcn) return 0; + + /* Do the converstions */ + conva = sort->conv_fcn ((gpointer)a); + convb = sort->conv_fcn ((gpointer)b); + + /* And now return the compare */ + return sort->comp_fcn (conva, convb, sort->options, sort->get_fcn); +} + +static QueryNew * sortQuery = NULL; + +static int sort_func (gconstpointer a, gconstpointer b) +{ + int retval; + + g_return_val_if_fail (sortQuery, 0); + + retval = cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, b); + if (retval == 0) { + retval = cmp_func (&(sortQuery->secondary_sort), sortQuery->defaultSort, + a, b); + if (retval == 0) { + retval = cmp_func (&(sortQuery->tertiary_sort), sortQuery->defaultSort, + a, b); + return sortQuery->tertiary_sort.increasing ? retval : -retval; + } else { + return sortQuery->secondary_sort.increasing ? retval : -retval; + } + } else { + return sortQuery->primary_sort.increasing ? retval : -retval; + } +} + +static int check_object (QueryNew *q, gpointer object) +{ + GList * and_ptr; + GList * or_ptr; + QueryNewTerm * qt; + int and_terms_ok=1; + gpointer result_obj; + + for(or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) { + and_terms_ok = 1; + for(and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) { + qt = (QueryNewTerm *)(and_ptr->data); + if (qt->conv_fcn && qt->pred_fcn) { + result_obj = ((qt->conv_fcn) (object)); + if (((qt->pred_fcn)(result_obj, qt->get_fcn, + qt->how, qt->pdata)) == qt->invert) { + and_terms_ok = 0; + break; + } + } else { + /* XXX: Don't know how to do this conversion -- do we care? */ + } + } + if(and_terms_ok) { + return 1; + } + } + return 0; +} + +static void compile_sort (QuerySort_t *sort, GNCIdType obj) +{ + sort->use_default = FALSE; + + /* An empty obj_name implies "no sort" */ + if (!sort->obj_name || *(sort->obj_name) == '\0') { + sort->conv_fcn = NULL; + return; + } + + /* Obtain the conversion function */ + sort->conv_fcn = gncQueryObjectGetConverter (obj, sort->obj_name); + + /* No need to continue if there is no conversion function */ + if (sort->conv_fcn) { + const QueryObjectDef *resObj = + gncQueryObjectGetParameter (sort->obj_name, sort->param_name); + + if (resObj) { + sort->get_fcn = resObj->param_getfcn; + sort->comp_fcn = gncQueryCoreGetCompare (resObj->param_type); + } else { + sort->get_fcn = NULL; + sort->comp_fcn = NULL; + } + + } else if (!safe_strcmp (sort->obj_name, QUERY_DEFAULT_SORT)) { + sort->use_default = TRUE; + } +} + +static void compile_terms (QueryNew *q) +{ + GList *or_ptr, *and_ptr; + + /* Find the specific functions for this Query. Note that the + * Query's last_run_type should now be set to the new type. + */ + for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) { + for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) { + QueryNewTerm *qt = and_ptr->data; + + /* Obtain the conversion function */ + qt->conv_fcn = + gncQueryObjectGetConverter (q->last_run_type, qt->obj_name); + + /* No need to continue if there is no conversion function */ + if (qt->conv_fcn) { + const QueryObjectDef *resObj = + gncQueryObjectGetParameter (qt->obj_name, qt->param_name); + + if (resObj) { + qt->get_fcn = resObj->param_getfcn; + qt->pred_fcn = gncQueryCoreGetPredicate (resObj->param_type); + } else { + qt->get_fcn = NULL; + qt->pred_fcn = NULL; + } + } + } + } + + /* Update the sort functions */ + compile_sort (&(q->primary_sort), q->last_run_type); + compile_sort (&(q->secondary_sort), q->last_run_type); + compile_sort (&(q->tertiary_sort), q->last_run_type); + + q->defaultSort = gncQueryObjectDefaultSort (q->last_run_type); +} + +static void check_item_cb (gpointer object, gpointer user_data) +{ + query_cb_t *ql = user_data; + + if (!object || !ql) return; + + if (check_object (ql->query, object)) { + ql->list = g_list_prepend (ql->list, object); + ql->count++; + } + + return; +} + +/********************************************************************/ +/* PUBLISHED API FUNCTIONS */ + +void gncQueryNewInit (void) +{ + gncQueryCoreInit (); + gncQueryObjectInit (); +} + +void gncQueryNewShutdown (void) +{ + gncQueryObjectShutdown (); + gncQueryCoreShutdown (); +} + +void gncQueryAddTerm (QueryNew *q, + GNCIdTypeConst obj_type, const char *param_name, + query_compare_t comparitor, QueryPredData_t pred_data, + QueryOp op) +{ + QueryNewTerm *qt; + QueryNew *qr, *qs; + + if (!q || !obj_type || !param_name || !pred_data) return; + + qt = g_new0 (QueryNewTerm, 1); + qt->obj_name = (GNCIdType)obj_type; + qt->param_name = param_name; + qt->pdata = pred_data; + qt->how = comparitor; + + qs = gncQueryCreate (); + query_init (qs, qt); + + if (gncQueryHasTerms (q)) + qr = gncQueryMerge (q, qs, op); + else + qr = gncQueryMerge (q, qs, QUERY_OR); + + swap_terms (q, qr); + gncQueryDestroy (qs); + gncQueryDestroy (qr); +} + +void gncQueryPurgeTerms (QueryNew *q, + GNCIdTypeConst obj_type, const char *param_name) +{ + QueryNewTerm *qt; + GList *or, *and; + + if (!q || !obj_type || !param_name) return; + + for (or = q->terms; or; or = or->next) { + for (and = or->data; and; and = and->next) { + qt = and->data; + if (!safe_strcmp (qt->obj_name, obj_type) && + !safe_strcmp (qt->param_name, param_name)) { + if (g_list_length (or->data) == 1) { + q->terms = g_list_remove_link (q->terms, or); + g_list_free_1 (or); + or = q->terms; + break; + } else { + or->data = g_list_remove_link (or->data, and); + g_list_free_1 (and); + and = or->data; + if (!and) break; + } + q->changed = 1; + free_query_term (qt); + } + } + if (!or) break; + } +} + +GList * gncQueryRun (QueryNew *q, GNCIdTypeConst obj_type) +{ + GList *matching_objects = NULL; + GList *node; + int object_count = 0; + + if (!q || !obj_type) return NULL; + + /* XXX: Prioritize the query terms? */ + + /* prepare the Query for processing */ + if (q->changed || safe_strcmp (q->last_run_type, obj_type)) { + q->last_run_type = (GNCIdType)obj_type; + compile_terms (q); + } + + /* Now run the query over all the objects and save the results */ + { + query_cb_t qcb; + + memset (&qcb, 0, sizeof (qcb)); + qcb.query = q; + + /* For each book */ + for (node=q->books; node; node=node->next) { + GNCBook *book = node->data; +#if 0 /* XXX FIXME! */ + Backend *be = book->backend; + + /* query the backend */ + if (be && be->run_query) + (be->run_query) (be, q); +#endif + + /* and then iterate over all the objects */ + gncObjectForeach (obj_type, book, check_item_cb, &qcb); + } + + matching_objects = qcb.list; + object_count = qcb.count; + } + + /* There is no absolute need to reverse this list, since it's being + * sorted below. However, in the common case, we will be searching + * in a confined location where the objects are already in order, + * thus reversing will put us in the correct order we want and make + * the sorting go much faster. + */ + matching_objects = g_list_reverse(matching_objects); + + /* now sort the matching objects based on the search criteria + * sortQuery is an unforgivable use of static global data... I just + * can't figure out how else to do this sanely. + */ + sortQuery = q; + matching_objects = g_list_sort(matching_objects, sort_func); + sortQuery = NULL; + + /* crop the list to limit the number of splits */ + if((object_count > q->max_results) && (q->max_results > -1)) + { + if(q->max_results > 0) + { + GList *mptr; + + /* mptr is set to the first node of what will be the new list */ + mptr = g_list_nth(matching_objects, object_count - q->max_results); + /* mptr should not be NULL, but let's be safe */ + if (mptr != NULL) + { + if (mptr->prev != NULL) mptr->prev->next = NULL; + mptr->prev = NULL; + } + g_list_free(matching_objects); + matching_objects = mptr; + } + else + { + /* q->max_results == 0 */ + g_list_free(matching_objects); + matching_objects = NULL; + } + object_count = q->max_results; + } + + q->changed = 0; + + g_list_free(q->results); + q->results = matching_objects; + + return matching_objects; +} + +void gncQueryClear (QueryNew *query) +{ + QueryNew *q2 = gncQueryCreate (); + swap_terms (query, q2); + gncQueryDestroy (q2); + + g_list_free (query->books); + query->books = NULL; + g_list_free (query->results); + query->results = NULL; + query->changed = 1; +} + +QueryNew * gncQueryCreate (void) +{ + QueryNew *qp = g_new0 (QueryNew, 1); + query_init (qp, NULL); + return qp; +} + +int gncQueryHasTerms (QueryNew *q) +{ + if (!q) return 0; + return g_list_length (q->terms); +} + +int gncQueryNumTerms (QueryNew *q) +{ + GList *o; + int n = 0; + if (!q) return 0; + for (o = q->terms; o; o=o->next) + n += g_list_length(o->data); + return n; +} + +GList * gncQueryGetTerms (QueryNew *q) +{ + if (!q) return NULL; + return q->terms; +} + +void gncQueryDestroy (QueryNew *q) +{ + if (!q) return; + free_members (q); + g_free (q); +} + +QueryNew * gncQueryCopy (QueryNew *q) +{ + QueryNew *copy; + if (!q) return NULL; + copy = gncQueryCreate (); + free_members (copy); + + memcpy (copy, q, sizeof (QueryNew)); + + copy->terms = copy_or_terms (q->terms); + copy->books = g_list_copy (q->books); + copy->results = g_list_copy (q->results); + + return copy; +} + +/******************************************************************** + * gncQueryInvert + * return a newly-allocated Query object which is the + * logical inverse of the original. + ********************************************************************/ + +QueryNew * gncQueryInvert (QueryNew *q) +{ + QueryNew * retval; + QueryNew * right, * left, * iright, * ileft; + QueryNewTerm * qt; + GList * aterms; + GList * cur; + GList * new_oterm; + int num_or_terms; + + num_or_terms = g_list_length(q->terms); + + switch(num_or_terms) + { + case 0: + retval = gncQueryCreate(); + retval->max_results = q->max_results; + break; + + /* this is demorgan expansion for a single AND expression. */ + /* !(abc) = !a + !b + !c */ + case 1: + retval = gncQueryCreate(); + retval->max_results = q->max_results; + + aterms = g_list_nth_data(q->terms, 0); + new_oterm = NULL; + for(cur=aterms; cur; cur=cur->next) { + qt = copy_query_term(cur->data); + qt->invert = !(qt->invert); + new_oterm = g_list_append(NULL, qt); + retval->terms = g_list_append(retval->terms, new_oterm); + } + break; + + /* if there are multiple OR-terms, we just recurse by + * breaking it down to !(a + b + c) = + * !a * !(b + c) = !a * !b * !c. */ + default: + right = gncQueryCreate(); + right->terms = copy_or_terms(g_list_nth(q->terms, 1)); + + left = gncQueryCreate(); + left->terms = g_list_append(NULL, + copy_and_terms(g_list_nth_data(q->terms, 0))); + + iright = gncQueryInvert(right); + ileft = gncQueryInvert(left); + + retval = gncQueryMerge(iright, ileft, QUERY_AND); + retval->max_results = q->max_results; + retval->changed = 1; + + gncQueryDestroy(iright); + gncQueryDestroy(ileft); + gncQueryDestroy(right); + gncQueryDestroy(left); + break; + } + + return retval; +} + +/******************************************************************** + * gncQueryMerge + * combine 2 Query objects by the logical operation in "op". + ********************************************************************/ + +QueryNew * gncQueryMerge(QueryNew *q1, QueryNew *q2, QueryOp op) +{ + + QueryNew * retval = NULL; + QueryNew * i1, * i2; + QueryNew * t1, * t2; + GList * i, * j; + + if(!q1 || !q2 ) return NULL; + + switch(op) + { + case QUERY_OR: + retval = gncQueryCreate(); + retval->terms = + g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms)); + retval->max_results = q1->max_results; + retval->changed = 1; + break; + + case QUERY_AND: + retval = gncQueryCreate(); + retval->max_results = q1->max_results; + retval->changed = 1; + + for(i=q1->terms; i; i=i->next) + { + for(j=q2->terms; j; j=j->next) + { + retval->terms = + g_list_append(retval->terms, + g_list_concat + (copy_and_terms(i->data), + copy_and_terms(j->data))); + } + } + break; + + case QUERY_NAND: + /* !(a*b) = (!a + !b) */ + i1 = gncQueryInvert(q1); + i2 = gncQueryInvert(q2); + retval = gncQueryMerge(i1, i2, QUERY_OR); + gncQueryDestroy(i1); + gncQueryDestroy(i2); + break; + + case QUERY_NOR: + /* !(a+b) = (!a*!b) */ + i1 = gncQueryInvert(q1); + i2 = gncQueryInvert(q2); + retval = gncQueryMerge(i1, i2, QUERY_AND); + gncQueryDestroy(i1); + gncQueryDestroy(i2); + break; + + case QUERY_XOR: + /* a xor b = (a * !b) + (!a * b) */ + i1 = gncQueryInvert(q1); + i2 = gncQueryInvert(q2); + t1 = gncQueryMerge(q1, i2, QUERY_AND); + t2 = gncQueryMerge(i1, q2, QUERY_AND); + retval = gncQueryMerge(t1, t2, QUERY_OR); + + gncQueryDestroy(i1); + gncQueryDestroy(i2); + gncQueryDestroy(t1); + gncQueryDestroy(t2); + break; + } + + return retval; +} + +void +gncQuerySetSortOrder (QueryNew *q, + GNCIdTypeConst prim_type, const char *prim_param, + GNCIdTypeConst sec_type, const char *sec_param, + GNCIdTypeConst tert_type, const char *tert_param) +{ + if (!q) return; + q->primary_sort.obj_name = (GNCIdType)prim_type; + q->primary_sort.param_name = prim_param; + q->primary_sort.options = 0; + q->secondary_sort.obj_name = (GNCIdType)sec_type; + q->secondary_sort.param_name = sec_param; + q->secondary_sort.options = 0; + q->tertiary_sort.obj_name = (GNCIdType)tert_type; + q->tertiary_sort.param_name = tert_param; + q->tertiary_sort.options = 0; +} + +void gncQuerySetSortOptions (QueryNew *q, gint prim_op, gint sec_op, + gint tert_op) +{ + if (!q) return; + q->primary_sort.options = prim_op; + q->secondary_sort.options = sec_op; + q->tertiary_sort.options = tert_op; +} + +void gncQuerySetSortIncreasing (QueryNew *q, gboolean prim_inc, + gboolean sec_inc, gboolean tert_inc) +{ + if (!q) return; + q->primary_sort.increasing = prim_inc; + q->secondary_sort.increasing = sec_inc; + q->tertiary_sort.increasing = tert_inc; +} + +void gncQuerySetMaxResults (QueryNew *q, int n) +{ + if (!q) return; + q->max_results = n; +} + +int gncQueryGetMaxResults (QueryNew *q) +{ + if (!q) return 0; + return q->max_results; +} + +void gncQueryAddGUIDMatch (QueryNew *q, QueryOp op, + GNCIdTypeConst obj_type, const char *param_name, + const GUID *guid) +{ + QueryPredData_t pdata; + GList *g = NULL; + + if (!q || !obj_type || !param_name) return; + + if (guid) + g = g_list_prepend (g, (gpointer)guid); + + pdata = gncQueryGUIDPredicate (guid ? GUID_MATCH_ANY : GUID_MATCH_NULL, g); + g_list_free (g); + + gncQueryAddTerm (q, obj_type, param_name, COMPARE_EQUAL, pdata, op); +} + +void gncQuerySetBook (QueryNew *q, GNCBook *book) +{ + if (!q || !book) return; + + q->books = g_list_prepend (q->books, book); + gncQueryAddGUIDMatch (q, QUERY_AND, GNC_ID_BOOK, BOOK_GUID, + gnc_book_get_guid(book)); +} + diff --git a/src/engine/QueryNew.h b/src/engine/QueryNew.h new file mode 100644 index 0000000000..3562fc77ac --- /dev/null +++ b/src/engine/QueryNew.h @@ -0,0 +1,187 @@ +/* + * QueryNew.h -- API for finding Gnucash objects + * Copyright (C) 2002 Derek Atkins + * + */ + +#ifndef GNC_QUERYNEW_H +#define GNC_QUERYNEW_H + +#include "GNCId.h" + +/* A Query */ +typedef struct querynew_s QueryNew; + +/* Query Term Operators, for combining Query Terms */ +typedef enum { + QUERY_AND=1, + QUERY_OR, + QUERY_NAND, + QUERY_NOR, + QUERY_XOR +} QueryOp; + + +/* Standard Query Term comparitors, for how to process a query term. + * Note that not all core types implement all comparitors + */ +typedef enum { + COMPARE_LT = 1, + COMPARE_LTE, + COMPARE_EQUAL, + COMPARE_GT, + COMPARE_GTE +} query_compare_t; + +#define QUERY_DEFAULT_SORT "GnucashQueryDefaultSortObject" + +/* "Known" Object Parameters */ +#define SPLIT_KVP "kvp" +#define SPLIT_GUID "guid" +#define SPLIT_DATE_RECONCILED "date-reconciled" +#define SPLIT_BALANCE "balance" +#define SPLIT_CLEARED_BALANCE "cleared-balance" +#define SPLIT_RECONCILED_BALANCE "reconciled-balance" +#define SPLIT_MEMO "memo" +#define SPLIT_ACTION "action" +#define SPLIT_RECONCILE "reconcile-flag" +#define SPLIT_AMOUNT "amount" +#define SPLIT_SHARE_PRICE "share-price" +#define SPLIT_VALUE "value" +#define SPLIT_TYPE "type" +#define SPLIT_VOIDED_AMOUNT "voided-amount" +#define SPLIT_VOIDED_VALUE "voided-value" + +#define TRANS_KVP "kvp" +#define TRANS_GUID "guid" +#define TRANS_NUM "num" +#define TRANS_DESCRIPTON "desc" +#define TRANS_DATE_ENTERED "date-entered" +#define TRANS_DATE_POSTED "date-posted" +#define TRANS_DATE_DUE "date-due" +#define TRANS_TYPE "type" +#define TRANS_VOID_STATUS "void-p" +#define TRANS_VOID_REASON "void-reason" +#define TRANS_VOID_TIME "void-time" + +#define ACCOUNT_KVP "kvp" +#define ACCOUNT_GUID "guid" +#define ACCOUNT_NAME "name" +#define ACCOUNT_CODE "code" +#define ACCOUNT_DESCRIPTION "desc" +#define ACCOUNT_NOTES "notes" +#define ACCOUNT_BALANCE "balance" +#define ACCOUNT_CLEARED_BALANCE "cleared-balance" +#define ACCOUNT_RECONCILED_BALANCE "reconciled-balance" +#define ACCOUNT_TAX_RELATED "tax-related-p" + +#define BOOK_KVP "kvp" +#define BOOK_GUID "guid" + +/* Type of Query Core Objects (String, Date, Numeric, GUID, etc. */ +typedef const char * QueryCoreType; + +/* + * List of known core query types... + * Each core query type defines it's set of optional "comparitor qualifiers". + */ +#define QUERYCORE_STRING "string" +typedef enum { + STRING_MATCH_NORMAL = 1, + STRING_MATCH_CASEINSENSITIVE +} string_match_t; + +#define QUERYCORE_DATE "date" +typedef enum { + DATE_MATCH_NORMAL = 1, + DATE_MATCH_ROUNDED +} date_match_t; + +#define QUERYCORE_NUMERIC "numeric" +typedef enum { + NUMERIC_MATCH_NEG_ONLY = 1, + NUMERIC_MATCH_POS_ONLY, + NUMERIC_MATCH_ANY +} numeric_match_t; + +#define QUERYCORE_GUID "guid" +typedef enum { + GUID_MATCH_ANY = 1, + GUID_MATCH_NONE, + GUID_MATCH_NULL +} guid_match_t; + +#define QUERYCORE_INT64 "gint64" +#define QUERYCORE_DOUBLE "double" +#define QUERYCORE_BOOLEAN "boolean" +#define QUERYCORE_KVP "kvp" + +/* A CHAR type is for a RECNCell */ +#define QUERYCORE_CHAR "character" +typedef enum { + CHAR_MATCH_ANY = 1, + CHAR_MATCH_NONE +} char_match_t; + +/* Basic API Functions */ + +/* This is the general function that adds a new Query Term to a query. + * It will find the 'obj_type' object of the search item and compare + * the 'param_name' parameter to the predicate data via the comparitor. + * + * For example: + * + * acct_name_pred_data = make_string_pred_data(STRING_MATCH_CASEINSENSITIVE, + * account_name); + * gncQueryAddTerm (query, GNC_ID_ACCOUNT, QUERY_ACCOUNT_NAME, + * COMPARE_EQUAL, acct_name_pred_data, QUERY_AND); + */ + +typedef struct query_pred_data *QueryPredData_t; + +void gncQueryAddTerm (QueryNew *query, + GNCIdTypeConst obj_type, const char *param_name, + query_compare_t comparitor, QueryPredData_t pred_data, + QueryOp op); + +void gncQuerySetBook (QueryNew *q, GNCBook *book); +void gncQueryAddGUIDMatch (QueryNew *q, QueryOp op, + GNCIdTypeConst obj_type, const char *param_name, + const GUID *guid); + +/* Run the query: + * + * ex: gncQueryRun (query, GNC_ID_SPLIT); + */ +GList * gncQueryRun (QueryNew *query, GNCIdTypeConst obj_type); + +QueryNew * gncQueryCreate (void); +void gncQueryDestroy (QueryNew *q); + +void gncQueryClear (QueryNew *query); +void gncQueryPurgeTerms (QueryNew *q, GNCIdTypeConst obj_type, + const char *param_name); +int gncQueryHasTerms (QueryNew *q); +int gncQueryNumTerms (QueryNew *q); +GList * gncQueryGetTerms (QueryNew *q); + +QueryNew * gncQueryCopy (QueryNew *q); +QueryNew * gncQueryInvert(QueryNew *q); +QueryNew * gncQueryMerge(QueryNew *q1, QueryNew *q2, QueryOp op); + +void gncQuerySetSortOrder (QueryNew *q, + GNCIdTypeConst prim_type, const char *prim_param, + GNCIdTypeConst secy_type, const char *sec_param, + GNCIdTypeConst tert_type, const char *tert_param); + +void gncQuerySetSortOptions (QueryNew *q, gint prim_op, gint sec_op, + gint tert_op); + +void gncQuerySetSortIncreasing (QueryNew *q, gboolean prim_inc, + gboolean sec_inc, gboolean tert_inc); + + +void gncQuerySetMaxResults (QueryNew *q, int n); +int gncQueryGetMaxResults (QueryNew *q); + +#endif /* GNC_QUERYNEW_H */ diff --git a/src/engine/QueryNewP.h b/src/engine/QueryNewP.h new file mode 100644 index 0000000000..5ba961565d --- /dev/null +++ b/src/engine/QueryNewP.h @@ -0,0 +1,13 @@ +/* + * QueryNewP.h -- API for finding Gnucash objects + * Copyright (C) 2002 Derek Atkins + */ + +#ifndef GNC_QUERYNEWP_H +#define GNC_QUERYNEWP_H + +/* Initialize/Shutdown */ +void gncQueryNewInit (void); +void gncQueryNewShutdown (void); + +#endif /* GNC_QUERYNEWP_H */ diff --git a/src/engine/QueryObject.c b/src/engine/QueryObject.c new file mode 100644 index 0000000000..7b8d954846 --- /dev/null +++ b/src/engine/QueryObject.c @@ -0,0 +1,317 @@ +/* + * QueryObject.c -- provide Gnucash Queriable data objects + * Copyright (C) 2002 Derek Atkins + * + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-engine-util.h" +#include "QueryObjectP.h" +#include "QueryNew.h" + +#include "Account.h" +#include "Transaction.h" + +static short module = MOD_QUERY; + +static GHashTable *paramTable = NULL; +static GHashTable *convTable = NULL; +static GHashTable *sortTable = NULL; +static gboolean initialized = FALSE; + +typedef enum { + TYPE_PARAM = 1, + TYPE_CONV +} QueryObjectType; + +/* Stupid function to perform a no-op, to make the interface clean */ +static gpointer self_convert (gpointer obj) +{ + return obj; +} + +static GHashTable * get_object_table (GNCIdType name, QueryObjectType type) +{ + GHashTable *ht = NULL, *obj_ht = NULL; + + g_return_val_if_fail (name, NULL); + g_return_val_if_fail (initialized, NULL); + + switch (type) { + case TYPE_PARAM: + ht = paramTable; + break; + case TYPE_CONV: + ht = convTable; + } + + if (!ht) { + PWARN ("Aiee -- no hash table"); + return NULL; + } + + obj_ht = g_hash_table_lookup (ht, name); + + /* If it doesn't already exist, create a new table for this object */ + if (!obj_ht) { + obj_ht = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (ht, (char *)name, obj_ht); + } + + return obj_ht; +} + +static void insert_method (GNCIdType objname, const char *param, + gconstpointer method, QueryObjectType type) +{ + GHashTable *ht; + + g_return_if_fail (objname); + g_return_if_fail (param); + g_return_if_fail (method); + + ht = get_object_table (objname, type); + g_return_if_fail (ht); + + g_hash_table_insert (ht, (char *)param, (gpointer)method); +} + +static void init_split (void) +{ + static const QueryObjectDef params[] = { + { SPLIT_KVP, QUERYCORE_KVP, (QueryAccess)xaccSplitGetSlots }, + { SPLIT_GUID, QUERYCORE_GUID, (QueryAccess) xaccSplitGetGUID }, + { SPLIT_DATE_RECONCILED, QUERYCORE_DATE, + (QueryAccess)xaccSplitRetDateReconciledTS }, + { SPLIT_BALANCE, QUERYCORE_NUMERIC, (QueryAccess)xaccSplitGetBalance }, + { SPLIT_CLEARED_BALANCE, QUERYCORE_NUMERIC, + (QueryAccess)xaccSplitGetClearedBalance }, + { SPLIT_RECONCILED_BALANCE, QUERYCORE_NUMERIC, + (QueryAccess)xaccSplitGetReconciledBalance }, + { SPLIT_MEMO, QUERYCORE_STRING, (QueryAccess)xaccSplitGetMemo }, + { SPLIT_ACTION, QUERYCORE_STRING, (QueryAccess)xaccSplitGetAction }, + { SPLIT_RECONCILE, QUERYCORE_CHAR, (QueryAccess)xaccSplitGetReconcile }, + { SPLIT_AMOUNT, QUERYCORE_NUMERIC, (QueryAccess)xaccSplitGetAmount }, + { SPLIT_SHARE_PRICE, QUERYCORE_NUMERIC, + (QueryAccess)xaccSplitGetSharePrice }, + { SPLIT_VALUE, QUERYCORE_NUMERIC, (QueryAccess)xaccSplitGetValue }, + { SPLIT_TYPE, QUERYCORE_STRING, (QueryAccess)xaccSplitGetType }, + { SPLIT_VOIDED_AMOUNT, QUERYCORE_NUMERIC, + (QueryAccess)xaccSplitVoidFormerAmount }, + { SPLIT_VOIDED_VALUE, QUERYCORE_NUMERIC, + (QueryAccess)xaccSplitVoidFormerValue }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_TRANS, (QueryConvert)xaccSplitGetParent }, + { GNC_ID_ACCOUNT, (QueryConvert)xaccSplitGetAccount }, + { GNC_ID_BOOK, (QueryConvert)xaccSplitGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (GNC_ID_SPLIT, (QuerySort)xaccSplitDateOrder, + params, converters); +} + +static void init_txn (void) +{ + static QueryObjectDef params[] = { + { TRANS_KVP, QUERYCORE_KVP, (QueryAccess)xaccTransGetSlots }, + { TRANS_GUID, QUERYCORE_GUID, (QueryAccess)xaccTransGetGUID }, + { TRANS_NUM, QUERYCORE_STRING, (QueryAccess)xaccTransGetNum }, + { TRANS_DESCRIPTON, QUERYCORE_STRING, (QueryAccess)xaccTransGetDescription }, + { TRANS_DATE_ENTERED, QUERYCORE_DATE, (QueryAccess)xaccTransRetDateEnteredTS }, + { TRANS_DATE_POSTED, QUERYCORE_DATE, (QueryAccess)xaccTransRetDatePostedTS }, + { TRANS_DATE_DUE, QUERYCORE_DATE, (QueryAccess)xaccTransRetDateDueTS }, + { TRANS_TYPE, QUERYCORE_CHAR, (QueryAccess)xaccTransGetTxnType }, + { TRANS_VOID_STATUS, QUERYCORE_BOOLEAN, (QueryAccess)xaccTransGetVoidStatus }, + { TRANS_VOID_REASON, QUERYCORE_STRING, (QueryAccess)xaccTransGetVoidReason }, + { TRANS_VOID_TIME, QUERYCORE_DATE, (QueryAccess)xaccTransGetVoidTime }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)xaccTransGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (GNC_ID_TRANS, (QuerySort)xaccTransOrder, + params, converters); +} + +static void init_account (void) +{ + static QueryObjectDef params[] = { + { ACCOUNT_KVP, QUERYCORE_KVP, (QueryAccess)xaccAccountGetSlots }, + { ACCOUNT_GUID, QUERYCORE_GUID, (QueryAccess)xaccAccountGetGUID }, + { ACCOUNT_NAME, QUERYCORE_STRING, (QueryAccess)xaccAccountGetName }, + { ACCOUNT_CODE, QUERYCORE_STRING, (QueryAccess)xaccAccountGetCode }, + { ACCOUNT_DESCRIPTION, QUERYCORE_STRING, (QueryAccess)xaccAccountGetDescription }, + { ACCOUNT_NOTES, QUERYCORE_STRING, (QueryAccess)xaccAccountGetNotes }, + { ACCOUNT_BALANCE, QUERYCORE_NUMERIC, (QueryAccess)xaccAccountGetBalance }, + { ACCOUNT_CLEARED_BALANCE, QUERYCORE_NUMERIC, (QueryAccess)xaccAccountGetClearedBalance }, + { ACCOUNT_RECONCILED_BALANCE, QUERYCORE_NUMERIC, (QueryAccess)xaccAccountGetReconciledBalance }, + { ACCOUNT_TAX_RELATED, QUERYCORE_BOOLEAN, (QueryAccess)xaccAccountGetTaxRelated }, + { NULL }, + }; + static const QueryConvertDef converters[] = { + { GNC_ID_BOOK, (QueryConvert)xaccAccountGetBook }, + { NULL }, + }; + + gncQueryObjectRegister (GNC_ID_ACCOUNT, (QuerySort)xaccAccountOrder, + params, converters); +} + +static void init_book (void) +{ + static QueryObjectDef params[] = { + { BOOK_KVP, QUERYCORE_KVP, (QueryAccess)gnc_book_get_slots }, + { BOOK_GUID, QUERYCORE_GUID, (QueryAccess)gnc_book_get_guid }, + { NULL }, + }; + + gncQueryObjectRegister (GNC_ID_BOOK, NULL, params, NULL); +} + +static void init_tables (void) +{ + init_split (); + init_txn (); + init_account (); + init_book (); +} + +static gboolean clear_table (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_destroy (value); + return TRUE; +} + +/********************************************************************/ +/* PUBLISHED API FUNCTIONS */ + +void gncQueryObjectRegister (GNCIdType obj_name, + QuerySort default_sort_function, + const QueryObjectDef *params, + const QueryConvertDef *converters) +{ + int i; + + if (!obj_name) return; + + if (default_sort_function) + g_hash_table_insert (sortTable, (char *)obj_name, default_sort_function); + + if (params) { + for (i = 0; params[i].param_name; i++) + insert_method (obj_name, params[i].param_name, &(params[i]), TYPE_PARAM); + } + + if (converters) { + for (i = 0; converters[i].desired_object_name; i++) + insert_method (obj_name, converters[i].desired_object_name, + &(converters[i]), TYPE_CONV); + } +} + +void gncQueryObjectInit(void) +{ + if (initialized) return; + initialized = TRUE; + + paramTable = g_hash_table_new (g_str_hash, g_str_equal); + convTable = g_hash_table_new (g_str_hash, g_str_equal); + sortTable = g_hash_table_new (g_str_hash, g_str_equal); + + init_tables (); +} + +void gncQueryObjectShutdown (void) +{ + if (!initialized) return; + initialized = FALSE; + + g_hash_table_foreach_remove (paramTable, clear_table, NULL); + g_hash_table_destroy (paramTable); + + g_hash_table_foreach_remove (convTable, clear_table, NULL); + g_hash_table_destroy (convTable); + + g_hash_table_destroy (sortTable); +} + + +const QueryObjectDef * gncQueryObjectGetParameter (GNCIdType obj_name, + const char *parameter) +{ + GHashTable *ht; + + g_return_val_if_fail (obj_name, NULL); + g_return_val_if_fail (parameter, NULL); + + ht = get_object_table (obj_name, TYPE_PARAM); + g_return_val_if_fail (ht, NULL); + + return (g_hash_table_lookup (ht, parameter)); +} + +QueryAccess gncQueryObjectGetParamaterGetter (GNCIdType obj_name, + const char *parameter) +{ + const QueryObjectDef *obj; + + g_return_val_if_fail (obj_name, NULL); + g_return_val_if_fail (parameter, NULL); + + obj = gncQueryObjectGetParameter (obj_name, parameter); + if (obj) + return obj->param_getfcn; + + return NULL; +} + +QueryConvert gncQueryObjectGetConverter (GNCIdType from_obj, + GNCIdType to_obj) +{ + GHashTable *ht; + QueryConvertDef *conv; + + g_return_val_if_fail (from_obj, NULL); + g_return_val_if_fail (to_obj, NULL); + + if (from_obj == to_obj || !safe_strcmp (from_obj, to_obj)) + return self_convert; + + ht = get_object_table (from_obj, TYPE_CONV); + g_return_val_if_fail (ht, NULL); + + conv = g_hash_table_lookup (ht, to_obj); + if (conv) + return conv->object_getfcn; + + return NULL; +} + +QueryCoreType gncQueryObjectParameterType (GNCIdType obj_name, + const char *param_name) +{ + const QueryObjectDef *obj; + + if (!obj_name || !param_name) return NULL; + + obj = gncQueryObjectGetParameter (obj_name, param_name); + if (!obj) return NULL; + + return (obj->param_type); +} + +QuerySort gncQueryObjectDefaultSort (GNCIdType obj_name) +{ + if (!obj_name) return NULL; + return g_hash_table_lookup (sortTable, obj_name); +} diff --git a/src/engine/QueryObject.h b/src/engine/QueryObject.h new file mode 100644 index 0000000000..ad985be124 --- /dev/null +++ b/src/engine/QueryObject.h @@ -0,0 +1,74 @@ +/* + * QueryObject.h -- API for registering queriable Gnucash objects + * Copyright (C) 2002 Derek Atkins + * + */ + +#ifndef GNC_QUERYOBJECT_H +#define GNC_QUERYOBJECT_H + +#include "QueryNew.h" + +/* Define an arbitrary function pointer for access functions. This is + * because C doesn't have templates, so we just cast a lot. Real + * functions must be of the form: + * + * function (object_type *obj); + */ +typedef void (*QueryAccess)(gpointer); + +/* This structure is for each queriable parameter in an object */ +typedef struct query_object_def { + const char * param_name; + QueryCoreType param_type; + QueryAccess param_getfcn; +} QueryObjectDef; + +/* This function-type will convert from one object-type to another */ +typedef gpointer (*QueryConvert)(gpointer); + +typedef struct query_convert_def { + GNCIdType desired_object_name; + QueryConvert object_getfcn; +} QueryConvertDef; + +/* This function is the default sort function for a particular object type */ +typedef int (*QuerySort)(gpointer, gpointer); + +/* This function registers a new Gnucash Object with the QueryNew + * subsystem. In particular it registers the set of parameters and + * converters to query the type-specific data. Both "params" and + * "converters" are NULL-terminated arrays of structures. Either + * argument may be NULL if there is nothing to be registered. + */ +void gncQueryObjectRegister (GNCIdType obj_name, + QuerySort default_sort_fcn, + const QueryObjectDef *params, + const QueryConvertDef *converters); + +/* An example: + * + * #define MY_QUERY_OBJ_MEMO "memo" + * #define MY_QUERY_OBJ_VALUE "value" + * #define MY_QUERY_OBJ_DATE "date" + * + * static QueryObjectDef myQueryObjectParams[] = { + * { MY_QUERY_OBJ_MEMO, QUERYCORE_STRING, myMemoGetter }, + * { MY_QUERY_OBJ_VALUE, QUERYCORE_NUMERIC, myValueGetter }, + * { MY_QUERY_OBJ_DATE, QUERYCORE_DATE, myDateGetter }, + * NULL }; + * + * static QueryConvertDef myQueryObjectsConvs[] = { + * { GNC_ID_ACCOUNT, myAccountGetter }, + * { GNC_ID_TRANS, myTransactionGetter }, + * NULL }; + * + * gncQueryObjectRegisterParamters ("myObjectName", &myQueryObjectParams, + * &myQueryObjectConvs); + */ + +/* Return the core datatype of the specified object's parameter */ +QueryCoreType gncQueryObjectParameterType (GNCIdType obj_name, + const char *param_name); + +#endif /* GNC_QUERYOBJECT_H */ diff --git a/src/engine/QueryObjectP.h b/src/engine/QueryObjectP.h new file mode 100644 index 0000000000..9eebf0fbf8 --- /dev/null +++ b/src/engine/QueryObjectP.h @@ -0,0 +1,26 @@ +/* + * QueryObject.h -- Private API for registering queriable Gnucash objects + * Copyright (C) 2002 Derek Atkins + * + */ + +#ifndef GNC_QUERYOBJECTP_H +#define GNC_QUERYOBJECTP_H + +#include "QueryObject.h" + +void gncQueryObjectInit(void); +void gncQueryObjectShutdown (void); + +const QueryObjectDef * gncQueryObjectGetParameter (GNCIdType obj_name, + const char *parameter); + +QueryAccess gncQueryObjectGetParamaterGetter (GNCIdType obj_name, + const char *parameter); + +QueryConvert gncQueryObjectGetConverter (GNCIdType from_obj, + GNCIdType to_obj); + +QuerySort gncQueryObjectDefaultSort (GNCIdType obj_name); + +#endif /* GNC_QUERYOBJECTP_H */ diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index a655c67b3e..9f8572d039 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -2600,6 +2600,16 @@ xaccTransGetDateDueTS (Transaction *trans, Timespec *ts) xaccTransGetDatePostedTS (trans, ts); } +Timespec +xaccTransRetDateDueTS (Transaction *trans) +{ + Timespec ts; + ts.tv_sec = 0; ts.tv_nsec = 0; + if (!trans) return ts; + xaccTransGetDateDueTS (trans, &ts); + return ts; +} + char xaccTransGetTxnType (Transaction *trans) { @@ -2793,6 +2803,13 @@ xaccSplitGetSharePrice (Split * split) { /********************************************************************\ \********************************************************************/ +GNCBook * +xaccSplitGetBook (Split *split) +{ + if (!split) return NULL; + return split->book; +} + const char * xaccSplitGetType(const Split *s) { diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index d6b38a5a7b..da76c6e943 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -227,6 +227,7 @@ Timespec xaccTransRetDateEnteredTS (Transaction *trans); Timespec xaccTransRetDatePostedTS (Transaction *trans); /* Dates and txn-type for A/R and A/P "invoice" postings */ +Timespec xaccTransRetDateDueTS (Transaction *trans); void xaccTransGetDateDueTS (Transaction *trans, Timespec *ts); char xaccTransGetTxnType (Transaction *trans); @@ -367,6 +368,7 @@ void xaccSplitSetBaseValue (Split *split, gnc_numeric value, * of all transactions that have been marked as reconciled. */ +GNCBook * xaccSplitGetBook (Split *split); gnc_numeric xaccSplitGetBalance (Split *split); gnc_numeric xaccSplitGetClearedBalance (Split *split); gnc_numeric xaccSplitGetReconciledBalance (Split *split); diff --git a/src/engine/gnc-book.c b/src/engine/gnc-book.c index e8ee8b2d2b..c610cad027 100644 --- a/src/engine/gnc-book.c +++ b/src/engine/gnc-book.c @@ -59,6 +59,7 @@ #include "gnc-event.h" #include "gnc-event-p.h" #include "gnc-module.h" +#include "gncObjectP.h" static short module = MOD_IO; @@ -105,6 +106,7 @@ gnc_book_new (void) ENTER (" "); book = g_new0(GNCBook, 1); gnc_book_init(book); + gncObjectBookBegin (book); gnc_engine_generate_event (&book->guid, GNC_EVENT_CREATE); LEAVE ("book=%p", book); @@ -119,6 +121,8 @@ gnc_book_destroy (GNCBook *book) ENTER ("book=%p", book); gnc_engine_generate_event (&book->guid, GNC_EVENT_DESTROY); + gncObjectBookEnd (book); + xaccAccountGroupBeginEdit (book->topgroup); xaccAccountGroupDestroy (book->topgroup); book->topgroup = NULL; diff --git a/src/engine/gnc-engine-util.c b/src/engine/gnc-engine-util.c index 0388183844..6d219b3689 100644 --- a/src/engine/gnc-engine-util.c +++ b/src/engine/gnc-engine-util.c @@ -316,6 +316,13 @@ safe_strcmp (const char * da, const char * db) return 0; } +int +safe_strcasecmp (const char * da, const char * db) +{ + SAFE_STRCASECMP (da, db); + return 0; +} + int null_strcmp (const char * da, const char * db) { diff --git a/src/engine/gnc-engine-util.h b/src/engine/gnc-engine-util.h index 7313c24d8c..507bbfaf79 100644 --- a/src/engine/gnc-engine-util.h +++ b/src/engine/gnc-engine-util.h @@ -199,9 +199,9 @@ void gnc_set_logfile (FILE *outfile); #define DEQEPS(x,y,eps) (((((x)+(eps))>(y)) ? 1 : 0) && ((((x)-(eps))<(y)) ? 1 : 0)) #define DEQ(x,y) DEQEPS(x,y,EPS) -#define SAFE_STRCMP(da,db) { \ +#define SAFE_STRCMP_REAL(fcn,da,db) { \ if ((da) && (db)) { \ - int retval = strcmp ((da), (db)); \ + int retval = fcn ((da), (db)); \ /* if strings differ, return */ \ if (retval) return retval; \ } else \ @@ -213,6 +213,9 @@ void gnc_set_logfile (FILE *outfile); } \ } +#define SAFE_STRCMP(da,db) SAFE_STRCMP_REAL(strcmp,(da),(db)) +#define SAFE_STRCASECMP(da,db) SAFE_STRCMP_REAL(strcasecmp,(da),(db)) + /* Define the long long int conversion for scanf */ #if HAVE_SCANF_LLD # define GNC_SCANF_LLD "%lld" @@ -228,6 +231,7 @@ void gnc_set_logfile (FILE *outfile); * a non-null string is always greater than a null string. */ int safe_strcmp (const char * da, const char * db); +int safe_strcasecmp (const char * da, const char * db); /* The null_strcmp compares strings a and b the same way that strcmp() * does, except that either may be null. This routine assumes that diff --git a/src/engine/gnc-engine.c b/src/engine/gnc-engine.c index 7c86b23fde..c68b87c971 100644 --- a/src/engine/gnc-engine.c +++ b/src/engine/gnc-engine.c @@ -26,6 +26,8 @@ #include #include "GNCIdP.h" +#include "QueryNewP.h" +#include "gncObjectP.h" #include "gnc-engine.h" static GList * engine_init_hooks = NULL; @@ -70,6 +72,8 @@ gnc_engine_init(int argc, char ** argv) gnc_engine_get_string_cache(); xaccGUIDInit (); + gncObjectInitialize (); + gncQueryNewInit (); /* call any engine hooks */ for (cur = engine_init_hooks; cur; cur = cur->next) @@ -102,9 +106,12 @@ gnc_engine_get_string_cache(void) void gnc_engine_shutdown (void) { + gncQueryNewShutdown (); + g_cache_destroy (gnc_string_cache); gnc_string_cache = NULL; + gncObjectShutdown (); xaccGUIDShutdown (); } diff --git a/src/engine/gncObject.c b/src/engine/gncObject.c new file mode 100644 index 0000000000..eeb150e56e --- /dev/null +++ b/src/engine/gncObject.c @@ -0,0 +1,153 @@ +/* + * gncObject.c -- the Core Object Object Registry + * Copyright (C) 2001 Derek Atkins + * Author: Derek Atkins + */ + +#include "config.h" + +#include + +#include "messages.h" +#include "gnc-engine-util.h" + +#include "gncObjectP.h" + +static gboolean object_is_initialized = FALSE; +static GList *object_modules = NULL; +static GList *book_list = NULL; + +void gncObjectBookBegin (GNCBook *book) +{ + GList *l; + + if (!book) return; + for (l = object_modules; l; l = l->next) { + GncObject_t *obj = l->data; + if (obj->book_begin) + obj->book_begin (book); + } + + /* Remember this book for later */ + book_list = g_list_prepend (book_list, book); +} + +void gncObjectBookEnd (GNCBook *book) +{ + GList *l; + + if (!book) return; + for (l = object_modules; l; l = l->next) { + GncObject_t *obj = l->data; + if (obj->book_end) + obj->book_end (book); + } + + /* Remove it from the list */ + book_list = g_list_remove (book_list, book); +} + +void gncObjectForeach (GNCIdTypeConst type_name, GNCBook *book, + foreachObjectCB cb, gpointer user_data) +{ + const GncObject_t *obj; + + if (!book || !type_name) return; + + obj = gncObjectLookup (type_name); + if (!obj) return; + + if (obj->foreach) + return (obj->foreach (book, cb, user_data)); + + return; +} + +const char * +gncObjectPrintable (GNCIdTypeConst type_name, gpointer obj) +{ + const GncObject_t *b_obj; + + if (!type_name || !obj) return NULL; + + b_obj = gncObjectLookup (type_name); + if (!b_obj) return NULL; + + if (b_obj->printable) + return (b_obj->printable (obj)); + + return NULL; +} + +const char * gncObjectGetTypeLabel (GNCIdTypeConst type_name) +{ + const GncObject_t *obj; + + if (!type_name) return NULL; + + obj = gncObjectLookup (type_name); + if (!obj) return NULL; + + return _(obj->type_label); +} + +/* INITIALIZATION and PRIVATE FUNCTIONS */ + +void gncObjectInitialize (void) +{ + if (object_is_initialized) return; + object_is_initialized = TRUE; +} + +void gncObjectShutdown (void) +{ + g_return_if_fail (object_is_initialized == TRUE); + g_list_free (object_modules); + object_modules = NULL; + g_list_free (book_list); + book_list = NULL; + object_is_initialized = FALSE; +} + +/* Register new types of object objects. + * Return TRUE if successful, + * return FALSE if it fails, invalid arguments, or if the object + * already exists + */ +gboolean gncObjectRegister (const GncObject_t *object) +{ + if (!object) return FALSE; + if (object->version != GNC_OBJECT_VERSION) return FALSE; + if (!object_is_initialized) return FALSE; + + if (g_list_index (object_modules, (gpointer)object) == -1) + object_modules = g_list_prepend (object_modules, (gpointer)object); + else + return FALSE; + + /* Now initialize all the known books */ + if (object->book_begin && book_list) { + GList *node; + for (node = book_list; node; node = node->next) + object->book_begin (node->data); + } + + return TRUE; +} + +const GncObject_t * gncObjectLookup (GNCIdTypeConst name) +{ + GList *iter; + const GncObject_t *obj; + + g_return_val_if_fail (object_is_initialized, NULL); + + if (!name) return NULL; + + for (iter = object_modules; iter; iter = iter->next) { + obj = iter->data; + if (!safe_strcmp (obj->name, name)) + return obj; + } + return NULL; +} diff --git a/src/engine/gncObject.h b/src/engine/gncObject.h new file mode 100644 index 0000000000..4857f3f230 --- /dev/null +++ b/src/engine/gncObject.h @@ -0,0 +1,62 @@ +/* + * gncObject.h -- the Core Object Registration/Lookup Interface + * Copyright (C) 2001,2002 Derek Atkins + * Author: Derek Atkins + */ + +#ifndef GNC_OBJECT_H_ +#define GNC_OBJECT_H_ + +#include "gnc-book.h" +#include "GNCId.h" + +/* Defines the version of the core object object registration + * interface. Only object modules compiled against this version + * of the interface will load properly + */ +#define GNC_OBJECT_VERSION 1 + +typedef void (*foreachObjectCB) (gpointer object, gpointer user_data); + +/* This is the Object Object descriptor */ +typedef struct _gncObjectDef { + gint version; /* of the object interface */ + GNCIdType name; /* the Object's GNC_ID */ + const char * type_label; /* "Printable" type-label string */ + + /* book_begin is called from within the Book routines to create + * module-specific hooks in a book whenever a book is created. + * book_end is called when the book is being closed, to clean + * up (and free memory). + */ + void (*book_begin)(GNCBook *); + void (*book_end)(GNCBook *); + + /* foreach() is used to execute a callback over each object + * stored in the particular book + */ + void (*foreach)(GNCBook *, foreachObjectCB, gpointer); + + /* Given a particular object, return a printable string */ + const char * (*printable)(gpointer obj); +} GncObject_t; + +void gncObjectForeach (GNCIdTypeConst type_name, GNCBook *book, + foreachObjectCB cb, gpointer user_data); + +const char * gncObjectPrintable (GNCIdTypeConst type_name, gpointer obj); + + +/* REGISTRATION AND REG-LOOKUP FUNCTIONS */ + +/* Register new types of object objects */ +gboolean gncObjectRegister (const GncObject_t *object); + +/* Get the printable label for a type */ +const char * gncObjectGetTypeLabel (GNCIdTypeConst type_name); + +/* Lookup a object definition */ +const GncObject_t * gncObjectLookup (GNCIdTypeConst type_name); + + +#endif /* GNC_OBJECT_H_ */ diff --git a/src/engine/gncObjectP.h b/src/engine/gncObjectP.h new file mode 100644 index 0000000000..f8c09e316a --- /dev/null +++ b/src/engine/gncObjectP.h @@ -0,0 +1,21 @@ +/* + * gncObjectP.h -- the Core Object Registration Interface + * Copyright (C) 2001, 2002 Derek Atkins + * Author: Derek Atkins + */ + +#ifndef GNC_OBJECTP_H_ +#define GNC_OBJECTP_H_ + +#include "gncObject.h" + +/* Initialize the object registration subsystem */ +void gncObjectInitialize (void); +void gncObjectShutdown (void); + +/* To be called from within the book */ +void gncObjectBookBegin (GNCBook *book); +void gncObjectBookEnd (GNCBook *book); + + +#endif /* GNC_OBJECTP_H_ */ diff --git a/src/engine/test/Makefile.am b/src/engine/test/Makefile.am index 91cd3d7138..9634737a54 100644 --- a/src/engine/test/Makefile.am +++ b/src/engine/test/Makefile.am @@ -23,6 +23,7 @@ TESTS = \ test-commodities \ test-date \ test-group-vs-book \ + test-object \ test-period \ test-query \ test-resolve-file-path \ @@ -45,6 +46,7 @@ noinst_PROGRAMS = \ test-freq-spec \ test-group-vs-book \ test-load-engine \ + test-object \ test-period \ test-query \ test-resolve-file-path \ diff --git a/src/engine/test/test-object.c b/src/engine/test/test-object.c new file mode 100644 index 0000000000..e8d4d7cd15 --- /dev/null +++ b/src/engine/test/test-object.c @@ -0,0 +1,132 @@ +#include +#include + +#include "guid.h" +#include "gnc-module.h" +#include "gnc-engine-util.h" +#include "messages.h" + +#include "gncObject.h" +#include "test-stuff.h" + +#define TEST_MODULE_NAME "object-test" +#define TEST_MODULE_DESC "Test Object" + +static void foreach (GNCBook *, foreachObjectCB, gpointer); +static const char * printable (gpointer obj); +static void test_printable (const char *name, gpointer obj); +static void test_foreach (GNCBook *, const char *); + +static GncObject_t bus_obj = { + GNC_OBJECT_VERSION, + TEST_MODULE_NAME, + TEST_MODULE_DESC, + NULL, /* create */ + NULL, /* destroy */ + foreach, + printable, +}; + +static void test_object (void) +{ + /* Test the global registration and lookup functions */ + { + do_test (!gncObjectRegister (NULL), "register NULL"); + do_test (gncObjectRegister (&bus_obj), "register test object"); + do_test (!gncObjectRegister (&bus_obj), "register test object again"); + do_test (gncObjectLookup (TEST_MODULE_NAME) == &bus_obj, + "lookup our installed object"); + do_test (gncObjectLookup ("snm98sn snml say dyikh9y9ha") == NULL, + "lookup non-existant object object"); + + do_test (!safe_strcmp (gncObjectGetTypeLabel (TEST_MODULE_NAME), + _(TEST_MODULE_DESC)), + "test description return"); + } + + test_foreach ((GNCBook*)1, TEST_MODULE_NAME); + test_printable (TEST_MODULE_NAME, (gpointer)1); +} + +static void +foreach (GNCBook *book, foreachObjectCB cb, gpointer u_d) +{ + int *foo = u_d; + + do_test (book != NULL, "foreach: NULL object"); + success ("called foreach callback"); + + *foo = 1; +} + +static void foreachCB (gpointer obj, gpointer u_d) +{ + do_test (FALSE, "FAIL"); +} + +static const char * +printable (gpointer obj) +{ + do_test (obj != NULL, "printable: object is NULL"); + success ("called printable callback"); + return ((const char *)obj); +} + +static void +test_foreach (GNCBook *book, const char *name) +{ + int res = 0; + + gncObjectForeach (NULL, NULL, NULL, &res); + do_test (res == 0, "object: Foreach: NULL, NULL, NULL"); + gncObjectForeach (NULL, NULL, foreachCB, &res); + do_test (res == 0, "object: Foreach: NULL, NULL, foreachCB"); + + gncObjectForeach (NULL, book, NULL, &res); + do_test (res == 0, "object: Foreach: NULL, book, NULL"); + gncObjectForeach (NULL, book, foreachCB, &res); + do_test (res == 0, "object: Foreach: NULL, book, foreachCB"); + + gncObjectForeach (name, NULL, NULL, &res); + do_test (res == 0, "object: Foreach: name, NULL, NULL"); + gncObjectForeach (name, NULL, foreachCB, &res); + do_test (res == 0, "object: Foreach: name, NULL, foreachCB"); + + gncObjectForeach (name, book, NULL, &res); + do_test (res != 0, "object: Foreach: name, book, NULL"); + + res = 0; + gncObjectForeach (name, book, foreachCB, &res); + do_test (res != 0, "object: Foreach: name, book, foreachCB"); +} + +static void +test_printable (const char *name, gpointer obj) +{ + const char *res; + + do_test (gncObjectPrintable (NULL, NULL) == NULL, + "object: Printable: NULL, NULL"); + do_test (gncObjectPrintable (NULL, obj) == NULL, + "object: Printable: NULL, object"); + do_test (gncObjectPrintable (name, NULL) == NULL, + "object: Printable: mod_name, NULL"); + res = gncObjectPrintable (name, obj); + do_test (res != NULL, "object: Printable: mod_name, object"); +} + +static void +main_helper (int argc, char **argv) +{ + gnc_module_load("gnucash/engine", 0); + test_object(); + print_test_results(); + exit(get_rv()); +} + +int +main (int argc, char **argv) +{ + gh_enter (argc, argv, main_helper); + return 0; +}