Files
gnucash/src/engine/qofquerycore.c
David Hampton c8ec96cdf2 Add code to extract the date terms from a query.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/branches/gnucash-gnome2-dev@9438 57a11ea4-9604-0410-9ed3-97b8803252fd
2003-10-01 02:26:47 +00:00

1412 lines
38 KiB
C

/********************************************************************\
* QueryCore.c -- API for providing core Query data types *
* Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#include "config.h"
#include <glib.h>
#include "gnc-engine-util.h"
#include "gnc-trace.h"
#include "qofquery.h"
#include "qofquerycore.h"
#include "qofquerycore-p.h"
static short module = MOD_QUERY;
/* A function to destroy a query predicate's pdata */
typedef void (*QueryPredDataFree) (QofQueryPredData *pdata);
/* A function to copy a query's predicate data */
typedef QofQueryPredData *(*QueryPredicateCopyFunc) (QofQueryPredData *pdata);
/* A function to take the object, apply the get_fcn, and return
* a printable string. Note that this QofAccessFunc function should
* be returning a type equal to this core object type.
*
* Note that this string MUST be freed by the caller.
*/
typedef char * (*QueryToString) (gpointer object, QofAccessFunc get_fcn);
/* A function to test for equality of predicate data */
typedef gboolean (*QueryPredicateEqual) (QofQueryPredData *p1,
QofQueryPredData *p2);
/* This function registers a new Core Object with the QofQuery
* subsystem. It maps the "core_name" object to the given
* query_predicate, predicate_copy, and predicate_data_free functions.
*/
static void qof_query_register_core_object (char const *type_name,
QofQueryPredicateFunc pred,
QofCompareFunc comp,
QueryPredicateCopyFunc copy,
QueryPredDataFree pd_free,
QueryToString to_string,
QueryPredicateEqual pred_equal);
/* An example:
*
* qof_query_register_core_object (QOF_QUERYCORE_STRING, string_match_predicate,
* string_compare_fcn, string_free_pdata,
* string_print_fcn, pred_equal_fcn);
*/
static QueryPredicateCopyFunc gncQueryCoreGetCopy (char const *type);
static QueryPredDataFree gncQueryCoreGetPredFree (char const *type);
/* Core Type Predicate helpers */
typedef const char * (*query_string_getter) (gpointer);
static const char * query_string_type = QOF_QUERYCORE_STRING;
typedef Timespec (*query_date_getter) (gpointer);
static const char * query_date_type = QOF_QUERYCORE_DATE;
typedef gnc_numeric (*query_numeric_getter) (gpointer);
static const char * query_numeric_type = QOF_QUERYCORE_NUMERIC;
typedef GList * (*query_glist_getter) (gpointer);
typedef const GUID * (*query_guid_getter) (gpointer);
static const char * query_guid_type = QOF_QUERYCORE_GUID;
typedef gint32 (*query_int32_getter) (gpointer);
static const char * query_int32_type = QOF_QUERYCORE_INT32;
typedef gint64 (*query_int64_getter) (gpointer);
static const char * query_int64_type = QOF_QUERYCORE_INT64;
typedef double (*query_double_getter) (gpointer);
static const char * query_double_type = QOF_QUERYCORE_DOUBLE;
typedef gboolean (*query_boolean_getter) (gpointer);
static const char * query_boolean_type = QOF_QUERYCORE_BOOLEAN;
typedef char (*query_char_getter) (gpointer);
static const char * query_char_type = QOF_QUERYCORE_CHAR;
typedef KvpFrame * (*query_kvp_getter) (gpointer);
static const char * query_kvp_type = QOF_QUERYCORE_KVP;
/* Tables for predicate storage and lookup */
static gboolean initialized = FALSE;
static GHashTable *predTable = NULL;
static GHashTable *cmpTable = NULL;
static GHashTable *copyTable = NULL;
static GHashTable *freeTable = NULL;
static GHashTable *toStringTable = NULL;
static GHashTable *predEqualTable = 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 */
/* QOF_QUERYCORE_STRING */
static int string_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
query_string_t pdata = (query_string_t) pd;
const char *s;
int ret = 0;
VERIFY_PREDICATE (query_string_type);
s = ((query_string_getter)get_fcn) (object);
if (!s) s = "";
if (pdata->is_regex) {
regmatch_t match;
if (!regexec (&pdata->compiled, s, 1, &match, 0))
ret = 1;
} else if (pdata->options == QOF_STRING_MATCH_CASEINSENSITIVE) {
if (strcasestr (s, pdata->matchstring))
ret = 1;
} else {
if (strstr (s, pdata->matchstring))
ret = 1;
}
switch (pd->how) {
case QOF_COMPARE_EQUAL:
return ret;
case QOF_COMPARE_NEQ:
return !ret;
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int string_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc 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 == QOF_STRING_MATCH_CASEINSENSITIVE)
return safe_strcasecmp (s1, s2);
return safe_strcmp (s1, s2);
}
static void string_free_pdata (QofQueryPredData *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 QofQueryPredData *string_copy_predicate (QofQueryPredData *pd)
{
query_string_t pdata = (query_string_t) pd;
VERIFY_PDATA_R (query_string_type);
return qof_query_string_predicate (pd->how, pdata->matchstring, pdata->options,
pdata->is_regex);
}
static gboolean string_predicate_equal (QofQueryPredData *p1,
QofQueryPredData *p2)
{
query_string_t pd1 = (query_string_t) p1;
query_string_t pd2 = (query_string_t) p2;
if (pd1->options != pd2->options) return FALSE;
if (pd1->is_regex != pd2->is_regex) return FALSE;
return (safe_strcmp (pd1->matchstring, pd2->matchstring) == 0);
}
QofQueryPredData *qof_query_string_predicate (QofQueryCompare how,
char *str, QofStringMatch options,
gboolean is_regex)
{
query_string_t pdata;
g_return_val_if_fail (str, NULL);
g_return_val_if_fail (*str != '\0', NULL);
g_return_val_if_fail (how == QOF_COMPARE_EQUAL || how == QOF_COMPARE_NEQ, NULL);
pdata = g_new0 (query_string_def, 1);
pdata->pd.type_name = query_string_type;
pdata->pd.how = how;
pdata->options = options;
pdata->matchstring = g_strdup (str);
if (is_regex) {
int flags = REG_EXTENDED;
if (options == QOF_STRING_MATCH_CASEINSENSITIVE)
flags |= REG_ICASE;
regcomp(&pdata->compiled, str, flags);
pdata->is_regex = TRUE;
}
return ((QofQueryPredData*)pdata);
}
static char * string_to_string (gpointer object, QofAccessFunc get)
{
const char *res = ((query_string_getter)get)(object);
if (res)
return g_strdup (res);
return NULL;
}
/* QOF_QUERYCORE_DATE */
static int date_compare (Timespec ta, Timespec tb, QofDateMatch options)
{
if (options == QOF_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, QofAccessFunc get_fcn,
QofQueryPredData *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 (pd->how) {
case QOF_COMPARE_LT:
return (compare < 0);
case QOF_COMPARE_LTE:
return (compare <= 0);
case QOF_COMPARE_EQUAL:
return (compare == 0);
case QOF_COMPARE_GT:
return (compare > 0);
case QOF_COMPARE_GTE:
return (compare >= 0);
case QOF_COMPARE_NEQ:
return (compare != 0);
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int date_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc 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 (QofQueryPredData *pd)
{
query_date_t pdata = (query_date_t)pd;
VERIFY_PDATA (query_date_type);
g_free (pdata);
}
static QofQueryPredData *
date_copy_predicate (QofQueryPredData *pd)
{
query_date_t pdata = (query_date_t)pd;
VERIFY_PDATA_R (query_date_type);
return qof_query_date_predicate (pd->how, pdata->options, pdata->date);
}
static gboolean date_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_date_t pd1 = (query_date_t) p1;
query_date_t pd2 = (query_date_t) p2;
if (pd1->options != pd2->options) return FALSE;
return timespec_equal (&(pd1->date), &(pd2->date));
}
QofQueryPredData *
qof_query_date_predicate (QofQueryCompare how,
QofDateMatch options, Timespec date)
{
query_date_t pdata;
pdata = g_new0 (query_date_def, 1);
pdata->pd.type_name = query_date_type;
pdata->pd.how = how;
pdata->options = options;
pdata->date = date;
return ((QofQueryPredData*)pdata);
}
gboolean
qof_query_date_predicate_get_date (QofQueryPredData *pd, Timespec *date)
{
query_date_t pdata = (query_date_t)pd;
if (pdata->pd.type_name != query_date_type)
return FALSE;
*date = pdata->date;
return TRUE;
}
static char * date_to_string (gpointer object, QofAccessFunc get)
{
Timespec ts = ((query_date_getter)get)(object);
if (ts.tv_sec || ts.tv_nsec)
return g_strdup (gnc_print_date (ts));
return NULL;
}
/* QOF_QUERYCORE_NUMERIC */
static int numeric_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData* 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 QOF_NUMERIC_MATCH_CREDIT:
if (gnc_numeric_positive_p (obj_val)) return 0;
break;
case QOF_NUMERIC_MATCH_DEBIT:
if (gnc_numeric_negative_p (obj_val)) return 0;
break;
default:
break;
}
if (pd->how == QOF_COMPARE_EQUAL || pd->how == QOF_COMPARE_NEQ) {
gnc_numeric cmp_val = gnc_numeric_create (1, 10000);
compare =
(gnc_numeric_compare (gnc_numeric_abs
(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 (pd->how) {
case QOF_COMPARE_LT:
return (compare < 0);
case QOF_COMPARE_LTE:
return (compare <= 0);
case QOF_COMPARE_EQUAL:
return compare;
case QOF_COMPARE_GT:
return (compare > 0);
case QOF_COMPARE_GTE:
return (compare >= 0);
case QOF_COMPARE_NEQ:
return !compare;
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int numeric_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc 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 (QofQueryPredData* pd)
{
query_numeric_t pdata = (query_numeric_t)pd;
VERIFY_PDATA (query_numeric_type);
g_free (pdata);
}
static QofQueryPredData *
numeric_copy_predicate (QofQueryPredData *pd)
{
query_numeric_t pdata = (query_numeric_t)pd;
VERIFY_PDATA_R (query_numeric_type);
return qof_query_numeric_predicate (pd->how, pdata->options, pdata->amount);
}
static gboolean
numeric_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_numeric_t pd1 = (query_numeric_t) p1;
query_numeric_t pd2 = (query_numeric_t) p2;
if (pd1->options != pd2->options) return FALSE;
return gnc_numeric_equal (pd1->amount, pd2->amount);
}
QofQueryPredData *
qof_query_numeric_predicate (QofQueryCompare how,
QofNumericMatch options,
gnc_numeric value)
{
query_numeric_t pdata;
pdata = g_new0 (query_numeric_def, 1);
pdata->pd.type_name = query_numeric_type;
pdata->pd.how = how;
pdata->options = options;
pdata->amount = value;
return ((QofQueryPredData*)pdata);
}
static char * numeric_to_string (gpointer object, QofAccessFunc get)
{
gnc_numeric num = ((query_numeric_getter)get)(object);
return g_strdup (gnc_numeric_to_string (num));
}
static char * debcred_to_string (gpointer object, QofAccessFunc get)
{
gnc_numeric num = ((query_numeric_getter)get)(object);
return g_strdup (gnc_numeric_to_string (num));
}
/* QOF_QUERYCORE_GUID */
static int guid_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
query_guid_t pdata = (query_guid_t)pd;
GList *node, *o_list;
const GUID *guid = NULL;
VERIFY_PREDICATE (query_guid_type);
switch (pdata->options) {
case QOF_GUID_MATCH_ALL:
/* object is a GList of objects; get_fcn must be called on each one.
* See if every guid in the predicate is accounted-for in the
* object list
*/
for (node = pdata->guids; node; node = node->next) {
/* See if this GUID matches the object's guid */
for (o_list = object; o_list; o_list = o_list->next) {
guid = ((query_guid_getter)get_fcn) (o_list->data);
if (guid_equal (node->data, guid))
break;
}
/*
* If o_list is NULL, we've walked the whole list without finding
* a match. Therefore break out now, the match has failed.
*/
if (o_list == NULL)
break;
}
/*
* The match is complete. If node == NULL then we've succesfully
* found a match for all the guids in the predicate. Return
* appropriately below.
*/
break;
case QOF_GUID_MATCH_LIST_ANY:
/* object is a single object, getter returns a GList* of GUID*
*
* see if any GUID* in the returned list matches any guid in the
* predicate match list
*/
o_list = ((query_glist_getter)get_fcn) (object);
for (node = o_list; node; node = node->next) {
GList *node2;
/* Search the predicate data for a match */
for (node2 = pdata->guids; node2; node2 = node2->next) {
if (guid_equal (node->data, node2->data))
break;
}
/* Check to see if we found a match. If so, break now */
if (node2 != NULL)
break;
}
g_list_free(o_list);
/* yea, node may point to an invalid location, but that's ok.
* we're not _USING_ the value, just checking that it's non-NULL
*/
break;
default:
/* object is a single object, getter returns a GUID*
*
* 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 QOF_GUID_MATCH_ANY:
case QOF_GUID_MATCH_LIST_ANY:
return (node != NULL);
break;
case QOF_GUID_MATCH_NONE:
case QOF_GUID_MATCH_ALL:
return (node == NULL);
break;
case QOF_GUID_MATCH_NULL:
return (guid == NULL);
break;
default:
PWARN ("bad match type");
return 0;
}
}
static void guid_free_pdata (QofQueryPredData *pd)
{
query_guid_t pdata = (query_guid_t)pd;
GList *node;
VERIFY_PDATA (query_guid_type);
for (node = pdata->guids; node; node = node->next)
guid_free (node->data);
g_list_free (pdata->guids);
g_free (pdata);
}
static QofQueryPredData *
guid_copy_predicate (QofQueryPredData *pd)
{
query_guid_t pdata = (query_guid_t)pd;
VERIFY_PDATA_R (query_guid_type);
return qof_query_guid_predicate (pdata->options, pdata->guids);
}
static gboolean
guid_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_guid_t pd1 = (query_guid_t) p1;
query_guid_t pd2 = (query_guid_t) p2;
GList *l1 = pd1->guids, *l2 = pd2->guids;
if (pd1->options != pd2->options) return FALSE;
if (g_list_length (l1) != g_list_length (l2)) return FALSE;
for ( ; l1 ; l1 = l1->next, l2 = l2->next)
if (!guid_equal (l1->data, l2->data))
return FALSE;
return TRUE;
}
QofQueryPredData *
qof_query_guid_predicate (QofGuidMatch options, GList *guids)
{
query_guid_t pdata;
GList *node;
pdata = g_new0 (query_guid_def, 1);
pdata->pd.how = QOF_COMPARE_EQUAL;
pdata->pd.type_name = query_guid_type;
pdata->options = options;
pdata->guids = g_list_copy (guids);
for (node = pdata->guids; node; node = node->next) {
GUID *guid = guid_malloc ();
*guid = *((GUID *)node->data);
node->data = guid;
}
return ((QofQueryPredData*)pdata);
}
/* ================================================================ */
/* QOF_QUERYCORE_INT32 */
static int int32_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
gint32 val;
query_int32_t pdata = (query_int32_t)pd;
VERIFY_PREDICATE (query_int32_type);
val = ((query_int32_getter)get_fcn) (object);
switch (pd->how) {
case QOF_COMPARE_LT:
return (val < pdata->val);
case QOF_COMPARE_LTE:
return (val <= pdata->val);
case QOF_COMPARE_EQUAL:
return (val == pdata->val);
case QOF_COMPARE_GT:
return (val > pdata->val);
case QOF_COMPARE_GTE:
return (val >= pdata->val);
case QOF_COMPARE_NEQ:
return (val != pdata->val);
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int int32_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc get_fcn)
{
gint32 v1, v2;
g_return_val_if_fail (a && b && get_fcn, COMPARE_ERROR);
v1 = ((query_int32_getter)get_fcn)(a);
v2 = ((query_int32_getter)get_fcn)(b);
if (v1 < v2) return -1;
if (v1 > v2) return 1;
return 0;
}
static void int32_free_pdata (QofQueryPredData *pd)
{
query_int32_t pdata = (query_int32_t)pd;
VERIFY_PDATA (query_int32_type);
g_free (pdata);
}
static QofQueryPredData *
int32_copy_predicate (QofQueryPredData *pd)
{
query_int32_t pdata = (query_int32_t)pd;
VERIFY_PDATA_R (query_int32_type);
return qof_query_int32_predicate (pd->how, pdata->val);
}
static gboolean
int32_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_int32_t pd1 = (query_int32_t) p1;
query_int32_t pd2 = (query_int32_t) p2;
return (pd1->val == pd2->val);
}
QofQueryPredData *
qof_query_int32_predicate (QofQueryCompare how, gint32 val)
{
query_int32_t pdata = g_new0 (query_int32_def, 1);
pdata->pd.type_name = query_int32_type;
pdata->pd.how = how;
pdata->val = val;
return ((QofQueryPredData*)pdata);
}
static char * int32_to_string (gpointer object, QofAccessFunc get)
{
gint32 num = ((query_int32_getter)get)(object);
return g_strdup_printf ("%d", num);
}
/* ================================================================ */
/* QOF_QUERYCORE_INT64 */
static int int64_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
gint64 val;
query_int64_t pdata = (query_int64_t)pd;
VERIFY_PREDICATE (query_int64_type);
val = ((query_int64_getter)get_fcn) (object);
switch (pd->how) {
case QOF_COMPARE_LT:
return (val < pdata->val);
case QOF_COMPARE_LTE:
return (val <= pdata->val);
case QOF_COMPARE_EQUAL:
return (val == pdata->val);
case QOF_COMPARE_GT:
return (val > pdata->val);
case QOF_COMPARE_GTE:
return (val >= pdata->val);
case QOF_COMPARE_NEQ:
return (val != pdata->val);
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int int64_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc 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 (QofQueryPredData *pd)
{
query_int64_t pdata = (query_int64_t)pd;
VERIFY_PDATA (query_int64_type);
g_free (pdata);
}
static QofQueryPredData *
int64_copy_predicate (QofQueryPredData *pd)
{
query_int64_t pdata = (query_int64_t)pd;
VERIFY_PDATA_R (query_int64_type);
return qof_query_int64_predicate (pd->how, pdata->val);
}
static gboolean
int64_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_int64_t pd1 = (query_int64_t) p1;
query_int64_t pd2 = (query_int64_t) p2;
return (pd1->val == pd2->val);
}
QofQueryPredData *
qof_query_int64_predicate (QofQueryCompare how, gint64 val)
{
query_int64_t pdata = g_new0 (query_int64_def, 1);
pdata->pd.type_name = query_int64_type;
pdata->pd.how = how;
pdata->val = val;
return ((QofQueryPredData*)pdata);
}
static char * int64_to_string (gpointer object, QofAccessFunc get)
{
gint64 num = ((query_int64_getter)get)(object);
return g_strdup_printf (GNC_SCANF_LLD, num);
}
/* ================================================================ */
/* QOF_QUERYCORE_DOUBLE */
static int double_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
double val;
query_double_t pdata = (query_double_t)pd;
VERIFY_PREDICATE (query_double_type);
val = ((query_double_getter)get_fcn) (object);
switch (pd->how) {
case QOF_COMPARE_LT:
return (val < pdata->val);
case QOF_COMPARE_LTE:
return (val <= pdata->val);
case QOF_COMPARE_EQUAL:
return (val == pdata->val);
case QOF_COMPARE_GT:
return (val > pdata->val);
case QOF_COMPARE_GTE:
return (val >= pdata->val);
case QOF_COMPARE_NEQ:
return (val != pdata->val);
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int double_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc 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 (QofQueryPredData *pd)
{
query_double_t pdata = (query_double_t)pd;
VERIFY_PDATA (query_double_type);
g_free (pdata);
}
static QofQueryPredData *
double_copy_predicate (QofQueryPredData *pd)
{
query_double_t pdata = (query_double_t)pd;
VERIFY_PDATA_R (query_double_type);
return qof_query_double_predicate (pd->how, pdata->val);
}
static gboolean
double_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_double_t pd1 = (query_double_t) p1;
query_double_t pd2 = (query_double_t) p2;
return (pd1->val == pd2->val);
}
QofQueryPredData *
qof_query_double_predicate (QofQueryCompare how, double val)
{
query_double_t pdata = g_new0 (query_double_def, 1);
pdata->pd.type_name = query_double_type;
pdata->pd.how = how;
pdata->val = val;
return ((QofQueryPredData*)pdata);
}
static char * double_to_string (gpointer object, QofAccessFunc get)
{
double num = ((query_double_getter)get)(object);
return g_strdup_printf ("%f", num);
}
/* QOF_QUERYCORE_BOOLEAN */
static int boolean_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
gboolean val;
query_boolean_t pdata = (query_boolean_t)pd;
VERIFY_PREDICATE (query_boolean_type);
val = ((query_boolean_getter)get_fcn) (object);
switch (pd->how) {
case QOF_COMPARE_EQUAL:
return (val == pdata->val);
case QOF_COMPARE_NEQ:
return (val != pdata->val);
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static int boolean_compare_func (gpointer a, gpointer b, gint options,
QofAccessFunc 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 (QofQueryPredData *pd)
{
query_boolean_t pdata = (query_boolean_t)pd;
VERIFY_PDATA (query_boolean_type);
g_free (pdata);
}
static QofQueryPredData *
boolean_copy_predicate (QofQueryPredData *pd)
{
query_boolean_t pdata = (query_boolean_t)pd;
VERIFY_PDATA_R (query_boolean_type);
return qof_query_boolean_predicate (pd->how, pdata->val);
}
static gboolean
boolean_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_boolean_t pd1 = (query_boolean_t) p1;
query_boolean_t pd2 = (query_boolean_t) p2;
return (pd1->val == pd2->val);
}
QofQueryPredData *
qof_query_boolean_predicate (QofQueryCompare how, gboolean val)
{
query_boolean_t pdata;
g_return_val_if_fail (how == QOF_COMPARE_EQUAL || how == QOF_COMPARE_NEQ, NULL);
pdata = g_new0 (query_boolean_def, 1);
pdata->pd.type_name = query_boolean_type;
pdata->pd.how = how;
pdata->val = val;
return ((QofQueryPredData*)pdata);
}
static char * boolean_to_string (gpointer object, QofAccessFunc get)
{
gboolean num = ((query_boolean_getter)get)(object);
return g_strdup_printf ("%s", (num ? "X" : ""));
}
/* QOF_QUERYCORE_CHAR */
static int char_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *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 QOF_CHAR_MATCH_ANY:
if (strchr (pdata->char_list, c)) return 1;
return 0;
case QOF_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,
QofAccessFunc 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 (QofQueryPredData *pd)
{
query_char_t pdata = (query_char_t)pd;
VERIFY_PDATA (query_char_type);
g_free (pdata->char_list);
g_free (pdata);
}
static QofQueryPredData *
char_copy_predicate (QofQueryPredData *pd)
{
query_char_t pdata = (query_char_t)pd;
VERIFY_PDATA_R (query_char_type);
return qof_query_char_predicate (pdata->options, pdata->char_list);
}
static gboolean
char_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_char_t pd1 = (query_char_t) p1;
query_char_t pd2 = (query_char_t) p2;
if (pd1->options != pd2->options) return FALSE;
return (safe_strcmp (pd1->char_list, pd2->char_list) == 0);
}
QofQueryPredData *
qof_query_char_predicate (QofCharMatch 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->pd.how = QOF_COMPARE_EQUAL;
pdata->options = options;
pdata->char_list = g_strdup (chars);
return ((QofQueryPredData*)pdata);
}
static char * char_to_string (gpointer object, QofAccessFunc get)
{
char num = ((query_char_getter)get)(object);
return g_strdup_printf ("%c", num);
}
/* QOF_QUERYCORE_KVP */
static int kvp_match_predicate (gpointer object, QofAccessFunc get_fcn,
QofQueryPredData *pd)
{
int compare;
KvpFrame *kvp;
KvpValue *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 (pd->how)
{
case QOF_COMPARE_LT:
return (compare < 0);
case QOF_COMPARE_LTE:
return (compare <= 0);
case QOF_COMPARE_EQUAL:
return (compare == 0);
case QOF_COMPARE_GTE:
return (compare >= 0);
case QOF_COMPARE_GT:
return (compare > 0);
case QOF_COMPARE_NEQ:
return (compare != 0);
default:
PWARN ("bad match type: %d", pd->how);
return 0;
}
}
static void kvp_free_pdata (QofQueryPredData *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 QofQueryPredData *
kvp_copy_predicate (QofQueryPredData *pd)
{
query_kvp_t pdata = (query_kvp_t)pd;
VERIFY_PDATA_R (query_kvp_type);
return qof_query_kvp_predicate (pd->how, pdata->path, pdata->value);
}
static gboolean
kvp_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
query_kvp_t pd1 = (query_kvp_t) p1;
query_kvp_t pd2 = (query_kvp_t) p2;
GSList *n1, *n2;
n1 = pd1->path;
n2 = pd2->path;
for ( ; n1 && n2; n1 = n1->next, n2 = n2->next)
if (safe_strcmp (n1->data, n2->data) != 0)
return FALSE;
if (n1 || n2)
return FALSE;
return (kvp_value_compare (pd1->value, pd2->value) == 0);
}
QofQueryPredData *
qof_query_kvp_predicate (QofQueryCompare how,
GSList *path, const KvpValue *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->pd.how = how;
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 ((QofQueryPredData*)pdata);
}
/* initialization */
static void init_tables (void)
{
unsigned int i;
struct {
char const *name;
QofQueryPredicateFunc pred;
QofCompareFunc comp;
QueryPredicateCopyFunc copy;
QueryPredDataFree pd_free;
QueryToString toString;
QueryPredicateEqual pred_equal;
} knownTypes[] = {
{ QOF_QUERYCORE_STRING, string_match_predicate, string_compare_func,
string_copy_predicate, string_free_pdata, string_to_string,
string_predicate_equal },
{ QOF_QUERYCORE_DATE, date_match_predicate, date_compare_func,
date_copy_predicate, date_free_pdata, date_to_string,
date_predicate_equal },
{ QOF_QUERYCORE_DEBCRED, numeric_match_predicate, numeric_compare_func,
numeric_copy_predicate, numeric_free_pdata, debcred_to_string,
numeric_predicate_equal },
{ QOF_QUERYCORE_NUMERIC, numeric_match_predicate, numeric_compare_func,
numeric_copy_predicate, numeric_free_pdata, numeric_to_string,
numeric_predicate_equal },
{ QOF_QUERYCORE_GUID, guid_match_predicate, NULL,
guid_copy_predicate, guid_free_pdata, NULL,
guid_predicate_equal },
{ QOF_QUERYCORE_INT32, int32_match_predicate, int32_compare_func,
int32_copy_predicate, int32_free_pdata, int32_to_string,
int32_predicate_equal },
{ QOF_QUERYCORE_INT64, int64_match_predicate, int64_compare_func,
int64_copy_predicate, int64_free_pdata, int64_to_string,
int64_predicate_equal },
{ QOF_QUERYCORE_DOUBLE, double_match_predicate, double_compare_func,
double_copy_predicate, double_free_pdata, double_to_string,
double_predicate_equal },
{ QOF_QUERYCORE_BOOLEAN, boolean_match_predicate, boolean_compare_func,
boolean_copy_predicate, boolean_free_pdata, boolean_to_string,
boolean_predicate_equal },
{ QOF_QUERYCORE_CHAR, char_match_predicate, char_compare_func,
char_copy_predicate, char_free_pdata, char_to_string,
char_predicate_equal },
{ QOF_QUERYCORE_KVP, kvp_match_predicate, NULL, kvp_copy_predicate,
kvp_free_pdata, NULL, kvp_predicate_equal },
};
/* Register the known data types */
for (i = 0; i < (sizeof(knownTypes)/sizeof(*knownTypes)); i++) {
qof_query_register_core_object (knownTypes[i].name,
knownTypes[i].pred,
knownTypes[i].comp,
knownTypes[i].copy,
knownTypes[i].pd_free,
knownTypes[i].toString,
knownTypes[i].pred_equal);
}
}
static QueryPredicateCopyFunc gncQueryCoreGetCopy (char const *type)
{
QueryPredicateCopyFunc rc;
g_return_val_if_fail (type, NULL);
rc = g_hash_table_lookup (copyTable, type);
return rc;
}
static QueryPredDataFree gncQueryCoreGetPredFree (char const *type)
{
g_return_val_if_fail (type, NULL);
return g_hash_table_lookup (freeTable, type);
}
static void
qof_query_register_core_object (char const *core_name,
QofQueryPredicateFunc pred,
QofCompareFunc comp,
QueryPredicateCopyFunc copy,
QueryPredDataFree pd_free,
QueryToString toString,
QueryPredicateEqual pred_equal)
{
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);
if (toString)
g_hash_table_insert (toStringTable, (char *)core_name, toString);
if (pred_equal)
g_hash_table_insert (predEqualTable, (char *)core_name, pred_equal);
}
/********************************************************************/
/* PUBLISHED API FUNCTIONS */
void qof_query_core_init (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);
toStringTable = g_hash_table_new (g_str_hash, g_str_equal);
predEqualTable = g_hash_table_new (g_str_hash, g_str_equal);
init_tables ();
}
void qof_query_core_shutdown (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);
g_hash_table_destroy (toStringTable);
g_hash_table_destroy (predEqualTable);
}
QofQueryPredicateFunc
qof_query_core_get_predicate (char const *type)
{
g_return_val_if_fail (type, NULL);
return g_hash_table_lookup (predTable, type);
}
QofCompareFunc
qof_query_core_get_compare (char const *type)
{
g_return_val_if_fail (type, NULL);
return g_hash_table_lookup (cmpTable, type);
}
void
qof_query_core_predicate_free (QofQueryPredData *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);
}
QofQueryPredData *
qof_query_core_predicate_copy (QofQueryPredData *pdata)
{
QueryPredicateCopyFunc 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));
}
char *
qof_query_core_to_string (char const *type, gpointer object,
QofAccessFunc get)
{
QueryToString toString;
g_return_val_if_fail (type, NULL);
g_return_val_if_fail (object, NULL);
g_return_val_if_fail (get, NULL);
toString = g_hash_table_lookup (toStringTable, type);
g_return_val_if_fail (toString, NULL);
return toString (object, get);
}
gboolean
qof_query_core_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2)
{
QueryPredicateEqual pred_equal;
if (p1 == p2) return TRUE;
if (!p1 || !p2) return FALSE;
if (p1->how != p2->how) return FALSE;
if (safe_strcmp (p1->type_name, p2->type_name)) return FALSE;
pred_equal = g_hash_table_lookup (predEqualTable, p1->type_name);
g_return_val_if_fail (pred_equal, FALSE);
return pred_equal (p1, p2);
}