* gncObject -- object registration in the engine; hook into the books

* QueryNew interface revision-1 check-in

 * update all the business objects to register with the new query system


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@6680 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Derek Atkins 2002-02-03 20:01:08 +00:00
parent beaab8a9f2
commit ff880b0efd
52 changed files with 3329 additions and 385 deletions

View File

@ -1,3 +1,20 @@
2002-02-03 Derek Atkins <warlord@MIT.EDU>
* 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 <dave@krondo.com>
* src/report/standard-reports/account-piecharts.scm: bump up default

View File

@ -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 ();

View File

@ -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 <warlord@MIT.EDU>
*/
#include "config.h"
#include <glib.h>
#include <string.h>
#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);
}

View File

@ -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 <warlord@MIT.EDU>
*/
#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_ */

View File

@ -1,6 +1,6 @@
/*
* gncCustomer.c -- the Core Customer Interface
* Copyright (C) 2001 Derek Atkins
* Copyright (C) 2001,2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
@ -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;

View File

@ -1,6 +1,6 @@
/*
* gncCustomer.h -- the Core Customer Interface
* Copyright (C) 2001 Derek Atkins
* Copyright (C) 2001,2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
@ -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_ */

View File

@ -1,6 +1,6 @@
/*
* gncEmployee.c -- the Core Employee Interface
* Copyright (C) 2001 Derek Atkins
* Copyright (C) 2001,2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
@ -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;

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -1,6 +1,6 @@
/*
* gncInvoice.c -- the Core Business Invoice
* Copyright (C) 2001 Derek Atkins
* Copyright (C) 2001,2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
@ -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 */

View File

@ -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_ */

View File

@ -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;

View File

@ -1,6 +1,6 @@
/*
* gncOrder.c -- the Core Business Order
* Copyright (C) 2001 Derek Atkins
* Copyright (C) 2001,2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
@ -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 */

View File

@ -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_ */

View File

@ -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;

View File

@ -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_ */

View File

@ -26,7 +26,6 @@
ws
(lambda (wrapset client-wrapset)
(list
"#include <gncBusiness.h>\n"
"#include <gncAddress.h>\n"
"#include <gncCustomer.h>\n"
"#include <gncEmployee.h>\n"
@ -57,18 +56,6 @@
(gw:wrap-as-wct ws '<gnc:GncOwner*> "GncOwner*" "const GncOwner*")
(gw:wrap-as-wct ws '<gnc:GncVendor*> "GncVendor*" "const GncVendor*")
;;
;; gncBusiness.h
;;
(gw:wrap-function
ws
'gnc:business-create-book
'<gw:void>
"gncBusinessCreateBook"
'((<gnc:Book*> book))
"Create the Business data tables in the book")
;; gncAddress.h
;; gncCustomer.h

View File

@ -19,7 +19,6 @@ test_address (void)
{
GncAddress *address;
GNCBook *book = gnc_book_new ();
gncBusinessCreateBook (book);
/* Test creation/destruction */
{

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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;

View File

@ -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")

View File

@ -9,12 +9,12 @@
#include <gnome.h>
#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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
};

View File

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

917
src/engine/QueryCore.c Normal file
View File

@ -0,0 +1,917 @@
/*
* QueryCore.c -- provide core Query data types
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#include "config.h"
#include <glib.h>
#include <regex.h>
#include <string.h>
#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));
}

89
src/engine/QueryCore.h Normal file
View File

@ -0,0 +1,89 @@
/*
* QueryCore.h -- API for providing core Query data types
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#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 <glib.h>
/* 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 */

22
src/engine/QueryCoreP.h Normal file
View File

@ -0,0 +1,22 @@
/*
* QueryCoreP.h -- Internal API for providing core Query data types
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#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 */

836
src/engine/QueryNew.c Normal file
View File

@ -0,0 +1,836 @@
/*
* QueryNew.c -- API for finding Gnucash objects
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#include "config.h"
#include <glib.h>
#include <regex.h>
#include <string.h>
#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));
}

187
src/engine/QueryNew.h Normal file
View File

@ -0,0 +1,187 @@
/*
* QueryNew.h -- API for finding Gnucash objects
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#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 */

13
src/engine/QueryNewP.h Normal file
View File

@ -0,0 +1,13 @@
/*
* QueryNewP.h -- API for finding Gnucash objects
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*/
#ifndef GNC_QUERYNEWP_H
#define GNC_QUERYNEWP_H
/* Initialize/Shutdown */
void gncQueryNewInit (void);
void gncQueryNewShutdown (void);
#endif /* GNC_QUERYNEWP_H */

317
src/engine/QueryObject.c Normal file
View File

@ -0,0 +1,317 @@
/*
* QueryObject.c -- provide Gnucash Queriable data objects
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#include "config.h"
#include <glib.h>
#include <regex.h>
#include <string.h>
#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);
}

74
src/engine/QueryObject.h Normal file
View File

@ -0,0 +1,74 @@
/*
* QueryObject.h -- API for registering queriable Gnucash objects
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#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:
*
* <param_type> 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 */

26
src/engine/QueryObjectP.h Normal file
View File

@ -0,0 +1,26 @@
/*
* QueryObject.h -- Private API for registering queriable Gnucash objects
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*
*/
#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 */

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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)
{

View File

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

View File

@ -26,6 +26,8 @@
#include <glib.h>
#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 ();
}

153
src/engine/gncObject.c Normal file
View File

@ -0,0 +1,153 @@
/*
* gncObject.c -- the Core Object Object Registry
* Copyright (C) 2001 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
#include "config.h"
#include <glib.h>
#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;
}

62
src/engine/gncObject.h Normal file
View File

@ -0,0 +1,62 @@
/*
* gncObject.h -- the Core Object Registration/Lookup Interface
* Copyright (C) 2001,2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
#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_ */

21
src/engine/gncObjectP.h Normal file
View File

@ -0,0 +1,21 @@
/*
* gncObjectP.h -- the Core Object Registration Interface
* Copyright (C) 2001, 2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
#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_ */

View File

@ -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 \

View File

@ -0,0 +1,132 @@
#include <glib.h>
#include <guile/gh.h>
#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;
}