mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-25 18:30:23 -06:00
dab32807ef
It's the done thing
1820 lines
51 KiB
C++
1820 lines
51 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 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
#include "guid.hpp"
|
|
#include <config.h>
|
|
|
|
#include <glib.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "qof.h"
|
|
#include "qofquerycore-p.h"
|
|
|
|
static QofLogModule log_module = QOF_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) (const QofQueryPredData *pdata);
|
|
|
|
/* A function to take the object, apply the getter->param_getfcn,
|
|
* and return a printable string. Note that this QofParam->getfnc
|
|
* 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, QofParam *getter);
|
|
|
|
/* A function to test for equality of predicate data */
|
|
typedef gboolean (*QueryPredicateEqual) (const QofQueryPredData *p1,
|
|
const QofQueryPredData *p2);
|
|
|
|
static QueryPredicateCopyFunc qof_query_copy_predicate (QofType type);
|
|
static QueryPredDataFree qof_query_predicate_free (QofType type);
|
|
|
|
/* Core Type Predicate helpers */
|
|
typedef const char * (*query_string_getter) (gpointer, QofParam *);
|
|
static const char * query_string_type = QOF_TYPE_STRING;
|
|
|
|
typedef time64 (*query_date_getter) (gpointer, QofParam *);
|
|
static const char * query_date_type = QOF_TYPE_DATE;
|
|
|
|
typedef gnc_numeric (*query_numeric_getter) (gpointer, QofParam *);
|
|
static const char * query_numeric_type = QOF_TYPE_NUMERIC;
|
|
|
|
typedef GList * (*query_glist_getter) (gpointer, QofParam *);
|
|
typedef const GncGUID * (*query_guid_getter) (gpointer, QofParam *);
|
|
static const char * query_guid_type = QOF_TYPE_GUID;
|
|
|
|
typedef gint32 (*query_int32_getter) (gpointer, QofParam *);
|
|
static const char * query_int32_type = QOF_TYPE_INT32;
|
|
|
|
typedef gint64 (*query_int64_getter) (gpointer, QofParam *);
|
|
static const char * query_int64_type = QOF_TYPE_INT64;
|
|
|
|
typedef double (*query_double_getter) (gpointer, QofParam *);
|
|
static const char * query_double_type = QOF_TYPE_DOUBLE;
|
|
|
|
typedef gboolean (*query_boolean_getter) (gpointer, QofParam *);
|
|
static const char * query_boolean_type = QOF_TYPE_BOOLEAN;
|
|
|
|
typedef char (*query_char_getter) (gpointer, QofParam *);
|
|
static const char * query_char_type = QOF_TYPE_CHAR;
|
|
|
|
typedef QofCollection * (*query_collect_getter) (gpointer, QofParam*);
|
|
static const char * query_collect_type = QOF_TYPE_COLLECT;
|
|
|
|
typedef const GncGUID * (*query_choice_getter) (gpointer, QofParam *);
|
|
static const char * query_choice_type = QOF_TYPE_CHOICE;
|
|
|
|
/* Tables for predicate storage and lookup */
|
|
static gboolean initialized = FALSE;
|
|
static GHashTable *predTable = nullptr;
|
|
static GHashTable *cmpTable = nullptr;
|
|
static GHashTable *copyTable = nullptr;
|
|
static GHashTable *freeTable = nullptr;
|
|
static GHashTable *toStringTable = nullptr;
|
|
static GHashTable *predEqualTable = nullptr;
|
|
|
|
#define COMPARE_ERROR -3
|
|
#define PREDICATE_ERROR -2
|
|
|
|
#define VERIFY_PDATA(str) { \
|
|
g_return_if_fail (pd != nullptr); \
|
|
g_return_if_fail (pd->type_name == str || \
|
|
!g_strcmp0 (str, pd->type_name)); \
|
|
}
|
|
#define VERIFY_PDATA_R(str) { \
|
|
g_return_val_if_fail (pd != nullptr, nullptr); \
|
|
g_return_val_if_fail (pd->type_name == str || \
|
|
!g_strcmp0 (str, pd->type_name), \
|
|
nullptr); \
|
|
}
|
|
#define VERIFY_PREDICATE(str) { \
|
|
g_return_val_if_fail (getter != nullptr, PREDICATE_ERROR); \
|
|
g_return_val_if_fail (getter->param_getfcn != nullptr, PREDICATE_ERROR); \
|
|
g_return_val_if_fail (pd != nullptr, PREDICATE_ERROR); \
|
|
g_return_val_if_fail (pd->type_name == str || \
|
|
!g_strcmp0 (str, pd->type_name), \
|
|
PREDICATE_ERROR); \
|
|
}
|
|
|
|
/* *******************************************************************/
|
|
/* TYPE-HANDLING FUNCTIONS */
|
|
|
|
/* QOF_TYPE_STRING */
|
|
|
|
static int
|
|
string_match_predicate (gpointer object,
|
|
QofParam *getter,
|
|
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)getter->param_getfcn) (object, getter);
|
|
|
|
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 (pd->how == QOF_COMPARE_CONTAINS || pd->how == QOF_COMPARE_NCONTAINS)
|
|
{
|
|
if (qof_utf8_substr_nocase (s, pdata->matchstring)) //uses strstr
|
|
ret = 1;
|
|
}
|
|
else
|
|
{
|
|
if (safe_strcasecmp (s, pdata->matchstring) == 0) //uses collate
|
|
ret = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pd->how == QOF_COMPARE_CONTAINS || pd->how == QOF_COMPARE_NCONTAINS)
|
|
{
|
|
if (strstr (s, pdata->matchstring))
|
|
ret = 1;
|
|
}
|
|
else
|
|
{
|
|
if (g_strcmp0 (s, pdata->matchstring) == 0)
|
|
ret = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (pd->how)
|
|
{
|
|
case QOF_COMPARE_CONTAINS:
|
|
return ret;
|
|
case QOF_COMPARE_NCONTAINS:
|
|
return !ret;
|
|
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,
|
|
QofParam *getter)
|
|
{
|
|
const char *s1, *s2;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
s1 = ((query_string_getter)getter->param_getfcn) (a, getter);
|
|
s2 = ((query_string_getter)getter->param_getfcn) (b, getter);
|
|
|
|
if (options == QOF_STRING_MATCH_CASEINSENSITIVE)
|
|
return safe_strcasecmp (s1, s2);
|
|
|
|
return g_strcmp0 (s1, s2);
|
|
}
|
|
|
|
int
|
|
qof_string_number_compare_func (gpointer a, gpointer b, gint options,
|
|
QofParam *getter)
|
|
{
|
|
const char *s1, *s2;
|
|
char *sr1, *sr2;
|
|
long i1, i2;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
s1 = ((query_string_getter)getter->param_getfcn) (a, getter);
|
|
s2 = ((query_string_getter)getter->param_getfcn) (b, getter);
|
|
|
|
// Deal with nullptr strings
|
|
if (s1 == s2) return 0;
|
|
if (!s1 && s2) return -1;
|
|
if (s1 && !s2) return 1;
|
|
|
|
// Convert to integers and test
|
|
i1 = strtol(s1, &sr1, 10);
|
|
i2 = strtol(s2, &sr2, 10);
|
|
if (i1 < i2) return -1;
|
|
if (i1 > i2) return 1;
|
|
|
|
// If the integers match, then test the REST of the string as text.
|
|
if (options == QOF_STRING_MATCH_CASEINSENSITIVE)
|
|
return safe_strcasecmp (sr1, sr2);
|
|
|
|
return g_strcmp0 (sr1, sr2);
|
|
}
|
|
|
|
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);
|
|
|
|
g_free (pdata->matchstring);
|
|
g_free (pdata);
|
|
}
|
|
|
|
static QofQueryPredData *
|
|
string_copy_predicate (const QofQueryPredData *pd)
|
|
{
|
|
const query_string_t pdata = (const 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 (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_string_t pd1 = (const query_string_t) p1;
|
|
const query_string_t pd2 = (const query_string_t) p2;
|
|
|
|
if (pd1->options != pd2->options) return FALSE;
|
|
if (pd1->is_regex != pd2->is_regex) return FALSE;
|
|
return (g_strcmp0 (pd1->matchstring, pd2->matchstring) == 0);
|
|
}
|
|
|
|
QofQueryPredData *
|
|
qof_query_string_predicate (QofQueryCompare how,
|
|
const char *str, QofStringMatch options,
|
|
gboolean is_regex)
|
|
{
|
|
query_string_t pdata;
|
|
|
|
g_return_val_if_fail (str, nullptr);
|
|
// g_return_val_if_fail (*str != '\0', nullptr);
|
|
g_return_val_if_fail (how == QOF_COMPARE_CONTAINS || how == QOF_COMPARE_NCONTAINS ||
|
|
how == QOF_COMPARE_EQUAL || how == QOF_COMPARE_NEQ, nullptr);
|
|
|
|
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 rc;
|
|
int flags = REG_EXTENDED;
|
|
if (options == QOF_STRING_MATCH_CASEINSENSITIVE)
|
|
flags |= REG_ICASE;
|
|
|
|
rc = regcomp(&pdata->compiled, str, flags);
|
|
if (rc)
|
|
{
|
|
g_free(pdata->matchstring);
|
|
g_free(pdata);
|
|
return nullptr;
|
|
}
|
|
pdata->is_regex = TRUE;
|
|
}
|
|
|
|
return ((QofQueryPredData*)pdata);
|
|
}
|
|
|
|
static char *
|
|
string_to_string (gpointer object, QofParam *getter)
|
|
{
|
|
const char *res;
|
|
res = ((query_string_getter)getter->param_getfcn)(object, getter);
|
|
if (res)
|
|
return g_strdup (res);
|
|
return nullptr;
|
|
}
|
|
|
|
/* QOF_TYPE_DATE =================================================== */
|
|
|
|
static int
|
|
date_compare (time64 ta, time64 tb, QofDateMatch options)
|
|
{
|
|
|
|
if (options == QOF_DATE_MATCH_DAY)
|
|
{
|
|
ta = time64CanonicalDayTime (ta);
|
|
tb = time64CanonicalDayTime (tb);
|
|
}
|
|
|
|
if (ta < tb)
|
|
return -1;
|
|
if (ta > tb)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
date_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
query_date_t pdata = (query_date_t)pd;
|
|
time64 objtime;
|
|
int compare;
|
|
|
|
VERIFY_PREDICATE (query_date_type);
|
|
|
|
objtime = ((query_date_getter)getter->param_getfcn) (object, getter);
|
|
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, QofParam *getter)
|
|
{
|
|
time64 ta, tb;
|
|
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
ta = ((query_date_getter)getter->param_getfcn) (a, getter);
|
|
tb = ((query_date_getter)getter->param_getfcn) (b, getter);
|
|
|
|
return date_compare (ta, tb, static_cast<QofDateMatch>(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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_date_t pdata = (const 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 (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_date_t pd1 = (const query_date_t) p1;
|
|
const query_date_t pd2 = (const query_date_t) p2;
|
|
|
|
if (pd1->options != pd2->options) return FALSE;
|
|
return (pd1->date == pd2->date);
|
|
}
|
|
|
|
QofQueryPredData *
|
|
qof_query_date_predicate (QofQueryCompare how,
|
|
QofDateMatch options, time64 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 (const QofQueryPredData *pd, time64 *date)
|
|
{
|
|
const query_date_t pdata = (const 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, QofParam *getter)
|
|
{
|
|
time64 tt = ((query_date_getter)getter->param_getfcn)(object, getter);
|
|
|
|
if (tt != INT64_MAX)
|
|
return qof_print_date (tt);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/* QOF_TYPE_NUMERIC ================================================= */
|
|
|
|
static int
|
|
numeric_match_predicate (gpointer object, QofParam *getter,
|
|
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)getter->param_getfcn) (object, getter);
|
|
|
|
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;
|
|
}
|
|
|
|
/* Amounts are considered to be 'equal' if they match to
|
|
* four decimal places. (epsilon=1/10000) */
|
|
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_HOW_RND_ROUND_HALF_UP)),
|
|
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, QofParam *getter)
|
|
{
|
|
gnc_numeric va, vb;
|
|
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
va = ((query_numeric_getter)getter->param_getfcn) (a, getter);
|
|
vb = ((query_numeric_getter)getter->param_getfcn) (b, getter);
|
|
|
|
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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_numeric_t pdata = (const 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 (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_numeric_t pd1 = (const query_numeric_t) p1;
|
|
const query_numeric_t pd2 = (const 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, QofParam *getter)
|
|
{
|
|
gnc_numeric num;
|
|
num = ((query_numeric_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return gnc_numeric_to_string (num);
|
|
}
|
|
|
|
static char *
|
|
debcred_to_string (gpointer object, QofParam *getter)
|
|
{
|
|
gnc_numeric num;
|
|
num = ((query_numeric_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return gnc_numeric_to_string (num);
|
|
}
|
|
|
|
/* QOF_TYPE_GUID =================================================== */
|
|
|
|
static int
|
|
guid_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
query_guid_t pdata = (query_guid_t)pd;
|
|
GList *node, *o_list;
|
|
const GncGUID *guid = nullptr;
|
|
|
|
VERIFY_PREDICATE (query_guid_type);
|
|
|
|
switch (pdata->options)
|
|
{
|
|
|
|
case QOF_GUID_MATCH_ALL:
|
|
/* object is a GList of objects; param_getfcn 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 GncGUID matches the object's guid */
|
|
for (o_list = static_cast<GList*>(object); o_list;
|
|
o_list = static_cast<GList*>(o_list->next))
|
|
{
|
|
guid = ((query_guid_getter)getter->param_getfcn) (o_list->data, getter);
|
|
if (guid_equal (static_cast<GncGUID*>(node->data), guid))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If o_list is nullptr, we've walked the whole list without finding
|
|
* a match. Therefore break out now, the match has failed.
|
|
*/
|
|
if (o_list == nullptr)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* The match is complete. If node == nullptr then we've successfully
|
|
* 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 GncGUID*
|
|
*
|
|
* See if any GncGUID* in the returned list matches any guid in the
|
|
* predicate match list.
|
|
*/
|
|
|
|
o_list = ((query_glist_getter)getter->param_getfcn) (object, getter);
|
|
|
|
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 (static_cast<GncGUID*>(node->data),
|
|
static_cast<GncGUID*>(node2->data)))
|
|
break;
|
|
}
|
|
|
|
/* Check to see if we found a match. If so, break now */
|
|
if (node2 != nullptr)
|
|
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-nullptr
|
|
*/
|
|
|
|
break;
|
|
|
|
default:
|
|
/* object is a single object, getter returns a GncGUID*
|
|
*
|
|
* See if the guid is in the list
|
|
*/
|
|
|
|
guid = ((query_guid_getter)getter->param_getfcn) (object, getter);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
if (guid_equal (static_cast<GncGUID*>(node->data), guid))
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (pdata->options)
|
|
{
|
|
case QOF_GUID_MATCH_ANY:
|
|
case QOF_GUID_MATCH_LIST_ANY:
|
|
return (node != nullptr);
|
|
break;
|
|
case QOF_GUID_MATCH_NONE:
|
|
case QOF_GUID_MATCH_ALL:
|
|
return (node == nullptr);
|
|
break;
|
|
case QOF_GUID_MATCH_NULL:
|
|
return ((guid == nullptr) || guid_equal(guid, 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 (static_cast<GncGUID*>(node->data));
|
|
}
|
|
g_list_free (pdata->guids);
|
|
g_free (pdata);
|
|
}
|
|
|
|
static QofQueryPredData *
|
|
guid_copy_predicate (const QofQueryPredData *pd)
|
|
{
|
|
const query_guid_t pdata = (const query_guid_t)pd;
|
|
VERIFY_PDATA_R (query_guid_type);
|
|
return qof_query_guid_predicate (pdata->options, pdata->guids);
|
|
}
|
|
|
|
static gboolean
|
|
guid_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_guid_t pd1 = (const query_guid_t) p1;
|
|
const query_guid_t pd2 = (const query_guid_t) p2;
|
|
GList *l1 = pd1->guids, *l2 = pd2->guids;
|
|
|
|
if (pd1->options != pd2->options) return FALSE;
|
|
for (; l1 || l2; l1 = l1->next, l2 = l2->next)
|
|
{
|
|
if (!l1 || !l2)
|
|
return FALSE;
|
|
if (!guid_equal (static_cast<GncGUID*>(l1->data),
|
|
static_cast<GncGUID*>(l2->data)))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
QofQueryPredData *
|
|
qof_query_guid_predicate (QofGuidMatch options, GList *guid_list)
|
|
{
|
|
query_guid_t pdata;
|
|
GList *node;
|
|
|
|
/* An empty list of guids is only valid when testing for a null GUID value */
|
|
if (!guid_list)
|
|
g_return_val_if_fail (options == QOF_GUID_MATCH_NULL, nullptr);
|
|
|
|
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 (guid_list);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
GncGUID *guid = guid_malloc ();
|
|
*guid = *((GncGUID *)node->data);
|
|
node->data = guid;
|
|
}
|
|
return ((QofQueryPredData*)pdata);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/* QOF_TYPE_INT32 */
|
|
|
|
static int
|
|
int32_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
gint32 val;
|
|
query_int32_t pdata = (query_int32_t)pd;
|
|
|
|
VERIFY_PREDICATE (query_int32_type);
|
|
|
|
val = ((query_int32_getter)getter->param_getfcn) (object, getter);
|
|
|
|
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,
|
|
QofParam *getter)
|
|
{
|
|
gint32 v1, v2;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
v1 = ((query_int32_getter)getter->param_getfcn)(a, getter);
|
|
v2 = ((query_int32_getter)getter->param_getfcn)(b, getter);
|
|
|
|
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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_int32_t pdata = (const query_int32_t)pd;
|
|
VERIFY_PDATA_R (query_int32_type);
|
|
return qof_query_int32_predicate (pd->how, pdata->val);
|
|
}
|
|
|
|
static gboolean
|
|
int32_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_int32_t pd1 = (const query_int32_t) p1;
|
|
const query_int32_t pd2 = (const 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, QofParam *getter)
|
|
{
|
|
gint32 num = ((query_int32_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return g_strdup_printf ("%d", num);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/* QOF_TYPE_INT64 */
|
|
|
|
static int
|
|
int64_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
gint64 val;
|
|
query_int64_t pdata = (query_int64_t)pd;
|
|
|
|
VERIFY_PREDICATE (query_int64_type);
|
|
|
|
val = ((query_int64_getter)getter->param_getfcn) (object, getter);
|
|
|
|
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,
|
|
QofParam *getter)
|
|
{
|
|
gint64 v1, v2;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
v1 = ((query_int64_getter)getter->param_getfcn)(a, getter);
|
|
v2 = ((query_int64_getter)getter->param_getfcn)(b, getter);
|
|
|
|
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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_int64_t pdata = (const query_int64_t)pd;
|
|
VERIFY_PDATA_R (query_int64_type);
|
|
return qof_query_int64_predicate (pd->how, pdata->val);
|
|
}
|
|
|
|
static gboolean
|
|
int64_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_int64_t pd1 = (const query_int64_t) p1;
|
|
const query_int64_t pd2 = (const 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, QofParam *getter)
|
|
{
|
|
gint64 num = ((query_int64_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return g_strdup_printf ("%" G_GINT64_FORMAT, num);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/* QOF_TYPE_DOUBLE */
|
|
|
|
static int
|
|
double_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
double val;
|
|
query_double_t pdata = (query_double_t)pd;
|
|
|
|
VERIFY_PREDICATE (query_double_type);
|
|
|
|
val = ((query_double_getter)getter->param_getfcn) (object, getter);
|
|
|
|
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,
|
|
QofParam *getter)
|
|
{
|
|
double v1, v2;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
|
|
v1 = ((query_double_getter)getter->param_getfcn) (a, getter);
|
|
v2 = ((query_double_getter)getter->param_getfcn) (b, getter);
|
|
|
|
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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_double_t pdata = (const query_double_t)pd;
|
|
VERIFY_PDATA_R (query_double_type);
|
|
return qof_query_double_predicate (pd->how, pdata->val);
|
|
}
|
|
|
|
static gboolean
|
|
double_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_double_t pd1 = (const query_double_t) p1;
|
|
const query_double_t pd2 = (const 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, QofParam *getter)
|
|
{
|
|
double num = ((query_double_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return g_strdup_printf ("%f", num);
|
|
}
|
|
|
|
/* QOF_TYPE_BOOLEAN =================================================== */
|
|
|
|
static int
|
|
boolean_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
gboolean val;
|
|
query_boolean_t pdata = (query_boolean_t)pd;
|
|
|
|
VERIFY_PREDICATE (query_boolean_type);
|
|
|
|
val = ((query_boolean_getter)getter->param_getfcn) (object, getter);
|
|
|
|
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,
|
|
QofParam *getter)
|
|
{
|
|
gboolean va, vb;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
va = ((query_boolean_getter)getter->param_getfcn) (a, getter);
|
|
vb = ((query_boolean_getter)getter->param_getfcn) (b, getter);
|
|
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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_boolean_t pdata = (const query_boolean_t)pd;
|
|
VERIFY_PDATA_R (query_boolean_type);
|
|
return qof_query_boolean_predicate (pd->how, pdata->val);
|
|
}
|
|
|
|
static gboolean
|
|
boolean_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_boolean_t pd1 = (const query_boolean_t) p1;
|
|
const query_boolean_t pd2 = (const 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, nullptr);
|
|
|
|
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, QofParam *getter)
|
|
{
|
|
gboolean num = ((query_boolean_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return g_strdup_printf ("%s", (num ? "X" : ""));
|
|
}
|
|
|
|
/* QOF_TYPE_CHAR =================================================== */
|
|
|
|
static int
|
|
char_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
char c;
|
|
query_char_t pdata = (query_char_t)pd;
|
|
|
|
VERIFY_PREDICATE (query_char_type);
|
|
|
|
c = ((query_char_getter)getter->param_getfcn) (object, getter);
|
|
|
|
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, QofParam *getter)
|
|
{
|
|
char va, vb;
|
|
g_return_val_if_fail (a && b && getter && getter->param_getfcn, COMPARE_ERROR);
|
|
va = ((query_char_getter)getter->param_getfcn)(a, getter);
|
|
vb = ((query_char_getter)getter->param_getfcn)(b, getter);
|
|
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 (const QofQueryPredData *pd)
|
|
{
|
|
const query_char_t pdata = (const 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 (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_char_t pd1 = (const query_char_t) p1;
|
|
const query_char_t pd2 = (const query_char_t) p2;
|
|
|
|
if (pd1->options != pd2->options) return FALSE;
|
|
return (g_strcmp0 (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, nullptr);
|
|
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, QofParam *getter)
|
|
{
|
|
char num = ((query_char_getter)getter->param_getfcn)(object, getter);
|
|
|
|
return g_strdup_printf ("%c", num);
|
|
}
|
|
|
|
/* QOF_TYPE_COLLECT =============================================== */
|
|
|
|
static int
|
|
collect_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
query_coll_t pdata;
|
|
GList *node, *node2, *o_list;
|
|
const GncGUID *guid;
|
|
|
|
pdata = (query_coll_t)pd;
|
|
VERIFY_PREDICATE (query_collect_type);
|
|
guid = nullptr;
|
|
switch (pdata->options)
|
|
{
|
|
case QOF_GUID_MATCH_ALL :
|
|
{
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
for (o_list = static_cast<GList*>(object); o_list;
|
|
o_list = static_cast<GList*>(o_list->next))
|
|
{
|
|
guid = ((query_guid_getter)getter->param_getfcn)
|
|
(o_list->data, getter);
|
|
if (guid_equal (static_cast<GncGUID*>(node->data), guid))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (o_list == nullptr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case QOF_GUID_MATCH_LIST_ANY :
|
|
{
|
|
o_list = ((query_glist_getter)getter->param_getfcn) (object, getter);
|
|
for (node = o_list; node; node = node->next)
|
|
{
|
|
for (node2 = pdata->guids; node2; node2 = node2->next)
|
|
{
|
|
if (guid_equal (static_cast<GncGUID*>(node->data),
|
|
static_cast<GncGUID*>(node2->data)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (node2 != nullptr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
g_list_free(o_list);
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
guid = ((query_guid_getter)getter->param_getfcn) (object, getter);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
if (guid_equal (static_cast<GncGUID*>(node->data), guid))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
switch (pdata->options)
|
|
{
|
|
case QOF_GUID_MATCH_ANY :
|
|
case QOF_GUID_MATCH_LIST_ANY :
|
|
{
|
|
return (node != nullptr);
|
|
break;
|
|
}
|
|
case QOF_GUID_MATCH_NONE :
|
|
case QOF_GUID_MATCH_ALL :
|
|
{
|
|
return (node == nullptr);
|
|
break;
|
|
}
|
|
case QOF_GUID_MATCH_NULL :
|
|
{
|
|
return ((guid == nullptr) || guid_equal(guid, guid_null()));
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
PWARN ("bad match type");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
collect_compare_func (gpointer a, gpointer b, gint options, QofParam *getter)
|
|
{
|
|
gint result;
|
|
QofCollection *c1, *c2;
|
|
|
|
c1 = ((query_collect_getter)getter->param_getfcn) (a, getter);
|
|
c2 = ((query_collect_getter)getter->param_getfcn) (b, getter);
|
|
result = qof_collection_compare(c1, c2);
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
collect_free_pdata (QofQueryPredData *pd)
|
|
{
|
|
query_coll_t pdata;
|
|
GList *node;
|
|
|
|
node = nullptr;
|
|
pdata = (query_coll_t) pd;
|
|
VERIFY_PDATA (query_collect_type);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
guid_free (static_cast<GncGUID*>(node->data));
|
|
}
|
|
qof_collection_destroy(pdata->coll);
|
|
g_list_free (pdata->guids);
|
|
g_free (pdata);
|
|
}
|
|
|
|
static QofQueryPredData *
|
|
collect_copy_predicate (const QofQueryPredData *pd)
|
|
{
|
|
const query_coll_t pdata = (const query_coll_t) pd;
|
|
|
|
VERIFY_PDATA_R (query_collect_type);
|
|
return qof_query_collect_predicate (pdata->options, pdata->coll);
|
|
}
|
|
|
|
static gboolean
|
|
collect_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_coll_t pd1 = (const query_coll_t) p1;
|
|
const query_coll_t pd2 = (const query_coll_t) p2;
|
|
gint result;
|
|
|
|
result = qof_collection_compare(pd1->coll, pd2->coll);
|
|
if (result == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
query_collect_cb(QofInstance* ent, gpointer user_data)
|
|
{
|
|
query_coll_t pdata;
|
|
GncGUID *guid;
|
|
|
|
guid = (GncGUID*)qof_entity_get_guid(ent);
|
|
pdata = (query_coll_t)user_data;
|
|
pdata->guids = g_list_append(pdata->guids, guid);
|
|
}
|
|
|
|
QofQueryPredData *
|
|
qof_query_collect_predicate (QofGuidMatch options, QofCollection *coll)
|
|
{
|
|
query_coll_t pdata;
|
|
|
|
g_return_val_if_fail (coll, nullptr);
|
|
pdata = g_new0 (query_coll_def, 1);
|
|
pdata->pd.type_name = query_collect_type;
|
|
pdata->options = options;
|
|
qof_collection_foreach(coll, query_collect_cb, pdata);
|
|
if (nullptr == pdata->guids)
|
|
{
|
|
return nullptr;
|
|
}
|
|
return ((QofQueryPredData*)pdata);
|
|
}
|
|
|
|
/* QOF_TYPE_CHOICE */
|
|
|
|
static int
|
|
choice_match_predicate (gpointer object, QofParam *getter,
|
|
QofQueryPredData *pd)
|
|
{
|
|
query_choice_t pdata = (query_choice_t)pd;
|
|
GList *node, *o_list;
|
|
const GncGUID *guid = nullptr;
|
|
|
|
VERIFY_PREDICATE (query_choice_type);
|
|
|
|
switch (pdata->options)
|
|
{
|
|
|
|
case QOF_GUID_MATCH_ALL:
|
|
/* object is a GList of objects; param_getfcn 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 GncGUID matches the object's guid */
|
|
for (o_list = static_cast<GList*>(object); o_list;
|
|
o_list = static_cast<GList*>(o_list->next))
|
|
{
|
|
guid = ((query_choice_getter)getter->param_getfcn) (o_list->data, getter);
|
|
if (guid_equal (static_cast<GncGUID*>(node->data), guid))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If o_list is nullptr, we've walked the whole list without finding
|
|
* a match. Therefore break out now, the match has failed.
|
|
*/
|
|
if (o_list == nullptr)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* The match is complete. If node == nullptr then we've successfully
|
|
* found a match for all the guids in the predicate. Return
|
|
* appropriately below.
|
|
*/
|
|
|
|
break;
|
|
|
|
case QOF_GUID_MATCH_LIST_ANY:
|
|
|
|
o_list = ((query_glist_getter)getter->param_getfcn) (object, getter);
|
|
|
|
for (node = o_list; node; node = node->next)
|
|
{
|
|
GList *node2;
|
|
|
|
for (node2 = pdata->guids; node2; node2 = node2->next)
|
|
{
|
|
if (guid_equal (static_cast<GncGUID*>(node->data),
|
|
static_cast<GncGUID*>(node2->data)))
|
|
break;
|
|
}
|
|
|
|
if (node2 != nullptr)
|
|
break;
|
|
}
|
|
|
|
g_list_free(o_list);
|
|
|
|
break;
|
|
|
|
default:
|
|
/* object is a single object, getter returns a GncGUID*
|
|
*
|
|
* See if the guid is in the list
|
|
*/
|
|
|
|
guid = ((query_choice_getter)getter->param_getfcn) (object, getter);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
if (guid_equal (static_cast<GncGUID*>(node->data), guid))
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (pdata->options)
|
|
{
|
|
case QOF_GUID_MATCH_ANY:
|
|
case QOF_GUID_MATCH_LIST_ANY:
|
|
return (node != nullptr);
|
|
break;
|
|
case QOF_GUID_MATCH_NONE:
|
|
case QOF_GUID_MATCH_ALL:
|
|
return (node == nullptr);
|
|
break;
|
|
case QOF_GUID_MATCH_NULL:
|
|
return ((guid == nullptr) || guid_equal(guid, guid_null()));
|
|
break;
|
|
default:
|
|
PWARN ("bad match type");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
choice_free_pdata (QofQueryPredData *pd)
|
|
{
|
|
query_choice_t pdata = (query_choice_t)pd;
|
|
GList *node;
|
|
VERIFY_PDATA (query_choice_type);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
guid_free (static_cast<GncGUID*>(node->data));
|
|
}
|
|
g_list_free (pdata->guids);
|
|
g_free (pdata);
|
|
}
|
|
|
|
static QofQueryPredData *
|
|
choice_copy_predicate (const QofQueryPredData *pd)
|
|
{
|
|
const query_choice_t pdata = (const query_choice_t)pd;
|
|
VERIFY_PDATA_R (query_choice_type);
|
|
return qof_query_choice_predicate (pdata->options, pdata->guids);
|
|
}
|
|
|
|
static gboolean
|
|
choice_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
const query_choice_t pd1 = (const query_choice_t) p1;
|
|
const query_choice_t pd2 = (const query_choice_t) p2;
|
|
GList *l1 = pd1->guids, *l2 = pd2->guids;
|
|
|
|
if (pd1->options != pd2->options) return FALSE;
|
|
for (; l1 || l2; l1 = l1->next, l2 = l2->next)
|
|
{
|
|
if (!l1 || !l2)
|
|
return FALSE;
|
|
if (!guid_equal (static_cast<GncGUID*>(l1->data),
|
|
static_cast<GncGUID*>(l2->data)))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
QofQueryPredData *
|
|
qof_query_choice_predicate (QofGuidMatch options, GList *guid_list)
|
|
{
|
|
query_choice_t pdata;
|
|
GList *node;
|
|
|
|
if (nullptr == guid_list) return nullptr;
|
|
|
|
pdata = g_new0 (query_choice_def, 1);
|
|
pdata->pd.how = QOF_COMPARE_EQUAL;
|
|
pdata->pd.type_name = query_choice_type;
|
|
pdata->options = options;
|
|
|
|
pdata->guids = g_list_copy (guid_list);
|
|
for (node = pdata->guids; node; node = node->next)
|
|
{
|
|
GncGUID *guid = guid_malloc ();
|
|
*guid = *((GncGUID *)node->data);
|
|
node->data = guid;
|
|
}
|
|
return ((QofQueryPredData*)pdata);
|
|
}
|
|
|
|
|
|
/* initialization ================================================== */
|
|
/** 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.
|
|
*
|
|
* An example:
|
|
* qof_query_register_core_object (QOF_TYPE_STRING, string_match_predicate,
|
|
* string_compare_fcn, string_free_pdata,
|
|
* string_print_fcn, pred_equal_fcn);
|
|
*/
|
|
|
|
|
|
static void
|
|
qof_query_register_core_object (QofType 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,
|
|
reinterpret_cast<void*>(pred));
|
|
|
|
if (comp)
|
|
g_hash_table_insert (cmpTable, (char *)core_name,
|
|
reinterpret_cast<void*>(comp));
|
|
|
|
if (copy)
|
|
g_hash_table_insert (copyTable, (char *)core_name,
|
|
reinterpret_cast<void*>(copy));
|
|
|
|
if (pd_free)
|
|
g_hash_table_insert (freeTable, (char *)core_name,
|
|
reinterpret_cast<void*>(pd_free));
|
|
|
|
if (toString)
|
|
g_hash_table_insert (toStringTable, (char *)core_name,
|
|
reinterpret_cast<void*>(toString));
|
|
|
|
if (pred_equal)
|
|
g_hash_table_insert (predEqualTable, (char *)core_name,
|
|
reinterpret_cast<void*>(pred_equal));
|
|
}
|
|
|
|
static void init_tables (void)
|
|
{
|
|
unsigned int i;
|
|
struct
|
|
{
|
|
QofType name;
|
|
QofQueryPredicateFunc pred;
|
|
QofCompareFunc comp;
|
|
QueryPredicateCopyFunc copy;
|
|
QueryPredDataFree pd_free;
|
|
QueryToString toString;
|
|
QueryPredicateEqual pred_equal;
|
|
} knownTypes[] =
|
|
{
|
|
{
|
|
QOF_TYPE_STRING, string_match_predicate, string_compare_func,
|
|
string_copy_predicate, string_free_pdata, string_to_string,
|
|
string_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_DATE, date_match_predicate, date_compare_func,
|
|
date_copy_predicate, date_free_pdata, date_to_string,
|
|
date_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_DEBCRED, numeric_match_predicate, numeric_compare_func,
|
|
numeric_copy_predicate, numeric_free_pdata, debcred_to_string,
|
|
numeric_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_NUMERIC, numeric_match_predicate, numeric_compare_func,
|
|
numeric_copy_predicate, numeric_free_pdata, numeric_to_string,
|
|
numeric_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_GUID, guid_match_predicate, nullptr,
|
|
guid_copy_predicate, guid_free_pdata, nullptr,
|
|
guid_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_INT32, int32_match_predicate, int32_compare_func,
|
|
int32_copy_predicate, int32_free_pdata, int32_to_string,
|
|
int32_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_INT64, int64_match_predicate, int64_compare_func,
|
|
int64_copy_predicate, int64_free_pdata, int64_to_string,
|
|
int64_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_DOUBLE, double_match_predicate, double_compare_func,
|
|
double_copy_predicate, double_free_pdata, double_to_string,
|
|
double_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_BOOLEAN, boolean_match_predicate, boolean_compare_func,
|
|
boolean_copy_predicate, boolean_free_pdata, boolean_to_string,
|
|
boolean_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_CHAR, char_match_predicate, char_compare_func,
|
|
char_copy_predicate, char_free_pdata, char_to_string,
|
|
char_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_COLLECT, collect_match_predicate, collect_compare_func,
|
|
collect_copy_predicate, collect_free_pdata, nullptr,
|
|
collect_predicate_equal
|
|
},
|
|
{
|
|
QOF_TYPE_CHOICE, choice_match_predicate, nullptr,
|
|
choice_copy_predicate, choice_free_pdata, nullptr, choice_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
|
|
qof_query_copy_predicate (QofType type)
|
|
{
|
|
QueryPredicateCopyFunc rc;
|
|
g_return_val_if_fail (type, nullptr);
|
|
rc = reinterpret_cast<QueryPredicateCopyFunc>(g_hash_table_lookup (copyTable, type));
|
|
return rc;
|
|
}
|
|
|
|
static QueryPredDataFree
|
|
qof_query_predicate_free (QofType type)
|
|
{
|
|
g_return_val_if_fail (type, nullptr);
|
|
return reinterpret_cast<QueryPredDataFree>(g_hash_table_lookup (freeTable, type));
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* 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 (QofType type)
|
|
{
|
|
g_return_val_if_fail (type, nullptr);
|
|
return reinterpret_cast<QofQueryPredicateFunc>(g_hash_table_lookup (predTable, type));
|
|
}
|
|
|
|
QofCompareFunc
|
|
qof_query_core_get_compare (QofType type)
|
|
{
|
|
g_return_val_if_fail (type, nullptr);
|
|
return reinterpret_cast<QofCompareFunc>(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 = qof_query_predicate_free (pdata->type_name);
|
|
free_fcn (pdata);
|
|
}
|
|
|
|
QofQueryPredData *
|
|
qof_query_core_predicate_copy (const QofQueryPredData *pdata)
|
|
{
|
|
QueryPredicateCopyFunc copy;
|
|
|
|
g_return_val_if_fail (pdata, nullptr);
|
|
g_return_val_if_fail (pdata->type_name, nullptr);
|
|
|
|
copy = qof_query_copy_predicate (pdata->type_name);
|
|
return (copy (pdata));
|
|
}
|
|
|
|
char *
|
|
qof_query_core_to_string (QofType type, gpointer object,
|
|
QofParam *getter)
|
|
{
|
|
QueryToString toString;
|
|
|
|
g_return_val_if_fail (type, nullptr);
|
|
g_return_val_if_fail (object, nullptr);
|
|
g_return_val_if_fail (getter, nullptr);
|
|
|
|
toString = reinterpret_cast<QueryToString>(g_hash_table_lookup (toStringTable, type));
|
|
g_return_val_if_fail (toString, nullptr);
|
|
|
|
return toString (object, getter);
|
|
}
|
|
|
|
gboolean
|
|
qof_query_core_predicate_equal (const QofQueryPredData *p1, const QofQueryPredData *p2)
|
|
{
|
|
QueryPredicateEqual pred_equal;
|
|
|
|
if (p1 == p2) return TRUE;
|
|
if (!p1 || !p2) return FALSE;
|
|
|
|
if (p1->how != p2->how) return FALSE;
|
|
if (g_strcmp0 (p1->type_name, p2->type_name)) return FALSE;
|
|
|
|
pred_equal = reinterpret_cast<QueryPredicateEqual>(g_hash_table_lookup (predEqualTable, p1->type_name));
|
|
g_return_val_if_fail (pred_equal, FALSE);
|
|
|
|
return pred_equal (p1, p2);
|
|
}
|