Remove QOF files from src/engine

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@11879 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Neil Williams 2005-11-07 15:38:14 +00:00
parent 884224870b
commit 577fcd7873
62 changed files with 0 additions and 24120 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,539 +0,0 @@
/********************************************************************\
* 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 *
\********************************************************************/
/** @addtogroup Date
Utility functions to handle date and time (adjusting, getting
the current date, printing the date and time, etc.)
Overall, this file is quite a mess. Note, however, that other
applications, besides just GnuCash, use this file. In particular,
GnoTime (gttr.sourcefore.net) uses this file, and this file is
formally a part of QOF (qof.sourceforge.net).
An important note about time-keeping: The general goal of any
program that works with numeric time values SHOULD BE to always
stores and use UNIVERSAL TIME internally. Universal time is the
'one true time' that is independent of one's location on planet
Earth. It is measured in seconds from midnight January 1, 1970
in localtime-Grenwich (GMT). If one wants to display the local
time, then the display-print routine should make all final
tweaks to print the local time. The local time *must not* be
kept as a numeric value anywhere in the program. If one wants
to parse a user's input string as if it were local time, then
the output of the parse routine MUST BE universal time.
A sane program must never ever store (to file or db) a time
that is not Universal Time. Break these rules, and you will
rue the day...
\warning HACK ALERT -- the scan and print routines should probably be moved
to somewhere else. The engine really isn't involved with things
like printing formats. This is needed mostly by the GUI and so on.
If a file-io backend needs date handling, it should do it itself,
instead of depending on the routines here.
@author Copyright (C) 1997 Robin D. Clark <rclark@cs.hmc.edu>
@author Copyright (C) 1998-2001,2003 Linas Vepstas <linas@linas.org>
*/
/* @{
@file gnc-date.h
@brief Date and Time handling routines
*/
#ifndef GNC_DATE_H
#define GNC_DATE_H
#include <glib.h>
#include <time.h>
/** The maximum length of a string created by the date printers */
#define MAX_DATE_LENGTH 31
/** Constants *******************************************************/
/** \brief UTC date format string.
Timezone independent, date and time inclusive, as used in the QSF backend.
The T and Z characters are from xsd:dateTime format in coordinated universal time, UTC.
You can reproduce the string from the GNU/Linux command line using the date utility:
date -u +%Y-%m-%dT%H:M:SZ = 2004-12-12T23:39:11Z The datestring must be timezone independent
and include all specified fields. Remember to use gmtime() NOT localtime()!
*/
#define QOF_UTC_DATE_FORMAT "%Y-%m-%dT%H:%M:%SZ"
/** Enum for determining a date format */
typedef enum
{
QOF_DATE_FORMAT_US, /**< United states: mm/dd/yyyy */
QOF_DATE_FORMAT_UK, /**< Britain: dd/mm/yyyy */
QOF_DATE_FORMAT_CE, /**< Continental Europe: dd.mm.yyyy */
QOF_DATE_FORMAT_ISO, /**< ISO: yyyy-mm-dd */
QOF_DATE_FORMAT_UTC, /**< UTC: 2004-12-12T23:39:11Z */
QOF_DATE_FORMAT_LOCALE, /**< Take from locale information */
QOF_DATE_FORMAT_CUSTOM /**< Used by the check printing code */
} QofDateFormat;
#define DATE_FORMAT_FIRST QOF_DATE_FORMAT_US
#define DATE_FORMAT_LAST QOF_DATE_FORMAT_LOCALE
/**
* This is how to format the month, as a number, an abbreviated string,
* or the full name.
*/
typedef enum {
GNCDATE_MONTH_NUMBER,
GNCDATE_MONTH_ABBREV,
GNCDATE_MONTH_NAME
} GNCDateMonthFormat;
/** \name String / DateFormat conversion. */
//@{
/** \brief The string->value versions return FALSE on success and TRUE on failure */
const char* gnc_date_dateformat_to_string(QofDateFormat format);
/** \brief Converts the date format to a printable string.
Note the reversed return values!
@return FALSE on success, TRUE on failure.
*/
gboolean gnc_date_string_to_dateformat(const char* format_string,
QofDateFormat *format);
const char* gnc_date_monthformat_to_string(GNCDateMonthFormat format);
/** \brief Converts the month format to a printable string.
Note the reversed return values!
@return FALSE on success, TRUE on failure.
*/
gboolean gnc_date_string_to_monthformat(const char *format_string,
GNCDateMonthFormat *format);
// @}
/* Datatypes *******************************************************/
/** \brief Use a 64-bit signed int timespec
*
* struct timespec64 is just like the unix 'struct timespec' except
* that we use a 64-bit
* signed int to store the seconds. This should adequately cover
* dates in the distant future as well as the distant past, as long as
* they're not more than a couple dozen times the age of the universe.
* Note that both gcc and the IBM Toronto xlC compiler (aka CSet,
* VisualAge, etc) correctly handle long long as a 64 bit quantity,
* even on the 32-bit Intel x86 and PowerPC architectures. I'm
* assuming that all the other modern compilers are clean on this
* issue too. */
#ifndef SWIG /* swig 1.1p5 can't hack the long long type */
struct timespec64
{
long long int tv_sec;
long int tv_nsec;
};
#endif /* SWIG */
/** The Timespec is just like the unix 'struct timespec'
* except that we use a 64-bit signed int to
* store the seconds. This should adequately cover dates in the
* distant future as well as the distant past, as long as they're not
* more than a couple dozen times the age of the universe. Note that
* both gcc and the IBM Toronto xlC compiler (aka CSet, VisualAge,
* etc) correctly handle long long as a 64 bit quantity, even on the
* 32-bit Intel x86 and PowerPC architectures. I'm assuming that all
* the other modern compilers are clean on this issue too. */
typedef struct timespec64 Timespec;
/* Prototypes ******************************************************/
/** \name Timespec functions */
// @{
/** strict equality */
gboolean timespec_equal(const Timespec *ta, const Timespec *tb);
/** comparison: if (ta < tb) -1; else if (ta > tb) 1; else 0; */
int timespec_cmp(const Timespec *ta, const Timespec *tb);
/** difference between ta and tb, results are normalised
* ie tv_sec and tv_nsec of the result have the same size
* abs(result.tv_nsec) <= 1000000000 */
Timespec timespec_diff(const Timespec *ta, const Timespec *tb);
/** absolute value, also normalised */
Timespec timespec_abs(const Timespec *t);
/** convert a timepair on a certain day (localtime) to
* the timepair representing midday on that day */
Timespec timespecCanonicalDayTime(Timespec t);
/** Turns a time_t into a Timespec */
void timespecFromTime_t( Timespec *ts, time_t t );
/** Turns a Timespec into a time_t */
time_t timespecToTime_t (Timespec ts);
/** Convert a day, month, and year to a Timespec */
Timespec gnc_dmy2timespec (int day, int month, int year);
/** Same as gnc_dmy2timespec, but last second of the day */
Timespec gnc_dmy2timespec_end (int day, int month, int year);
/** The gnc_iso8601_to_timespec_gmt() routine converts an ISO-8601 style
* date/time string to Timespec. Please note that ISO-8601 strings
* are a representation of Universal Time (UTC), and as such, they
* 'store' UTC. To make them human readable, they show timezone
* information along with a local-time string. But fundamentally,
* they *are* UTC. Thus, thir routine takes a UTC input, and
* returns a UTC output.
*
* For example: 1998-07-17 11:00:00.68-0500
* is 680 milliseconds after 11 o'clock, central daylight time
* It is also 680 millisecs after 16:00:00 hours UTC.
* \return The universl time.
*
* XXX Caution: this routine does not handle strings that specify
* times before January 1 1970.
*/
Timespec gnc_iso8601_to_timespec_gmt(const char *);
/** The gnc_timespec_to_iso8601_buff() routine takes the input
* UTC Timespec value and prints it as an ISO-8601 style string.
* The buffer must be long enough to contain the NULL-terminated
* string (32 characters + NUL). This routine returns a pointer
* to the null terminator (and can thus be used in the 'stpcpy'
* metaphor of string concatenation).
*
* Please note that ISO-8601 strings are a representation of
* Universal Time (UTC), and as such, they 'store' UTC. To make them
* human readable, they show timezone information along with a
* local-time string. But fundamentally, they *are* UTC. Thus,
* this routine takes a UTC input, and returns a UTC output.
*
* The string generated by this routine uses the local timezone
* on the machine on which it is executing to create the timestring.
*/
char * gnc_timespec_to_iso8601_buff (Timespec ts, char * buff);
/** DOCUMENT ME! FIXME: Probably similar to xaccDMYToSec() this date
* routine might return incorrect values for dates before 1970. */
void gnc_timespec2dmy (Timespec ts, int *day, int *month, int *year);
/** Add a number of months to a time value and normalize. Optionally
* also track the last day of the month, i.e. 1/31 -> 2/28 -> 3/30. */
void date_add_months (struct tm *tm, int months, gboolean track_last_day);
/** \warning hack alert XXX FIXME -- these date routines return incorrect
* values for dates before 1970. Most of them are good only up
* till 2038. This needs fixing ...
*
* XXX This routine should be modified to assume that the
* the user wanted the time at noon, localtime. The returned
* time_t should be seconds (at GMT) of the local noon-time.
*/
time_t xaccDMYToSec (int day, int month, int year);
/** The gnc_timezone function returns the number of seconds *west*
* of UTC represented by the tm argument, adjusted for daylight
* savings time.
*
* This function requires a tm argument returned by localtime or set
* by mktime. This is a strange function! It requires that localtime
* or mktime be called before use. Subsequent calls to localtime or
* mktime *may* invalidate the result! The actual contents of tm *may*
* be used for both timezone offset and daylight savings time, or only
* daylight savings time! Timezone stuff under unix is not
* standardized and is a big mess.
*/
long int gnc_timezone (struct tm *tm);
// @}
/* ------------------------------------------------------------------------ */
/** \name QofDateFormat functions */
// @{
/** The qof_date_format_get routine returns the date format that
* the date printing will use when printing a date, and the scaning
* routines will assume when parsing a date.
* @returns: the one of the enumerated date formats.
*/
QofDateFormat qof_date_format_get(void);
/**
* The qof_date_format_set() routine sets date format to one of
* US, UK, CE, OR ISO. Checks to make sure it's a legal value.
* Args: QofDateFormat: enumeration indicating preferred format
*/
void qof_date_format_set(QofDateFormat df);
/** This function returns a strftime formatting string for printing an
* all numeric date (e.g. 2005-09-14). The string returned is based
* upon the location specified.
*
* @param df The date style (us, uk, iso, etc) that should be provided.
*
* @return A formatting string that will print a date in the
* requested style */
const gchar *qof_date_format_get_string(QofDateFormat df);
/** This function returns a strftime formatting string for printing a
* date using words and numbers (e.g. 2005-September-14). The string
* returned is based upon the location specified.
*
* @param df The date style (us, uk, iso, etc) that should be provided.
*
* @return A formatting string that will print a date in the
* requested style */
const gchar *qof_date_text_format_get_string(QofDateFormat df);
// @}
/** dateSeparator
* Return the field separator for the current date format
*
* Args: none
*
* Return: date character
*
* Globals: global dateFormat value
*/
char dateSeparator(void);
/** \name Date Printing/Scanning functions
*/
// @{
/**
* \warning HACK ALERT -- the scan and print routines should probably
* be moved to somewhere else. The engine really isn't involved with
* things like printing formats. This is needed mostly by the GUI and
* so on. If a file-io thing needs date handling, it should do it
* itself, instead of depending on the routines here.
*/
/** qof_print_date_dmy_buff
* Convert a date as day / month / year integers into a localized string
* representation
*
* Args: buff - pointer to previously allocated character array; its size
* must be at lease MAX_DATE_LENTH bytes.
* len - length of the buffer, in bytes.
* day - day of the month as 1 ... 31
* month - month of the year as 1 ... 12
* year - year (4-digit)
*
* Returns: number of characters printed
*
* Globals: global dateFormat value
**/
size_t qof_print_date_dmy_buff (char * buff, size_t buflen, int day, int month, int year);
/** Convenience: calls through to qof_print_date_dmy_buff(). **/
size_t qof_print_date_buff (char * buff, size_t buflen, time_t secs);
/** Convenience; calls through to qof_print_date_dmy_buff(). **/
size_t qof_print_gdate( char *buf, size_t bufflen, GDate *gd );
/** Convenience; calls through to qof_print_date_dmy_buff().
* Return: string, which should be freed when no longer needed.
* **/
char * qof_print_date (time_t secs);
/** Convenience; calls through to qof_print_date_dmy_buff().
* Return: static global string.
* \warning This routine is not thread-safe, because it uses a single
* global buffer to store the return value. Use qof_print_date_buff()
* or qof_print_date() instead.
* **/
const char * gnc_print_date(Timespec ts);
/* ------------------------------------------------------------------ */
/* time printing utilities */
/** The qof_print_hours_elapsed_buff() routine will print the 'secs' argument
* as HH:MM, and will print the seconds if show_secs is true.
* Thus, for example, secs=3599 will print as 0:59
* Returns the number of bytes copied.
*/
size_t qof_print_hours_elapsed_buff (char * buff, size_t len, int secs, gboolean show_secs);
size_t qof_print_minutes_elapsed_buff (char * buff, size_t len, int secs, gboolean show_secs);
/** The qof_print_time_buff() routine prints only the hour-part of the date.
* Thus, if secs is ...
* Returns the number of bytes printed.
*/
size_t qof_print_time_buff (char * buff, size_t len, time_t secs);
size_t qof_print_date_time_buff (char * buff, size_t len, time_t secs);
/** The qof_is_same_day() routine returns 0 if both times are in the
* same day.
*/
gboolean qof_is_same_day (time_t, time_t);
/* ------------------------------------------------------------------ */
/** The xaccDateUtilGetStamp() routine will take the given time in
* seconds and return a buffer containing a textual for the date.
* @param thyme The time in seconds to convert.
* @return A pointer to the generated string.
* @note The caller owns this buffer and must free it when done. */
char * xaccDateUtilGetStamp (time_t thyme);
/** qof_scan_date
* Convert a string into day / month / year integers according to
* the current dateFormat value.
*
* Args: buff - pointer to date string
* day - will store day of the month as 1 ... 31
* month - will store month of the year as 1 ... 12
* year - will store the year (4-digit)
*
* Return: TRUE if the string seemed to be a valid date; else FALSE.
*
* Globals: uses global dateFormat value to assist in parsing.
*/
gboolean qof_scan_date (const char *buff, int *day, int *month, int *year);
/** as above, but returns seconds */
gboolean qof_scan_date_secs (const char *buff, time_t *secs);
// @}
/** \name Date Start/End Adjustment routines
* Given a time value, adjust it to be the beginning or end of that day.
*/
// @{
/** The gnc_tm_set_day_start() inline routine will set the appropriate
* fields in the struct tm to indicate the first second of that day.
* This routine assumes that the contents of the data structure is
* already in normalized form. */
static inline
void gnc_tm_set_day_start (struct tm *tm)
{
/* First second of the day */
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
}
/** The gnc_tm_set_day_start() inline routine will set the appropriate
* fields in the struct tm to indicate noon of that day. This
* routine assumes that the contents of the data structure is already
* in normalized form.*/
static inline
void gnc_tm_set_day_middle (struct tm *tm)
{
/* First second of the day */
tm->tm_hour = 12;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
}
/** The gnc_tm_set_day_start() inline routine will set the appropriate
* fields in the struct tm to indicate the last second of that day.
* This routine assumes that the contents of the data structure is
* already in normalized form.*/
static inline
void gnc_tm_set_day_end (struct tm *tm)
{
/* Last second of the day */
tm->tm_hour = 23;
tm->tm_min = 59;
tm->tm_sec = 59;
tm->tm_isdst = -1;
}
/** The gnc_tm_get_day_start() routine will convert the given time in
* seconds to the struct tm format, and then adjust it to the
* first second of that day. */
void gnc_tm_get_day_start(struct tm *tm, time_t time_val);
/** The gnc_tm_get_day_end() routine will convert the given time in
* seconds to the struct tm format, and then adjust it to the
* last second of that day. */
void gnc_tm_get_day_end(struct tm *tm, time_t time_val);
/** The gnc_timet_get_day_start() routine will take the given time in
* seconds and adjust it to the last second of that day. */
time_t gnc_timet_get_day_start(time_t time_val);
/** The gnc_timet_get_day_end() routine will take the given time in
* seconds and adjust it to the last second of that day. */
time_t gnc_timet_get_day_end(time_t time_val);
#ifndef GNUCASH_MAJOR_VERSION
/** The gnc_timet_get_day_start() routine will take the given time in
* GLib GDate format and adjust it to the last second of that day.
*
* @deprecated
*/
time_t gnc_timet_get_day_start_gdate (GDate *date);
/** The gnc_timet_get_day_end() routine will take the given time in
* GLib GDate format and adjust it to the last second of that day.
*
* @deprecated
*/
time_t gnc_timet_get_day_end_gdate (GDate *date);
#endif /* GNUCASH_MAJOR_VERSION */
/** Get the numerical last date of the month. (28, 29, 30, 31) */
int date_get_last_mday(struct tm *tm);
/** Is the mday field the last day of the specified month.*/
gboolean date_is_last_mday(struct tm *tm);
/** DOCUMENT ME! Probably the same as date_get_last_mday() */
int gnc_date_my_last_mday (int month, int year);
/** DOCUMENT ME! Probably the same as date_get_last_mday() */
int gnc_timespec_last_mday (Timespec ts);
// @}
/* ======================================================== */
/** \name Today's Date */
// @{
/** The gnc_tm_get_today_start() routine takes a pointer to a struct
* tm and fills it in with the first second of the today. */
void gnc_tm_get_today_start(struct tm *tm);
/** The gnc_tm_get_today_end() routine takes a pointer to a struct
* tm and fills it in with the last second of the today. */
void gnc_tm_get_today_end(struct tm *tm);
/** The gnc_timet_get_today_start() routine returns a time_t value
* corresponding to the first second of today. */
time_t gnc_timet_get_today_start(void);
/** The gnc_timet_get_today_end() routine returns a time_t value
* corresponding to the last second of today. */
time_t gnc_timet_get_today_end(void);
/** The xaccDateUtilGetStampNow() routine returns the current time in
* seconds in textual format.
* @return A pointer to the generated string.
* @note The caller owns this buffer and must free it when done. */
char * xaccDateUtilGetStampNow (void);
//@}
//@}
#endif /* GNC_DATE_H */

View File

@ -1,307 +0,0 @@
/********************************************************************\
* gnc-engine-util.c -- QOF utility functions *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1997-2001,2004 Linas Vepstas <linas@linas.org> *
* *
* 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 *
* *
* Author: Rob Clark (rclark@cs.hmc.edu) *
* Author: Linas Vepstas (linas@linas.org) *
\********************************************************************/
#include "config.h"
#include <ctype.h>
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include "qof.h"
#include "gnc-engine-util.h"
/********************************************************************\
\********************************************************************/
/* Search for str2 in first nchar chars of str1, ignore case.. Return
* pointer to first match, or null. */
char *
strncasestr(const char *str1, const char *str2, size_t len)
{
while (*str1 && len--)
{
if (toupper(*str1) == toupper(*str2))
{
if (strncasecmp(str1,str2,strlen(str2)) == 0)
{
return (char *) str1;
}
}
str1++;
}
return NULL;
}
/* Search for str2 in str1, ignore case. Return pointer to first
* match, or null. */
char *
strcasestr(const char *str1, const char *str2)
{
size_t len = strlen (str1);
char * retval = strncasestr (str1, str2, len);
return retval;
}
/********************************************************************\
\********************************************************************/
int
safe_strcmp (const char * da, const char * db)
{
SAFE_STRCMP (da, 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)
{
if (da && db) return strcmp (da, db);
if (!da && db && 0==db[0]) return 0;
if (!db && da && 0==da[0]) return 0;
if (!da && db) return -1;
if (da && !db) return +1;
return 0;
}
/********************************************************************\
\********************************************************************/
#define MAX_DIGITS 50
/* inverse of strtoul */
char *
ultostr (unsigned long val, int base)
{
char buf[MAX_DIGITS];
unsigned long broke[MAX_DIGITS];
int i;
unsigned long places=0, reval;
if ((2>base) || (36<base)) return NULL;
/* count digits */
places = 0;
for (i=0; i<MAX_DIGITS; i++) {
broke[i] = val;
places ++;
val /= base;
if (0 == val) break;
}
/* normalize */
reval = 0;
for (i=places-2; i>=0; i--) {
reval += broke[i+1];
reval *= base;
broke[i] -= reval;
}
/* print */
for (i=0; i<(int)places; i++) {
if (10>broke[i]) {
buf[places-1-i] = 0x30+broke[i]; /* ascii digit zero */
} else {
buf[places-1-i] = 0x41-10+broke[i]; /* ascii capital A */
}
}
buf[places] = 0x0;
return g_strdup (buf);
}
/********************************************************************\
* returns TRUE if the string is a number, possibly with whitespace
\********************************************************************/
gboolean
gnc_strisnum(const char *s)
{
if (s == NULL) return FALSE;
if (*s == 0) return FALSE;
while (*s && isspace(*s))
s++;
if (*s == 0) return FALSE;
if (!isdigit(*s)) return FALSE;
while (*s && isdigit(*s))
s++;
if (*s == 0) return TRUE;
while (*s && isspace(*s))
s++;
if (*s == 0) return TRUE;
return FALSE;
}
/********************************************************************\
* our own version of stpcpy
\********************************************************************/
char *
gnc_stpcpy (char *dest, const char *src)
{
strcpy (dest, src);
return (dest + strlen (src));
}
/* =================================================================== */
/* Return NULL if the field is whitespace (blank, tab, formfeed etc.)
* Else return pointer to first non-whitespace character. */
const char *
qof_util_whitespace_filter (const char * val)
{
size_t len;
if (!val) return NULL;
len = strspn (val, "\a\b\t\n\v\f\r ");
if (0 == val[len]) return NULL;
return val+len;
}
/* =================================================================== */
/* Return integer 1 if the string starts with 't' or 'T' or contains the
* word 'true' or 'TRUE'; if string is a number, return that number. */
int
qof_util_bool_to_int (const char * val)
{
const char * p = qof_util_whitespace_filter (val);
if (!p) return 0;
if ('t' == p[0]) return 1;
if ('T' == p[0]) return 1;
if ('y' == p[0]) return 1;
if ('Y' == p[0]) return 1;
if (strstr (p, "true")) return 1;
if (strstr (p, "TRUE")) return 1;
if (strstr (p, "yes")) return 1;
if (strstr (p, "YES")) return 1;
return atoi (val);
}
/********************************************************************\
* The engine string cache
\********************************************************************/
static GCache * gnc_string_cache = NULL;
#ifdef THESE_CAN_BE_USEFUL_FOR_DEGUGGING
static guint g_str_hash_KEY(gconstpointer v) {
return g_str_hash(v);
}
static guint g_str_hash_VAL(gconstpointer v) {
return g_str_hash(v);
}
static gpointer g_strdup_VAL(gpointer v) {
return g_strdup(v);
}
static gpointer g_strdup_KEY(gpointer v) {
return g_strdup(v);
}
static void g_free_VAL(gpointer v) {
return g_free(v);
}
static void g_free_KEY(gpointer v) {
return g_free(v);
}
static gboolean gnc_str_equal(gconstpointer v, gconstpointer v2)
{
return (v && v2) ? g_str_equal(v, v2) : FALSE;
}
#endif
GCache*
gnc_engine_get_string_cache(void)
{
if(!gnc_string_cache) {
gnc_string_cache = g_cache_new(
(GCacheNewFunc) g_strdup, /* value_new_func */
g_free, /* value_destroy_func */
(GCacheDupFunc) g_strdup, /* key_dup_func */
g_free, /* key_destroy_func */
g_str_hash, /* hash_key_func */
g_str_hash, /* hash_value_func */
g_str_equal); /* key_equal_func */
}
return gnc_string_cache;
}
void
gnc_engine_string_cache_destroy (void)
{
if (gnc_string_cache)
g_cache_destroy (gnc_string_cache);
gnc_string_cache = NULL;
}
void
gnc_string_cache_remove(gconstpointer key)
{
g_cache_remove(gnc_engine_get_string_cache(), key);
}
gpointer
gnc_string_cache_insert(gpointer key)
{
return g_cache_insert(gnc_engine_get_string_cache(), key);
}
void
qof_init (void)
{
gnc_engine_get_string_cache ();
guid_init ();
qof_object_initialize ();
qof_query_init ();
qof_book_register ();
}
void
qof_close(void)
{
qof_query_shutdown ();
qof_object_shutdown ();
guid_shutdown ();
gnc_engine_string_cache_destroy ();
}
/************************* END OF FILE ******************************\
\********************************************************************/

View File

@ -1,264 +0,0 @@
/********************************************************************\
* gnc-engine-util.h -- QOF utility functions *
* *
* 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 *
\********************************************************************/
/** @addtogroup Utilities
@{ */
/** @file gnc-engine-util.h
@brief QOF utility functions
@author Copyright (C) 1997 Robin D. Clark <rclark@cs.hmc.edu>
@author Copyright (C) 2000 Bill Gribble <grib@billgribble.com>
@author Copyright (C) 1997-2002,2004 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_UTIL_H
#define QOF_UTIL_H
#include <glib.h>
#include <stddef.h>
#include "config.h"
#include "qof.h"
/** Macros *****************************************************/
/* CAS: Notice that this macro does nothing if pointer args are equal.
Otherwise, it returns an integer. Actually, perhaps these macro
should be private. They are NOT good substitutes for the function
versions like safe_strcmp(). Maybe external users of these 3
macros should be converted to use safe_strcmp(). Actually, THESE
MACROS AFFECT CONTROL FLOW. YUCK! */
#define SAFE_STRCMP_REAL(fcn,da,db) { \
if ((da) && (db)) { \
if ((da) != (db)) { \
int retval = fcn ((da), (db)); \
/* if strings differ, return */ \
if (retval) return retval; \
} \
} else \
if ((!(da)) && (db)) { \
return -1; \
} else \
if ((da) && (!(db))) { \
return +1; \
} \
}
#define SAFE_STRCMP(da,db) SAFE_STRCMP_REAL(strcmp,(da),(db))
#define SAFE_STRCASECMP(da,db) SAFE_STRCMP_REAL(strcasecmp,(da),(db))
/** \name typedef enum as string macros
@{
*/
#define ENUM_BODY(name, value) \
name value,
#define AS_STRING_CASE(name, value) \
case name: { return #name; }
#define FROM_STRING_CASE(name, value) \
if (strcmp(str, #name) == 0) { \
return name; }
#define DEFINE_ENUM(name, list) \
typedef enum { \
list(ENUM_BODY) \
}name;
#define AS_STRING_DEC(name, list) \
const char* name##asString(name n);
#define AS_STRING_FUNC(name, list) \
const char* name##asString(name n) { \
switch (n) { \
list(AS_STRING_CASE) \
default: return ""; } }
#define FROM_STRING_DEC(name, list) \
name name##fromString \
(const char* str);
#define FROM_STRING_FUNC(name, list) \
name name##fromString \
(const char* str) { \
if(str == NULL) { return 0; } \
list(FROM_STRING_CASE) \
return 0; }
/** @} */
/** \name enum as string with no typedef
@{
Similar but used when the enum is NOT a typedef
note the LACK of a define_enum macro - don't use one!
ENUM_BODY is used in both types.
*/
#define FROM_STRING_DEC_NON_TYPEDEF(name, list) \
void name##fromString \
(const char* str, enum name *type);
#define FROM_STRING_CASE_NON_TYPEDEF(name, value) \
if (strcmp(str, #name) == 0) { *type = name; }
#define FROM_STRING_FUNC_NON_TYPEDEF(name, list) \
void name##fromString \
(const char* str, enum name *type) { \
if(str == NULL) { return; } \
list(FROM_STRING_CASE_NON_TYPEDEF) }
#define AS_STRING_DEC_NON_TYPEDEF(name, list) \
const char* name##asString(enum name n);
#define AS_STRING_FUNC_NON_TYPEDEF(name, list) \
const char* name##asString(enum name n) { \
switch (n) { \
list(AS_STRING_CASE_NON_TYPEDEF) \
default: return ""; } }
#define AS_STRING_CASE_NON_TYPEDEF(name, value) \
case name: { return #name; }
/** @} */
/* Define the long long int conversion for scanf */
#if HAVE_SCANF_LLD
# define GNC_SCANF_LLD "%lld"
#else
# define GNC_SCANF_LLD "%qd"
#endif
/** @name Convenience wrappers
@{
*/
/** \brief Initialise the Query Object Framework
Used for non-Guile applications or test routines.
*/
void qof_init (void);
/** \brief Safely close down the Query Object Framework
Used for non-Guile applications or test routines.
*/
void qof_close (void);
/** @} */
/** Prototypes *************************************************/
/** The safe_strcmp compares strings a and b the same way that strcmp()
* does, except that either may be null. This routine assumes that
* 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
* a null string is equal to the empty string.
*/
int null_strcmp (const char * da, const char * db);
/** Search for str2 in first nchar chars of str1, ignore case. Return
* pointer to first match, or null. These are just like that strnstr
* and the strstr functions, except that they ignore the case. */
extern char *strncasestr(const char *str1, const char *str2, size_t len);
extern char *strcasestr(const char *str1, const char *str2);
/** The ultostr() subroutine is the inverse of strtoul(). It accepts a
* number and prints it in the indicated base. The returned string
* should be g_freed when done. */
char * ultostr (unsigned long val, int base);
/** Returns true if string s is a number, possibly surrounded by
* whitespace. */
gboolean gnc_strisnum(const char *s);
/** Local copy of stpcpy, used wtih libc's that don't have one. */
char * gnc_stpcpy (char *dest, const char *src);
#ifndef HAVE_STPCPY
#define stpcpy gnc_stpcpy
#endif
/** Return NULL if the field is whitespace (blank, tab, formfeed etc.)
* Else return pointer to first non-whitespace character.
*/
const char * qof_util_whitespace_filter (const char * val);
/** Return integer 1 if the string starts with 't' or 'T' or
* contains the word 'true' or 'TRUE'; if string is a number,
* return that number. (Leading whitespace is ignored). */
int qof_util_bool_to_int (const char * val);
/** Gnucash's String Cache:
*
* Many strings used throughout the engine are likely to be duplicated.
* So we provide a reference counted cache system for the strings, which
* shares strings whenever possible.
*
* Use gnc_string_cache_insert to insert a string into the cache (it
* will return a pointer to the cached string). Basically you should
* use this instead of g_strdup.
*
* Use gnc_string_cache_remove (giving it a pointer to a cached
* string) if the string is unused. If this is the last reference to
* the string it will be removed from the cache, otherwise it will
* just decrement the reference count. Basically you should use this
* instead of g_free.
*
* Just in case it's not clear: The remove function must NOT be called
* for the string you passed INTO the insert function. It must be
* called for the _cached_ string that is _returned_ by the insert
* function.
*
* Note that all the work is done when inserting or removing. Once
* cached the strings are just plain C strings.
*
* The string cache is demand-created on first use.
*
**/
/** \deprecated use qof_init instead.
Get the gnc_string_cache. Create it if it doesn't exist already
*/
GCache* gnc_engine_get_string_cache(void);
/** Destroy the gnc_string_cache */
void gnc_engine_string_cache_destroy (void);
/* You can use this function as a destroy notifier for a GHashTable
that uses common strings as keys (or values, for that matter.) */
void gnc_string_cache_remove(gconstpointer key);
/* You can use this function with g_hash_table_insert(), or the key
(or value), as long as you use the destroy notifier above. */
gpointer gnc_string_cache_insert(gpointer key);
#define CACHE_INSERT(str) gnc_string_cache_insert((gpointer)(str));
#define CACHE_REMOVE(str) gnc_string_cache_remove((str));
#endif /* QOF_UTIL_H */
/** @} */

View File

@ -1,37 +0,0 @@
/********************************************************************
* gnc-event-p.h -- private engine event handling interface *
* Copyright 2000 Dave Peticolas <dave@krondo.com> *
* *
* 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 *
* *
********************************************************************/
#ifndef GNC_EVENT_P_H
#define GNC_EVENT_P_H
#include "gnc-event.h"
#include "qofid.h"
/* XXX deprecated, but still usedion on postgres backend */
void gnc_engine_generate_event (const GUID *, QofIdType, GNCEngineEventType);
/* generates an event even when events are suspended! */
void gnc_engine_force_event (QofEntity *entity,
GNCEngineEventType event_type);
#endif

View File

@ -1,224 +0,0 @@
/********************************************************************
* gnc-event.c -- engine event handling implementation *
* Copyright 2000 Dave Peticolas <dave@krondo.com> *
* *
* 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 "gnc-event-p.h"
#include "gnc-trace.h"
/** Declarations ****************************************************/
typedef struct
{
GNCEngineEventHandler handler;
gpointer user_data;
gint handler_id;
} HandlerInfo;
/** Static Variables ************************************************/
static guint suspend_counter = 0;
static gint next_handler_id = 1;
static GList *handlers = NULL;
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = QOF_MOD_ENGINE;
/** Implementations *************************************************/
gint
gnc_engine_register_event_handler (GNCEngineEventHandler handler,
gpointer user_data)
{
HandlerInfo *hi;
gint handler_id;
GList *node;
ENTER ("(handler=%p, data=%p)", handler, user_data);
/* sanity check */
if (!handler)
{
PERR ("no handler specified");
return 0;
}
/* look for a free handler id */
handler_id = next_handler_id;
node = handlers;
while (node)
{
hi = node->data;
if (hi->handler_id == handler_id)
{
handler_id++;
node = handlers;
continue;
}
node = node->next;
}
/* Found one, add the handler */
hi = g_new0 (HandlerInfo, 1);
hi->handler = handler;
hi->user_data = user_data;
hi->handler_id = handler_id;
handlers = g_list_prepend (handlers, hi);
/* Update id for next registration */
next_handler_id = handler_id + 1;
LEAVE ("(handler=%p, data=%p) handler_id=%d", handler, user_data, handler_id);
return handler_id;
}
void
gnc_engine_unregister_event_handler (gint handler_id)
{
GList *node;
ENTER ("(handler_id=%d)", handler_id);
for (node = handlers; node; node = node->next)
{
HandlerInfo *hi = node->data;
if (hi->handler_id != handler_id)
continue;
/* Found it, take out of list */
handlers = g_list_remove_link (handlers, node);
LEAVE ("(handler_id=%d) handler=%p data=%p", handler_id, hi->handler, hi->user_data);
/* safety */
hi->handler = NULL;
g_list_free_1 (node);
g_free (hi);
return;
}
PERR ("no such handler: %d", handler_id);
}
void
gnc_engine_suspend_events (void)
{
suspend_counter++;
if (suspend_counter == 0)
{
PERR ("suspend counter overflow");
}
}
void
gnc_engine_resume_events (void)
{
if (suspend_counter == 0)
{
PERR ("suspend counter underflow");
return;
}
suspend_counter--;
}
static void
gnc_engine_generate_event_internal (QofEntity *entity,
GNCEngineEventType event_type)
{
GList *node;
GList *next_node = NULL;
g_return_if_fail(entity);
switch (event_type)
{
case GNC_EVENT_NONE:
return;
case GNC_EVENT_CREATE:
case GNC_EVENT_MODIFY:
case GNC_EVENT_DESTROY:
case GNC_EVENT_ADD:
case GNC_EVENT_REMOVE:
break;
default:
PERR ("bad event type %d", event_type);
return;
}
for (node = handlers; node; node = next_node)
{
HandlerInfo *hi = node->data;
next_node = node->next;
PINFO ("id=%d hi=%p han=%p", hi->handler_id, hi, hi->handler);
if (hi->handler)
hi->handler ((GUID *)&entity->guid, entity->e_type, event_type, hi->user_data);
}
}
void
gnc_engine_force_event (QofEntity *entity,
GNCEngineEventType event_type)
{
if (!entity)
return;
gnc_engine_generate_event_internal (entity, event_type);
}
void
gnc_engine_gen_event (QofEntity *entity, GNCEngineEventType event_type)
{
if (!entity)
return;
if (suspend_counter)
return;
gnc_engine_generate_event_internal (entity, event_type);
}
void
gnc_engine_generate_event (const GUID *guid, QofIdType e_type,
GNCEngineEventType event_type)
{
QofEntity ent;
ent.guid = *guid;
ent.e_type = e_type;
if (suspend_counter) return;
gnc_engine_generate_event_internal (&ent, event_type);
}
/* =========================== END OF FILE ======================= */

View File

@ -1,118 +0,0 @@
/********************************************************************
* gnc-event.h -- engine event handling interface *
* Copyright 2000 Dave Peticolas <dave@krondo.com> *
* *
* 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 *
* *
********************************************************************/
/** @addtogroup Event
@{
*/
/** @file gnc-event.h
@brief engine event handling interface
@author Copyright 2000 Dave Peticolas <dave@krondo.com>
*/
#ifndef GNC_EVENT_H
#define GNC_EVENT_H
#include <glib.h>
#include "guid.h"
#include "qofid.h"
typedef enum
{
GNC_EVENT_NONE = 0,
GNC_EVENT_CREATE = 1 << 0,
GNC_EVENT_MODIFY = 1 << 1,
GNC_EVENT_DESTROY = 1 << 2,
GNC_EVENT_ADD = 1 << 3,
GNC_EVENT_REMOVE = 1 << 4,
GNC_EVENT_ALL = 0xff
} GNCEngineEventType;
/** GNCEngineEventHandler
* Handler invoked when an engine event occurs.
*
* @param entity: GUID of entity generating event
* @param type: QofIdType of the entity generating the event
* @param event_type: one of the single-bit GNCEngineEventTypes, not a combination
* @param user_data: user_data supplied when handler was registered.
*/
typedef void (*GNCEngineEventHandler) (GUID *entity, QofIdType type,
GNCEngineEventType event_type,
gpointer user_data);
/** gnc_engine_register_event_handler
* Register a handler for engine events.
*
* @param handler: handler to register
* @param user_data: data provided when handler is invoked
*
* @return id identifying handler
*/
gint gnc_engine_register_event_handler (GNCEngineEventHandler handler,
gpointer user_data);
/** gnc_engine_unregister_event_handler
* Unregister an engine event handler.
*
* @param handler_id: the id of the handler to unregister
*/
void gnc_engine_unregister_event_handler (gint handler_id);
/** gnc_engine_generate_event
* Invoke all registered event handlers using the given arguments.
*
* GNC_EVENT_CREATE events should be generated after the object
* has been created and registered in the engine entity table.
* GNC_EVENT_MODIFY events should be generated whenever any data
* member or submember (i.e., splits) is changed.
* GNC_EVENT_DESTROY events should be called before the object
* has been destroyed or removed from the entity table.
*
* @param entity: the GUID of the entity generating the event
* @param event_type: the type of event -- this should be one of the
* single-bit GNCEngineEventType values, not a combination.
*/
void gnc_engine_gen_event (QofEntity *entity,
GNCEngineEventType event_type);
/** gnc_engine_suspend_events
* Suspend all engine events. This function may be
* called multiple times. To resume event generation,
* an equal number of calls to gnc_engine_resume_events
* must be made.
*/
void gnc_engine_suspend_events (void);
/** gnc_engine_resume_events
* Resume engine event generation.
*/
void gnc_engine_resume_events (void);
#endif
/** @} */

View File

@ -1,370 +0,0 @@
/* *****************************************************************\
* gnc-trace.c -- QOF error logging and tracing facility *
* Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org> *
* Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk> *
* *
* 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 *
* *
* Author: Rob Clark (rclark@cs.hmc.edu) *
* Author: Linas Vepstas (linas@linas.org) *
\********************************************************************/
/** @addtogroup Trace
@{ */
/** @file gnc-trace.c
@brief QOF error logging facility
@author Neil Williams <linux@codehelp.co.uk>
*/
#include "config.h"
#include <glib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#else
/* What to do? */
#endif
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include "qof.h"
#include "gnc-trace.h"
static FILE *fout = NULL;
static gchar* filename = NULL;
static const int MAX_TRACE_FILENAME = 100;
static GHashTable *log_table = NULL;
AS_STRING_FUNC(gncLogLevel, LOG_LEVEL_LIST) /**< enum_as_string function
uses the enum_as_string macro from QOF
but the From macro is not required. Lookups
are done on the string. */
FROM_STRING_FUNC(gncLogLevel, LOG_LEVEL_LIST)
/* Don't be fooled: gnc_trace_num_spaces has external linkage and
static storage, but can't be defined with 'extern' because it has
an initializer, and can't be declared with 'static' because that
would give it internal linkage. */
gint __attribute__ ((unused)) gnc_trace_num_spaces = 0;
static void
fh_printer (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer user_data)
{
extern gint gnc_trace_num_spaces;
FILE *fh = user_data;
fprintf (fh, "%*s%s\n", gnc_trace_num_spaces, "", message);
fflush(fh);
}
void
gnc_log_init (void)
{
if(!fout) //allow gnc_set_logfile
{
fout = fopen ("/tmp/qof.trace", "w");
}
if(!fout && (filename = (char *)g_malloc(MAX_TRACE_FILENAME))) {
snprintf(filename, MAX_TRACE_FILENAME-1, "/tmp/qof.trace.%d",
getpid());
fout = fopen (filename, "w");
g_free(filename);
}
if(!fout)
fout = stderr;
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, fh_printer, fout);
}
/* Set the logging level of the given module. */
void
gnc_set_log_level(QofLogModule log_module, gncLogLevel level)
{
gchar* level_string;
if(!log_module || level == 0) { return; }
level_string = g_strdup(gncLogLevelasString(level));
if(!log_table)
{
log_table = g_hash_table_new(g_str_hash, g_str_equal);
}
g_hash_table_insert(log_table, (gpointer)log_module, level_string);
}
static void
log_module_foreach(gpointer key, gpointer value, gpointer data)
{
g_hash_table_insert(log_table, key, data);
}
/* Set the logging level for all known modules. */
void
gnc_set_log_level_global(gncLogLevel level)
{
gchar* level_string;
if(!log_table || level == 0) { return; }
level_string = g_strdup(gncLogLevelasString(level));
g_hash_table_foreach(log_table, log_module_foreach, level_string);
}
void
gnc_set_logfile (FILE *outfile)
{
if(!outfile) { fout = stderr; return; }
fout = outfile;
}
void
qof_log_init_filename (const gchar* logfilename)
{
if(!logfilename)
{
fout = stderr;
}
else
{
filename = g_strdup(logfilename);
fout = fopen(filename, "w");
}
gnc_log_init();
}
void
qof_log_shutdown (void)
{
if(fout && fout != stderr) { fclose(fout); }
if(filename) { g_free(filename); }
g_hash_table_destroy(log_table);
}
#define MAX_CHARS 50
/* gnc_log_prettify() cleans up subroutine names. AIX/xlC has the habit
* of printing signatures not names; clean this up. On other operating
* systems, truncate name to 30 chars. Note this routine is not thread
* safe. Note we wouldn't need this routine if AIX did something more
* reasonable. Hope thread safety doesn't poke us in eye. */
const char *
gnc_log_prettify (const char *name)
{
static char bf[128];
char *p;
if (!name)
return "";
strncpy (bf, name, MAX_CHARS-1); bf[MAX_CHARS-2] = 0;
p = strchr (bf, '(');
if (p)
{
*(p+1) = ')';
*(p+2) = 0x0;
}
else
strcpy (&bf[MAX_CHARS-4], "...()");
return bf;
}
/********************************************************************\
\********************************************************************/
#define NUM_CLOCKS 10
static
struct timeval gnc_clock[NUM_CLOCKS] = {
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
};
static
struct timeval gnc_clock_total[NUM_CLOCKS] = {
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
};
void
gnc_start_clock (int clockno, QofLogModule log_module, gncLogLevel log_level,
const char *function_name, const char *format, ...)
{
struct timezone tz;
va_list ap;
if ((0>clockno) || (NUM_CLOCKS <= clockno)) return;
gettimeofday (&gnc_clock[clockno], &tz);
if (!fout) gnc_log_init();
fprintf (fout, "Clock %d Start: %s: ",
clockno, gnc_log_prettify (function_name));
va_start (ap, format);
vfprintf (fout, format, ap);
va_end (ap);
fprintf (fout, "\n");
fflush (fout);
}
void
gnc_report_clock (int clockno, QofLogModule log_module, gncLogLevel log_level,
const char *function_name, const char *format, ...)
{
struct timezone tz;
struct timeval now;
va_list ap;
if ((0>clockno) || (NUM_CLOCKS <= clockno)) return;
gettimeofday (&now, &tz);
/* need to borrow to make difference */
if (now.tv_usec < gnc_clock[clockno].tv_usec)
{
now.tv_sec --;
now.tv_usec += 1000000;
}
now.tv_sec -= gnc_clock[clockno].tv_sec;
now.tv_usec -= gnc_clock[clockno].tv_usec;
gnc_clock_total[clockno].tv_sec += now.tv_sec;
gnc_clock_total[clockno].tv_usec += now.tv_usec;
if (!fout) gnc_log_init();
fprintf (fout, "Clock %d Elapsed: %ld.%06lds %s: ",
clockno, (long int) now.tv_sec, (long int) now.tv_usec,
gnc_log_prettify (function_name));
va_start (ap, format);
vfprintf (fout, format, ap);
va_end (ap);
fprintf (fout, "\n");
fflush (fout);
}
void
gnc_report_clock_total (int clockno,
QofLogModule log_module, gncLogLevel log_level,
const char *function_name, const char *format, ...)
{
va_list ap;
if ((0>clockno) || (NUM_CLOCKS <= clockno)) return;
/* need to normalize usec */
while (gnc_clock_total[clockno].tv_usec >= 1000000)
{
gnc_clock_total[clockno].tv_sec ++;
gnc_clock_total[clockno].tv_usec -= 1000000;
}
if (!fout) gnc_log_init();
fprintf (fout, "Clock %d Total Elapsed: %ld.%06lds %s: ",
clockno,
(long int) gnc_clock_total[clockno].tv_sec,
(long int) gnc_clock_total[clockno].tv_usec,
gnc_log_prettify (function_name));
va_start (ap, format);
vfprintf (fout, format, ap);
va_end (ap);
fprintf (fout, "\n");
fflush (fout);
}
gboolean
gnc_should_log(QofLogModule log_module, gncLogLevel log_level)
{
gchar* log_string;
gncLogLevel maximum; /* Any log_level less than this will be logged. */
log_string = NULL;
if(!log_table || log_module == NULL || log_level == 0) { return FALSE; }
log_string = (gchar*)g_hash_table_lookup(log_table, log_module);
/* if log_module not found, do not log. */
if(!log_string) { return FALSE; }
maximum = gncLogLevelfromString(log_string);
if(log_level <= maximum) { return TRUE; }
return FALSE;
}
void qof_log_set_default(gncLogLevel log_level)
{
gnc_set_log_level(QOF_MOD_BACKEND, log_level);
gnc_set_log_level(QOF_MOD_CLASS, log_level);
gnc_set_log_level(QOF_MOD_ENGINE, log_level);
gnc_set_log_level(QOF_MOD_OBJECT, log_level);
gnc_set_log_level(QOF_MOD_KVP, log_level);
gnc_set_log_level(QOF_MOD_MERGE, log_level);
gnc_set_log_level(QOF_MOD_QUERY, log_level);
gnc_set_log_level(QOF_MOD_SESSION, log_level);
}
struct hash_s
{
QofLogCB cb;
gpointer data;
};
static void hash_cb (gpointer key, gpointer value, gpointer data)
{
struct hash_s *iter;
iter = (struct hash_s*)data;
if(!iter) { return; }
(iter->cb)(key, value, iter->data);
}
void qof_log_module_foreach(QofLogCB cb, gpointer data)
{
struct hash_s iter;
if(!cb) { return; }
iter.cb = cb;
iter.data = data;
g_hash_table_foreach(log_table, hash_cb, (gpointer)&iter);
}
gint qof_log_module_count(void)
{
if(!log_table) { return 0; }
return g_hash_table_size(log_table);
}
/** @} */
/************************* END OF FILE ******************************\
\********************************************************************/

View File

@ -1,274 +0,0 @@
/********************************************************************\
* gnc-trace.h -- QOF error logging and tracing facility *
* Copyright (C) 1998-2003 Linas Vepstas <linas@linas.org> *
* Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk> *
* *
* 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 *
* *
* Author: Linas Vepstas (linas@linas.org) *
\********************************************************************/
/** @addtogroup Trace
@{ */
/** @file gnc-trace.h
* @brief QOF error logging and tracing facility
* @author Neil Williams <linux@codehelp.co.uk>
*/
#ifndef GNC_TRACE_H
#define GNC_TRACE_H
#include <glib.h>
#include <stdarg.h>
#include <stdio.h>
#include "qof.h"
#include "gnc-engine-util.h"
#define QOF_MOD_ENGINE "qof-engine"
#define LOG_LEVEL_LIST(_) \
_(GNC_LOG_FATAL, = 0) \
_(GNC_LOG_ERROR, = 1) \
_(GNC_LOG_WARNING, = 2) \
_(GNC_LOG_INFO, = 3) \
_(GNC_LOG_DEBUG, = 4) \
_(GNC_LOG_DETAIL, = 5) \
_(GNC_LOG_TRACE, = 6)
DEFINE_ENUM (gncLogLevel, LOG_LEVEL_LIST)
/** Convert gncLogLevel to a string.
The macro correlates the enum value and an
exact copy as a string, removing the need to
keep two separate lists in sync.
*/
AS_STRING_DEC(gncLogLevel, LOG_LEVEL_LIST)
/** Convert the log_string to a gncLogLevel
Only for use as a partner to ::gncLogLevelasString
*/
FROM_STRING_DEC(gncLogLevel, LOG_LEVEL_LIST)
#define GNC_TRACE_INDENT_WIDTH 4
/** Initialize the error logging subsystem
\note Applications should call gnc_set_logfile
to set the output, otherwise the
default of \a /tmp/qof.trace will be used.
As an alternative, use qof_log_init_filename
which sets the filename and initialises the
logging subsystem in one operation.
*/
void gnc_log_init (void);
/** Set the logging level of the given log_module. */
void gnc_set_log_level(QofLogModule module, gncLogLevel level);
/** Set the logging level for all known log_modules.
\note Unless a log_module has been registered using
gnc_set_log_level, it will be unaffected by this change.
*/
void gnc_set_log_level_global(gncLogLevel level);
/** Specify an alternate log output, to pipe or file. By default,
* all logging goes to /tmp/qof.trace
Needs to be called \b before gnc_log_init()
*/
void gnc_set_logfile (FILE *outfile);
/** Specify a filename for log output.
Calls gnc_log_init() for you.
*/
void qof_log_init_filename (const gchar* logfilename);
/** Be nice, close the logfile is possible. */
void qof_log_shutdown (void);
/** gnc_log_prettify() cleans up subroutine names. AIX/xlC has the habit
* of printing signatures not names; clean this up. On other operating
* systems, truncate name to 30 chars. Note this routine is not thread
* safe. Note we wouldn't need this routine if AIX did something more
* reasonable. Hope thread safety doesn't poke us in eye. */
const char * gnc_log_prettify (const char *name);
/** Do not log log_modules that have not been enabled.
Whether to log cannot be decided inline because a hashtable is
now used. This is the price of extending logging to non-Gnucash
log_modules.
*/
gboolean gnc_should_log(QofLogModule log_module, gncLogLevel log_level);
/** Set the default QOF log_modules to the log level. */
void qof_log_set_default(gncLogLevel log_level);
typedef void (*QofLogCB) (QofLogModule log_module, gncLogLevel* log_level, gpointer user_data);
/** Iterate over each known log_module
Only log_modules with log_levels set will
be available.
*/
void qof_log_module_foreach(QofLogCB cb, gpointer data);
/** Number of log_modules registered*/
gint qof_log_module_count(void);
#define FUNK gnc_log_prettify(__FUNCTION__)
/** Log error/waring/info messages to stderr or to other pipe.
* This logging infrastructure is meant for validating the
* correctness of the execution of the code. 'Info' level
* messages help trace program flow. 'Error' messages are
* meant to indicate internal data inconsistencies.
*
* Messages can be logged to stdout, stderr, or to any desired
* FILE * file handle. Use fdopen() to get a file handle from a
* file descriptor. Use gnc_set_logfile to set the logging file
* handle.
*/
/** Log an fatal error */
#define FATAL(format, args...) do { \
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
"Fatal Error: %s(): " format, FUNK , ## args); \
} while (0)
/** Log an serious error */
#define PERR(format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_ERROR)) {\
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
"Error: %s(): " format, FUNK , ## args); \
} \
} while (0)
/** Log an warning */
#define PWARN(format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_WARNING)) { \
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
"Warning: %s(): " format, FUNK , ## args); \
} \
} while (0)
/** Print an informational note */
#define PINFO(format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_INFO)) { \
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
"Info: %s(): " format, \
FUNK , ## args); \
} \
} while (0)
/** Print an debugging message */
#define DEBUG(format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_DEBUG)) {\
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
"Debug: %s(): " format, \
FUNK , ## args); \
} \
} while (0)
/** Print an function entry debugging message */
#define ENTER(format, args...) do { \
extern gint gnc_trace_num_spaces; \
if (gnc_should_log (log_module, GNC_LOG_DEBUG)) {\
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
"Enter in %s: %s()" format, __FILE__, \
FUNK , ## args); \
gnc_trace_num_spaces += GNC_TRACE_INDENT_WIDTH;\
} \
} while (0)
/** Print an function exit debugging message */
#define LEAVE(format, args...) do { \
extern gint gnc_trace_num_spaces; \
if (gnc_should_log (log_module, GNC_LOG_DEBUG)) {\
gnc_trace_num_spaces -= GNC_TRACE_INDENT_WIDTH;\
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
"Leave: %s()" format, \
FUNK , ## args); \
} \
} while (0)
/** Print an function trace debugging message */
#define TRACE(format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_TRACE)) {\
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
"Trace: %s(): " format, FUNK , ## args); \
} \
} while (0)
#define DEBUGCMD(x) do { \
if (gnc_should_log (log_module, GNC_LOG_DEBUG)) { \
(x); \
} \
} while (0)
/* -------------------------------------------------------- */
/** Infrastructure to make timing measurements for critical peices
* of code. Used for only for performance tuning & debugging.
*/
void gnc_start_clock (int clockno, QofLogModule log_module, gncLogLevel log_level,
const char *function_name, const char *format, ...);
void gnc_report_clock (int clockno,
QofLogModule log_module,
gncLogLevel log_level,
const char *function_name,
const char *format, ...);
void gnc_report_clock_total (int clockno,
QofLogModule log_module,
gncLogLevel log_level,
const char *function_name,
const char *format, ...);
/** start a particular timer */
#define START_CLOCK(clockno,format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_INFO)) \
gnc_start_clock (clockno, log_module, GNC_LOG_INFO, \
__FUNCTION__, format , ## args); \
} while (0)
/** report elapsed time since last report on a particular timer */
#define REPORT_CLOCK(clockno,format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_INFO)) \
gnc_report_clock (clockno, log_module, GNC_LOG_INFO, \
__FUNCTION__, format , ## args); \
} while (0)
/** report total elapsed time since timer started */
#define REPORT_CLOCK_TOTAL(clockno,format, args...) do { \
if (gnc_should_log (log_module, GNC_LOG_INFO)) \
gnc_report_clock_total (clockno, log_module, GNC_LOG_INFO, \
__FUNCTION__, format , ## args); \
} while (0)
#endif /* GNC_TRACE_H */
/* @} */

View File

@ -1,681 +0,0 @@
/********************************************************************\
* guid.c -- globally unique ID implementation *
* Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.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 *
* *
\********************************************************************/
#define _GNU_SOURCE
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/times.h>
#include <time.h>
#include <unistd.h>
#include "guid.h"
#include "md5.h"
#include "qofid.h"
#include "gnc-trace.h"
# ifndef P_tmpdir
# define P_tmpdir "/tmp"
# endif
/** Constants *******************************************************/
#define DEBUG_GUID 0
#define BLOCKSIZE 4096
#define THRESHOLD (2 * BLOCKSIZE)
/** Static global variables *****************************************/
static gboolean guid_initialized = FALSE;
static struct md5_ctx guid_context;
static GMemChunk *guid_memchunk = NULL;
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = QOF_MOD_ENGINE;
/** Memory management routines ***************************************/
static void
guid_memchunk_init (void)
{
if (!guid_memchunk)
guid_memchunk = g_mem_chunk_create (GUID, 512, G_ALLOC_AND_FREE);
}
static void
guid_memchunk_shutdown (void)
{
if (guid_memchunk)
{
g_mem_chunk_destroy (guid_memchunk);
guid_memchunk = NULL;
}
}
GUID *
guid_malloc (void)
{
if (!guid_memchunk) guid_memchunk_init();
return g_chunk_new (GUID, guid_memchunk);
}
void
guid_free (GUID *guid)
{
if (!guid)
return;
g_chunk_free (guid, guid_memchunk);
}
const GUID *
guid_null(void)
{
static int null_inited = 0;
static GUID null_guid;
if (!null_inited)
{
int i;
char *tmp = "NULLGUID.EMPTY.";
/* 16th space for '\O' */
for (i = 0; i < 16; i++)
null_guid.data[i] = tmp[i];
null_inited = 1;
}
return &null_guid;
}
/** Function implementations ****************************************/
/* This code is based on code in md5.c in GNU textutils. */
static size_t
init_from_stream(FILE *stream, size_t max_size)
{
char buffer[BLOCKSIZE + 72];
size_t sum, block_size, total;
if (max_size <= 0)
return 0;
total = 0;
/* Iterate over file contents. */
while (1)
{
/* We read the file in blocks of BLOCKSIZE bytes. One call of the
* computation function processes the whole buffer so that with the
* next round of the loop another block can be read. */
size_t n;
sum = 0;
if (max_size < BLOCKSIZE)
block_size = max_size;
else
block_size = BLOCKSIZE;
/* Read block. Take care for partial reads. */
do
{
n = fread (buffer + sum, 1, block_size - sum, stream);
sum += n;
}
while (sum < block_size && n != 0);
max_size -= sum;
if (n == 0 && ferror (stream))
return total;
/* If end of file or max_size is reached, end the loop. */
if ((n == 0) || (max_size == 0))
break;
/* Process buffer with BLOCKSIZE bytes. Note that
* BLOCKSIZE % 64 == 0 */
md5_process_block (buffer, BLOCKSIZE, &guid_context);
total += sum;
}
/* Add the last bytes if necessary. */
if (sum > 0)
{
md5_process_bytes (buffer, sum, &guid_context);
total += sum;
}
return total;
}
static size_t
init_from_file(const char *filename, size_t max_size)
{
struct stat stats;
size_t total = 0;
size_t file_bytes;
FILE *fp;
memset(&stats, 0, sizeof(stats));
if (stat(filename, &stats) != 0)
return 0;
md5_process_bytes(&stats, sizeof(stats), &guid_context);
total += sizeof(stats);
if (max_size <= 0)
return total;
fp = fopen (filename, "r");
if (fp == NULL)
return total;
file_bytes = init_from_stream(fp, max_size);
PINFO ("guid_init got %llu bytes from %s", (unsigned long long int) file_bytes,
filename);
total += file_bytes;
fclose(fp);
return total;
}
static size_t
init_from_dir(const char *dirname, unsigned int max_files)
{
char filename[1024];
struct dirent *de;
struct stat stats;
size_t total;
int result;
DIR *dir;
if (max_files <= 0)
return 0;
dir = opendir (dirname);
if (dir == NULL)
return 0;
total = 0;
do
{
de = readdir(dir);
if (de == NULL)
break;
md5_process_bytes(de, sizeof(struct dirent), &guid_context);
total += sizeof(struct dirent);
result = snprintf(filename, sizeof(filename),
"%s/%s", dirname, de->d_name);
if ((result < 0) || (result >= (int)sizeof(filename)))
continue;
memset(&stats, 0, sizeof(stats));
if (stat(filename, &stats) != 0)
continue;
md5_process_bytes(&stats, sizeof(stats), &guid_context);
total += sizeof(stats);
max_files--;
} while (max_files > 0);
closedir(dir);
return total;
}
static size_t
init_from_time(void)
{
size_t total;
time_t t_time;
clock_t clocks;
struct tms tms_buf;
total = 0;
t_time = time(NULL);
md5_process_bytes(&t_time, sizeof(t_time), &guid_context);
total += sizeof(t_time);
clocks = times(&tms_buf);
md5_process_bytes(&clocks, sizeof(clocks), &guid_context);
md5_process_bytes(&tms_buf, sizeof(tms_buf), &guid_context);
total += sizeof(clocks) + sizeof(tms_buf);
return total;
}
static size_t
init_from_int(int val)
{
md5_process_bytes(&val, sizeof(val), &guid_context);
return sizeof(int);
}
static size_t
init_from_buff(unsigned char * buf, size_t buflen)
{
md5_process_bytes(buf, buflen, &guid_context);
return buflen;
}
void
guid_init(void)
{
size_t bytes = 0;
/* Not needed; taken care of on first malloc.
* guid_memchunk_init(); */
md5_init_ctx(&guid_context);
/* entropy pool */
bytes += init_from_file ("/dev/urandom", 512);
/* files */
{
const char * files[] =
{ "/etc/passwd",
"/proc/loadavg",
"/proc/meminfo",
"/proc/net/dev",
"/proc/rtc",
"/proc/self/environ",
"/proc/self/stat",
"/proc/stat",
"/proc/uptime",
NULL
};
int i;
for (i = 0; files[i] != NULL; i++)
bytes += init_from_file(files[i], BLOCKSIZE);
}
/* directories */
{
const char * dirname;
const char * dirs[] =
{
"/proc",
P_tmpdir,
"/var/lock",
"/var/log",
"/var/mail",
"/var/spool/mail",
"/var/run",
NULL
};
int i;
for (i = 0; dirs[i] != NULL; i++)
bytes += init_from_dir(dirs[i], 32);
dirname = getenv("HOME");
if (dirname != NULL)
bytes += init_from_dir(dirname, 32);
}
/* process and parent ids */
{
pid_t pid;
pid = getpid();
md5_process_bytes(&pid, sizeof(pid), &guid_context);
bytes += sizeof(pid);
pid = getppid();
md5_process_bytes(&pid, sizeof(pid), &guid_context);
bytes += sizeof(pid);
}
/* user info */
{
uid_t uid;
gid_t gid;
char *s;
s = getlogin();
if (s != NULL)
{
md5_process_bytes(s, strlen(s), &guid_context);
bytes += strlen(s);
}
uid = getuid();
md5_process_bytes(&uid, sizeof(uid), &guid_context);
bytes += sizeof(uid);
gid = getgid();
md5_process_bytes(&gid, sizeof(gid), &guid_context);
bytes += sizeof(gid);
}
/* host info */
{
char string[1024];
memset(string, 0, sizeof(string));
gethostname(string, sizeof(string));
md5_process_bytes(string, sizeof(string), &guid_context);
bytes += sizeof(string);
}
/* plain old random */
{
int n, i;
srand((unsigned int) time(NULL));
for (i = 0; i < 32; i++)
{
n = rand();
md5_process_bytes(&n, sizeof(n), &guid_context);
bytes += sizeof(n);
}
}
/* time in secs and clock ticks */
bytes += init_from_time();
PINFO ("got %llu bytes", (unsigned long long int) bytes);
if (bytes < THRESHOLD)
PWARN("only got %llu bytes.\n"
"The identifiers might not be very random.\n",
(unsigned long long int)bytes);
guid_initialized = TRUE;
}
void
guid_init_with_salt(const void *salt, size_t salt_len)
{
guid_init();
md5_process_bytes(salt, salt_len, &guid_context);
}
void
guid_init_only_salt(const void *salt, size_t salt_len)
{
md5_init_ctx(&guid_context);
md5_process_bytes(salt, salt_len, &guid_context);
guid_initialized = TRUE;
}
void
guid_shutdown (void)
{
guid_memchunk_shutdown();
}
#define GUID_PERIOD 5000
void
guid_new(GUID *guid)
{
static int counter = 0;
struct md5_ctx ctx;
if (guid == NULL)
return;
if (!guid_initialized)
guid_init();
/* make the id */
ctx = guid_context;
md5_finish_ctx(&ctx, guid->data);
/* update the global context */
init_from_time();
/* Make it a little extra salty. I think init_from_time was buggy,
* or something, since duplicate id's actually happened. Or something
* like that. I think this is because init_from_time kept returning
* the same values too many times in a row. So we'll do some 'block
* chaining', and feed in the old guid as new random data.
*
* Anyway, I think the whole fact that I saw a bunch of duplicate
* id's at one point, but can't reproduce the bug is rather alarming.
* Something must be broken somewhere, and merely adding more salt
* is just hiding the problem, not fixing it.
*/
init_from_int (433781*counter);
init_from_buff (guid->data, 16);
if (counter == 0)
{
FILE *fp;
fp = fopen ("/dev/urandom", "r");
if (fp == NULL)
return;
init_from_stream(fp, 32);
fclose(fp);
counter = GUID_PERIOD;
}
counter--;
}
GUID
guid_new_return(void)
{
GUID guid;
guid_new (&guid);
return guid;
}
/* needs 32 bytes exactly, doesn't print a null char */
static void
encode_md5_data(const unsigned char *data, char *buffer)
{
size_t count;
for (count = 0; count < 16; count++, buffer += 2)
sprintf(buffer, "%02x", data[count]);
}
/* returns true if the first 32 bytes of buffer encode
* a hex number. returns false otherwise. Decoded number
* is packed into data in little endian order. */
static gboolean
decode_md5_string(const char *string, unsigned char *data)
{
unsigned char n1, n2;
size_t count = -1;
char c1, c2;
if (NULL == data) return FALSE;
if (NULL == string) goto badstring;
for (count = 0; count < 16; count++)
{
/* check for a short string e.g. null string ... */
if ((0==string[2*count]) || (0==string[2*count+1])) goto badstring;
c1 = tolower(string[2 * count]);
if (!isxdigit(c1)) goto badstring;
c2 = tolower(string[2 * count + 1]);
if (!isxdigit(c2)) goto badstring;
if (isdigit(c1))
n1 = c1 - '0';
else
n1 = c1 - 'a' + 10;
if (isdigit(c2))
n2 = c2 - '0';
else
n2 = c2 - 'a' + 10;
data[count] = (n1 << 4) | n2;
}
return TRUE;
badstring:
for (count = 0; count < 16; count++)
{
data[count] = 0;
}
return FALSE;
}
/* Allocate the key */
const char *
guid_to_string(const GUID * guid)
{
#ifdef G_THREADS_ENABLED
static GStaticPrivate guid_buffer_key = G_STATIC_PRIVATE_INIT;
gchar *string;
string = g_static_private_get (&guid_buffer_key);
if (string == NULL) {
string = malloc(GUID_ENCODING_LENGTH+1);
g_static_private_set (&guid_buffer_key, string, g_free);
}
#else
static char string[64];
#endif
encode_md5_data(guid->data, string);
string[GUID_ENCODING_LENGTH] = '\0';
return string;
}
char *
guid_to_string_buff(const GUID * guid, char *string)
{
if (!string || !guid) return NULL;
encode_md5_data(guid->data, string);
string[GUID_ENCODING_LENGTH] = '\0';
return &string[GUID_ENCODING_LENGTH];
}
gboolean
string_to_guid(const char * string, GUID * guid)
{
return decode_md5_string(string, (guid != NULL) ? guid->data : NULL);
}
gboolean
guid_equal(const GUID *guid_1, const GUID *guid_2)
{
if (guid_1 && guid_2)
return (memcmp(guid_1, guid_2, sizeof(GUID)) == 0);
else
return FALSE;
}
gint
guid_compare(const GUID *guid_1, const GUID *guid_2)
{
if (guid_1 == guid_2)
return 0;
/* nothing is always less than something */
if (!guid_1 && guid_2)
return -1;
if (guid_1 && !guid_2)
return 1;
return memcmp (guid_1, guid_2, sizeof (GUID));
}
guint
guid_hash_to_guint (gconstpointer ptr)
{
const GUID *guid = ptr;
if (!guid)
{
PERR ("received NULL guid pointer.");
return 0;
}
if (sizeof(guint) <= sizeof(guid->data))
{
return (*((guint *) guid->data));
}
else
{
guint hash = 0;
unsigned int i, j;
for (i = 0, j = 0; i < sizeof(guint); i++, j++) {
if (j == 16) j = 0;
hash <<= 4;
hash |= guid->data[j];
}
return hash;
}
}
static gint
guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
{
return guid_equal (guid_a, guid_b);
}
GHashTable *
guid_hash_table_new (void)
{
return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
}

View File

@ -1,204 +0,0 @@
/********************************************************************\
* guid.h -- globally unique ID User API *
* Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.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 *
* *
\********************************************************************/
#ifndef GUID_H
#define GUID_H
#include <glib.h>
#include <stddef.h>
/** @addtogroup Entity
@{ */
/** @addtogroup GUID
Globally Unique ID's provide a way to uniquely identify
some thing. A GUID is a unique, cryptographically
random 128-bit value. The identifier is so random that
it is safe to assume that there is no other such item
on the planet Earth, and indeed, not even in the Galaxy
or beyond.
QOF GUID's can be used independently of any other subsystem
in QOF. In particular, they do not require the use of
other parts of the object subsystem.
@{ */
/** @file guid.h
@brief globally unique ID User API
@author Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu>
*/
/** The type used to store guids */
typedef union _GUID
{
unsigned char data[16];
int __align_me; /* this just ensures that GUIDs are 32-bit
* aligned on systems that need them to be. */
} GUID;
/** number of characters needed to encode a guid as a string
* not including the null terminator. */
#define GUID_ENCODING_LENGTH 32
/** Initialize the id generator with a variety of random
* sources.
*
* @note Only one of guid_init(), guid_init_with_salt() and
* guid_init_only_salt() should be called. Calling any
* initialization function a second time will reset the generator and
* erase the effect of the first call.
*/
void guid_init(void);
/** Initialize the id generator with a variety of random sources. and
* with the data given in the salt argument. This argument can be
* used to add additional randomness to the generated ids.
*
* @param salt The additional random values to add to the generator.
*
* @param salt_len The length of the additional random values.
*
* @note Only one of guid_init(), guid_init_with_salt() and
* guid_init_only_salt() should be called. Calling any
* initialization function a second time will reset the generator and
* erase the effect of the first call.
*/
void guid_init_with_salt(const void *salt, size_t salt_len);
/** Initialize the id generator with the data given in the salt
* argument, but not with any other source. Calling this function with
* a specific argument will reliably produce a specific sequence of
* ids.
*
* @param salt The additional random values to add to the generator.
*
* @param salt_len The length of the additional random values.
*
* @note Only one of guid_init(), guid_init_with_salt() and
* guid_init_only_salt() should be called. Calling any
* initialization function a second time will reset the generator and
* erase the effect of the first call.
*/
void guid_init_only_salt(const void *salt, size_t salt_len);
/** Release the memory chunk associated with gui storage. Use this
* only when shutting down the program, as it invalidates *all*
* GUIDs at once. */
void guid_shutdown (void);
/** Generate a new id. If no initialization function has been called,
* guid_init() will be called before the id is created.
*
* @param guid A pointer to an existing guid data structure. The
* existing value will be replaced with a new value.
*
* This routine uses the md5 algorithm to build strong random guids.
* Note that while guid's are generated randomly, the odds of this
* routine returning a non-unique id are astronomically small.
* (Literally astronomically: If you had Cray's on every solar
* system in the universe running for the entire age of the universe,
* you'd still have less than a one-in-a-million chance of coming up
* with a duplicate id. 2^128 == 10^38 is a really really big number.)
*/
void guid_new(GUID *guid);
/** Generate a new id. If no initialization function has been called,
* guid_init() will be called before the id is created.
*
* @return guid A pointer to a data structure containing a new GUID.
* The memory pointed to is owned by this routine and the guid must
* be copied out.
*
* CAS: huh? make that: @return guid A data structure containing a newly
* allocated GUID. Caller is responsible for calling guid_free().
*/
GUID guid_new_return(void);
/** Returns a GUID which is guaranteed to never reference any entity. */
/* CAS: AFAICT: this isn't really guaranteed, but it's only as likely
as any other md5 collision. This could be guaranteed if GUID
contained a validity flag. */
const GUID * guid_null (void);
/** Efficiently allocate & free memory for GUIDs */
GUID * guid_malloc (void);
/* Return a guid set to all zero's */
void guid_free (GUID *guid);
/** The guid_to_string() routine returns a null-terminated string
* encoding of the id. String encodings of identifiers are hex
* numbers printed only with the characters '0' through '9' and
* 'a' through 'f'. The encoding will always be GUID_ENCODING_LENGTH
* characters long.
*
* XXX This routine is not thread safe and is deprecated. Please
* use the routine guid_to_string_buff() instead.
*
* @param guid The guid to print.
*
* @return A pointer to the starting character of the string. The
* returned memory is owned by this routine and may not be freed by
* the caller.
*/
const char * guid_to_string (const GUID * guid);
/** The guid_to_string_buff() routine puts a null-terminated string
* encoding of the id into the memory pointed at by buff. The
* buffer must be at least GUID_ENCODING_LENGTH+1 characters long.
* This routine is handy for avoiding a malloc/free cycle. It
* returns a pointer to the >>end<< of what was written. (i.e. it
* can be used like 'stpcpy' during string concatenation)
*
* @param guid The guid to print.
*
* @param buff The buffer to print it into.
*
* @return A pointer to the terminating null character of the string.
*/
char * guid_to_string_buff (const GUID * guid, char *buff);
/** Given a string, decode the id into the guid if guid is non-NULL.
* The function returns TRUE if the string was a valid 32 character
* hexadecimal number. This function accepts both upper and lower case
* hex digits. If the return value is FALSE, the effect on guid is
* undefined. */
gboolean string_to_guid(const char * string, GUID * guid);
/** Given two GUIDs, return TRUE if they are non-NULL and equal.
* Return FALSE, otherwise. */
gboolean guid_equal(const GUID *guid_1, const GUID *guid_2);
gint guid_compare(const GUID *g1, const GUID *g2);
/** Given a GUID *, hash it to a guint */
guint guid_hash_to_guint(gconstpointer ptr);
GHashTable *guid_hash_table_new(void);
/* @} */
/* @} */
#endif

View File

@ -1,108 +0,0 @@
/********************************************************************\
* kvp-util-p.h -- misc odd-job kvp utils (private file) *
* *
* 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 *
* *
\********************************************************************/
#ifndef XACC_KVP_UTIL_P_H
#define XACC_KVP_UTIL_P_H
#include "config.h"
#include "guid.h"
#include "kvp_frame.h"
/** @addtogroup KVP
@{
*/
/** @file kvp-util-p.h
* @brief misc odd-job kvp utils engine-private routines
* @author Copyright (C) 2001, 2003 Linas Vepstas <linas@linas.org> *
*/
/** @name KvpBag Bags of GUID Pointers
@{
*/
/** The gnc_kvp_bag_add() routine is used to maintain a collection
* of pointers in a kvp tree.
*
* The thing being pointed at is uniquely identified by its GUID.
* This routine is typically used to create a linked list, and/or
* a collection of pointers to objects that are 'related' to each
* other in some way.
*
* The var-args should be pairs of strings (const char *) followed by
* the corresponding GUID pointer (const GUID *). Terminate the varargs
* with a NULL as the last string argument.
*
* The actual 'pointer' is stored in a subdirectory in a bag located at
* the node directory 'path'. A 'bag' is merely a collection of
* (unamed) values. The name of our bag is 'path'. A bag can contain
* any kind of values, including frames. This routine will create a
* frame, and put it in the bag. The frame will contain named data
* from the subroutine arguments. Thus, for example:
*
* gnc_kvp_array (kvp, "foo", secs, "acct_guid", aguid,
* "book_guid", bguid, NULL);
*
* will create a frame containing "/acct_guid" and "/book_guid", whose
* values are aguid and bguid respecitvely. The frame will also
* contain "/date", whose value will be secs. This frame will be
* placed into the bag located at "foo".
*
* This routine returns a pointer to the frame that was created, or
* NULL if an error occured.
*/
KvpFrame * gnc_kvp_bag_add (KvpFrame *kvp_root, const char *path, time_t secs,
const char *first_name, ...);
/** The gnc_kvp_bag_merge() routine will move the bag contents from
* the 'kvp_from', to the 'into' bag. It will then delete the
* 'from' bag from the kvp tree.
*/
void gnc_kvp_bag_merge (KvpFrame *kvp_into, const char *intopath,
KvpFrame *kvp_from, const char *frompath);
/** The gnc_kvp_bag_find_by_guid() routine examines the bag pointed
* located at root. It looks for a frame in that bag that has the
* guid value of "desired_guid" filed under the key name "guid_name".
* If it finds that matching guid, then it returns a pointer to
* the KVP frame that contains it. If it is not found, or if there
* is any other error, NULL is returned.
*/
KvpFrame * gnc_kvp_bag_find_by_guid (KvpFrame *root, const char * path,
const char *guid_name, GUID *desired_guid);
/** Remove the given frame from the bag. The frame is removed,
* however, it is not deleted. Note that the frame pointer must
* be a pointer to the actual frame (for example, as returned by
* gnc_kvp_bag_find_by_guid() for by gnc_kvp_bag_add()), and not
* some copy of the frame.
*/
void gnc_kvp_bag_remove_frame (KvpFrame *root, const char *path,
KvpFrame *fr);
/** @} */
/** @} */
#endif /* XACC_KVP_UTIL_P_H */

View File

@ -1,235 +0,0 @@
/********************************************************************\
* kvp_util.c -- misc odd-job kvp utils *
* Copyright (C) 2001 Linas Vepstas <linas@linas.org> *
* *
* 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 <stdio.h>
#include "kvp_frame.h"
#include "kvp-util.h"
#include "kvp-util-p.h"
/* ================================================================ */
static KvpFrame *
gnc_kvp_array_va (KvpFrame *kvp_root, const char * path,
time_t secs, const char * first_name, va_list ap)
{
KvpFrame *cwd;
Timespec ts;
const char *name;
if (!kvp_root) return NULL;
if (!first_name) return NULL;
/* Create subdirectory and put the actual data */
cwd = kvp_frame_new();
/* Record the time */
ts.tv_sec = secs;
ts.tv_nsec = 0;
kvp_frame_set_timespec (cwd, "date", ts);
/* Loop over the args */
name = first_name;
while (name)
{
const GUID *guid;
guid = va_arg (ap, const GUID *);
kvp_frame_set_guid (cwd, name, guid);
name = va_arg (ap, const char *);
}
/* Attach cwd into the array */
kvp_frame_add_frame_nc (kvp_root, path, cwd);
return cwd;
}
/* ================================================================ */
KvpFrame *
gnc_kvp_bag_add (KvpFrame *pwd, const char * path,
time_t secs, const char *first_name, ...)
{
KvpFrame *cwd;
va_list ap;
va_start (ap, first_name);
cwd = gnc_kvp_array_va (pwd, path, secs, first_name, ap);
va_end (ap);
return cwd;
}
/* ================================================================ */
#define MATCH_GUID(elt) { \
KvpFrame *fr = kvp_value_get_frame (elt); \
if (fr) { \
GUID *guid = kvp_frame_get_guid (fr, guid_name); \
if (guid && guid_equal (desired_guid, guid)) return fr; \
} \
}
KvpFrame *
gnc_kvp_bag_find_by_guid (KvpFrame *root, const char * path,
const char *guid_name, GUID *desired_guid)
{
KvpValue *arr;
KvpValueType valtype;
GList *node;
arr = kvp_frame_get_value (root, path);
valtype = kvp_value_get_type (arr);
if (KVP_TYPE_FRAME == valtype)
{
MATCH_GUID (arr);
return NULL;
}
/* Its gotta be a single isolated frame, or a list of them. */
if (KVP_TYPE_GLIST != valtype) return NULL;
for (node = kvp_value_get_glist(arr); node; node=node->next)
{
KvpValue *va = node->data;
MATCH_GUID (va);
}
return NULL;
}
/* ================================================================ */
void
gnc_kvp_bag_remove_frame (KvpFrame *root, const char *path, KvpFrame *fr)
{
KvpValue *arr;
KvpValueType valtype;
GList *node, *listhead;
arr = kvp_frame_get_value (root, path);
valtype = kvp_value_get_type (arr);
if (KVP_TYPE_FRAME == valtype)
{
if (fr == kvp_value_get_frame (arr))
{
KvpValue *old_val = kvp_frame_replace_value_nc (root, path, NULL);
kvp_value_replace_frame_nc (old_val, NULL);
kvp_value_delete (old_val);
}
return;
}
/* Its gotta be a single isolated frame, or a list of them. */
if (KVP_TYPE_GLIST != valtype) return;
listhead = kvp_value_get_glist(arr);
for (node = listhead; node; node=node->next)
{
KvpValue *va = node->data;
if (fr == kvp_value_get_frame (va))
{
listhead = g_list_remove_link (listhead, node);
g_list_free_1 (node);
kvp_value_replace_glist_nc (arr, listhead);
kvp_value_replace_frame_nc (va, NULL);
kvp_value_delete (va);
return;
}
}
}
/* ================================================================ */
static KvpFrame *
gnc_kvp_bag_get_first (KvpFrame *root, const char * path)
{
KvpValue *arr, *va;
KvpValueType valtype;
GList *node;
arr = kvp_frame_get_value (root, path);
valtype = kvp_value_get_type (arr);
if (KVP_TYPE_FRAME == valtype)
{
return kvp_value_get_frame(arr);
}
/* Its gotta be a single isolated frame, or a list of them. */
if (KVP_TYPE_GLIST != valtype) return NULL;
node = kvp_value_get_glist(arr);
if (NULL == node) return NULL;
va = node->data;
return kvp_value_get_frame(va);
}
void
gnc_kvp_bag_merge (KvpFrame *kvp_into, const char *intopath,
KvpFrame *kvp_from, const char *frompath)
{
KvpFrame *fr;
fr = gnc_kvp_bag_get_first (kvp_from, frompath);
while (fr)
{
gnc_kvp_bag_remove_frame (kvp_from, frompath, fr);
kvp_frame_add_frame_nc (kvp_into, intopath, fr);
fr = gnc_kvp_bag_get_first (kvp_from, frompath);
}
}
/* ================================================================ */
/*
* See header for docs.
*/
static void
kv_pair_helper(gpointer key, gpointer val, gpointer user_data)
{
GSList **result = (GSList **) user_data;
GHashTableKVPair *kvp = g_new(GHashTableKVPair, 1);
kvp->key = key;
kvp->value = val;
*result = g_slist_prepend(*result, kvp);
}
GSList *
g_hash_table_key_value_pairs(GHashTable *table)
{
GSList *result_list = NULL;
g_hash_table_foreach(table, kv_pair_helper, &result_list);
return result_list;
}
void
g_hash_table_kv_pair_free_gfunc(gpointer data, gpointer user_data)
{
GHashTableKVPair *kvp = (GHashTableKVPair *) data;
g_free(kvp);
}
/*======================== END OF FILE =============================*/

View File

@ -1,64 +0,0 @@
/********************************************************************\
* kvp-util.h -- misc KVP utilities *
* Copyright (C) 2003 Linas Vepstas <linas@linas.org> *
* *
* 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 *
\********************************************************************/
/** @addtogroup KVP
@{
*/
/** @file kvp-util.h
@brief GnuCash KVP utility functions
*/
/** @name Hash Utilities
@{
*/
#ifndef GNC_KVP_UTIL_H
#define GNC_KVP_UTIL_H
#include "config.h"
typedef struct {
gpointer key;
gpointer value;
} GHashTableKVPair;
/**
Returns a GSList* of all the
keys and values in a given hash table. Data elements of lists are
actual hash elements, so be careful, and deallocation of the
GHashTableKVPairs in the result list are the caller's
responsibility. A typical sequence might look like this:
GSList *kvps = g_hash_table_key_value_pairs(hash);
... use kvps->data->key and kvps->data->val, etc. here ...
g_slist_foreach(kvps, g_hash_table_kv_pair_free_gfunc, NULL);
g_slist_free(kvps);
*/
GSList *g_hash_table_key_value_pairs(GHashTable *table);
void g_hash_table_kv_pair_free_gfunc(gpointer data, gpointer user_data);
/***********************************************************************/
/** @} */
/** @} */
#endif /* GNC_KVP_UTIL_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,694 +0,0 @@
/********************************************************************\
* kvp_frame.h -- Implements a key-value frame system *
* 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 *
* *
\********************************************************************/
/** @addtogroup KVP
* A KvpFrame is a set of associations between character strings
* (keys) and KvpValue structures. A KvpValue is a union with
* possible types enumerated in the KvpValueType enum, and includes,
* among other things, ints, doubles, strings, guid's, lists, time
* and numeric values. KvpValues may also be other frames, so
* KVP is inherently hierarchical.
*
* Values are stored in a 'slot' associated with a key.
* Pointers passed as arguments into set_slot and get_slot are the
* responsibility of the caller. Pointers returned by get_slot are
* owned by the kvp_frame. Make copies as needed.
*
* A 'path' is a sequence of keys that can be followed to a value.
* Paths may be specified as varargs (variable number of arguments
* to a subrutine, NULL-terminated), as a GSList, or as a standard
* URL-like path name. The later is parsed and treated in the same
* way as file paths would be: / separates keys, /./ is treated as /
* and /../ means backup one level. Repeated slashes are treated
* as one slash.
*
* Note that although, in principle, keys may contain the / and . and
* .. characters, doing so may lead to confusion, and will make
* path-string parsing routines fail. In other words, don't use
* a key such as 'some/key' or 'some/./other/../key' because you
* may get unexpected results.
*
* To set a value into a frame, you will want to use one of the
* kvp_frame_set_xxx() routines. Most of the other routines provide
* only low-level access that you probably shouldn't use.
@{
*/
/** @file kvp_frame.h
@brief A key-value frame system
@author Copyright (C) 2000 Bill Gribble
@author Copyright (C) 2003 Linas Vepstas <linas@linas.org>
*/
#ifndef KVP_FRAME_H
#define KVP_FRAME_H
#include <glib.h>
#include "gnc-date.h"
#include "gnc-numeric.h"
#include "guid.h"
#define QOF_MOD_KVP "qof-kvp"
/** Opaque frame structure */
typedef struct _KvpFrame KvpFrame;
/** A KvpValue is a union with possible types enumerated in the
* KvpValueType enum. */
typedef struct _KvpValue KvpValue;
/** \brief possible types in the union KvpValue
* \todo : People have asked for boolean values,
* e.g. in xaccAccountSetAutoInterestXfer
*
* \todo In the long run, this should be synchronized with the
* core QOF types, which in turn should be synced to the g_types
* in GLib. Unfortunately, this requires writing a pile of code
* to handle all of the different cases.
* An alternative might be to make kvp values inherit from the
* core g_types (i.e. add new core g_types) ??
*/
typedef enum {
KVP_TYPE_GINT64=1, /**< QOF_TYPE_INT64 gint64 */
KVP_TYPE_DOUBLE, /**< QOF_TYPE_DOUBLE gdouble */
KVP_TYPE_NUMERIC, /**< QOF_TYPE_NUMERIC */
KVP_TYPE_STRING, /**< QOF_TYPE_STRING gchar* */
KVP_TYPE_GUID, /**< QOF_TYPE_GUID */
KVP_TYPE_TIMESPEC, /**< QOF_TYPE_DATE */
KVP_TYPE_BINARY, /**< no QOF equivalent. */
KVP_TYPE_GLIST, /**< no QOF equivalent. */
KVP_TYPE_FRAME /**< no QOF equivalent. */
} KvpValueType;
/** \deprecated Deprecated backwards compat tokens
do \b not use these in new code.
*/
#define kvp_frame KvpFrame
#define kvp_value KvpValue
#define kvp_value_t KvpValueType
/** @name KvpFrame Constructors
@{
*/
/** Return a new empty instance of KvpFrame */
KvpFrame * kvp_frame_new(void);
/** Perform a deep (recursive) delete of the frame and any subframes.
* kvp_frame_delete and kvp_value_delete are deep (recursive) deletes.
* kvp_frame_copy and kvp_value_copy are deep value copies.
*/
void kvp_frame_delete(KvpFrame * frame);
/** Perform a deep (recursive) value copy, copying the fraame,
* subframes, and the values as well. */
KvpFrame * kvp_frame_copy(const KvpFrame * frame);
/** Return TRUE if the KvpFrame is empty */
gboolean kvp_frame_is_empty(KvpFrame * frame);
/** @} */
/** @name KvpFrame Basic Value Storing
@{
*/
/** store the value of the
* gint64 at the indicated path. If not all frame components of
* the path exist, they are created.
*
*/
void kvp_frame_set_gint64(KvpFrame * frame, const char * path, gint64 ival);
/**
* store the value of the
* double at the indicated path. If not all frame components of
* the path exist, they are created.
*/
void kvp_frame_set_double(KvpFrame * frame, const char * path, double dval);
/** \deprecated
Use kvp_frame_set_numeric instead of kvp_frame_set_gnc_numeric
*/
#define kvp_frame_set_gnc_numeric kvp_frame_set_numeric
/** store the value of the
* gnc_numeric at the indicated path.
* If not all frame components of
* the path exist, they are created.
*/
void kvp_frame_set_numeric(KvpFrame * frame, const char * path, gnc_numeric nval);
/** store the value of the
* Timespec at the indicated path.
* If not all frame components of
* the path exist, they are created.
*/
void kvp_frame_set_timespec(KvpFrame * frame, const char * path, Timespec ts);
/** \deprecated
Use kvp_frame_set_string instead of kvp_frame_set_str
*/
#define kvp_frame_set_str kvp_frame_set_string
/** \brief Store a copy of the string at the indicated path.
* If not all frame components of the path
* exist, they are created. If there was another string previously
* stored at that path, the old copy is deleted.
*
* Similarly, the set_guid and set_frame will make copies and
* store those. Old copies, if any, are deleted.
*
* The kvp_frame_set_frame_nc() routine works as above, but does
* *NOT* copy the frame.
*/
void kvp_frame_set_string(KvpFrame * frame, const char * path, const char* str);
void kvp_frame_set_guid(KvpFrame * frame, const char * path, const GUID *guid);
void kvp_frame_set_frame(KvpFrame *frame, const char *path, KvpFrame *chld);
void kvp_frame_set_frame_nc(KvpFrame *frame, const char *path, KvpFrame *chld);
/** The kvp_frame_set_value() routine copies the value into the frame,
* at the location 'path'. If the path contains slashes '/', these
* are assumed to represent a sequence of keys. The returned value
* is a pointer to the actual frame into which the value was inserted;
* it is NULL if the frame couldn't be found (and thus the value wasn't
* inserted). The old value at this location, if any, is destroyed.
*
* Pointers passed as arguments into this routine are the responsibility
* of the caller; the pointers are *not* taken over or managed.
*/
KvpFrame * kvp_frame_set_value(KvpFrame * frame,
const char * path, const KvpValue * value);
/**
* The kvp_frame_set_value_nc() routine puts the value (without copying
* it) into the frame, putting it at the location 'path'. If the path
* contains slashes '/', these are assumed to represent a sequence of keys.
* The returned value is a pointer to the actual frame into which the value
* was inserted; it is NULL if the frame couldn't be found (and thus the
* value wasn't inserted). The old value at this location, if any,
* is destroyed.
*
* This routine is handy for avoiding excess memory allocations & frees.
* Note that because the KvpValue was grabbed, you can't just delete
* unless you remove the key as well (or unless you replace the value).
*/
KvpFrame * kvp_frame_set_value_nc(KvpFrame * frame,
const char * path, KvpValue * value);
/** The kvp_frame_replace_value_nc() routine places the new value
* at the indicated path. It returns the old value, if any.
* It returns NULL if there was an error, or if there was no
* old value. If the path doesn't exist, it is created, unless
* new_value is NULL. Passing in a NULL new_value has the
* effect of deleting the trailing slot (i.e. the trailing path
* element).
*/
KvpValue * kvp_frame_replace_value_nc (KvpFrame * frame, const char * slot,
KvpValue * new_value);
/** @} */
/** @name KvpFrame URL handling
@{
*/
/** The kvp_frame_add_url_encoding() routine will parse the
* value string, assuming it to be URL-encoded in the standard way,
* turning it into a set of key-value pairs, and adding those to the
* indicated frame. URL-encoded strings are the things that are
* returned by web browsers when a form is filled out. For example,
* 'start-date=June&end-date=November' consists of two keys,
* 'start-date' and 'end-date', which have the values 'June' and
* 'November', respectively. This routine also handles % encoding.
*
* This routine treats all values as strings; it does *not* attempt
* to perform any type-conversion.
* */
void kvp_frame_add_url_encoding (KvpFrame *frame, const char *enc);
/** @} */
/** @name KvpFrame Glist Bag Storing
@{
*/
/** The kvp_frame_add_gint64() routine will add the value of the
* gint64 to the glist bag of values at the indicated path.
* If not all frame components of the path exist, they are
* created. If the value previously stored at this path was
* not a glist bag, then a bag will be formed there, the old
* value placed in the bag, and the new value added to the bag.
*
* Similarly, the add_double, add_numeric, and add_timespec
* routines perform the same function, for each of the respective
* types.
*/
void kvp_frame_add_gint64(KvpFrame * frame, const char * path, gint64 ival);
void kvp_frame_add_double(KvpFrame * frame, const char * path, double dval);
/** \deprecated
Use kvp_frame_add_numeric instead of kvp_frame_add_gnc_numeric
*/
#define kvp_frame_add_gnc_numeric kvp_frame_add_numeric
void kvp_frame_add_numeric(KvpFrame * frame, const char * path, gnc_numeric nval);
void kvp_frame_add_timespec(KvpFrame * frame, const char * path, Timespec ts);
/** \deprecated
Use kvp_frame_add_string instead of kvp_frame_add_str
*/
#define kvp_frame_add_str kvp_frame_add_string
/** \brief Copy of the string to the glist bag at the indicated path.
* If not all frame components
* of the path exist, they are created. If there was another
* item previously stored at that path, then the path is converted
* to a bag, and the old value, along with the new value, is added
* to the bag.
*
* Similarly, the add_guid and add_frame will make copies and
* add those.
*
* The kvp_frame_add_frame_nc() routine works as above, but does
* *NOT* copy the frame.
*/
void kvp_frame_add_string(KvpFrame * frame, const char * path, const char* str);
void kvp_frame_add_guid(KvpFrame * frame, const char * path, const GUID *guid);
void kvp_frame_add_frame(KvpFrame *frame, const char *path, KvpFrame *chld);
void kvp_frame_add_frame_nc(KvpFrame *frame, const char *path, KvpFrame *chld);
/* The kvp_frame_add_value() routine will add a copy of the value
* to the glist bag at the indicated path. If not all frame components
* of the path exist, they are created. If there was another
* item previously stored at that path, then the path is converted
* to a bag, and the old value, along with the new value, is added
* to the bag. This routine returns the pointer to the last frame
* (the actual frame to which the value was added), or NULL if there
* was an error of any sort (typically, a parse error in the path).
*
* The *_nc() routine is analogous, except that it doesn't copy the
* value.
*/
KvpFrame * kvp_frame_add_value(KvpFrame * frame, const char * path, KvpValue *value);
KvpFrame * kvp_frame_add_value_nc(KvpFrame * frame, const char * path, KvpValue *value);
/** @} */
/** @name KvpFrame Value Fetching
Value accessors. These all take a unix-style slash-separated
path as an argument, and return the value stored at that location.
If the object at the end of that path is not of the type that was
asked for, then a NULL or a zero is returned. So, for example,
asking for a string when the path stored an int will return a NULL.
In some future date, this may be changed to a looser type system,
such as perl's automatic re-typing (e.g. an integer value might be
converted to a printed string representing that value).
If any part of the path does not exist, then NULL or zero will be
returned.
The values returned for GUID, binary, GList, KvpFrame and string
are "non-copying" -- the returned item is the actual item stored.
Do not delete this item unless you take the required care to avoid
possible bad pointer derefrences (i.e. core dumps). Also, be
careful hanging on to those references if you are also storing
at the same path names: the referenced item will be freed during
the store.
That is, if you get a string value (or guid, binary or frame),
and then store something else at that path, the string that you've
gotten will be freed during the store (internally, by the set_*()
routines), and you will be left hanging onto an invalid pointer.
@{
*/
gint64 kvp_frame_get_gint64(const KvpFrame *frame, const char *path);
double kvp_frame_get_double(const KvpFrame *frame, const char *path);
gnc_numeric kvp_frame_get_numeric(const KvpFrame *frame, const char *path);
char * kvp_frame_get_string(const KvpFrame *frame, const char *path);
GUID * kvp_frame_get_guid(const KvpFrame *frame, const char *path);
void * kvp_frame_get_binary(const KvpFrame *frame, const char *path,
guint64 * size_return);
Timespec kvp_frame_get_timespec(const KvpFrame *frame, const char *path);
KvpValue * kvp_frame_get_value(const KvpFrame *frame, const char *path);
/** Value accessor. Takes a unix-style slash-separated path as an
* argument, and return the KvpFrame stored at that location. If the
* KvpFrame does not exist, then a NULL is returned.
*
* @note The semantics here have changed: In gnucash-1.8, if the
* KvpFrame did not exist, this function automatically created one
* and returned it. However, now this function will return NULL in
* this case and the caller has to create a KvpFrame on his own. The
* old functionality is now implemented by
* kvp_frame_get_frame_path(). This happened on 2003-09-14, revision
* 1.31. FIXME: Is it really a good idea to change the semantics of
* an existing function and move the old semantics to a new
* function??! It would save us a lot of trouble if the new semantics
* would have been available in a new function!
*
* @return The KvpFrame at the specified path, or NULL if it doesn't
* exist.
*/
KvpFrame * kvp_frame_get_frame(const KvpFrame *frame, const char *path);
/** This routine returns the last frame of the path.
* If the frame path doesn't exist, it is created.
* Note that this is *VERY DIFFERENT FROM* like kvp_frame_get_frame()
*
* @note The semantics of this function implemented the gnucash-1.8
* behaviour of kvp_frame_get_frame: In gnucash-1.8, if the KvpFrame
* did not exist, kvp_frame_get_frame automatically created one and
* returned it. However, now that one will return NULL in this case
* and the caller has to create a KvpFrame on his own. The old
* functionality is implemented by this
* kvp_frame_get_frame_path(). This happened on 2003-09-14, revision
* 1.31.
*/
KvpFrame * kvp_frame_get_frame_path (KvpFrame *frame, const char *,...);
/** This routine returns the last frame of the path.
* If the frame path doesn't exist, it is created.
* Note that this is *VERY DIFFERENT FROM* kvp_frame_get_frame()
*/
KvpFrame * kvp_frame_get_frame_gslist (KvpFrame *frame,
GSList *key_path);
/** This routine returns the last frame of the path.
* If the frame path doesn't exist, it is created.
* Note that this is *VERY DIFFERENT FROM* kvp_frame_get_frame()
*
* The kvp_frame_get_frame_slash() routine takes a single string
* where the keys are separated by slashes; thus, for example:
* /this/is/a/valid/path and///so//is////this/
* Multiple slashes are compresed. Leading slash is optional.
* The pointers . and .. are *not* currently followed/obeyed.
* (This is a bug that needs fixing).
*/
KvpFrame * kvp_frame_get_frame_slash (KvpFrame *frame,
const char *path);
/** @} */
/** @name KvpFrame KvpValue low-level storing routines.
You probably shouldn't be using these low-level routines
All of the kvp_frame_set_slot_*() routines set the slot values
"destructively", in that if there was an old value there, that
old value is destroyed (and the memory freed). Thus, one
should not hang on to value pointers, as these will get
trashed if set_slot is called on the corresponding key.
If you want the old value, use kvp_frame_replace_slot().
@{
*/
/** The kvp_frame_replace_slot_nc() routine places the new value into
* the indicated frame, for the given key. It returns the old
* value, if any. It returns NULL if the slot doesn't exist,
* if there was some other an error, or if there was no old value.
* Passing in a NULL new_value has the effect of deleting that
* slot.
*/
KvpValue * kvp_frame_replace_slot_nc (KvpFrame * frame, const char * slot,
KvpValue * new_value);
/** The kvp_frame_set_slot() routine copies the value into the frame,
* associating it with a copy of 'key'. Pointers passed as arguments
* into kvp_frame_set_slot are the responsibility of the caller;
* the pointers are *not* taken over or managed. The old value at
* this location, if any, is destroyed.
*/
void kvp_frame_set_slot(KvpFrame * frame,
const char * key, const KvpValue * value);
/**
* The kvp_frame_set_slot_nc() routine puts the value (without copying
* it) into the frame, associating it with a copy of 'key'. This
* routine is handy for avoiding excess memory allocations & frees.
* Note that because the KvpValue was grabbed, you can't just delete
* unless you remove the key as well (or unless you replace the value).
* The old value at this location, if any, is destroyed.
*/
void kvp_frame_set_slot_nc(KvpFrame * frame,
const char * key, KvpValue * value);
/** The kvp_frame_set_slot_path() routine walks the hierarchy,
* using the key values to pick each branch. When the terminal
* node is reached, the value is copied into it. The old value
* at this location, if any, is destroyed.
*/
void kvp_frame_set_slot_path (KvpFrame *frame,
const KvpValue *value,
const char *first_key, ...);
/** The kvp_frame_set_slot_path_gslist() routine walks the hierarchy,
* using the key values to pick each branch. When the terminal node
* is reached, the value is copied into it. The old value at this
* location, if any, is destroyed.
*/
void kvp_frame_set_slot_path_gslist (KvpFrame *frame,
const KvpValue *value,
GSList *key_path);
/** @} */
/** @name KvpFrame KvpValue Low-Level Retrieval Routines
You probably shouldn't be using these low-level routines
Returns the KvpValue in the given KvpFrame 'frame' that is
associated with 'key'. If there is no key in the frame, NULL
is returned. If the value associated with the key is NULL,
NULL is returned.
Pointers passed as arguments into get_slot are the responsibility
of the caller. Pointers returned by get_slot are owned by the
kvp_frame. Make copies as needed.
@{
*/
KvpValue * kvp_frame_get_slot(const KvpFrame * frame, const char * key);
/** This routine return the value at the end of the
* path, or NULL if any portion of the path doesn't exist.
*/
KvpValue * kvp_frame_get_slot_path (KvpFrame *frame,
const char *first_key, ...);
/** This routine return the value at the end of the
* path, or NULL if any portion of the path doesn't exist.
*/
KvpValue * kvp_frame_get_slot_path_gslist (KvpFrame *frame,
GSList *key_path);
/**
* Similar returns as strcmp.
*/
gint kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb);
gint double_compare(double v1, double v2);
/** @} */
/** @name KvpValue List Convenience Functions
You probably shouldn't be using these low-level routines
kvp_glist_compare() compares <b>GLists of kvp_values</b> (not to
be confused with GLists of something else): it iterates over
the list elements, performing a kvp_value_compare on each.
@{
*/
gint kvp_glist_compare(const GList * list1, const GList * list2);
/** kvp_glist_copy() performs a deep copy of a <b>GList of
* kvp_values</b> (not to be confused with GLists of something
* else): same as mapping kvp_value_copy() over the elements and
* then copying the spine.
*/
GList * kvp_glist_copy(const GList * list);
/** kvp_glist_delete() performs a deep delete of a <b>GList of
* kvp_values</b> (not to be confused with GLists of something
* else): same as mapping * kvp_value_delete() over the elements
* and then deleting the GList.
*/
void kvp_glist_delete(GList * list);
/** @} */
/** @name KvpValue Constructors
You probably shouldn't be using these low-level routines
The following routines are constructors for kvp_value.
Those with pointer arguments copy in the value.
The *_nc() versions do *not* copy in thier values,
but use them directly.
@{
*/
KvpValue * kvp_value_new_gint64(gint64 value);
KvpValue * kvp_value_new_double(double value);
/** \deprecated
Use kvp_value_new_numeric instead of kvp_value_new_gnc_numeric
*/
#define kvp_value_new_gnc_numeric kvp_value_new_numeric
KvpValue * kvp_value_new_numeric(gnc_numeric value);
KvpValue * kvp_value_new_string(const char * value);
KvpValue * kvp_value_new_guid(const GUID * guid);
KvpValue * kvp_value_new_timespec(Timespec timespec);
KvpValue * kvp_value_new_binary(const void * data, guint64 datasize);
KvpValue * kvp_value_new_frame(const KvpFrame * value);
/** Creates a KvpValue from a <b>GList of kvp_value's</b>! (Not to be
* confused with GList's of something else!) */
KvpValue * kvp_value_new_glist(const GList * value);
/** value constructors (non-copying - KvpValue takes pointer ownership)
values *must* have been allocated via glib allocators! (gnew, etc.) */
KvpValue * kvp_value_new_binary_nc(void * data, guint64 datasize);
/** Creates a KvpValue from a <b>GList of kvp_value's</b>! (Not to be
* confused with GList's of something else!)
*
* This value constructor is non-copying (KvpValue takes pointer
* ownership). The values *must* have been allocated via glib
* allocators! (gnew, etc.) */
KvpValue * kvp_value_new_glist_nc(GList *lst);
/** value constructors (non-copying - KvpValue takes pointer ownership)
values *must* have been allocated via glib allocators! (gnew, etc.) */
KvpValue * kvp_value_new_frame_nc(KvpFrame * value);
/** This is a deep (recursive) delete. */
void kvp_value_delete(KvpValue * value);
/** This is a deep value copy. */
KvpValue * kvp_value_copy(const KvpValue * value);
/** Replace old frame value with new, return old frame */
KvpFrame * kvp_value_replace_frame_nc(KvpValue *value, KvpFrame * newframe);
/** Replace old glist value with new, return old glist */
GList * kvp_value_replace_glist_nc(KvpValue *value, GList *newlist);
/** @} */
/** @name KvpValue Value access
You probably shouldn't be using these low-level routines
@{
*/
KvpValueType kvp_value_get_type(const KvpValue * value);
/** Value accessors. Those for GUID, binary, GList, KvpFrame and
* string are non-copying -- the caller can modify the value
* directly. Just don't free it, or you screw up everything.
* Note that if another value is stored at the key location
* that this value came from, then this value will be
* uncermoniously deleted, and you will be left pointing to
* garbage. So don't store values at the same time you are
* examining their contents.
*/
gint64 kvp_value_get_gint64(const KvpValue * value);
double kvp_value_get_double(const KvpValue * value);
gnc_numeric kvp_value_get_numeric(const KvpValue * value);
/** Value accessor. This one is non-copying -- the caller can modify
* the value directly. */
char * kvp_value_get_string(const KvpValue * value);
/** Value accessor. This one is non-copying -- the caller can modify
* the value directly. */
GUID * kvp_value_get_guid(const KvpValue * value);
/** Value accessor. This one is non-copying -- the caller can modify
* the value directly. */
void * kvp_value_get_binary(const KvpValue * value,
guint64 * size_return);
/** Returns the GList of kvp_frame's (not to be confused with GList's
* of something else!) from the given kvp_frame. This one is
* non-copying -- the caller can modify the value directly. */
GList * kvp_value_get_glist(const KvpValue * value);
/** Value accessor. This one is non-copying -- the caller can modify
* the value directly. */
KvpFrame * kvp_value_get_frame(const KvpValue * value);
Timespec kvp_value_get_timespec(const KvpValue * value);
/**
* Similar returns as strcmp.
**/
gint kvp_value_compare(const KvpValue *va, const KvpValue *vb);
/** @} */
/** \brief General purpose function to convert any KvpValue to a string.
Only the bare string is returned, there is no debugging information.
*/
gchar* kvp_value_to_bare_string(const KvpValue *val);
/** \brief Debug version of kvp_value_to_string
This version is used only by ::qof_query_printValueForParam,
itself a debugging and development utility function.
*/
gchar* kvp_value_to_string(const KvpValue *val);
/** Manipulator:
*
* copying - but more efficient than creating a new KvpValue manually. */
gboolean kvp_value_binary_append(KvpValue *v, void *data, guint64 size);
/** @name Iterators
@{
*/
/** Traverse all of the slots in the given kvp_frame. This function
does not descend recursively to traverse any kvp_frames stored as
slot values. You must handle that in proc, with a suitable
recursive call if desired. */
void kvp_frame_for_each_slot(KvpFrame *f,
void (*proc)(const char *key,
KvpValue *value,
gpointer data),
gpointer data);
/** @} */
/** Internal helper routines, you probably shouldn't be using these. */
gchar* kvp_frame_to_string(const KvpFrame *frame);
gchar* binary_to_string(const void *data, guint32 size);
gchar* kvp_value_glist_to_string(const GList *list);
GHashTable* kvp_frame_get_hash(const KvpFrame *frame);
/** @} */
#endif

View File

@ -1,445 +0,0 @@
/* md5.c - Functions to compute MD5 message digest of files or memory blocks
according to the definition of MD5 in RFC 1321 from April 1992.
Copyright (C) 1995, 1996 Free Software Foundation, Inc.
NOTE: The canonical source of this file is maintained with the GNU C
Library. Bugs can be reported to bug-glibc@prep.ai.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, 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, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#if STDC_HEADERS || defined _LIBC
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_MEMCPY
#include <string.h>
# define memcpy(d, s, n) bcopy ((s), (d), (n))
# endif
#endif
#include "md5.h"
#ifdef _LIBC
# include <endian.h>
# if __BYTE_ORDER == __BIG_ENDIAN
# define WORDS_BIGENDIAN 1
# endif
#endif
#ifdef WORDS_BIGENDIAN
# define SWAP(n) \
(((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
#else
# define SWAP(n) (n)
#endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (RFC 1321, 3.1: Step 1) */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
/* Initialize structure containing state of computation.
(RFC 1321, 3.3: Step 3) */
void
md5_init_ctx (ctx)
struct md5_ctx *ctx;
{
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
ctx->D = 0x10325476;
ctx->total[0] = ctx->total[1] = 0;
ctx->buflen = 0;
}
/* Put result from CTX in first 16 bytes following RESBUF. The result
must be in little endian byte order.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void *
md5_read_ctx (ctx, resbuf)
const struct md5_ctx *ctx;
void *resbuf;
{
((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
return resbuf;
}
/* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void *
md5_finish_ctx (ctx, resbuf)
struct md5_ctx *ctx;
void *resbuf;
{
/* Take yet unprocessed bytes into account. */
md5_uint32 bytes = ctx->buflen;
size_t pad;
/* Now count remaining bytes. */
ctx->total[0] += bytes;
if (ctx->total[0] < bytes)
++ctx->total[1];
pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
memcpy (&ctx->buffer[bytes], fillbuf, pad);
/* Put the 64-bit file length in *bits* at the end of the buffer. */
*(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
*(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
(ctx->total[0] >> 29));
/* Process last bytes. */
md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
return md5_read_ctx (ctx, resbuf);
}
/* Compute MD5 message digest for bytes read from STREAM. The
resulting message digest number will be written into the 16 bytes
beginning at RESBLOCK. */
int
md5_stream (stream, resblock)
FILE *stream;
void *resblock;
{
/* Important: BLOCKSIZE must be a multiple of 64. */
#define BLOCKSIZE 4096
struct md5_ctx ctx;
char buffer[BLOCKSIZE + 72];
size_t sum;
/* Initialize the computation context. */
md5_init_ctx (&ctx);
/* Iterate over full file contents. */
while (1)
{
/* We read the file in blocks of BLOCKSIZE bytes. One call of the
computation function processes the whole buffer so that with the
next round of the loop another block can be read. */
size_t n;
sum = 0;
/* Read block. Take care for partial reads. */
do
{
n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
sum += n;
}
while (sum < BLOCKSIZE && n != 0);
if (n == 0 && ferror (stream))
return 1;
/* If end of file is reached, end the loop. */
if (n == 0)
break;
/* Process buffer with BLOCKSIZE bytes. Note that
BLOCKSIZE % 64 == 0
*/
md5_process_block (buffer, BLOCKSIZE, &ctx);
}
/* Add the last bytes if necessary. */
if (sum > 0)
md5_process_bytes (buffer, sum, &ctx);
/* Construct result in desired memory. */
md5_finish_ctx (&ctx, resblock);
return 0;
}
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest. */
void *
md5_buffer (buffer, len, resblock)
const char *buffer;
size_t len;
void *resblock;
{
struct md5_ctx ctx;
/* Initialize the computation context. */
md5_init_ctx (&ctx);
/* Process whole buffer but last len % 64 bytes. */
md5_process_bytes (buffer, len, &ctx);
/* Put result in desired memory area. */
return md5_finish_ctx (&ctx, resblock);
}
void
md5_process_bytes (buffer, len, ctx)
const void *buffer;
size_t len;
struct md5_ctx *ctx;
{
#define NUM_MD5_WORDS 1024
size_t add = 0;
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
if (ctx->buflen != 0)
{
size_t left_over = ctx->buflen;
add = 128 - left_over > len ? len : 128 - left_over;
memcpy (&ctx->buffer[left_over], buffer, add);
ctx->buflen += add;
if (left_over + add > 64)
{
md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
/* The regions in the following copy operation cannot overlap. */
memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
(left_over + add) & 63);
ctx->buflen = (left_over + add) & 63;
}
buffer = (const char *) buffer + add;
len -= add;
}
/* Process available complete blocks. */
if (len > 64)
{
if ((add & 3) == 0) /* buffer is still 32-bit aligned */
{
md5_process_block (buffer, len & ~63, ctx);
buffer = (const char *) buffer + (len & ~63);
}
else /* buffer is not 32-bit aligned */
{
md5_uint32 md5_buffer[NUM_MD5_WORDS];
size_t num_bytes;
size_t buf_bytes;
num_bytes = len & ~63;
while (num_bytes > 0)
{
buf_bytes = (num_bytes < sizeof(md5_buffer)) ?
num_bytes : sizeof(md5_buffer);
memcpy (md5_buffer, buffer, buf_bytes);
md5_process_block (md5_buffer, buf_bytes, ctx);
num_bytes -= buf_bytes;
buffer = (const char *) buffer + buf_bytes;
}
}
len &= 63;
}
/* Move remaining bytes in internal buffer. */
if (len > 0)
{
memcpy (ctx->buffer, buffer, len);
ctx->buflen = len;
}
}
/* These are the four functions used in the four steps of the MD5 algorithm
and defined in the RFC 1321. The first function is a little bit optimized
(as found in Colin Plumbs public domain implementation). */
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
#define FF(b, c, d) (d ^ (b & (c ^ d)))
#define FG(b, c, d) FF (d, b, c)
#define FH(b, c, d) (b ^ c ^ d)
#define FI(b, c, d) (c ^ (b | ~d))
/* Process LEN bytes of BUFFER, accumulating context into CTX.
It is assumed that LEN % 64 == 0. */
void
md5_process_block (buffer, len, ctx)
const void *buffer;
size_t len;
struct md5_ctx *ctx;
{
md5_uint32 correct_words[16];
const md5_uint32 *words = buffer;
size_t nwords = len / sizeof (md5_uint32);
const md5_uint32 *endp = words + nwords;
md5_uint32 A = ctx->A;
md5_uint32 B = ctx->B;
md5_uint32 C = ctx->C;
md5_uint32 D = ctx->D;
/* First increment the byte count. RFC 1321 specifies the possible
length of the file up to 2^64 bits. Here we only compute the
number of bytes. Do a double word increment. */
ctx->total[0] += len;
if (ctx->total[0] < len)
++ctx->total[1];
/* Process all bytes in the buffer with 64 bytes in each round of
the loop. */
while (words < endp)
{
md5_uint32 *cwp = correct_words;
md5_uint32 A_save = A;
md5_uint32 B_save = B;
md5_uint32 C_save = C;
md5_uint32 D_save = D;
/* First round: using the given function, the context and a constant
the next context is computed. Because the algorithms processing
unit is a 32-bit word and it is determined to work on words in
little endian byte order we perhaps have to change the byte order
before the computation. To reduce the work for the next steps
we store the swapped words in the array CORRECT_WORDS. */
#define OP(a, b, c, d, s, T) \
do \
{ \
a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
++words; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* It is unfortunate that C does not provide an operator for
cyclic rotation. Hope the C compiler is smart enough. */
#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
/* Before we start, one word to the strange constants.
They are defined in RFC 1321 as
T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
*/
/* Round 1. */
OP (A, B, C, D, 7, 0xd76aa478);
OP (D, A, B, C, 12, 0xe8c7b756);
OP (C, D, A, B, 17, 0x242070db);
OP (B, C, D, A, 22, 0xc1bdceee);
OP (A, B, C, D, 7, 0xf57c0faf);
OP (D, A, B, C, 12, 0x4787c62a);
OP (C, D, A, B, 17, 0xa8304613);
OP (B, C, D, A, 22, 0xfd469501);
OP (A, B, C, D, 7, 0x698098d8);
OP (D, A, B, C, 12, 0x8b44f7af);
OP (C, D, A, B, 17, 0xffff5bb1);
OP (B, C, D, A, 22, 0x895cd7be);
OP (A, B, C, D, 7, 0x6b901122);
OP (D, A, B, C, 12, 0xfd987193);
OP (C, D, A, B, 17, 0xa679438e);
OP (B, C, D, A, 22, 0x49b40821);
/* For the second to fourth round we have the possibly swapped words
in CORRECT_WORDS. Redefine the macro to take an additional first
argument specifying the function to use. */
#undef OP
#define OP(f, a, b, c, d, k, s, T) \
do \
{ \
a += f (b, c, d) + correct_words[k] + T; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* Round 2. */
OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
OP (FG, D, A, B, C, 6, 9, 0xc040b340);
OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
OP (FG, D, A, B, C, 10, 9, 0x02441453);
OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
/* Round 3. */
OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
OP (FH, D, A, B, C, 8, 11, 0x8771f681);
OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
OP (FH, B, C, D, A, 6, 23, 0x04881d05);
OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
/* Round 4. */
OP (FI, A, B, C, D, 0, 6, 0xf4292244);
OP (FI, D, A, B, C, 7, 10, 0x432aff97);
OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
OP (FI, C, D, A, B, 6, 15, 0xa3014314);
OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
/* Add the starting values of the context. */
A += A_save;
B += B_save;
C += C_save;
D += D_save;
}
/* Put checksum in context given as argument. */
ctx->A = A;
ctx->B = B;
ctx->C = C;
ctx->D = D;
}

View File

@ -1,163 +0,0 @@
/* md5.h - Declaration of functions and data types used for MD5 sum
computing library functions.
Copyright (C) 1995, 1996 Free Software Foundation, Inc.
NOTE: The canonical source of this file is maintained with the GNU C
Library. Bugs can be reported to bug-glibc@prep.ai.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, 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, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifndef _MD5_H
#define _MD5_H 1
#include <stddef.h>
#include <stdio.h>
#if defined HAVE_LIMITS_H || _LIBC
# include <limits.h>
#endif
/* The following contortions are an attempt to use the C preprocessor
to determine an unsigned integral type that is 32 bits wide. An
alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
doing that would require that the configure script compile and *run*
the resulting executable. Locally running cross-compiled executables
is usually not possible. */
#ifdef _LIBC
# include <sys/types.h>
typedef u_int32_t md5_uint32;
#else
# if defined __STDC__ && __STDC__
# define UINT_MAX_32_BITS 4294967295U
# else
# define UINT_MAX_32_BITS 0xFFFFFFFF
# endif
/* If UINT_MAX isn't defined, assume it's a 32-bit type.
This should be valid for all systems GNU cares about because
that doesn't include 16-bit systems, and only modern systems
(that certainly have <limits.h>) have 64+-bit integral types. */
# ifndef UINT_MAX
# define UINT_MAX UINT_MAX_32_BITS
# endif
# if UINT_MAX == UINT_MAX_32_BITS
typedef unsigned int md5_uint32;
# else
# if USHRT_MAX == UINT_MAX_32_BITS
typedef unsigned short md5_uint32;
# else
# if ULONG_MAX == UINT_MAX_32_BITS
typedef unsigned long md5_uint32;
# else
/* The following line is intended to evoke an error.
Using #error is not portable enough. */
"Cannot determine unsigned 32-bit data type."
# endif
# endif
# endif
#endif
#undef __P
#if defined (__STDC__) && __STDC__
#define __P(x) x
#else
#define __P(x) ()
#endif
/* Structure to save state of computation between the single steps. */
struct md5_ctx
{
md5_uint32 A;
md5_uint32 B;
md5_uint32 C;
md5_uint32 D;
md5_uint32 total[2];
md5_uint32 buflen;
char buffer[128];
};
/*
* The following three functions are build up the low level used in
* the functions `md5_stream' and `md5_buffer'.
*/
/* Initialize structure containing state of computation.
(RFC 1321, 3.3: Step 3) */
extern void md5_init_ctx __P ((struct md5_ctx *ctx));
/* Starting with the result of former calls of this function (or the
initialization function update the context for the next LEN bytes
starting at BUFFER.
It is necessary that LEN is a multiple of 64!!!
IMPORTANT: On some systems it is required that buffer be 32-bit
aligned. */
extern void md5_process_block __P ((const void *buffer, size_t len,
struct md5_ctx *ctx));
/* Starting with the result of former calls of this function (or the
initialization function) update the context for the next LEN bytes
starting at BUFFER.
It is NOT required that LEN is a multiple of 64.
IMPORTANT: On some systems it is required that buffer be 32-bit
aligned. */
extern void md5_process_bytes __P ((const void *buffer, size_t len,
struct md5_ctx *ctx));
/* Process the remaining bytes in the buffer and put result from CTX
in first 16 bytes following RESBUF. The result is always in little
endian byte order, so that a byte-wise output yields to the wanted
ASCII representation of the message digest.
IMPORTANT: On some systems it is required that RESBUF be correctly
aligned for a 32 bits value. */
extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
/* Put result from CTX in first 16 bytes following RESBUF. The result is
always in little endian byte order, so that a byte-wise output yields
to the wanted ASCII representation of the message digest.
IMPORTANT: On some systems it is required that RESBUF be correctly
aligned for a 32 bits value. */
extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
/* Compute MD5 message digest for bytes read from STREAM. The
resulting message digest number will be written into the 16 bytes
beginning at RESBLOCK.
IMPORTANT: On some systems it is required that resblock be 32-bit
aligned. */
extern int md5_stream __P ((FILE *stream, void *resblock));
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest.
IMPORTANT: On some systems it is required that buffer and resblock
be correctly 32-bit aligned. */
extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock));
#endif

View File

@ -1,186 +0,0 @@
/********************************************************************\
* qof-be-utils.h: api for data storage backend *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Backend
@{ */
/** @file qof-be-utils.h
@brief QOF Backend Utilities
@author Derek Atkins <derek@ihtfp.com>
@author Neil Williams <linux@codehelp.co.uk>
Common code used by objects to define begin_edit() and
commit_edit() functions.
*/
#ifndef QOF_BE_UTILS_H
#define QOF_BE_UTILS_H
#include "gnc-trace.h"
#include "gnc-engine-util.h"
#include "qofbackend-p.h"
#include "qofbook.h"
#include "qofinstance.h"
/** begin_edit helper
*
* @param inst: an instance of QofInstance
*
* The caller should use this macro first and then perform any other operations.
Uses newly created functions to allow the macro to be used
when QOF is linked as a library. qofbackend-p.h is a private header.
*/
#define QOF_BEGIN_EDIT(inst) \
QofBackend * be; \
if (!(inst)) return; \
\
(inst)->editlevel++; \
if (1 < (inst)->editlevel) return; \
\
if (0 >= (inst)->editlevel) \
{ \
PERR ("unbalanced call - resetting (was %d)", (inst)->editlevel); \
(inst)->editlevel = 1; \
} \
ENTER ("(inst=%p)", (inst)); \
\
/* See if there's a backend. If there is, invoke it. */ \
be = qof_book_get_backend ((inst)->book); \
if (be && qof_backend_begin_exists((be))) { \
qof_backend_run_begin((be), (inst)); \
} else { \
/* We tried and failed to start transaction! */ \
(inst)->dirty = TRUE; \
} \
LEAVE (" ");
/** \brief function version of QOF_BEGIN_EDIT
The macro cannot be used in a function that returns a value,
this function can be used instead.
*/
gboolean qof_begin_edit(QofInstance *inst);
/**
* commit_edit helpers
*
* The caller should call PART1 as the first thing, then
* perform any local operations prior to calling the backend.
* Then call PART2.
*/
/**
* part1 -- deal with the editlevel
*
* @param inst: an instance of QofInstance
*/
#define QOF_COMMIT_EDIT_PART1(inst) { \
if (!(inst)) return; \
\
(inst)->editlevel--; \
if (0 < (inst)->editlevel) return; \
\
/* The pricedb sufffers from delayed update... */ \
/* This may be setting a bad precedent for other types, I fear. */ \
/* Other types probably really should handle begin like this. */ \
if ((-1 == (inst)->editlevel) && (inst)->dirty) \
{ \
QofBackend * be; \
be = qof_book_get_backend ((inst)->book); \
if (be && qof_backend_begin_exists((be))) { \
qof_backend_run_begin((be), (inst)); \
} \
(inst)->editlevel = 0; \
} \
if (0 > (inst)->editlevel) \
{ \
PERR ("unbalanced call - resetting (was %d)", (inst)->editlevel); \
(inst)->editlevel = 0; \
} \
ENTER ("(inst=%p) dirty=%d do-free=%d", \
(inst), (inst)->dirty, (inst)->do_free); \
}
/** \brief function version of QOF_COMMIT_EDIT_PART1
The macro cannot be used in a function that returns a value,
this function can be used instead. Only Part1 is implemented.
*/
gboolean qof_commit_edit(QofInstance *inst);
/**
* part2 -- deal with the backend
*
* @param inst: an instance of QofInstance
* @param on_error: a function called if there is a backend error.
* void (*on_error)(inst, QofBackendError)
* @param on_done: a function called after the commit is complete
* but before the instect is freed. Perform any other
* operations after the commit.
* void (*on_done)(inst)
* @param on_free: a function called if inst->do_free is TRUE.
* void (*on_free)(inst)
*/
#define QOF_COMMIT_EDIT_PART2(inst,on_error,on_done,on_free) { \
QofBackend * be; \
\
/* See if there's a backend. If there is, invoke it. */ \
be = qof_book_get_backend ((inst)->book); \
if (be && qof_backend_commit_exists((be))) \
{ \
QofBackendError errcode; \
\
/* clear errors */ \
do { \
errcode = qof_backend_get_error (be); \
} while (ERR_BACKEND_NO_ERR != errcode); \
\
qof_backend_run_commit((be), (inst)); \
errcode = qof_backend_get_error (be); \
if (ERR_BACKEND_NO_ERR != errcode) \
{ \
/* XXX Should perform a rollback here */ \
(inst)->do_free = FALSE; \
\
/* Push error back onto the stack */ \
qof_backend_set_error (be, errcode); \
(on_error)((inst), errcode); \
} \
/* XXX the backend commit code should clear dirty!! */ \
(inst)->dirty = FALSE; \
} \
(on_done)(inst); \
\
LEAVE ("inst=%p, dirty=%d do-free=%d", \
(inst), (inst)->dirty, (inst)->do_free); \
if ((inst)->do_free) { \
(on_free)(inst); \
return; \
} \
}
#endif /* QOF_BE_UTILS_H */
/** @} */
/** @} */

View File

@ -1,101 +0,0 @@
/********************************************************************\
* qof.h -- Master QOF public include file *
* 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 *
* *
\********************************************************************/
#ifndef QOF_H_
#define QOF_H_
/** @defgroup QOF Query Object Framework
@{
*/
/**
@addtogroup Date Date: Date and Time Printing, Parsing and Manipulation
@ingroup QOF
*/
/**
@addtogroup Entity Entity: Types, Identity and Instance Framework
@ingroup QOF
*/
/**
@addtogroup KVP KVP: Key-Value Pairs
@ingroup QOF
*/
/**
@addtogroup Math128 Math128: 128-bit Integer Math Library
@ingroup QOF
*/
/**
@addtogroup Numeric Numeric: Rational Number Handling w/ Rounding Error Control
@ingroup QOF
*/
/**
@addtogroup Object Object: Dynamic Object Class Framework
@ingroup QOF
*/
/** @addtogroup Choice Choice and collect : One to many links.
@ingroup QOF
*/
/**
@addtogroup Query Query: Querying for Objects
@ingroup QOF
*/
/**
@addtogroup Trace Trace: Error Reporting and Debugging
@ingroup QOF
*/
/** @addtogroup BookMerge Merging QofBook structures
@ingroup QOF
*/
/** @addtogroup Event Event: QOF event handlers.
@ingroup QOF
*/
/**
@addtogroup Utilities Misc Utilities
@ingroup QOF
*/
/** @} */
#include "qofid.h"
#include "gnc-trace.h"
#include "gnc-date.h"
#include "gnc-numeric.h"
#include "gnc-event.h"
#include "gnc-engine-util.h"
#include "guid.h"
#include "kvp_frame.h"
#include "kvp-util.h"
#include "kvp-util-p.h"
#include "qofbackend.h"
#include "qofid-p.h"
#include "qofinstance-p.h"
#include "qofbook.h"
#include "qofclass.h"
#include "qofobject.h"
#include "qofquery.h"
#include "qofquerycore.h"
#include "qofsession.h"
#include "qofsql.h"
#include "qofchoice.h"
#include "qof_book_merge.h"
#include "qof-be-utils.h"
#include "qofla-dir.h"
#endif /* QOF_H_ */

View File

@ -1,992 +0,0 @@
/*********************************************************************
* qof_book_merge.c -- api for QoFBook merge with collision handling *
* Copyright (C) 2004-2005 Neil Williams <linux@codehelp.co.uk> *
* *
* 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 "gnc-trace.h"
#include "qof_book_merge.h"
#include "qofinstance-p.h"
#include "qofchoice.h"
#include "qofid-p.h"
static QofLogModule log_module = QOF_MOD_MERGE;
/* private rule iteration struct */
struct qof_book_mergeRuleIterate {
qof_book_mergeRuleForeachCB fcn;
qof_book_mergeData *data;
qof_book_mergeRule *rule;
GList *ruleList;
guint remainder;
};
/* Make string type parameters 3 times more
important in the match than default types.
i.e. even if two other parameters differ,
a string match will still provide a better target
than when other types match and the string does not.
*/
#define DEFAULT_MERGE_WEIGHT 1
#define QOF_STRING_WEIGHT 3
#define QOF_DATE_STRING_LENGTH MAX_DATE_LENGTH
static qof_book_mergeRule*
qof_book_mergeUpdateRule(qof_book_mergeRule *currentRule, gboolean match, gint weight)
{
gboolean absolute;
absolute = currentRule->mergeAbsolute;
if(absolute && match && currentRule->mergeResult == MERGE_UNDEF) {
currentRule->mergeResult = MERGE_ABSOLUTE;
}
if(absolute && !match) { currentRule->mergeResult = MERGE_UPDATE; }
if(!absolute && match &&currentRule->mergeResult == MERGE_UNDEF) {
currentRule->mergeResult = MERGE_DUPLICATE;
}
if(!absolute && !match) {
currentRule->difference += weight;
if(currentRule->mergeResult == MERGE_DUPLICATE) {
currentRule->mergeResult = MERGE_REPORT;
}
}
return currentRule;
}
struct collect_list_s
{
GSList *linkedEntList;
};
static void
collect_reference_cb (QofEntity *ent, gpointer user_data)
{
struct collect_list_s *s;
s = (struct collect_list_s*)user_data;
if(!ent || !s) { return; }
s->linkedEntList = g_slist_prepend(s->linkedEntList, ent);
}
static int
qof_book_mergeCompare( qof_book_mergeData *mergeData )
{
qof_book_mergeRule *currentRule;
QofCollection *mergeColl, *targetColl;
gchar *stringImport, *stringTarget, *charImport, *charTarget;
QofEntity *mergeEnt, *targetEnt, *referenceEnt;
const GUID *guidImport, *guidTarget;
QofParam *qtparam;
KvpFrame *kvpImport, *kvpTarget;
QofIdType mergeParamName;
QofType mergeType;
GSList *paramList;
gboolean absolute, mergeError, knowntype, mergeMatch, booleanImport, booleanTarget,
(*boolean_getter) (QofEntity*, QofParam*);
Timespec tsImport, tsTarget, (*date_getter) (QofEntity*, QofParam*);
gnc_numeric numericImport, numericTarget, (*numeric_getter) (QofEntity*, QofParam*);
double doubleImport, doubleTarget, (*double_getter) (QofEntity*, QofParam*);
gint32 i32Import, i32Target, (*int32_getter) (QofEntity*, QofParam*);
gint64 i64Import, i64Target, (*int64_getter) (QofEntity*, QofParam*);
g_return_val_if_fail((mergeData != NULL), -1);
currentRule = mergeData->currentRule;
g_return_val_if_fail((currentRule != NULL), -1);
absolute = currentRule->mergeAbsolute;
mergeEnt = currentRule->importEnt;
targetEnt = currentRule->targetEnt;
paramList = currentRule->mergeParam;
currentRule->difference = 0;
currentRule->mergeResult = MERGE_UNDEF;
currentRule->linkedEntList = NULL;
g_return_val_if_fail((targetEnt)||(mergeEnt)||(paramList), -1);
kvpImport = kvp_frame_new();
kvpTarget = kvp_frame_new();
mergeError = FALSE;
while(paramList != NULL) {
mergeMatch = FALSE;
knowntype = FALSE;
qtparam = paramList->data;
mergeParamName = qtparam->param_name;
g_return_val_if_fail(mergeParamName != NULL, -1);
mergeType = qtparam->param_type;
if(safe_strcmp(mergeType, QOF_TYPE_STRING) == 0) {
stringImport = qtparam->param_getfcn(mergeEnt,qtparam);
stringTarget = qtparam->param_getfcn(targetEnt,qtparam);
/* very strict string matches may need to be relaxed. */
if(stringImport == NULL) { stringImport = ""; }
if(stringTarget == NULL) { stringTarget = ""; }
if(safe_strcmp(stringImport,stringTarget) == 0) { mergeMatch = TRUE; }
/* Give special weight to a string match */
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, QOF_STRING_WEIGHT);
stringImport = stringTarget = NULL;
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_DATE) == 0) {
date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
tsImport = date_getter(mergeEnt, qtparam);
tsTarget = date_getter(targetEnt, qtparam);
if(timespec_cmp(&tsImport, &tsTarget) == 0) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if((safe_strcmp(mergeType, QOF_TYPE_NUMERIC) == 0) ||
(safe_strcmp(mergeType, QOF_TYPE_DEBCRED) == 0)) {
numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
numericImport = numeric_getter(mergeEnt,qtparam);
numericTarget = numeric_getter(targetEnt,qtparam);
if(gnc_numeric_compare (numericImport, numericTarget) == 0) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_GUID) == 0) {
guidImport = qtparam->param_getfcn(mergeEnt,qtparam);
guidTarget = qtparam->param_getfcn(targetEnt,qtparam);
if(guid_compare(guidImport, guidTarget) == 0) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_INT32) == 0) {
int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
i32Import = int32_getter(mergeEnt, qtparam);
i32Target = int32_getter(targetEnt, qtparam);
if(i32Target == i32Import) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_INT64) == 0) {
int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
i64Import = int64_getter(mergeEnt, qtparam);
i64Target = int64_getter(targetEnt, qtparam);
if(i64Target == i64Import) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_DOUBLE) == 0) {
double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
doubleImport = double_getter(mergeEnt, qtparam);
doubleTarget = double_getter(mergeEnt, qtparam);
if(doubleImport == doubleTarget) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_BOOLEAN) == 0){
boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
booleanImport = boolean_getter(mergeEnt, qtparam);
booleanTarget = boolean_getter(targetEnt, qtparam);
if(booleanImport != FALSE && booleanImport != TRUE) { booleanImport = FALSE; }
if(booleanTarget != FALSE && booleanTarget != TRUE) { booleanTarget = FALSE; }
if(booleanImport == booleanTarget) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_KVP) == 0) {
kvpImport = kvp_frame_copy(qtparam->param_getfcn(mergeEnt,qtparam));
kvpTarget = kvp_frame_copy(qtparam->param_getfcn(targetEnt,qtparam));
if(kvp_frame_compare(kvpImport, kvpTarget) == 0) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_CHAR) == 0) {
charImport = qtparam->param_getfcn(mergeEnt,qtparam);
charTarget = qtparam->param_getfcn(targetEnt,qtparam);
if(charImport == charTarget) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype= TRUE;
}
/* No object should have QofSetterFunc defined for the book, but just to be safe, do nothing. */
if(safe_strcmp(mergeType, QOF_ID_BOOK) == 0) { knowntype= TRUE; }
if(safe_strcmp(mergeType, QOF_TYPE_COLLECT) == 0) {
struct collect_list_s s;
s.linkedEntList = NULL;
mergeColl = qtparam->param_getfcn(mergeEnt, qtparam);
targetColl = qtparam->param_getfcn(targetEnt, qtparam);
s.linkedEntList = g_slist_copy(currentRule->linkedEntList);
qof_collection_foreach(mergeColl, collect_reference_cb, &s);
currentRule->linkedEntList = g_slist_copy(s.linkedEntList);
if(0 == qof_collection_compare(mergeColl, targetColl)) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
knowntype = TRUE;
}
if(safe_strcmp(mergeType, QOF_TYPE_CHOICE) ==0) {
referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
currentRule->linkedEntList = g_slist_prepend(currentRule->linkedEntList, referenceEnt);
if(referenceEnt == qtparam->param_getfcn(targetEnt, qtparam)) { mergeMatch = TRUE; }
knowntype = TRUE;
}
if(knowntype == FALSE) {
referenceEnt = qtparam->param_getfcn(mergeEnt, qtparam);
if((referenceEnt != NULL)
&&(safe_strcmp(referenceEnt->e_type, mergeType) == 0)) {
currentRule->linkedEntList = g_slist_prepend(currentRule->linkedEntList, referenceEnt);
if(referenceEnt == qtparam->param_getfcn(targetEnt, qtparam)) { mergeMatch = TRUE; }
currentRule = qof_book_mergeUpdateRule(currentRule, mergeMatch, DEFAULT_MERGE_WEIGHT);
}
}
paramList = g_slist_next(paramList);
}
mergeData->currentRule = currentRule;
g_free(kvpImport);
g_free(kvpTarget);
return 0;
}
static void
qof_book_mergeCommitForeachCB(gpointer rule, gpointer arg)
{
struct qof_book_mergeRuleIterate *iter;
g_return_if_fail(arg != NULL);
iter = (struct qof_book_mergeRuleIterate*)arg;
g_return_if_fail(iter->data != NULL);
iter->fcn (iter->data, (qof_book_mergeRule*)rule, iter->remainder);
iter->remainder--;
}
static void
qof_book_mergeCommitForeach (
qof_book_mergeRuleForeachCB cb,
qof_book_mergeResult mergeResult,
qof_book_mergeData *mergeData)
{
struct qof_book_mergeRuleIterate iter;
qof_book_mergeRule *currentRule;
GList *subList;
g_return_if_fail(cb != NULL);
g_return_if_fail(mergeData != NULL);
currentRule = mergeData->currentRule;
g_return_if_fail(currentRule != NULL);
g_return_if_fail(mergeResult > 0);
g_return_if_fail((mergeResult != MERGE_INVALID)||(mergeResult != MERGE_UNDEF)||(mergeResult != MERGE_REPORT));
iter.fcn = cb;
subList = NULL;
iter.ruleList = g_list_copy(mergeData->mergeList);
while(iter.ruleList!=NULL) {
currentRule = iter.ruleList->data;
if(currentRule->mergeResult == mergeResult) {
subList = g_list_prepend(subList, currentRule);
}
iter.ruleList = g_list_next(iter.ruleList);
}
iter.remainder = g_list_length(subList);
iter.data = mergeData;
g_list_foreach (subList, qof_book_mergeCommitForeachCB, &iter);
}
/* build the table of target comparisons
This can get confusing, so bear with me. (!)
Whilst iterating through the entities in the mergeBook, qof_book_mergeForeach assigns
a targetEnt to each mergeEnt (until it runs out of targetEnt or mergeEnt). Each match
is made against the one targetEnt that best matches the mergeEnt. Fine so far.
Any mergeEnt is only ever assigned a targetEnt if the calculated difference between
the two is less than the difference between that targetEnt and any previous mergeEnt
match.
The next mergeEnt may be a much better match for that targetEnt and the target_table
is designed to solve the issues that result from this conflict. The previous match
must be re-assigned because if two mergeEnt's are matched with only one targetEnt,
data loss \b WILL follow. Equally, the current mergeEnt must replace the previous
one as it is a better match. qof_entity_rating holds the details required to identify
the correct mergeEnt to be re-assigned and these mergeEnt entities are therefore
orphaned - to be re-matched later.
Meanwhile, the current mergeEnt is entered into target_table with it's difference and
rule data, in case an even better match is found later in the mergeBook.
Finally, each mergeEnt in the orphan_list is now put through the comparison again.
*/
static gboolean
qof_book_merge_rule_cmp(gconstpointer a, gconstpointer b)
{
qof_book_mergeRule *ra = (qof_book_mergeRule *) a;
qof_book_mergeRule *rb = (qof_book_mergeRule *) b;
if (ra->difference == rb->difference) { return TRUE; }
else return FALSE;
}
static void
qof_book_merge_orphan_check(double difference, qof_book_mergeRule *mergeRule, qof_book_mergeData *mergeData)
{
/* Called when difference is lower than previous
Lookup target to find previous match
and re-assign mergeEnt to orphan_list */
qof_book_mergeRule *rule;
g_return_if_fail(mergeRule != NULL);
g_return_if_fail(mergeData != NULL);
if(g_hash_table_size(mergeData->target_table) == 0) { return; }
rule = (qof_book_mergeRule*)g_hash_table_lookup(mergeData->target_table, mergeRule->targetEnt);
/* If NULL, no match was found. */
if(rule == NULL) { return; }
/* Only orphan if this is a better match than already exists. */
if(difference >= rule->difference) { return; }
rule->targetEnt = NULL;
rule->mergeResult = MERGE_UNDEF;
mergeData->orphan_list = g_slist_append(mergeData->orphan_list, rule);
}
static void
qof_book_merge_match_orphans(qof_book_mergeData *mergeData)
{
GSList *orphans, *targets;
qof_book_mergeRule *rule, *currentRule;
QofEntity *best_matchEnt;
double difference;
g_return_if_fail(mergeData != NULL);
currentRule = mergeData->currentRule;
g_return_if_fail(currentRule != NULL);
/* This routine does NOT copy the orphan list, it
is used recursively until empty. */
orphans = mergeData->orphan_list;
targets = g_slist_copy(mergeData->targetList);
while(orphans != NULL) {
rule = orphans->data;
g_return_if_fail(rule != NULL);
difference = g_slist_length(mergeData->mergeObjectParams);
if(rule->targetEnt == NULL) {
rule->mergeResult = MERGE_NEW;
rule->difference = 0;
mergeData->mergeList = g_list_prepend(mergeData->mergeList,rule);
orphans = g_slist_next(orphans);
continue;
}
mergeData->currentRule = rule;
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
if(difference > mergeData->currentRule->difference) {
best_matchEnt = currentRule->targetEnt;
difference = currentRule->difference;
rule = currentRule;
mergeData->mergeList = g_list_prepend(mergeData->mergeList,rule);
qof_book_merge_orphan_check(difference, rule, mergeData);
}
orphans = g_slist_next(orphans);
}
g_slist_free(mergeData->orphan_list);
g_slist_free(targets);
}
static void
qof_book_mergeForeachTarget (QofEntity* targetEnt, gpointer user_data)
{
qof_book_mergeData *mergeData;
g_return_if_fail(user_data != NULL);
mergeData = (qof_book_mergeData*)user_data;
g_return_if_fail(targetEnt != NULL);
mergeData->targetList = g_slist_prepend(mergeData->targetList,targetEnt);
}
static void
qof_book_mergeForeachTypeTarget ( QofObject* merge_obj, gpointer user_data)
{
qof_book_mergeData *mergeData;
qof_book_mergeRule *currentRule;
g_return_if_fail(user_data != NULL);
mergeData = (qof_book_mergeData*)user_data;
currentRule = mergeData->currentRule;
g_return_if_fail(currentRule != NULL);
g_return_if_fail(merge_obj != NULL);
if(safe_strcmp(merge_obj->e_type, currentRule->importEnt->e_type) == 0) {
qof_object_foreach(currentRule->importEnt->e_type, mergeData->targetBook,
qof_book_mergeForeachTarget, user_data);
}
}
static void
qof_book_mergeForeach ( QofEntity* mergeEnt, gpointer user_data)
{
qof_book_mergeRule *mergeRule, *currentRule;
qof_book_mergeData *mergeData;
QofEntity *targetEnt, *best_matchEnt;
GUID *g;
double difference;
GSList *c;
g_return_if_fail(user_data != NULL);
mergeData = (qof_book_mergeData*)user_data;
g_return_if_fail(mergeEnt != NULL);
currentRule = mergeData->currentRule;
g_return_if_fail(currentRule != NULL);
g = guid_malloc();
*g = mergeEnt->guid;
mergeRule = g_new(qof_book_mergeRule,1);
mergeRule->importEnt = mergeEnt;
mergeRule->difference = difference = 0;
mergeRule->mergeAbsolute = FALSE;
mergeRule->mergeResult = MERGE_UNDEF;
mergeRule->updated = FALSE;
mergeRule->mergeType = mergeEnt->e_type;
mergeRule->mergeLabel = qof_object_get_type_label(mergeEnt->e_type);
mergeRule->mergeParam = g_slist_copy(mergeData->mergeObjectParams);
mergeRule->linkedEntList = NULL;
mergeData->currentRule = mergeRule;
targetEnt = best_matchEnt = NULL;
targetEnt = qof_collection_lookup_entity (
qof_book_get_collection (mergeData->targetBook, mergeEnt->e_type), g);
if( targetEnt != NULL) {
mergeRule->mergeAbsolute = TRUE;
mergeRule->targetEnt = targetEnt;
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
mergeData->mergeList = g_list_prepend(mergeData->mergeList,mergeRule);
return;
}
/* no absolute match exists */
g_slist_free(mergeData->targetList);
mergeData->targetList = NULL;
qof_object_foreach_type(qof_book_mergeForeachTypeTarget, mergeData);
if(g_slist_length(mergeData->targetList) == 0) {
mergeRule->mergeResult = MERGE_NEW;
}
difference = g_slist_length(mergeRule->mergeParam);
c = g_slist_copy(mergeData->targetList);
while(c != NULL) {
mergeRule->targetEnt = c->data;
currentRule = mergeRule;
/* compare two entities and sum the differences */
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
if(mergeRule->difference == 0) {
/* check if this is a better match than one already assigned */
best_matchEnt = mergeRule->targetEnt;
mergeRule->mergeResult = MERGE_DUPLICATE;
difference = 0;
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
g_slist_free(c);
guid_free(g);
/* exact match, return */
return;
}
if(difference > mergeRule->difference) {
/* The chosen targetEnt determines the parenting of any child object */
/* check if this is a better match than one already assigned */
best_matchEnt = mergeRule->targetEnt;
difference = mergeRule->difference;
/* Use match to lookup the previous entity that matched this targetEnt (if any)
and remove targetEnt from the rule for that mergeEnt.
Add the previous mergeEnt to orphan_list.
*/
qof_book_merge_orphan_check(difference, mergeRule, mergeData);
}
c = g_slist_next(c);
}
g_slist_free(c);
if(best_matchEnt != NULL ) {
mergeRule->targetEnt = best_matchEnt;
mergeRule->difference = difference;
/* Set this entity in the target_table in case a better match can be made
with the next mergeEnt. */
g_hash_table_insert(mergeData->target_table, mergeRule->targetEnt, mergeRule);
/* compare again with the best partial match */
g_return_if_fail(qof_book_mergeCompare(mergeData) != -1);
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
}
else {
mergeRule->targetEnt = NULL;
mergeRule->difference = 0;
mergeRule->mergeResult = MERGE_NEW;
mergeRule->linkedEntList = g_slist_copy(currentRule->linkedEntList);
}
mergeData->mergeList = g_list_prepend(mergeData->mergeList,mergeRule);
guid_free(g);
/* return to qof_book_mergeInit */
}
static void
qof_book_mergeForeachParam( QofParam* param, gpointer user_data)
{
qof_book_mergeData *mergeData;
g_return_if_fail(user_data != NULL);
mergeData = (qof_book_mergeData*)user_data;
g_return_if_fail(param != NULL);
if((param->param_getfcn != NULL)&&(param->param_setfcn != NULL)) {
mergeData->mergeObjectParams = g_slist_append(mergeData->mergeObjectParams, param);
}
}
static void
qof_book_mergeForeachType ( QofObject* merge_obj, gpointer user_data)
{
qof_book_mergeData *mergeData;
g_return_if_fail(user_data != NULL);
mergeData = (qof_book_mergeData*)user_data;
g_return_if_fail((merge_obj != NULL));
/* Skip unsupported objects */
if((merge_obj->create == NULL)||(merge_obj->foreach == NULL)){
DEBUG (" merge_obj QOF support failed %s", merge_obj->e_type);
return;
}
if(mergeData->mergeObjectParams != NULL) g_slist_free(mergeData->mergeObjectParams);
mergeData->mergeObjectParams = NULL;
qof_class_param_foreach(merge_obj->e_type, qof_book_mergeForeachParam , mergeData);
qof_object_foreach(merge_obj->e_type, mergeData->mergeBook, qof_book_mergeForeach, mergeData);
}
static void
qof_book_mergeRuleCB(gpointer rule, gpointer arg)
{
struct qof_book_mergeRuleIterate *iter;
qof_book_mergeData *mergeData;
g_return_if_fail(arg != NULL);
iter = (struct qof_book_mergeRuleIterate*)arg;
mergeData = iter->data;
g_return_if_fail(mergeData != NULL);
g_return_if_fail(mergeData->abort == FALSE);
iter->fcn (mergeData, (qof_book_mergeRule*)rule, iter->remainder);
iter->data = mergeData;
iter->remainder--;
}
static void
qof_book_mergeCommitRuleLoop(
qof_book_mergeData *mergeData,
qof_book_mergeRule *rule,
guint remainder)
{
QofInstance *inst;
gboolean registered_type;
QofEntity *referenceEnt;
/* cm_ prefix used for variables that hold the data to commit */
QofCollection *cm_coll;
QofParam *cm_param;
gchar *cm_string;
const GUID *cm_guid;
KvpFrame *cm_kvp;
/* function pointers and variables for parameter getters that don't use pointers normally */
gnc_numeric cm_numeric, (*numeric_getter) (QofEntity*, QofParam*);
double cm_double, (*double_getter) (QofEntity*, QofParam*);
gboolean cm_boolean, (*boolean_getter) (QofEntity*, QofParam*);
gint32 cm_i32, (*int32_getter) (QofEntity*, QofParam*);
gint64 cm_i64, (*int64_getter) (QofEntity*, QofParam*);
Timespec cm_date, (*date_getter) (QofEntity*, QofParam*);
char cm_char, (*char_getter) (QofEntity*, QofParam*);
/* function pointers to the parameter setters */
void (*string_setter) (QofEntity*, const char*);
void (*date_setter) (QofEntity*, Timespec);
void (*numeric_setter) (QofEntity*, gnc_numeric);
void (*guid_setter) (QofEntity*, const GUID*);
void (*double_setter) (QofEntity*, double);
void (*boolean_setter) (QofEntity*, gboolean);
void (*i32_setter) (QofEntity*, gint32);
void (*i64_setter) (QofEntity*, gint64);
void (*char_setter) (QofEntity*, char);
void (*kvp_frame_setter) (QofEntity*, KvpFrame*);
void (*reference_setter) (QofEntity*, QofEntity*);
void (*collection_setter)(QofEntity*, QofCollection*);
g_return_if_fail(rule != NULL);
g_return_if_fail(mergeData != NULL);
g_return_if_fail(mergeData->targetBook != NULL);
g_return_if_fail((rule->mergeResult != MERGE_NEW)||(rule->mergeResult != MERGE_UPDATE));
/* create a new object for MERGE_NEW */
/* The new object takes the GUID from the import to retain an absolute match */
if(rule->mergeResult == MERGE_NEW) {
inst = (QofInstance*)qof_object_new_instance(rule->importEnt->e_type, mergeData->targetBook);
g_return_if_fail(inst != NULL);
rule->targetEnt = &inst->entity;
qof_entity_set_guid(rule->targetEnt, qof_entity_get_guid(rule->importEnt));
}
/* currentRule->targetEnt is now set,
1. by an absolute GUID match or
2. by best_matchEnt and difference or
3. by MERGE_NEW.
*/
while(rule->mergeParam != NULL) {
registered_type = FALSE;
g_return_if_fail(rule->mergeParam->data);
cm_param = rule->mergeParam->data;
rule->mergeType = cm_param->param_type;
if(safe_strcmp(rule->mergeType, QOF_TYPE_STRING) == 0) {
cm_string = cm_param->param_getfcn(rule->importEnt, cm_param);
string_setter = (void(*)(QofEntity*, const char*))cm_param->param_setfcn;
if(string_setter != NULL) { string_setter(rule->targetEnt, cm_string); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_DATE) == 0) {
date_getter = (Timespec (*)(QofEntity*, QofParam*))cm_param->param_getfcn;
cm_date = date_getter(rule->importEnt, cm_param);
date_setter = (void(*)(QofEntity*, Timespec))cm_param->param_setfcn;
if(date_setter != NULL) { date_setter(rule->targetEnt, cm_date); }
registered_type = TRUE;
}
if((safe_strcmp(rule->mergeType, QOF_TYPE_NUMERIC) == 0) ||
(safe_strcmp(rule->mergeType, QOF_TYPE_DEBCRED) == 0)) {
numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*))cm_param->param_getfcn;
cm_numeric = numeric_getter(rule->importEnt, cm_param);
numeric_setter = (void(*)(QofEntity*, gnc_numeric))cm_param->param_setfcn;
if(numeric_setter != NULL) { numeric_setter(rule->targetEnt, cm_numeric); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_GUID) == 0) {
cm_guid = cm_param->param_getfcn(rule->importEnt, cm_param);
guid_setter = (void(*)(QofEntity*, const GUID*))cm_param->param_setfcn;
if(guid_setter != NULL) { guid_setter(rule->targetEnt, cm_guid); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_INT32) == 0) {
int32_getter = (gint32 (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
cm_i32 = int32_getter(rule->importEnt, cm_param);
i32_setter = (void(*)(QofEntity*, gint32))cm_param->param_setfcn;
if(i32_setter != NULL) { i32_setter(rule->targetEnt, cm_i32); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_INT64) == 0) {
int64_getter = (gint64 (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
cm_i64 = int64_getter(rule->importEnt, cm_param);
i64_setter = (void(*)(QofEntity*, gint64))cm_param->param_setfcn;
if(i64_setter != NULL) { i64_setter(rule->targetEnt, cm_i64); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_DOUBLE) == 0) {
double_getter = (double (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
cm_double = double_getter(rule->importEnt, cm_param);
double_setter = (void(*)(QofEntity*, double))cm_param->param_setfcn;
if(double_setter != NULL) { double_setter(rule->targetEnt, cm_double); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_BOOLEAN) == 0){
boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
cm_boolean = boolean_getter(rule->importEnt, cm_param);
boolean_setter = (void(*)(QofEntity*, gboolean))cm_param->param_setfcn;
if(boolean_setter != NULL) { boolean_setter(rule->targetEnt, cm_boolean); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_KVP) == 0) {
cm_kvp = kvp_frame_copy(cm_param->param_getfcn(rule->importEnt,cm_param));
kvp_frame_setter = (void(*)(QofEntity*, KvpFrame*))cm_param->param_setfcn;
if(kvp_frame_setter != NULL) { kvp_frame_setter(rule->targetEnt, cm_kvp); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_CHAR) == 0) {
char_getter = (char (*)(QofEntity*, QofParam*)) cm_param->param_getfcn;
cm_char = char_getter(rule->importEnt,cm_param);
char_setter = (void(*)(QofEntity*, char))cm_param->param_setfcn;
if(char_setter != NULL) { char_setter(rule->targetEnt, cm_char); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_COLLECT) == 0) {
cm_coll = cm_param->param_getfcn(rule->importEnt, cm_param);
collection_setter = (void(*)(QofEntity*, QofCollection*))cm_param->param_setfcn;
if(collection_setter != NULL) { collection_setter(rule->targetEnt, cm_coll); }
registered_type = TRUE;
}
if(safe_strcmp(rule->mergeType, QOF_TYPE_CHOICE) == 0) {
referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
if(reference_setter != NULL)
{
reference_setter(rule->targetEnt, referenceEnt);
}
registered_type = TRUE;
}
if(registered_type == FALSE) {
referenceEnt = cm_param->param_getfcn(rule->importEnt, cm_param);
if(referenceEnt) {
reference_setter = (void(*)(QofEntity*, QofEntity*))cm_param->param_setfcn;
if(reference_setter != NULL)
{
reference_setter(rule->targetEnt, referenceEnt);
}
}
}
rule->mergeParam = g_slist_next(rule->mergeParam);
}
}
/* ================================================================ */
/* API functions. */
qof_book_mergeData*
qof_book_mergeInit( QofBook *importBook, QofBook *targetBook)
{
qof_book_mergeData *mergeData;
qof_book_mergeRule *currentRule;
GList *check;
g_return_val_if_fail((importBook != NULL)&&(targetBook != NULL), NULL);
mergeData = g_new(qof_book_mergeData, 1);
mergeData->abort = FALSE;
mergeData->mergeList = NULL;
mergeData->targetList = NULL;
mergeData->mergeBook = importBook;
mergeData->targetBook = targetBook;
mergeData->mergeObjectParams = NULL;
mergeData->orphan_list = NULL;
mergeData->target_table = g_hash_table_new( g_direct_hash, qof_book_merge_rule_cmp);
currentRule = g_new(qof_book_mergeRule, 1);
mergeData->currentRule = currentRule;
qof_object_foreach_type(qof_book_mergeForeachType, mergeData);
g_return_val_if_fail(mergeData->mergeObjectParams, NULL);
if(mergeData->orphan_list != NULL) {
qof_book_merge_match_orphans(mergeData);
}
check = g_list_copy(mergeData->mergeList);
while(check != NULL) {
currentRule = check->data;
if(currentRule->mergeResult == MERGE_INVALID) {
mergeData->abort = TRUE;
return(NULL);
}
check = g_list_next(check);
}
g_list_free(check);
return mergeData;
}
void
qof_book_merge_abort (qof_book_mergeData *mergeData)
{
qof_book_mergeRule *currentRule;
g_return_if_fail(mergeData != NULL);
while(mergeData->mergeList != NULL) {
currentRule = mergeData->mergeList->data;
g_slist_free(currentRule->linkedEntList);
g_slist_free(currentRule->mergeParam);
g_free(mergeData->mergeList->data);
if(currentRule) {
g_slist_free(currentRule->linkedEntList);
g_slist_free(currentRule->mergeParam);
g_free(currentRule);
}
mergeData->mergeList = g_list_next(mergeData->mergeList);
}
g_list_free(mergeData->mergeList);
g_slist_free(mergeData->mergeObjectParams);
g_slist_free(mergeData->targetList);
if(mergeData->orphan_list != NULL) { g_slist_free(mergeData->orphan_list); }
g_hash_table_destroy(mergeData->target_table);
g_free(mergeData);
}
/* The QOF_TYPE_DATE output format from
qof_book_merge_param_as_string has been changed to QSF_XSD_TIME,
a UTC formatted timestring: 2005-01-01T10:55:23Z
If you change QOF_UTC_DATE_FORMAT, change
backend/file/qsf-xml.c : qsf_entity_foreach to
reformat to QSF_XSD_TIME or the QSF XML will
FAIL the schema validation and QSF exports will become invalid.
The QOF_TYPE_BOOLEAN is lowercase for the same reason.
*/
char*
qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt)
{
gchar *param_string, param_date[QOF_DATE_STRING_LENGTH];
char param_sa[GUID_ENCODING_LENGTH + 1];
QofType paramType;
const GUID *param_guid;
time_t param_t;
gnc_numeric param_numeric, (*numeric_getter) (QofEntity*, QofParam*);
Timespec param_ts, (*date_getter) (QofEntity*, QofParam*);
double param_double, (*double_getter) (QofEntity*, QofParam*);
gboolean param_boolean, (*boolean_getter) (QofEntity*, QofParam*);
gint32 param_i32, (*int32_getter) (QofEntity*, QofParam*);
gint64 param_i64, (*int64_getter) (QofEntity*, QofParam*);
char param_char, (*char_getter) (QofEntity*, QofParam*);
param_string = NULL;
paramType = qtparam->param_type;
if(safe_strcmp(paramType, QOF_TYPE_STRING) == 0) {
param_string = g_strdup(qtparam->param_getfcn(qtEnt,qtparam));
if(param_string == NULL) { param_string = ""; }
return param_string;
}
if(safe_strcmp(paramType, QOF_TYPE_DATE) == 0) {
date_getter = (Timespec (*)(QofEntity*, QofParam*))qtparam->param_getfcn;
param_ts = date_getter(qtEnt, qtparam);
param_t = timespecToTime_t(param_ts);
strftime(param_date, QOF_DATE_STRING_LENGTH, QOF_UTC_DATE_FORMAT, gmtime(&param_t));
param_string = g_strdup(param_date);
return param_string;
}
if((safe_strcmp(paramType, QOF_TYPE_NUMERIC) == 0) ||
(safe_strcmp(paramType, QOF_TYPE_DEBCRED) == 0)) {
numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
param_numeric = numeric_getter(qtEnt,qtparam);
param_string = g_strdup(gnc_numeric_to_string(param_numeric));
return param_string;
}
if(safe_strcmp(paramType, QOF_TYPE_GUID) == 0) {
param_guid = qtparam->param_getfcn(qtEnt,qtparam);
guid_to_string_buff(param_guid, param_sa);
param_string = g_strdup(param_sa);
return param_string;
}
if(safe_strcmp(paramType, QOF_TYPE_INT32) == 0) {
int32_getter = (gint32 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
param_i32 = int32_getter(qtEnt, qtparam);
param_string = g_strdup_printf("%d", param_i32);
return param_string;
}
if(safe_strcmp(paramType, QOF_TYPE_INT64) == 0) {
int64_getter = (gint64 (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
param_i64 = int64_getter(qtEnt, qtparam);
param_string = g_strdup_printf("%" G_GINT64_FORMAT, param_i64);
return param_string;
}
if(safe_strcmp(paramType, QOF_TYPE_DOUBLE) == 0) {
double_getter = (double (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
param_double = double_getter(qtEnt, qtparam);
param_string = g_strdup_printf("%f", param_double);
return param_string;
}
if(safe_strcmp(paramType, QOF_TYPE_BOOLEAN) == 0){
boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
param_boolean = boolean_getter(qtEnt, qtparam);
/* Boolean values need to be lowercase for QSF validation. */
if(param_boolean == TRUE) { param_string = g_strdup("true"); }
else { param_string = g_strdup("false"); }
return param_string;
}
/* "kvp" contains repeating values, cannot be a single string for the frame. */
if(safe_strcmp(paramType, QOF_TYPE_KVP) == 0) { return param_string; }
if(safe_strcmp(paramType, QOF_TYPE_CHAR) == 0) {
char_getter = (char (*)(QofEntity*, QofParam*)) qtparam->param_getfcn;
param_char = char_getter(qtEnt, qtparam);
param_string = g_strdup_printf("%c", param_char);
return param_string;
}
return NULL;
}
qof_book_mergeData*
qof_book_mergeUpdateResult(qof_book_mergeData *mergeData,
qof_book_mergeResult tag)
{
qof_book_mergeRule *resolved;
g_return_val_if_fail((mergeData != NULL), NULL);
g_return_val_if_fail((tag > 0), NULL);
g_return_val_if_fail((tag != MERGE_REPORT), NULL);
resolved = mergeData->currentRule;
g_return_val_if_fail((resolved != NULL), NULL);
if((resolved->mergeAbsolute == TRUE)&&(tag == MERGE_DUPLICATE))
{
tag = MERGE_ABSOLUTE;
}
if((resolved->mergeAbsolute == TRUE)&&(tag == MERGE_NEW))
{
tag = MERGE_UPDATE;
}
if((resolved->mergeAbsolute == FALSE)&& (tag == MERGE_ABSOLUTE))
{
tag = MERGE_DUPLICATE;
}
if((resolved->mergeResult == MERGE_NEW)&&(tag == MERGE_UPDATE))
{
tag = MERGE_NEW;
}
if(resolved->updated == FALSE) { resolved->mergeResult = tag; }
resolved->updated = TRUE;
if(tag >= MERGE_INVALID) {
mergeData->abort = TRUE;
mergeData->currentRule = resolved;
return NULL;
}
mergeData->currentRule = resolved;
return mergeData;
}
int
qof_book_mergeCommit( qof_book_mergeData *mergeData )
{
qof_book_mergeRule *currentRule;
GList *check;
g_return_val_if_fail(mergeData != NULL, -1);
g_return_val_if_fail(mergeData->mergeList != NULL, -1);
g_return_val_if_fail(mergeData->targetBook != NULL, -1);
if(mergeData->abort == TRUE) return -1;
check = g_list_copy(mergeData->mergeList);
g_return_val_if_fail(check != NULL, -1);
while(check != NULL) {
currentRule = check->data;
if(currentRule->mergeResult == MERGE_INVALID) {
qof_book_merge_abort(mergeData);
return(-2);
}
if(currentRule->mergeResult == MERGE_REPORT) {
g_list_free(check);
return 1;
}
check = g_list_next(check);
}
qof_book_mergeCommitForeach( qof_book_mergeCommitRuleLoop, MERGE_NEW, mergeData);
qof_book_mergeCommitForeach( qof_book_mergeCommitRuleLoop, MERGE_UPDATE, mergeData);
/* Placeholder for QofObject merge_helper_cb - all objects and all parameters set */
while(mergeData->mergeList != NULL) {
currentRule = mergeData->mergeList->data;
g_slist_free(currentRule->mergeParam);
g_slist_free(currentRule->linkedEntList);
mergeData->mergeList = g_list_next(mergeData->mergeList);
}
g_list_free(mergeData->mergeList);
g_slist_free(mergeData->mergeObjectParams);
g_slist_free(mergeData->targetList);
if(mergeData->orphan_list != NULL) { g_slist_free(mergeData->orphan_list); }
g_hash_table_destroy(mergeData->target_table);
g_free(mergeData);
return 0;
}
void
qof_book_mergeRuleForeach( qof_book_mergeData *mergeData,
qof_book_mergeRuleForeachCB cb,
qof_book_mergeResult mergeResult )
{
struct qof_book_mergeRuleIterate iter;
qof_book_mergeRule *currentRule;
GList *matching_rules;
g_return_if_fail(cb != NULL);
g_return_if_fail(mergeData != NULL);
currentRule = mergeData->currentRule;
g_return_if_fail(mergeResult > 0);
g_return_if_fail(mergeResult != MERGE_INVALID);
g_return_if_fail(mergeData->abort == FALSE);
iter.fcn = cb;
iter.data = mergeData;
matching_rules = NULL;
iter.ruleList = g_list_copy(mergeData->mergeList);
while(iter.ruleList!=NULL) {
currentRule = iter.ruleList->data;
if(currentRule->mergeResult == mergeResult) {
matching_rules = g_list_prepend(matching_rules, currentRule);
}
iter.ruleList = g_list_next(iter.ruleList);
}
iter.remainder = g_list_length(matching_rules);
g_list_foreach (matching_rules, qof_book_mergeRuleCB, &iter);
g_list_free(matching_rules);
}
/* End of file. */
/* ==================================================================== */

View File

@ -1,461 +0,0 @@
/*********************************************************************
* qof_book_merge.h -- api for QofBook merge with collision handling *
* Copyright (C) 2004 Neil Williams <linux@codehelp.co.uk> *
* *
* 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 *
* *
********************************************************************/
#ifndef QOFBOOKMERGE_H
#define QOFBOOKMERGE_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define QOF_MOD_MERGE "qof-merge"
/** @addtogroup BookMerge
<b>Collision handling principles.</b>\n
\n
-# Always check for a ::GUID first and compare. qof_book_merge only accepts valid ::QofBook
data and therefore ALL objects in the import book will include valid GUID's.
-# If the original import data did not contain a GUID (e.g. an external non-GnuCash source)
the GUID values will have been created during the import and will not match any existing
GUID's in the target book so objects that do not have a GUID match cannot be assumed to
be ::MERGE_NEW - parameter values must be checked.
-# If import contains data from closed books, store the data from the closed
books in the current book as active. i.e. re-open the books.
- If a GUID match exists, set qof_book_mergeRule::mergeAbsolute to \a TRUE.
-# If ALL parameters in the import object match the target object with the same \a GUID,
set ::qof_book_mergeResult to \a MERGE_ABSOLUTE.
-# If any parameters differ, set ::MERGE_UPDATE.
- If the import object \a GUID does not match an existing object,
mergeAbsolute is unchanged from the default \a FALSE
The parameter values of the object are compared to other objects of the same
type in the target book.
-# If the same data exists in the target book with a different GUID, the object
is tagged as DUPLICATE.
-# If the data has changed, the object is tagged as REPORT.
-# If the data does not match, the object is tagged as NEW
More information is at http://code.neil.williamsleesmill.me.uk/
Each foreach function uses g_return_if_fail checks to protect the target book. If
any essential data is missing, the loop returns without changing the target book.
Note that this will not set or return an error value. However, g_return is only
used for critical errors that arise from programming errors, not for invalid import data
which should be cleaned up before creating the import QofBook.
Only ::qof_book_mergeUpdateResult and ::qof_book_mergeCommit return
any error values to the calling process. ::qof_book_mergeInit returns a
pointer to the ::qof_book_mergeData struct - the calling process needs to
make sure this is non-NULL to know that the Init has been successful.
@{
*/
/** @file qof_book_merge.h
@brief API for merging two \c QofBook structures with collision handling
@author Copyright (c) 2004-2005 Neil Williams <linux@codehelp.co.uk>
*/
#include <glib.h>
#include "gnc-engine-util.h"
#include "qofbook.h"
#include "qofclass.h"
#include "qofobject.h"
#include "qofinstance.h"
#include "gnc-trace.h"
/** \brief Results of collisions and user resolution.
All rules are initialised as ::MERGE_UNDEF.
Once the comparison is complete, each object within the import will be
updated.
::MERGE_ABSOLUTE, ::MERGE_NEW, ::MERGE_DUPLICATE and ::MERGE_UPDATE can be reported
to the user along with all ::MERGE_REPORT objects for confirmation.
It may be useful later to allow \a MERGE_ABSOLUTE, \a MERGE_NEW, \a MERGE_DUPLICATE and
\a MERGE_UPDATE to not be reported, if the user sets a preferences option
for each result. (Always accept new items: Y/N default NO, ignores all
MERGE_NEW if set to Y etc.) This option would not require any changes
to qof_book_merge.
\a MERGE_NEW, \a MERGE_DUPLICATE and \a MERGE_UPDATE are only actioned after
conflicts are resolved by the user using a dialog and all \a MERGE_REPORT objects are
re-assigned to one of MERGE_NEW, MERGE_DUPLICATE or MERGE_UPDATE. There is no automatic
merge, even if no entities are tagged as MERGE_REPORT, the calling process must still
check for REPORT items using ::qof_book_mergeRuleForeach and call ::qof_book_mergeCommit.
\a MERGE_INVALID data should be rare and allows for user-abort - the imported file/source
may be corrupted and the prescence of invalid data should raise concerns that
the rest of the data may be corrupted, damaged or otherwise altered. If any entity is
tagged as MERGE_INVALID, the merge operation will abort and leave the target book
completely unchanged.
\a MERGE_ABSOLUTE is only used for a complete match. The import object contains
the same data in the same parameters with no omissions or amendments. If any data is missing,
amended or added, the data is labelled \a MERGE_UPDATE.
Every piece of data has a corresponding result. Only when the count of items labelled
\a MERGE_REPORT is equal to zero are \a MERGE_NEW and \a MERGE_UPDATE
items added to the existing book.\n \a MERGE_DUPLICATE items are silently ignored.
Aborting the dialog/process (by the user or in a program crash) at any point before the
final commit leaves the existing book completely untouched.
*/
typedef enum {
MERGE_UNDEF, /**< default value before comparison is made. */
MERGE_ABSOLUTE, /**< GUID exact match, no new data - \b ignore */
MERGE_NEW, /**< import object does \b not exist in the target book - \b add */
MERGE_REPORT, /**< import object needs user intervention - \b report */
MERGE_DUPLICATE, /**< import object with different GUID exactly matches existing GUID - \b ignore */
MERGE_UPDATE, /**< import object matches an existing entity but includes new or
modified parameter data - \b update */
MERGE_INVALID /**< import object didn't match registered object or parameter
types or user decided to abort - \b abort */
}qof_book_mergeResult;
/** \brief One rule per entity, built into a single GList for the entire merge
All rules are stored in the GList qof_book_mergeData::mergeList.
If the ::GUID matches it's the always same semantic object,
regardless of whether other data fields are changed.
\n
The boolean value mergeAbsolute defaults to \c FALSE
NOTE 1: if mergeAbsolute == \c TRUE, ::qof_book_mergeResult will still be set to
::MERGE_UPDATE if parameters within this entity have been modified.
NOTE 2: ::qof_book_merge_param_as_string returns \b string representations of the parameter
data that is causing a collision. These values must \b NOT be used to set the target
parameter - the function is provided for display purposes only, to make it simple to
explain the collision to the user using MERGE_REPORT and the dialog.
The GHashTable targetTable in qof_book_mergeRule will probably replace the GSList of the
same name in mergeData.
*/
typedef struct
{
/* internal counters and reference variables */
gboolean mergeAbsolute; /**< Only set if the GUID of the import matches the target */
double difference; /**< used to find best match in a book where no GUID matches */
gboolean updated; /**< prevent the mergeResult from being overwritten. */
/* rule objects set from or by external calls */
QofIdType mergeType; /**< type of comparison required for check for collision */
const char* mergeLabel; /**< Descriptive label for the object type, useful for the
user intervention dialog. */
GSList *mergeParam; /**< list of usable parameters for the object type */
GSList *linkedEntList; /**< list of complex data types included in this object.
linkedEntList contains an ::QofEntity reference to any parameter that is not
one of the core QOF_TYPE data types. This entity must be already registered with QOF
and the results of the comparison for the linked entity will modulate the mergeResult
of this object. e.g. if an invoice is the same value but for a different customer,
the invoice will be set to MERGE_REPORT and the customer as MERGE_NEW.
*/
qof_book_mergeResult mergeResult; /**< result of comparison with main ::QofBook */
QofEntity *importEnt; /**< pointer to the current entity in the import book. */
QofEntity *targetEnt; /**< pointer to the corresponding entity in the target book, if any. */
}qof_book_mergeRule;
/** \brief mergeData contains the essential context data for any merge.
Used to dictate what to merge, how to merge it, where to get the new data and
where to put the amended data.
Combines lists of \a ::QofParam, \a ::QofEntity and \a ::qof_book_mergeRule into one struct that
can be easily passed between callbacks. Also holds the pointers to the import and target ::QofBook
structures.
- targetList and mergeObjectParams change each time a new object type is set for compare.
- mergeList is the complete list of rules for all objects in the import book.
*/
typedef struct
{
GSList *mergeObjectParams; /**< GSList of ::QofParam details for each parameter in the current object. */
GList *mergeList; /**< GList of all ::qof_book_mergeRule rules for the merge operation. */
GSList *targetList; /**< GSList of ::QofEntity * for each object of this type in the target book */
QofBook *mergeBook; /**< pointer to the import book for this merge operation. */
QofBook *targetBook; /**< pointer to the target book for this merge operation. */
gboolean abort; /**< set to TRUE if MERGE_INVALID is set. */
qof_book_mergeRule *currentRule; /**< placeholder for the rule currently being tested or applied. */
GSList *orphan_list; /**< List of QofEntity's that need to be rematched.
When one QofEntity has a lower difference to the targetEnt than the previous best_match,
the new match takes precedence. This list holds those orphaned entities that are not a good
enough match so that these can be rematched later. The ranking is handled using
the private qof_entity_rating struct and the GHashTable ::qof_book_mergeData::target_table.
*/
GHashTable *target_table; /**< The GHashTable to hold the qof_entity_rating values. */
}qof_book_mergeData;
/* ======================================================================== */
/** @name qof_book_merge API
@{
*/
/** \brief Initialise the qof_book_merge process
First function of the qof_book_merge API. Every merge must begin with Init.
Requires the book to import (::QofBook *) and the book to receive the import, the target book
(::QofBook *). Returns a pointer to ::qof_book_mergeData which must be checked for a
NULL before continuing. \n
Process:
-# Invoke the callback ::qof_book_mergeForeachType on every registered object class definition.
-# Callback obtains the registered parameter list for each object type. This provides run time
access to all registered objects and all object parameters without any changes to
qof_book_merge - no registered object or parameter is omitted from any merge operation.
-# Use ::qof_object_foreach to invoke the callback ::qof_book_mergeForeach, one object at a time
on every instance stored in mergeBook. This is the first point where real data from the import
book is accessed.
-# qof_book_mergeForeach obtains the ::GUID for the object from the import book and runs the first
check on the original book, checking for any exact GUID match. With the full parameter list,
the rules for this object can be created. If there is a GUID match, the data in each parameter
of the import object is compared with the same semantic object in the original book. If there is
no GUID in the import object or no GUID match with the original book, the original book is
searched to find a parameter match - checking for a ::MERGE_DUPLICATE result.
-# ::qof_book_mergeCompare sets the ::qof_book_mergeResult of the comparison.
-# Inserts the completed rule into qof_book_mergeData::mergeList GSList.
\return NULL in case of error, otherwise a ::qof_book_mergeData* metadata context.
*/
qof_book_mergeData*
qof_book_mergeInit( QofBook *importBook, QofBook *targetBook);
/** \brief Definition of the dialog control callback routine
All ::MERGE_REPORT rules must be offered for user intervention using this template.\n
Commit will fail if any rules are still tagged as \a MERGE_REPORT.
Calling processes are free to also offer MERGE_NEW, MERGE_UPDATE, MERGE_DUPLICATE and
MERGE_ABSOLUTE for user intervention. Attempting to query MERGE_INVALID rules
will cause an error.
For an example, consider test_rule_loop, declared as:
<tt>void test_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder);\n
void test_rule_loop(qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder) \n
{\n
g_return_if_fail(rule != NULL);\n
g_return_if_fail(mergeData != NULL);
printf("Rule Result %s", rule->mergeType);\n
qof_book_mergeUpdateResult(mergeData, rule, MERGE_UPDATE);\n
}</tt>
The dialog is free to call ::qof_book_mergeUpdateResult in the loop or at the end
as long as the link between the rule and the result is maintained, e.g. by using a
GHashTable.
\n
The parameters are:
- data : pointer to the ::qof_book_mergeData metadata context returned by Init.
- rule : pointer to the ::qof_book_mergeRule that generated the collision report
- remainder : guint value returned from g_slist_length for the number of other
rules remaining with the same result. This might be useful for a progress dialog, it might not.
When updating MERGE_REPORT, remainder must equal zero before calling
::qof_book_mergeCommit or the import will abort.
\n
If the dialog sets \b any rule result to ::MERGE_INVALID, the import will abort when
::qof_book_mergeCommit is called. It is the responsibility of the calling function to
handle the error code from ::qof_book_mergeCommit, close the dialog and return.
The merge routines in these files will already have halted the merge operation and
freed any memory allocated to merge structures before returning the error code.
There is no need for the dialog process to report back to qof_book_merge in this situation.
*/
typedef void (* qof_book_mergeRuleForeachCB)( qof_book_mergeData*, qof_book_mergeRule*, guint);
/** \brief Dialog Control Callback
This function is designed to be used to iterate over all rules tagged with a specific
::qof_book_mergeResult value.
@param callback external loop of type qof_book_mergeRuleForeachCB
@param mergeResult ::qof_book_mergeResult value to look up.
@param mergeData ::qof_book_mergeData merge context.
\b Note : MERGE_NEW causes a new entity to be created in the target book at Commit
which is then assigned as the targetEnt of that rule. If mergeResult == MERGE_NEW,
the rules returned by qof_book_mergeRuleForeach will have a NULL set for the targetEnt.
This is because Commit has not yet been called and no changes can be made to the target
book. The calling process must handle the NULL targetEnt and NOT call any param_getfcn
routines for the target entity. The import entity is available for display.
Uses ::qof_book_get_collection with the qof_book_mergeRule::mergeType object type to
return a collection of ::QofEntity entities from either the qof_book_mergeData::mergeBook or
qof_book_mergeData::targetBook. Then uses ::qof_collection_lookup_entity to lookup
the qof_book_mergeRule::importEnt and again the qof_book_mergeRule::targetEnt to
return the two specific entities.
*/
void qof_book_mergeRuleForeach( qof_book_mergeData* mergeData,
qof_book_mergeRuleForeachCB callback ,
qof_book_mergeResult mergeResult);
/** \brief provides easy string access to parameter data for dialog use
Uses the param_getfcn to retrieve the parameter value as a string, suitable for
display in dialogs and user intervention output. Within a qof_book_merge context,
only the parameters used in the merge are available, i.e. parameters where both
param_getfcn and param_setfcn are not NULL.
Note that the object type description (a full text version of the object name) is
also available to the dialog as qof_book_mergeRule::mergeLabel.
This allows the dialog to display the description of the object and all parameter data.
*/
char* qof_book_merge_param_as_string(QofParam *qtparam, QofEntity *qtEnt);
/** \brief called by dialog callback to set the result of user intervention
Set \b any rule result to ::MERGE_INVALID to abort the import when
::qof_book_mergeCommit is called, without changing the target book.
The calling process should make it absolutely clear that a merge operation
\b cannot be undone and that a backup copy should always be available
\b before a merge is initialised.
Recommended method: Only offer three options to the user per rule:
-# Allow import data to be merged into target data
- change MERGE_REPORT to MERGE_UPDATE
-# Allow import data without an exact match to be
added as new
- change MERGE_REPORT to MERGE_NEW \b IF mergeAbsolute = FALSE
-# Ignore import data and leave target data unchanged
- change MERGE_REPORT to MERGE_ABSOLUTE or MERGE_DUPLICATE
Handle the required result changes in code: Check the value of
qof_book_mergeRule::mergeAbsolute and use these principles:
To ignore entities tagged as:
- MERGE_REPORT, you must check the value of mergeAbsolute.
- if mergeAbsolute is TRUE, change MERGE_REPORT to MERGE_ABSOLUTE
- if mergeAbsolute is FALSE, change MERGE_REPORT to MERGE_DUPLICATE
- MERGE_NEW, set MERGE_DUPLICATE.
- MERGE_UPDATE, you must check the value of mergeAbsolute.
- if mergeAbsolute is TRUE, change MERGE_UPDATE to MERGE_ABSOLUTE
- if mergeAbsolute is FALSE, change MERGE_UPDATE to MERGE_DUPLICATE
To merge entities that are not pre-set to MERGE_NEW, set MERGE_UPDATE.\n
Attempting to merge an entity when the pre-set value was MERGE_NEW will
force a change back to MERGE_NEW because no suitable target exists for the
merge.
To add entities, check mergeAbsolute is FALSE and set MERGE_NEW.\n
An entity \b only be added if mergeAbsolute is FALSE. Attempting to
add an entity when mergeAbsolute is TRUE will always force a MERGE_UPDATE.
It is not possible to update the same rule more than once.
-# \b MERGE_NEW is reserved for new objects and is only pre-set if
all parameters, including GUID, have already failed to match any
relevant object. ::qof_book_mergeCommit will create new
entities for all rules tagged as MERGE_NEW.
- if mergeAbsolute is TRUE and the user wants to import the
data, requests to set MERGE_NEW will be forced to MERGE_UPDATE
because an entity with that GUID already exists in the target book.
- if MERGE_NEW is pre-set, requests to change to MERGE_UPDATE will be
ignored because a new entity is needed.
-# \b MERGE_UPDATE is reserved for existing objects - ::qof_book_mergeCommit
will require a matching entity to update and will force a change to back to
MERGE_NEW if none is known to exist, using the principle above.
-# \b MERGE_INVALID will cause an abort of the merge process.
-# \b MERGE_UNDEF and \b MERGE_REPORT cannot be set - the entity result will be unchanged.
-# \b MERGE_DUPLICATE and \b MERGE_ABSOLUTE are handled identically but are semantically
different - qof_book_mergeRule::mergeAbsolute is used to dictate which to set:
- if mergeAbsolute is TRUE but MERGE_DUPLICATE is requested,
force a change to MERGE_ABSOLUTE.
- if mergeAbsolute is FALSE but MERGE_ABSOLUTE is requested,
force a change to MERGE_DUPLICATE.
::qof_book_mergeCommit only commits entities tagged
with MERGE_NEW and MERGE_UPDATE results.
\n
Entities tagged with MERGE_ABSOLUTE and MERGE_DUPLICATE results are ignored.
The calling process must check the return value and call
::qof_book_merge_abort(mergeData) if non-zero.
@param mergeData the merge context, ::qof_book_mergeData*
@param tag the result to attempt to set, ::qof_book_mergeResult
\return -1 if supplied parameters are invalid or NULL, 0 on success.
*/
qof_book_mergeData*
qof_book_mergeUpdateResult(qof_book_mergeData *mergeData, qof_book_mergeResult tag);
/** \brief Commits the import data to the target book
The last function in the API and the final part of any qof_book_merge operation.
qof_book_mergeCommit will abort the \b entire merge operation if any rule is set to
::MERGE_INVALID. It is the responsibility of the calling
function to handle the error code from ::qof_book_mergeCommit, close the dialog
and return. qof_book_mergeCommit will already have halted the merge
operation and freed any memory allocated to all merge structures before returning the error
code. There is no way for the dialog process to report back to qof_book_merge in this situation.
qof_book_mergeCommit checks for any entities still tagged as ::MERGE_REPORT and then proceeds
to import all entities tagged as ::MERGE_UPDATE or ::MERGE_NEW into the target book.
\n
<b>This final process cannot be UNDONE!</b>\n
\n
@param mergeData the merge context, ::qof_book_mergeData*
\return
- -2 if any rules are tagged as ::MERGE_INVALID
- mergeData will have been g_free'd).
- note that this will be before any operations are done on the target
QofBook.
- -1 if mergeData is invalid or no merge has been initialised with
::qof_book_mergeInit - the calling process must check the value of mergeData
- +1 if some entities are still tagged as \a MERGE_REPORT - use
::qof_book_mergeUpdateRule and try again (mergeData is retained).
- 0 on success - mergeData will have been freed.
*/
int
qof_book_mergeCommit( qof_book_mergeData *mergeData );
/** \brief Abort the merge and free all memory allocated by the merge
Sometimes, setting ::MERGE_INVALID is insufficient: e.g. if the user aborts the
merge from outside the functions dealing with the merge ruleset. This function
causes an immediate abort - the calling process must start again at Init if
a new merge is required.
*/
void
qof_book_merge_abort(qof_book_mergeData *mergeData);
#endif // QOFBOOKMERGE_H
/** @} */
/** @} */

View File

@ -1,410 +0,0 @@
/********************************************************************\
* qofbackend-p.h -- private api for data storage backend *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Object_Private
Private interfaces, not meant to be used by applications.
@{ */
/** @name Backend_Private
Pseudo-object defining how the engine can interact with different
back-ends (which may be SQL databases, or network interfaces to
remote GnuCash servers. File-io is just one type of backend).
The callbacks will be called at the appropriate times during
a book session to allow the backend to store the data as needed.
@file qofbackend-p.h
@brief private api for data storage backend
@author Copyright (c) 2000,2001,2004 Linas Vepstas <linas@linas.org>
@author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
@{ */
#ifndef QOF_BACKEND_P_H
#define QOF_BACKEND_P_H
#include "config.h"
#include "qof-be-utils.h"
#include "qofbackend.h"
#include "qofbook.h"
#include "qofinstance-p.h"
#include "qofquery.h"
#include "qofsession.h"
/**
* The session_begin() routine gives the backend a second initialization
* opportunity. It is suggested that the backend check that
* the URL is syntactically correct, and that it is actually
* reachable. This is probably(?) a good time to initialize
* the actual network connection.
*
* The 'ignore_lock' argument indicates whether the single-user
* lock on the backend should be cleared. The typical GUI sequence
* leading to this is: (1) GUI attempts to open the backend
* by calling this routine with FALSE==ignore_lock. (2) If backend
* error'ed BACKEND_LOCK, then GUI asks user what to do. (3) if user
* answers 'break & enter' then this routine is called again with
* TRUE==ignore_lock.
*
* The 'create_if_nonexistent' argument indicates whether this
* routine should create a new 'database', if it doesn't already
* exist. For example, for a file-backend, this would create the
* file, if it didn't already exist. For an SQL backend, this
* would create the database (the schema) if it didn't already
* exist. This flag is used to implement the 'SaveAs' GUI, where
* the user requests to save data to a new backend.
*
* The load() routine should load the minimal set of application data
* needed for the application to be operable at initial startup.
* It is assumed that the application will perform a 'run_query()'
* to obtain any additional data that it needs. For file-based
* backends, it is acceptable for the backend to return all data
* at load time; for SQL-based backends, it is acceptable for the
* backend to return no data.
*
* Thus, for example, for GnuCash, the postrges backend returns
* the account tree, all currencies, and the pricedb, as these
* are needed at startup. It does not have to return any
* transactions whatsoever, as these are obtained at a later stage
* when a user opens a register, resulting in a query being sent to
* the backend.
*
* (Its OK to send over transactions at this point, but one should
* be careful of the network load; also, its possible that whatever
* is sent is not what the user wanted anyway, which is why its
* better to wait for the query).
*
* The begin() routine is called when the engine is about to
* make a change to a data structure. It can provide an advisory
* lock on data.
*
* The commit() routine commits the changes from the engine to the
* backend data storage.
*
* The rollback() routine is used to revert changes in the engine
* and unlock the backend. For transactions it is invoked in one
* of two different ways. In one case, the user may hit 'undo' in
* the GUI, resulting in xaccTransRollback() being called, which in
* turn calls this routine. In this manner, xaccTransRollback()
* implements a single-level undo convenience routine for the GUI.
* The other way in which this routine gets invoked involves
* conflicting edits by two users to the same transaction. The
* second user to make an edit will typically fail in
* trans_commit_edit(), with trans_commit_edit() returning an error
* code. This causes xaccTransCommitEdit() to call
* xaccTransRollback() which in turn calls this routine. Thus,
* this routine gives the backend a chance to clean up failed
* commits.
*
* If the second user tries to modify a transaction that
* the first user deleted, then the backend should set the error
* to ERR_BACKEND_MOD_DESTROY from this routine, so that the
* engine can properly clean up.
*
* The compile_query() method compiles a Gnucash query object into
* a backend-specific data structure and returns the compiled
* query. For an SQL backend, the contents of the query object
* need to be turned into a corresponding SQL query statement, and
* sent to the database for evaluation.
*
* The free_query() method frees the data structure returned from
* compile_query()
*
* The run_query() callback takes a compiled query (generated by
* compile_query) and runs the query in across the backend,
* inserting the responses into the engine. The database will
* return a set of splits and transactions, and this callback needs
* to poke these into the account-group hierarchy held by the query
* object.
*
* For a network-communications backend, essentially the same is
* done, except that this routine would convert the query to wire
* protocol, get an answer from the remote server, and push that
* into the account-group object.
*
* Note a peculiar design decision we've used here. The query
* callback has returned a list of splits; these could be returned
* directly to the caller. They are not. By poking them into the
* existing account hierarchy, we are essentially building a local
* cache of the split data. This will allow the GnuCash client to
* continue functioning even when disconnected from the server:
* this is because it will have its local cache of data to work from.
*
* The sync() routine synchronizes the engine contents to the backend.
* This is done by using version numbers (hack alert -- the engine
* does not currently contain version numbers).
* If the engine contents are newer than what's in the backend, the
* data is stored to the backend. If the engine contents are older,
* then the engine contents are updated.
*
* Note that this sync operation is only meant to apply to the
* current contents of the engine. This routine is not intended
* to be used to fetch account/transaction data from the backend.
* (It might pull new splits from the backend, if this is what is
* needed to update an existing transaction. It might pull new
* currencies (??))
*
* The counter() routine increments the named counter and returns the
* post-incremented value. Returns -1 if there is a problem.
*
* The events_pending() routines should return true if there are
* external events which need to be processed to bring the
* engine up to date with the backend.
*
* The process_events() routine should process any events indicated
* by the events_pending() routine. It should return TRUE if
* the engine was changed while engine events were suspended.
*
* The last_err member indicates the last error that occurred.
* It should probably be implemented as an array (actually,
* a stack) of all the errors that have occurred.
*
* For support of book partitioning, use special "Book" begin_edit()
* and commit_edit() QOF_ID types.
*
* Call the book begin() at the begining of a book partitioning. A
* 'partitioning' is the splitting off of a chunk of the current
* book into a second book by means of a query. Every transaction
* in that query is to be moved ('transfered') to the second book
* from the existing book. The argument of this routine is a
* pointer to the second book, where the results of the query
* should go.
*
* Cann the book commit() to complete the book partitioning.
*
* After the begin(), there will be a call to run_query(), followed
* probably by a string of account and transaction calls, and
* completed by commit(). It should be explicitly understood that
* the results of that run_query() precisely constitute the set of
* transactions that are to be moved between the initial and the
* new book. This specification can be used by a clever backend to
* avoid excess data movement between the server and the gnucash
* client, as explained below.
*
* There are several possible ways in which a backend may choose to
* implement the book splitting process. A 'file-type' backend may
* choose to ignore this call, and the subsequent query, and simply
* write out the new book to a file when the commit() call is made.
* By that point, the engine will have performed all of the
* nitty-gritty of moving transactions from one book to the other.
*
* A 'database-type' backend has several interesting choices. One
* simple choice is to simply perform the run_query() as it
* normally would, and likewise treat the account and transaction
* edits as usual. In this scenario, the commit() is more or less
* a no-op. This implementation has a drawback, however: the
* run_query() may cause the transfer of a *huge* amount of data
* between the backend and the engine. For a large dataset, this
* is quite undesirable. In addition, there are risks associated
* with the loss of network connectivity during the transfer; thus
* a partition might terminate half-finished, in some indeterminate
* state, due to network errors. That might be difficult to
* recover from: the engine does not take any special transactional
* safety measures during the transfer.
*
* Thus, for a large database, an alternate implementation
* might be to use the run_query() call as an opportunity to
* transfer transactions between the two books in the database,
* and not actually return any new data to the engine. In
* this scenario, the engine will attempt to transfer those
* transactions that it does know about. It does not, however,
* need to know about all the other transactions that also would
* be transfered over. In this way, a backend could perform
* a mass transfer of transactions between books without having
* to actually move much (or any) data to the engine.
*
*
* To support configuration options from the frontend, the backend
* can be passed a GHashTable - according to the allowed options
* for that backend, using load_config(). Configuration can be
* updated at any point - it is up to the frontend to load the
* data in time for whatever the backend needs to do. e.g. an
* option to save a new book in a compressed format need not be
* loaded until the backend is about to save. If the configuration
* is updated by the user, the frontend should call load_config
* again to update the backend.
*/
struct QofBackendProvider_s
{
/** Some arbitrary name given for this particular backend provider */
const char * provider_name;
/** The access method that this provider provides, for example,
* http:// or postgres:// or rpc://, but without the :// at the end
*/
const char * access_method;
/** \brief Partial QofBook handler
TRUE if the backend handles external references
to entities outside this book and can save a QofBook that
does not contain any specific QOF objects.
*/
gboolean partial_book_supported;
/** Return a new, initialized backend backend. */
QofBackend * (*backend_new) (void);
/** \brief Distinguish two providers with same access method.
More than 1 backend can be registered under the same access_method,
so each one is passed the path to the data (e.g. a file) and
should return TRUE only:
-# if the backend recognises the type as one that it can load and write or
-# if the path contains no data but can be used (e.g. a new session).
\note If the backend can cope with more than one type, the backend
should not try to store or cache the sub-type for this data.
It is sufficient only to return TRUE if any ONE of the supported
types match the incoming data. The backend should not assume that
returning TRUE will mean that the data will naturally follow.
*/
gboolean (*check_data_type) (const char*);
/** Free this structure, unregister this backend handler. */
void (*provider_free) (QofBackendProvider *);
};
struct QofBackend_s
{
void (*session_begin) (QofBackend *be,
QofSession *session,
const char *book_id,
gboolean ignore_lock,
gboolean create_if_nonexistent);
void (*session_end) (QofBackend *);
void (*destroy_backend) (QofBackend *);
void (*load) (QofBackend *, QofBook *);
void (*begin) (QofBackend *, QofInstance *);
void (*commit) (QofBackend *, QofInstance *);
void (*rollback) (QofBackend *, QofInstance *);
gpointer (*compile_query) (QofBackend *, QofQuery *);
void (*free_query) (QofBackend *, gpointer);
void (*run_query) (QofBackend *, gpointer);
void (*sync) (QofBackend *, QofBook *);
void (*load_config) (QofBackend *, KvpFrame *);
KvpFrame* (*get_config) (QofBackend *);
gint64 (*counter) (QofBackend *, const char *counter_name);
gboolean (*events_pending) (QofBackend *);
gboolean (*process_events) (QofBackend *);
QofBePercentageFunc percentage;
QofBackendProvider *provider;
/** Document Me !!! what is this supposed to do ?? */
gboolean (*save_may_clobber_data) (QofBackend *);
QofBackendError last_err;
char * error_msg;
KvpFrame* backend_configuration;
gint config_count;
/** Each backend resolves a fully-qualified file path.
* This holds the filepath and communicates it to the frontends.
*/
char * fullpath;
#ifdef GNUCASH_MAJOR_VERSION
/** XXX price_lookup should be removed during the redesign
* of the SQL backend... prices can now be queried using
* the generic query mechanism.
*
* Note the correct signature for this call is
* void (*price_lookup) (QofBackend *, GNCPriceLookup *);
* we use gpointer to avoid an unwanted include file dependency.
*/
void (*price_lookup) (QofBackend *, gpointer);
/** XXX Export should really _NOT_ be here, but is left here for now.
* I'm not sure where this should be going to. It should be
* removed ASAP. This is a temporary hack-around until period-closing
* is fully implemented.
*/
void (*export) (QofBackend *, QofBook *);
#endif
};
/** Let the sytem know about a new provider of backends. This function
* is typically called by the provider library at library load time.
* This function allows the backend library to tell the QOF infrastructure
* that it can handle URL's of a certain type. Note that a single
* backend library may register more than one provider, if it is
* capable of handling more than one URL access method.
*/
void qof_backend_register_provider (QofBackendProvider *);
/** The qof_backend_set_error() routine pushes an error code onto the error
* stack. (FIXME: the stack is 1 deep in current implementation).
*/
void qof_backend_set_error (QofBackend *be, QofBackendError err);
/** The qof_backend_get_error() routine pops an error code off the error stack.
*/
QofBackendError qof_backend_get_error (QofBackend *be);
/** The qof_backend_set_message() assigns a string to the backend error message.
*/
void qof_backend_set_message(QofBackend *be, const char *format, ...);
/** The qof_backend_get_message() pops the error message string from
* the Backend. This string should be freed with g_free().
*/
char * qof_backend_get_message(QofBackend *be);
void qof_backend_init(QofBackend *be);
/** Allow backends to see if the book is open
@return 'y' if book is open, otherwise 'n'.
*/
gchar qof_book_get_open_marker(QofBook *book);
/** get the book version
used for tracking multiuser updates in backends.
@return -1 if no book exists, 0 if the book is
new, otherwise the book version number.
*/
gint32 qof_book_get_version (QofBook *book);
/** get the book tag number
used for kvp management in sql backends.
*/
guint32 qof_book_get_idata (QofBook *book);
void qof_book_set_version (QofBook *book, gint32 version);
void qof_book_set_idata(QofBook *book, guint32 idata);
/* @} */
/* @} */
/* @} */
#endif /* QOF_BACKEND_P_H */

View File

@ -1,427 +0,0 @@
/********************************************************************\
* qofbackend.c -- utility routines for dealing with the data backend *
* Copyright (C) 2000 Linas Vepstas <linas@linas.org> *
* Copyright (C) 2004-5 Neil Williams <linux@codehelp.co.uk> *
* *
* 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 *
* *
\********************************************************************/
#define _GNU_SOURCE
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <regex.h>
#include <glib.h>
#include <gmodule.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <errno.h>
#include "qofbackend-p.h"
static QofLogModule log_module = QOF_MOD_BACKEND;
#define QOF_CONFIG_DESC "desc"
#define QOF_CONFIG_TIP "tip"
/********************************************************************\
* error handling *
\********************************************************************/
void
qof_backend_set_error (QofBackend *be, QofBackendError err)
{
if (!be) return;
/* use stack-push semantics. Only the earliest error counts */
if (ERR_BACKEND_NO_ERR != be->last_err) return;
be->last_err = err;
}
QofBackendError
qof_backend_get_error (QofBackend *be)
{
QofBackendError err;
if (!be) return ERR_BACKEND_NO_BACKEND;
/* use 'stack-pop' semantics */
err = be->last_err;
be->last_err = ERR_BACKEND_NO_ERR;
return err;
}
void
qof_backend_set_message (QofBackend *be, const char *format, ...)
{
va_list args;
char * buffer;
if (!be) return;
/* If there's already something here, free it */
if (be->error_msg) g_free(be->error_msg);
if (!format) {
be->error_msg = NULL;
return;
}
va_start(args, format);
buffer = (char *)g_strdup_vprintf(format, args);
va_end(args);
be->error_msg = buffer;
}
/* This should always return a valid char * */
char *
qof_backend_get_message (QofBackend *be)
{
char * msg;
if (!be) return g_strdup("ERR_BACKEND_NO_BACKEND");
if (!be->error_msg) return NULL;
/*
* Just return the contents of the error_msg and then set it to
* NULL. This is necessary, because the Backends don't seem to
* have a destroy_backend function to take care if freeing stuff
* up. The calling function should free the copy.
* Also, this is consistent with the qof_backend_get_error() popping.
*/
msg = be->error_msg;
be->error_msg = NULL;
return msg;
}
/***********************************************************************/
/* Get a clean backend */
void
qof_backend_init(QofBackend *be)
{
be->session_begin = NULL;
be->session_end = NULL;
be->destroy_backend = NULL;
be->load = NULL;
be->begin = NULL;
be->commit = NULL;
be->rollback = NULL;
be->compile_query = NULL;
be->free_query = NULL;
be->run_query = NULL;
be->sync = NULL;
be->load_config = NULL;
be->events_pending = NULL;
be->process_events = NULL;
be->last_err = ERR_BACKEND_NO_ERR;
if (be->error_msg) g_free (be->error_msg);
be->error_msg = NULL;
be->percentage = NULL;
be->backend_configuration = kvp_frame_new();
#ifdef GNUCASH_MAJOR_VERSION
/* XXX remove these */
be->fullpath = NULL;
be->price_lookup = NULL;
be->export = NULL;
#endif
}
void
qof_backend_run_begin(QofBackend *be, QofInstance *inst)
{
if(!be || !inst) { return; }
if(!be->begin) { return; }
(be->begin) (be, inst);
}
gboolean
qof_backend_begin_exists(QofBackend *be)
{
if(be->begin) { return TRUE; }
else { return FALSE; }
}
void
qof_backend_run_commit(QofBackend *be, QofInstance *inst)
{
if(!be || !inst) { return; }
if(!be->commit) { return; }
(be->commit) (be, inst);
}
/* =========== Backend Configuration ================ */
void qof_backend_prepare_frame(QofBackend *be)
{
g_return_if_fail(be);
if(!kvp_frame_is_empty(be->backend_configuration)) {
kvp_frame_delete(be->backend_configuration);
be->backend_configuration = kvp_frame_new();
}
be->config_count = 0;
}
void qof_backend_prepare_option(QofBackend *be, QofBackendOption *option)
{
KvpValue *value;
gchar *temp;
gint count;
g_return_if_fail(be || option);
count = be->config_count;
count++;
value = NULL;
ENTER (" %d", count);
switch (option->type)
{
case KVP_TYPE_GINT64 : {
value = kvp_value_new_gint64(*(gint64*)option->value);
break;
}
case KVP_TYPE_DOUBLE : {
value = kvp_value_new_double(*(double*)option->value);
break;
}
case KVP_TYPE_NUMERIC : {
value = kvp_value_new_numeric(*(gnc_numeric*)option->value);
break;
}
case KVP_TYPE_STRING : {
value = kvp_value_new_string((const char*)option->value);
break;
}
case KVP_TYPE_GUID : { break; } /* unsupported */
case KVP_TYPE_TIMESPEC : {
value = kvp_value_new_timespec(*(Timespec*)option->value);
break;
}
case KVP_TYPE_BINARY : { break; } /* unsupported */
case KVP_TYPE_GLIST : { break; } /* unsupported */
case KVP_TYPE_FRAME : { break; } /* unsupported */
}
if(value) {
temp = g_strdup_printf("/%s", option->option_name);
kvp_frame_set_value(be->backend_configuration, temp, value);
PINFO (" setting value at %s", temp);
g_free(temp);
temp = g_strdup_printf("/%s/%s", QOF_CONFIG_DESC, option->option_name);
PINFO (" setting description %s at %s", option->description, temp);
kvp_frame_set_string(be->backend_configuration, temp, option->description);
PINFO (" check= %s", kvp_frame_get_string(be->backend_configuration, temp));
g_free(temp);
temp = g_strdup_printf("/%s/%s", QOF_CONFIG_TIP, option->option_name);
PINFO (" setting tooltip %s at %s", option->tooltip, temp);
kvp_frame_set_string(be->backend_configuration, temp, option->tooltip);
PINFO (" check= %s", kvp_frame_get_string(be->backend_configuration, temp));
g_free(temp);
/* only increment the counter if successful */
be->config_count = count;
}
LEAVE (" ");
}
KvpFrame* qof_backend_complete_frame(QofBackend *be)
{
g_return_val_if_fail(be, NULL);
be->config_count = 0;
return be->backend_configuration;
}
struct config_iterate {
QofBackendOptionCB fcn;
gpointer data;
gint count;
KvpFrame *recursive;
};
static void
config_foreach_cb (const char *key, KvpValue *value, gpointer data)
{
QofBackendOption option;
gint64 int64;
double db;
gnc_numeric num;
Timespec ts;
gchar *parent;
struct config_iterate *helper;
g_return_if_fail(key || value || data);
helper = (struct config_iterate*)data;
if(!helper->recursive) { PERR (" no parent frame"); return; }
// skip the presets.
if(0 == safe_strcmp(key, QOF_CONFIG_DESC)) { return; }
if(0 == safe_strcmp(key, QOF_CONFIG_TIP)) { return; }
ENTER (" key=%s", key);
option.option_name = key;
option.type = kvp_value_get_type(value);
if(!option.type) { return; }
switch (option.type)
{
case KVP_TYPE_GINT64 : {
int64 = kvp_value_get_gint64(value);
option.value = (gpointer)&int64;
break;
}
case KVP_TYPE_DOUBLE : {
db = kvp_value_get_double(value);
option.value = (gpointer)&db;
break;
}
case KVP_TYPE_NUMERIC : {
num = kvp_value_get_numeric(value);
option.value = (gpointer)&num;
break;
}
case KVP_TYPE_STRING : {
option.value = (gpointer)kvp_value_get_string(value);
break;
}
case KVP_TYPE_GUID : { break; } /* unsupported */
case KVP_TYPE_TIMESPEC : {
ts = kvp_value_get_timespec(value);
option.value = (gpointer)&ts;
break;
}
case KVP_TYPE_BINARY : { break; } /* unsupported */
case KVP_TYPE_GLIST : { break; } /* unsupported */
case KVP_TYPE_FRAME : { break; } /* unsupported */
}
parent = g_strdup_printf("/%s/%s", QOF_CONFIG_DESC, key);
option.description = kvp_frame_get_string(helper->recursive, parent);
g_free(parent);
parent = g_strdup_printf("/%s/%s", QOF_CONFIG_TIP, key);
option.tooltip = kvp_frame_get_string(helper->recursive, parent);
helper->count++;
helper->fcn (&option, helper->data);
LEAVE (" desc=%s tip=%s", option.description, option.tooltip);
}
void qof_backend_option_foreach(KvpFrame *config, QofBackendOptionCB cb, gpointer data)
{
struct config_iterate helper;
if(!config || !cb) { return; }
ENTER (" ");
helper.fcn = cb;
helper.count = 1;
helper.data = data;
helper.recursive = config;
kvp_frame_for_each_slot(config, config_foreach_cb, &helper);
LEAVE (" ");
}
void
qof_backend_load_config(QofBackend *be, KvpFrame *config)
{
if(!be || !config) { return; }
if(!be->load_config) { return; }
(be->load_config) (be, config);
}
KvpFrame*
qof_backend_get_config(QofBackend *be)
{
if(!be) { return NULL; }
if(!be->get_config) { return NULL; }
return (be->get_config) (be);
}
gboolean
qof_backend_commit_exists(QofBackend *be)
{
if(!be) { return FALSE; }
if(be->commit) { return TRUE; }
else { return FALSE; }
}
gboolean
qof_begin_edit(QofInstance *inst)
{
QofBackend * be;
if (!inst) { return FALSE; }
(inst->editlevel)++;
if (1 < inst->editlevel) { return FALSE; }
if (0 >= inst->editlevel) { inst->editlevel = 1; }
be = qof_book_get_backend (inst->book);
if (be && qof_backend_begin_exists(be)) {
qof_backend_run_begin(be, inst);
} else { inst->dirty = TRUE; }
return TRUE;
}
gboolean qof_commit_edit(QofInstance *inst)
{
QofBackend * be;
if (!inst) { return FALSE; }
(inst->editlevel)--;
if (0 < inst->editlevel) { return FALSE; }
if ((-1 == inst->editlevel) && inst->dirty)
{
be = qof_book_get_backend ((inst)->book);
if (be && qof_backend_begin_exists(be)) {
qof_backend_run_begin(be, inst);
}
inst->editlevel = 0;
}
if (0 > inst->editlevel) { inst->editlevel = 0; }
return TRUE;
}
gboolean
qof_load_backend_library (const char *directory,
const char* filename, const char* init_fcn)
{
struct stat sbuf;
gchar *fullpath;
typedef void (* backend_init) (void);
GModule *backend;
backend_init gmod_init;
gpointer g;
g_return_val_if_fail(g_module_supported(), FALSE);
fullpath = g_module_build_path(directory, filename);
PINFO (" fullpath=%s", fullpath);
g_return_val_if_fail((stat(fullpath, &sbuf) == 0), FALSE);
backend = g_module_open(fullpath, G_MODULE_BIND_LAZY);
if(!backend) {
g_message ("%s: %s\n", PACKAGE, g_module_error ());
return FALSE;
}
g = &gmod_init;
if (!g_module_symbol (backend, init_fcn, g))
{
g_message ("%s: %s\n", PACKAGE, g_module_error ());
return FALSE;
}
g_module_make_resident(backend);
gmod_init();
return TRUE;
}
/************************* END OF FILE ********************************/

View File

@ -1,265 +0,0 @@
/********************************************************************\
* qofbackend.h: api for data storage backend *
* 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 *
* *
\********************************************************************/
/** @addtogroup Backend
The QOF Backend is a pseudo-object providing an interface between the
engine and a persistant data store (e.g. a server, a database, or
a file). Backends are not meant to be used directly by an
application; instead the Session should be used to make a
connection with some particular backend.
There are no backend functions that are 'public' to
users of the engine. The backend can, however, report errors to
the GUI & other front-end users. This file defines these errors.
Backends are used to save and restore Entities in a Book.
@{
*/
/** @file qofbackend.h
@brief API for data storage Backend
@author Copyright (C) 2000-2001 Linas Vepstas <linas@linas.org>
@author Copyright 2004-2005 Neil Williams <linux@codehelp.co.uk>
*/
#ifndef QOF_BACKEND_H
#define QOF_BACKEND_H
#include "qofinstance.h"
#define QOF_MOD_BACKEND "qof-backend"
/** \brief The errors that can be reported to the GUI & other front-end users
* \warning (GnuCash) If you modify QofBackendError, please update
* src/engine/gw-engine-spec.scm
*/
typedef enum {
ERR_BACKEND_NO_ERR = 0,
ERR_BACKEND_NO_HANDLER, /**< no backend handler found for this access method (ENOSYS) */
ERR_BACKEND_NO_BACKEND, /**< Backend * pointer was unexpectedly null */
ERR_BACKEND_BAD_URL, /**< Can't parse url */
ERR_BACKEND_NO_SUCH_DB, /**< the named database doesn't exist */
ERR_BACKEND_CANT_CONNECT, /**< bad dbname/login/passwd or network failure */
ERR_BACKEND_CONN_LOST, /**< Lost connection to server */
ERR_BACKEND_LOCKED, /**< in use by another user (ETXTBSY) */
ERR_BACKEND_READONLY, /**< cannot write to file/directory */
ERR_BACKEND_TOO_NEW, /**< file/db version newer than what we can read */
ERR_BACKEND_DATA_CORRUPT, /**< data in db is corrupt */
ERR_BACKEND_SERVER_ERR, /**< error in response from server */
ERR_BACKEND_ALLOC, /**< internal memory allocation failure */
ERR_BACKEND_PERM, /**< user login successful, but no permissions
to access the desired object */
ERR_BACKEND_MODIFIED, /**< commit of object update failed because
another user has modified the object */
ERR_BACKEND_MOD_DESTROY, /**< commit of object update failed because
another user has deleted the object */
ERR_BACKEND_MISC, /**< undetermined error */
/* QSF add-ons */
ERR_QSF_INVALID_OBJ, /**< The QSF object failed to validate against the QSF object schema */
ERR_QSF_INVALID_MAP, /**< The QSF map failed to validate against the QSF map schema */
ERR_QSF_BAD_OBJ_GUID, /**< The QSF object contains one or more invalid GUIDs. */
ERR_QSF_BAD_QOF_VERSION, /**< QSF map or object doesn't match the current QOF_OBJECT_VERSION. */
ERR_QSF_BAD_MAP, /**< The selected map validates but is unusable.
This is usually because not all the required parameters for the defined objects
have calculations described in the map.
*/
ERR_QSF_NO_MAP, /**< The QSF object file was loaded without a map
The QSF Object file requires a map but it was not provided.
*/
ERR_QSF_WRONG_MAP, /**< The selected map validates but is for different objects.
The list of objects defined in this map does not include all the objects described in
the current QSF object file.
*/
ERR_QSF_MAP_NOT_OBJ, /**< Selected file is a QSF map and cannot be opened as a QSF object */
ERR_QSF_OVERFLOW, /**< EOVERFLOW - generated by strtol or strtoll.
When converting XML strings into numbers, an overflow has been detected. The XML file
contains invalid data in a field that is meant to hold a signed long integer or signed long long
integer.
*/
ERR_QSF_OPEN_NOT_MERGE, /** QSF files cannot be opened alone. The data must be merged.
This error is more of a warning that can be ignored by any routine
that uses qof_book_merge on the new session.
*/
/* fileio errors */
ERR_FILEIO_FILE_BAD_READ = 1000, /**< read failed or file prematurely truncated */
ERR_FILEIO_FILE_EMPTY, /**< file exists, is readable, but is empty */
ERR_FILEIO_FILE_LOCKERR, /**< mangled locks (unspecified error) */
ERR_FILEIO_FILE_NOT_FOUND, /**< not found / no such file */
ERR_FILEIO_FILE_TOO_OLD, /**< file version so old we can't read it */
ERR_FILEIO_UNKNOWN_FILE_TYPE, /**< didn't recognize the file type */
ERR_FILEIO_PARSE_ERROR, /**< couldn't parse the data in the file */
ERR_FILEIO_BACKUP_ERROR, /**< couldn't make a backup of the file */
ERR_FILEIO_WRITE_ERROR, /**< couldn't write to the file */
/* network errors */
ERR_NETIO_SHORT_READ = 2000, /**< not enough bytes received */
ERR_NETIO_WRONG_CONTENT_TYPE, /**< wrong kind of server, wrong data served */
ERR_NETIO_NOT_GNCXML, /**< whatever it is, we can't parse it. */
/* database errors */
ERR_SQL_MISSING_DATA = 3000, /**< database doesn't contain expected data */
ERR_SQL_DB_TOO_OLD, /**< database is old and needs upgrading */
ERR_SQL_DB_BUSY, /**< database is busy, cannot upgrade version */
/* RPC errors */
ERR_RPC_HOST_UNK = 4000, /**< Host unknown */
ERR_RPC_CANT_BIND, /**< can't bind to address */
ERR_RPC_CANT_ACCEPT, /**< can't accept connection */
ERR_RPC_NO_CONNECTION, /**< no connection to server */
ERR_RPC_BAD_VERSION, /**< RPC Version Mismatch */
ERR_RPC_FAILED, /**< Operation failed */
ERR_RPC_NOT_ADDED, /**< object not added */
} QofBackendError;
/**
* A structure that declares backend services that can be gotten.
* The Provider specifies a URL access method, and specifies the
* function to create a backend that can handle that URL access
* function.
*/
typedef struct QofBackendProvider_s QofBackendProvider;
/** \brief Pseudo-object providing an interface between the
* engine and a persistant data store (e.g. a server, a database,
* or a file).
*
* There are no backend functions that are 'public' to users of the
* engine. The backend can, however, report errors to the GUI & other
* front-end users.
*/
typedef struct QofBackend_s QofBackend;
/** \brief DOCUMENT ME! */
typedef void (*QofBePercentageFunc) (const char *message, double percent);
/** @name Allow access to the begin routine for this backend.
QOF_BEGIN_EDIT and QOF_COMMIT_EDIT_PART1 and part2 rely on
calling QofBackend *be->begin and be->commit. This means the
QofBackend struct becomes part of the public API.
These function replaces those calls to allow the macros to be
used when QOF is built as a library. */
//@{
void qof_backend_run_begin(QofBackend *be, QofInstance *inst);
gboolean qof_backend_begin_exists(QofBackend *be);
void qof_backend_run_commit(QofBackend *be, QofInstance *inst);
gboolean qof_backend_commit_exists(QofBackend *be);
//@}
/** @name Backend Configuration using KVP
The backend uses qof_backend_get_config to pass back a KvpFrame of QofBackendOption
that includes the \b translated strings that serve as description and
tooltip for that option. i.e. backends need to run gettext in the init function.
qof_backend_prepare_frame, qof_backend_prepare_option and qof_backend_complete_frame
are intended to be used by the backend itself to create the options.
qof_backend_get_config, qof_backend_option_foreach and qof_backend_load_config
are intended for either the backend or the frontend to retrieve the option data
from the frame or set new data.
@{
*/
/** A single Backend Configuration Option. */
typedef struct QofBackendOption_s {
KvpValueType type; /**< Only GINT64, DOUBLE, NUMERIC, STRING and TIMESPEC supported. */
const char *option_name; /**< non-translated, key. */
const char *description; /**< translatable description. */
const char *tooltip; /**< translatable tooltip */
gpointer value; /**< The value of the option. */
}QofBackendOption;
/** Initialise the backend_configuration */
void qof_backend_prepare_frame(QofBackend *be);
/** Add an option to the backend_configuration. Repeat for more. */
void qof_backend_prepare_option(QofBackend *be, QofBackendOption *option);
/** Complete the backend_configuration and return the frame. */
KvpFrame* qof_backend_complete_frame(QofBackend *be);
/** Backend configuration option foreach callback prototype. */
typedef void (*QofBackendOptionCB)(QofBackendOption*, gpointer data);
/** Iterate over the frame and process each option. */
void qof_backend_option_foreach(KvpFrame *config, QofBackendOptionCB cb, gpointer data);
/** \brief Load configuration options specific to this backend.
@param be The backend to configure.
@param config A KvpFrame of QofBackendOptions that this backend
will recognise. Each backend needs to document their own config
types and acceptable values.
*/
void qof_backend_load_config (QofBackend *be, KvpFrame *config);
/** \brief Get the available configuration options
To retrieve the options from the returned KvpFrame, the caller
needs to parse the XML file that documents the option names and
data types. The XML file itself is part of the backend and is
installed in a directory determined by the backend. Therefore,
loading a new backend requires two paths: the path to the .la file
and the path to the xml. Both paths are available by including a
generated header file, e.g. gncla-dir.h defines GNC_LIB_DIR for
the location of the .la file and GNC_XML_DIR for the xml.
@param be The QofBackend to be configured.
@return A new KvpFrame containing the available options or
NULL on failure.
*/
KvpFrame* qof_backend_get_config(QofBackend *be);
//@}
/** \brief Load a QOF-compatible backend shared library.
\param directory Can be NULL if filename is a complete path.
\param filename Name of the .la file that describes the
shared library. This provides platform independence,
courtesy of libtool.
\param init_fcn The QofBackendProvider init function.
\return FALSE in case or error, otherwise TRUE.
*/
gboolean
qof_load_backend_library (const char *directory,
const char* filename, const char* init_fcn);
/** \brief Retrieve the backend used by this book */
QofBackend* qof_book_get_backend (QofBook *book);
void qof_book_set_backend (QofBook *book, QofBackend *);
#endif /* QOF_BACKEND_H */
/** @} */

View File

@ -1,114 +0,0 @@
/********************************************************************\
* qof-book-p.h -- private functions for QOF books. *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Object_Private
Private interfaces, not meant to be used by applications.
@{ */
/** @name Book_Private
@{ */
/*
* HISTORY:
* Created 2001 by Rob Browning
* Copyright (c) 2001 Rob Browning
* Copyright (c) 2001,2003 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_BOOK_P_H
#define QOF_BOOK_P_H
#include "kvp_frame.h"
#include "qofbackend.h"
#include "qofbook.h"
#include "qofid.h"
#include "qofid-p.h"
#include "qofinstance-p.h"
/** Book structure */
struct _QofBook
{
QofInstance inst; /**< Unique guid for this book. */
/** The entity table associates the GUIDs of all the objects
* belonging to this book, with their pointers to the respective
* objects. This allows a lookup of objects based on thier guid.
*/
GHashTable * hash_of_collections;
/** In order to store arbitrary data, for extensibility, add a table
* that will be used to hold arbitrary pointers.
*/
GHashTable *data_tables;
/** Hash table of destroy callbacks for the data table. */
GHashTable *data_table_finalizers;
/** state flag: 'y' means 'open for editing',
* 'n' means 'book is closed'
* xxxxx shouldn't this be replaced by the instance editlevel ???
*/
char book_open;
/** a flag denoting whether the book is closing down, used to
* help the QOF objects shut down cleanly without maintaining
* internal consistency.
* XXX shouldn't this be replaced by instance->do_free ???
*/
gboolean shutting_down;
/** version number, used for tracking multiuser updates */
gint32 version;
/** To be technically correct, backends belong to sessions and
* not books. So the pointer below "really shouldn't be here",
* except that it provides a nice convenience, avoiding a lookup
* from the session. Better solutions welcome ... */
QofBackend *backend;
/* -------------------------------------------------------------- */
/** Backend private expansion data */
guint32 idata; /**< used by the sql backend for kvp management */
};
/**
* These qof_book_set_*() routines are used by backends to
* initialize the pointers in the book structure to
* something that contains actual data. These routines
* should not be used otherwise. (Its somewhat questionable
* if the backends should even be doing this much, but for
* backwards compatibility, we leave these here.)
*/
void qof_book_set_schedxactions( QofBook *book, GList *newList );
void qof_book_set_backend (QofBook *book, QofBackend *be);
/** Register books with the engine */
gboolean qof_book_register (void);
/** @deprecated */
#define qof_book_set_guid(book,guid) \
qof_entity_set_guid(QOF_ENTITY(book), guid)
/* @} */
/* @} */
/* @} */
#endif /* QOF_BOOK_P_H */

View File

@ -1,387 +0,0 @@
/********************************************************************\
* qofbook.c -- dataset access (set of accounting books) *
* *
* 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 *
\********************************************************************/
/*
* FILE:
* qofbook.c
*
* FUNCTION:
* Encapsulate all the information about a gnucash dataset.
* See src/doc/books.txt for design overview.
*
* HISTORY:
* Created by Linas Vepstas December 1998
* Copyright (c) 1998-2001,2003 Linas Vepstas <linas@linas.org>
* Copyright (c) 2000 Dave Peticolas
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "gnc-event.h"
#include "gnc-event-p.h"
#include "gnc-trace.h"
#include "qofbackend-p.h"
#include "qofbook.h"
#include "qofbook-p.h"
#include "qofclass.h"
#include "qofid-p.h"
#include "qofobject-p.h"
#include "gnc-engine-util.h"
#include "guid.h"
static QofLogModule log_module = QOF_MOD_ENGINE;
/* ====================================================================== */
/* constructor / destructor */
static void coll_destroy(gpointer col)
{
qof_collection_destroy((QofCollection *) col);
}
static void
qof_book_init (QofBook *book)
{
if (!book) return;
book->hash_of_collections = g_hash_table_new_full(
g_str_hash, g_str_equal,
(GDestroyNotify)gnc_string_cache_remove, /* key_destroy_func */
coll_destroy); /* value_destroy_func */
qof_instance_init (&book->inst, QOF_ID_BOOK, book);
book->data_tables = g_hash_table_new (g_str_hash, g_str_equal);
book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal);
book->book_open = 'y';
book->version = 0;
book->idata = 0;
}
QofBook *
qof_book_new (void)
{
QofBook *book;
ENTER (" ");
book = g_new0(QofBook, 1);
qof_book_init(book);
qof_object_book_begin (book);
gnc_engine_gen_event (&book->inst.entity, GNC_EVENT_CREATE);
LEAVE ("book=%p", book);
return book;
}
static void
book_final (gpointer key, gpointer value, gpointer booq)
{
QofBookFinalCB cb = value;
QofBook *book = booq;
gpointer user_data = g_hash_table_lookup (book->data_tables, key);
(*cb) (book, key, user_data);
}
void
qof_book_destroy (QofBook *book)
{
if (!book) return;
ENTER ("book=%p", book);
book->shutting_down = TRUE;
gnc_engine_force_event (&book->inst.entity, GNC_EVENT_DESTROY);
/* Call the list of finalizers, let them do their thing.
* Do this before tearing into the rest of the book.
*/
g_hash_table_foreach (book->data_table_finalizers, book_final, book);
qof_object_book_end (book);
g_hash_table_destroy (book->data_table_finalizers);
book->data_table_finalizers = NULL;
g_hash_table_destroy (book->data_tables);
book->data_tables = NULL;
qof_instance_release (&book->inst);
g_hash_table_destroy (book->hash_of_collections);
book->hash_of_collections = NULL;
g_free (book);
LEAVE ("book=%p", book);
}
/* ====================================================================== */
/* XXX this should probably be calling is_equal callbacks on gncObject */
gboolean
qof_book_equal (QofBook *book_1, QofBook *book_2)
{
if (book_1 == book_2) return TRUE;
if (!book_1 || !book_2) return FALSE;
return TRUE;
}
/* ====================================================================== */
gboolean
qof_book_not_saved(QofBook *book)
{
if (!book) return FALSE;
return(book->inst.dirty || qof_object_is_dirty (book));
}
void
qof_book_mark_saved(QofBook *book)
{
if (!book) return;
book->inst.dirty = FALSE;
qof_object_mark_clean (book);
}
/* ====================================================================== */
/* getters */
QofBackend *
qof_book_get_backend (QofBook *book)
{
if (!book) return NULL;
return book->backend;
}
gboolean
qof_book_shutting_down (QofBook *book)
{
if (!book) return FALSE;
return book->shutting_down;
}
/* ====================================================================== */
/* setters */
void
qof_book_set_backend (QofBook *book, QofBackend *be)
{
if (!book) return;
ENTER ("book=%p be=%p", book, be);
book->backend = be;
LEAVE (" ");
}
void qof_book_kvp_changed (QofBook *book)
{
if (!book) return;
book->inst.dirty = TRUE;
}
/* ====================================================================== */
/* Store arbitrary pointers in the QofBook for data storage extensibility */
/* XXX if data is NULL, we should remove the key from the hash table!
*/
void
qof_book_set_data (QofBook *book, const char *key, gpointer data)
{
if (!book || !key) return;
g_hash_table_insert (book->data_tables, (gpointer)key, data);
}
void
qof_book_set_data_fin (QofBook *book, const char *key, gpointer data, QofBookFinalCB cb)
{
if (!book || !key) return;
g_hash_table_insert (book->data_tables, (gpointer)key, data);
if (!cb) return;
g_hash_table_insert (book->data_table_finalizers, (gpointer)key, cb);
}
gpointer
qof_book_get_data (QofBook *book, const char *key)
{
if (!book || !key) return NULL;
return g_hash_table_lookup (book->data_tables, (gpointer)key);
}
/* ====================================================================== */
QofCollection *
qof_book_get_collection (QofBook *book, QofIdType entity_type)
{
QofCollection *col;
if (!book || !entity_type) return NULL;
col = g_hash_table_lookup (book->hash_of_collections, entity_type);
if (!col) {
col = qof_collection_new (entity_type);
g_hash_table_insert(
book->hash_of_collections,
gnc_string_cache_insert((gpointer) entity_type), col);
}
return col;
}
struct _iterate {
QofCollectionForeachCB fn;
gpointer data;
};
static void
foreach_cb (gpointer key, gpointer item, gpointer arg)
{
struct _iterate *iter = arg;
QofCollection *col = item;
iter->fn (col, iter->data);
}
void
qof_book_foreach_collection (QofBook *book,
QofCollectionForeachCB cb, gpointer user_data)
{
struct _iterate iter;
g_return_if_fail (book);
g_return_if_fail (cb);
iter.fn = cb;
iter.data = user_data;
g_hash_table_foreach (book->hash_of_collections, foreach_cb, &iter);
}
/* ====================================================================== */
void qof_book_mark_closed (QofBook *book)
{
if(!book) { return; }
book->book_open = 'n';
}
gchar qof_book_get_open_marker(QofBook *book)
{
if(!book) { return 'n'; }
return book->book_open;
}
gint32 qof_book_get_version (QofBook *book)
{
if(!book) { return -1; }
return book->version;
}
guint32 qof_book_get_idata (QofBook *book)
{
if(!book) { return 0; }
return book->idata;
}
void qof_book_set_version (QofBook *book, gint32 version)
{
if(!book && version < 0) { return; }
book->version = version;
}
void qof_book_set_idata(QofBook *book, guint32 idata)
{
if(!book && idata < 0) { return; }
book->idata = idata;
}
gint64
qof_book_get_counter (QofBook *book, const char *counter_name)
{
QofBackend *be;
KvpFrame *kvp;
KvpValue *value;
gint64 counter;
if (!book) {
PWARN ("No book!!!");
return -1;
}
if (!counter_name || *counter_name == '\0') {
PWARN ("Invalid counter name.");
return -1;
}
/* If we've got a backend with a counter method, call it */
be = book->backend;
if (be && be->counter)
return ((be->counter)(be, counter_name));
/* If not, then use the KVP in the book */
kvp = qof_book_get_slots (book);
if (!kvp) {
PWARN ("Book has no KVP_Frame");
return -1;
}
value = kvp_frame_get_slot_path (kvp, "counters", counter_name, NULL);
if (value) {
/* found it */
counter = kvp_value_get_gint64 (value);
} else {
/* New counter */
counter = 0;
}
/* Counter is now valid; increment it */
counter++;
/* Save off the new counter */
value = kvp_value_new_gint64 (counter);
kvp_frame_set_slot_path (kvp, value, "counters", counter_name, NULL);
kvp_value_delete (value);
/* and return the value */
return counter;
}
/* QofObject function implementation and registration */
gboolean qof_book_register (void)
{
static QofParam params[] = {
{ QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_entity_get_guid, NULL },
{ QOF_PARAM_KVP, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, NULL },
{ NULL },
};
qof_class_register (QOF_ID_BOOK, NULL, params);
return TRUE;
}
/* ========================== END OF FILE =============================== */

View File

@ -1,178 +0,0 @@
/********************************************************************\
* qofbook.h -- Encapsulate all the information about a dataset. *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Book
A QOF Book is a dataset. It provides a single handle
through which all the various collections of entities
can be found. In particular, given only the type of
the entity, the collection can be found.
Books also provide the 'natural' place to working with
a storage backend, as a book can encapsulate everything
held in storage.
@{ */
/** @file qofbook.h
* @brief Encapsulate all the information about a dataset.
*
* @author Copyright (c) 1998, 1999, 2001, 2003 Linas Vepstas <linas@linas.org>
* @author Copyright (c) 2000 Dave Peticolas
*/
#ifndef QOF_BOOK_H
#define QOF_BOOK_H
#include <glib.h>
#include "qofid.h"
#include "kvp_frame.h"
/** @brief Encapsulates all the information about a dataset
* manipulated by GnuCash. This is the top-most structure
* used for anchoring data.
*/
/** Lookup an entity by guid, returning pointer to the entity */
#define QOF_BOOK_LOOKUP_ENTITY(book,guid,e_type,c_type) ({ \
QofEntity *val = NULL; \
if (guid && book) { \
QofCollection *col; \
col = qof_book_get_collection (book, e_type); \
val = qof_collection_lookup_entity (col, guid); \
} \
(c_type *) val; \
})
/** \brief QofBook reference */
typedef struct _QofBook QofBook;
/** GList of QofBook */
typedef GList QofBookList;
typedef void (*QofBookFinalCB) (QofBook *, gpointer key, gpointer user_data);
/** Register the book object with the QOF object system. */
gboolean qof_book_register (void);
/** Allocate, initialise and return a new QofBook. Books contain references
* to all of the top-level object containers. */
QofBook * qof_book_new (void);
/** End any editing sessions associated with book, and free all memory
associated with it. */
void qof_book_destroy (QofBook *book);
/** Close a book to editing.
It is up to the application to check this flag,
and once marked closed, books cannnot be marked as open.
*/
void qof_book_mark_closed (QofBook *book);
/** \return The table of entities of the given type.
*
* When an object's constructor calls qof_instance_init(), a
* reference to the object is stored in the book. The book stores
* all the references to initialized instances, sorted by type. This
* function returns a collection of the references for the specified
* type.
*
* If the collection doesn't yet exist for the indicated type,
* it is created. Thus, this routine is gaurenteed to return
* a non-NULL value. (Unless the system malloc failed (out of
* memory) in which case what happens??).
*/
QofCollection * qof_book_get_collection (QofBook *, QofIdType);
/** Invoke the indicated callback on each collection in the book. */
typedef void (*QofCollectionForeachCB) (QofCollection *, gpointer user_data);
void qof_book_foreach_collection (QofBook *, QofCollectionForeachCB, gpointer);
/** \return The kvp data for the book.
* Note that the book KVP data is persistant, and is stored/retrieved
* from the file/database. Thus, the book KVP is the correct place to
* store data that needs to be persistant accross sessions (or shared
* between multiple users). To store application runtime data, use
* qof_book_set_data() instead.
*/
#define qof_book_get_slots(book) qof_instance_get_slots(QOF_INSTANCE(book))
/** The qof_book_set_data() allows arbitrary pointers to structs
* to be stored in QofBook. This is the "prefered" method for
* extending QofBook to hold new data types. This is also
* the ideal location to store other arbitrary runtime data
* that the application may need.
*
* The book data differs from the book KVP in that the contents
* of the book KVP are persistant (are saved and restored to file
* or database), whereas the data pointers exist only at runtime.
*/
void qof_book_set_data (QofBook *book, const char *key, gpointer data);
/** Same as qof_book_set_data(), except that the callback will be called
* when the book is destroyed. The argument to the callback will be
* the book followed by the data pointer.
*/
void qof_book_set_data_fin (QofBook *book, const char *key, gpointer data,
QofBookFinalCB);
/** Retrieves arbitrary pointers to structs stored by qof_book_set_data. */
gpointer qof_book_get_data (QofBook *book, const char *key);
/** Is the book shutting down? */
gboolean qof_book_shutting_down (QofBook *book);
/** qof_book_not_saved() will return TRUE if any
* data in the book hasn't been saved to long-term storage.
* (Actually, that's not quite true. The book doesn't know
* anything about saving. Its just that whenever data is modified,
* the 'dirty' flag is set. This routine returns the value of the
* 'dirty' flag. Its up to the backend to periodically reset this
* flag, when it actually does save the data.)
*/
gboolean qof_book_not_saved (QofBook *book);
/** The qof_book_mark_saved() routine marks the book as having been
* saved (to a file, to a database). Used by backends to mark the
* notsaved flag as FALSE just after loading. Also used by the
* main window code when the used has said to abandon any changes.
*/
void qof_book_mark_saved(QofBook *book);
/** Call this function when you change the book kvp, to make sure the book
* is marked 'dirty'. */
void qof_book_kvp_changed (QofBook *book);
/** The qof_book_equal() method returns TRUE if books are equal.
* XXX this routine is broken, and does not currently compare data.
*/
gboolean qof_book_equal (QofBook *book_1, QofBook *book_2);
/** This will 'get and increment' the named counter for this book.
* The return value is -1 on error or the incremented counter.
*/
gint64 qof_book_get_counter (QofBook *book, const char *counter_name);
/** deprecated */
#define qof_book_get_guid(X) qof_entity_get_guid (QOF_ENTITY(X))
#endif /* QOF_BOOK_H */
/** @} */
/** @} */

View File

@ -1,109 +0,0 @@
/***************************************************************************
* qofchoice.c
*
* Thu Jul 7 12:24:30 2005
* Copyright 2005 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* 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 Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#include "qofchoice.h"
static GHashTable *qof_choice_table = NULL;
/* To initialise, call qof_choice_add_class in
qof_object_register for the choice object. */
static gboolean qof_choice_is_initialized(void)
{
if(!qof_choice_table)
{
qof_choice_table = g_hash_table_new(g_str_hash, g_str_equal);
}
if(!qof_choice_table) { return FALSE; }
return TRUE;
}
gboolean qof_object_is_choice(QofIdType type)
{
gpointer value, check;
value = NULL;
check = NULL;
g_return_val_if_fail(qof_choice_is_initialized(), FALSE);
g_return_val_if_fail(type != NULL, FALSE);
value = g_hash_table_lookup(qof_choice_table, type);
if((GHashTable*)value) { return TRUE; }
g_message("DEBUG: QOF_TYPE_CHOICE setup failed for %s\n", type);
return FALSE;
}
gboolean
qof_choice_create(char* type)
{
GHashTable *param_table;
g_return_val_if_fail(type != NULL, FALSE);
g_return_val_if_fail(qof_choice_is_initialized() == TRUE, FALSE);
param_table = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(qof_choice_table, type, param_table);
return TRUE;
}
gboolean qof_choice_add_class(char* select, char* option, char* param_name)
{
GHashTable *param_table;
GList *option_list;
option_list = NULL;
param_table = NULL;
g_return_val_if_fail(select != NULL, FALSE);
g_return_val_if_fail(qof_object_is_choice(select), FALSE);
param_table = (GHashTable*)g_hash_table_lookup(qof_choice_table, select);
g_return_val_if_fail(param_table, FALSE);
option_list = (GList*)g_hash_table_lookup(param_table, param_name);
option_list = g_list_append(option_list, option);
g_hash_table_insert(param_table, param_name, option_list);
return TRUE;
}
GList* qof_object_get_choices(QofIdType type, QofParam *param)
{
GList *choices;
GHashTable *param_table;
g_return_val_if_fail(type != NULL, NULL);
g_return_val_if_fail(qof_choice_is_initialized() == TRUE, FALSE);
choices = NULL;
param_table = g_hash_table_lookup(qof_choice_table, type);
choices = g_hash_table_lookup(param_table, param->param_name);
return choices;
}
gboolean qof_choice_check(char* choice_obj, char *param_name, char* choice )
{
GList *choices, *result;
GHashTable *param_table;
choices = result = NULL;
g_return_val_if_fail(qof_object_is_choice(choice_obj), FALSE);
param_table = g_hash_table_lookup(qof_choice_table, choice_obj);
choices = g_hash_table_lookup(param_table, param_name);
result = g_list_find(choices, choice);
if(!result) { return FALSE; }
return TRUE;
}

View File

@ -1,151 +0,0 @@
/***************************************************************************
* qofchoice.h
*
* Thu Jul 7 12:25:24 2005
* Copyright 2005 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QOFCHOICE_H
#define _QOFCHOICE_H
/** @addtogroup Choice
Objects can be linked together one-to-one by simply using the name of
the related object as the parameter type in the QofClass parameter list.
\verbatim
{ FOO_PARAM, BAR_ID, (QofAccessFunc)qofFooGetBar, (QofSetterFunc)qofFooSetBar },
\endverbatim
This is limited as each FOO entity can contain only one reference to a
single BAR entity per parameter. Also, this parameter cannot be used to
link to a similar object, OBJ. This requires "one to many" links.
There are two types of one-to-many links in QOF.
-# ::QOF_TYPE_COLLECT - one to many entities all of only one type.
- Handles links between one object and a series of objects
that are \b all of the same type, e.g. a simple list.
-# ::QOF_TYPE_CHOICE - one to a single entity of many possible types.
- Handles links between one object and a series of
\b dissimilar objects, one of each type.
Currently, there is no explicit way to support many-to-many links
but existing methods can be combined to give approximately the same results.
A QOF_TYPE_CHOICE object is like a C++ template. QOF_TYPE_CHOICE doesn't
really exist by itself:
\verbatim
QOF_TYPE_CHOICE<QOF_X, QOF_Y, QOF_Z>
\endverbatim
It holds a single entity of type X, Y, or Z for the purposes of QOF
or ::QSF. For querying the object it queries as if it's an X, Y, or Z.
Each choice type has it's own definition of the allowable objects -
each of which need to be registered as normal. Objects can declare
themselves to be one option of a particular choice. There is no
requirement for any object to be either a choice or an option for a
choice object.
-# Each ::QOF_TYPE_CHOICE parameter provides access to \b ONE entity of
a pre-determined set of object types.
-# The entity type within the choice can be determined at run time.
-# Each entity can have a different *type* of entity to it's siblings,
provided that it is one of the pre-determined object types.
-# Objects declare themselves as containing choices and other objects
can add themselves to the list of acceptable choices of suitable objects.
-# QOF_TYPE_CHOICE is transparent - objects should retrieve the e_type of
the received entity and handle the entity appropriately.
-# The number of different entity types that can be pre-determined for
any one QOF_TYPE_CHOICE parameter is not fixed. You can have as many
types as the ::QofAccessFunc and ::QofSetterFunc can handle.
-# Any one parameter can only have one QOF type. You cannot have a parameter
that is both ::QOF_TYPE_COLLECT and QOF_TYPE_CHOICE any more than you can have
one parameter that is both ::QOF_TYPE_BOOLEAN and ::QOF_TYPE_NUMERIC.
-# When setting references using QOF_TYPE_CHOICE, QOF passes a single entity
to the QofSetterFunc and it is left to the function to determine how to
handle that entity type. When retrieving references using QOF_TYPE_CHOICE,
the object must return a single entity matching one of the choice types.
@{
*/
/** @file qofchoice.h
@brief Linking one entity to a single entity of many possible types.
@author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
*/
#include "qofclass.h"
#include "qofobject.h"
/** \brief Identify an object as containing a choice. */
#define QOF_TYPE_CHOICE "choice"
/** \brief Does this object contain a choice parameter?
Returns TRUE if any parameter in the object definition
uses a choice of elements, whether or not those
parameters contain any data.
@param type Type of object/entity.
@return TRUE if one or more choice parameters has been
registered using the object definition, otherwise FALSE.
*/
gboolean qof_object_is_choice(QofIdType type);
/** \brief Set an object as using QOF_TYPE_CHOICE. */
gboolean qof_choice_create(char* type);
/** \brief Add the choices for this parameter to the object.
@param choice The choice object.
@param add The object to be added as an option.
@param param_name The parameter that will be used to get or set options.
@return FALSE if object is not a choice object or on error
otherwise TRUE.
*/
gboolean qof_choice_add_class(char* choice, char* add, char* param_name);
/** \brief Return the list of all object types usable with this parameter.
@param type The choice object type.
@param param The name of the parameter that will be used to
get or set options.
@return NULL on error or if no options exist for this parameter,
otherwise a GList of all QofIdType object type(s) available
via this choice object using this parameter.
*/
GList* qof_object_get_choices(QofIdType type, QofParam *param);
/** \brief Is the choice valid for this param_name?
@param choice_obj The object containing the QOF_TYPE_CHOICE parameter.
@param param_name The name of a QOF_TYPE_CHOICE parameter in this object.
@param choice The QofIdType to look for in the list of choices.
@return TRUE if choice is found in the list of allowed choices for
this parameter of this object. Otherwise, FALSE
*/
gboolean qof_choice_check(char* choice_obj, char *param_name, char* choice);
/** @} */
#endif /* _QOFCHOICE_H */

View File

@ -1,44 +0,0 @@
/********************************************************************\
* qofclass-p.h -- Private API for registering queriable objects *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Object_Private
Private interfaces, not meant to be used by applications.
@{ */
/** @name Class_Private
@{ */
#ifndef QOF_CLASSP_H
#define QOF_CLASSP_H
#include "qofclass.h"
void qof_class_init(void);
void qof_class_shutdown (void);
QofSortFunc qof_class_get_default_sort (QofIdTypeConst obj_name);
/* @} */
/* @} */
/* @} */
#endif /* QOF_CLASSP_H */

View File

@ -1,294 +0,0 @@
/********************************************************************\
* qofclass.c -- provide QOF paramterized data objects *
* 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-trace.h"
#include "gnc-engine-util.h"
#include "qofclass.h"
#include "qofclass-p.h"
#include "qofquery.h"
static QofLogModule log_module = QOF_MOD_CLASS;
static GHashTable *classTable = NULL;
static GHashTable *sortTable = NULL;
static gboolean initialized = FALSE;
static gboolean clear_table (gpointer key, gpointer value, gpointer user_data)
{
g_hash_table_destroy (value);
return TRUE;
}
/********************************************************************/
/* PUBLISHED API FUNCTIONS */
void
qof_class_register (QofIdTypeConst obj_name,
QofSortFunc default_sort_function,
const QofParam *params)
{
GHashTable *ht;
int i;
if (!obj_name) return;
if (default_sort_function)
{
g_hash_table_insert (sortTable, (char *)obj_name, default_sort_function);
}
ht = g_hash_table_lookup (classTable, obj_name);
/* If it doesn't already exist, create a new table for this object */
if (!ht)
{
ht = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (classTable, (char *)obj_name, ht);
}
/* At least right now, we allow dummy, parameterless objects,
* for testing purposes. Although I suppose that should be
* an error.. */
/* Now insert all the parameters */
if (params)
{
for (i = 0; params[i].param_name; i++)
g_hash_table_insert (ht,
(char *)params[i].param_name,
(gpointer)&(params[i]));
}
}
void
qof_class_init(void)
{
if (initialized) return;
initialized = TRUE;
classTable = g_hash_table_new (g_str_hash, g_str_equal);
sortTable = g_hash_table_new (g_str_hash, g_str_equal);
}
void
qof_class_shutdown (void)
{
if (!initialized) return;
initialized = FALSE;
g_hash_table_foreach_remove (classTable, clear_table, NULL);
g_hash_table_destroy (classTable);
g_hash_table_destroy (sortTable);
}
gboolean
qof_class_is_registered (QofIdTypeConst obj_name)
{
if (!obj_name) return FALSE;
if (g_hash_table_lookup (classTable, obj_name)) return TRUE;
return FALSE;
}
const QofParam *
qof_class_get_parameter (QofIdTypeConst obj_name,
const char *parameter)
{
GHashTable *ht;
g_return_val_if_fail (obj_name, NULL);
g_return_val_if_fail (parameter, NULL);
ht = g_hash_table_lookup (classTable, obj_name);
if (!ht)
{
PWARN ("no object of type %s", obj_name);
return NULL;
}
return (g_hash_table_lookup (ht, parameter));
}
QofAccessFunc
qof_class_get_parameter_getter (QofIdTypeConst obj_name,
const char *parameter)
{
const QofParam *prm;
g_return_val_if_fail (obj_name, NULL);
g_return_val_if_fail (parameter, NULL);
prm = qof_class_get_parameter (obj_name, parameter);
if (prm)
return prm->param_getfcn;
return NULL;
}
QofSetterFunc
qof_class_get_parameter_setter (QofIdTypeConst obj_name,
const char *parameter)
{
const QofParam *prm;
g_return_val_if_fail (obj_name, NULL);
g_return_val_if_fail (parameter, NULL);
prm = qof_class_get_parameter (obj_name, parameter);
if (prm)
return prm->param_setfcn;
return NULL;
}
QofType
qof_class_get_parameter_type (QofIdTypeConst obj_name,
const char *param_name)
{
const QofParam *prm;
if (!obj_name || !param_name) return NULL;
prm = qof_class_get_parameter (obj_name, param_name);
if (!prm) return NULL;
return (prm->param_type);
}
QofSortFunc
qof_class_get_default_sort (QofIdTypeConst obj_name)
{
if (!obj_name) return NULL;
return g_hash_table_lookup (sortTable, obj_name);
}
/* ================================================================ */
struct class_iterate {
QofClassForeachCB fcn;
gpointer data;
};
static void
class_foreach_cb (gpointer key, gpointer item, gpointer arg)
{
struct class_iterate *iter = arg;
QofIdTypeConst id = key;
iter->fcn (id, iter->data);
}
void
qof_class_foreach (QofClassForeachCB cb, gpointer user_data)
{
struct class_iterate iter;
if (!cb) return;
if (!classTable) return;
iter.fcn = cb;
iter.data = user_data;
g_hash_table_foreach (classTable, class_foreach_cb, &iter);
}
/* ================================================================ */
struct parm_iterate {
QofParamForeachCB fcn;
gpointer data;
};
static void
param_foreach_cb (gpointer key, gpointer item, gpointer arg)
{
struct parm_iterate *iter = arg;
QofParam *parm = item;
iter->fcn (parm, iter->data);
}
void
qof_class_param_foreach (QofIdTypeConst obj_name,
QofParamForeachCB cb, gpointer user_data)
{
struct parm_iterate iter;
GHashTable *param_ht;
if (!obj_name || !cb) return;
if (!classTable) return;
param_ht = g_hash_table_lookup (classTable, obj_name);
if (!param_ht) return;
iter.fcn = cb;
iter.data = user_data;
g_hash_table_foreach (param_ht, param_foreach_cb, &iter);
}
struct param_ref_list
{
GList *list;
};
static void
find_reference_param_cb(QofParam *param, gpointer user_data)
{
struct param_ref_list *b;
b = (struct param_ref_list*)user_data;
if((param->param_getfcn == NULL)||(param->param_setfcn == NULL)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_STRING)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_NUMERIC)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_DATE)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_CHAR)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_DEBCRED)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_GUID)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_INT32)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_INT64)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_DOUBLE)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_KVP)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_TYPE_BOOLEAN)) { return; }
if(0 == safe_strcmp(param->param_type, QOF_ID_BOOK)) { return; }
b->list = g_list_append(b->list, param);
}
GList*
qof_class_get_referenceList(QofIdTypeConst type)
{
GList *ref_list;
struct param_ref_list b;
ref_list = NULL;
b.list = NULL;
qof_class_param_foreach(type, find_reference_param_cb, &b);
ref_list = g_list_copy(b.list);
return ref_list;
}
/* ============================= END OF FILE ======================== */

View File

@ -1,268 +0,0 @@
/********************************************************************\
* qofclass.h -- API for registering parameters on objects *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Class
This file defines a class messaging system reminiscent of
traditional OO-style setter and getter interfaces to object
properties. A C-language object can declare a collection
of setters and getters on itself that can then be used
to perform run-time (as opposed to compile-time) bindings
to the object.
To put it differently, a QOF class is a set of parameter
getters and setters that are associated with an object type.
Given a pointer to some thing, the setters and getters can
be used to get and set values out of that thing. Note
that the pointer to "some thing" need not be a pointer
to a QOF Entity or Instance (although QOF classes are
more interesting when used with Entities/Instances).
What "some thing" is defined entirely by the programmer
declaring a new QOF Class.
Because a QOF Class associates getters and setters with
a type, one can then ask, at run time, what parameters
are associated with a given type, even if those parameters
were not known at compile time. Thus, a QOF Class is
sort-of like a DynAny implementation. QOF classes can
be used to provide "object introspection", i.e. asking
object to describe itself.
The QOF Query subsystem depends on QOF classes having been
declared; the Query uses the getters to get values associated
with particular instances.
A QofAccessFunc or QofSetterFunc do not need to be public
functions, if you need to add functions to an object with an
established API, define the additional QOF routines as static.
Only the register routine needs to be public.
@{ */
/** @file qofclass.h
@brief API for registering paramters on objects
@author Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
@author Copyright (C) 2003 Linas Vepstas <linas@linas.org>
@author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
*/
#ifndef QOF_CLASS_H
#define QOF_CLASS_H
#include "qofid.h"
#define QOF_MOD_CLASS "qof-class"
/** \name Core types
Core data types for objects that can be used in parameters.
Note that QofIdTypes may also be used and will create a
single reference between two known objects.
@{
*/
#define QOF_TYPE_STRING "string"
#define QOF_TYPE_DATE "date"
#define QOF_TYPE_NUMERIC "numeric"
#define QOF_TYPE_DEBCRED "debcred"
#define QOF_TYPE_GUID "guid"
#define QOF_TYPE_INT32 "gint32"
#define QOF_TYPE_INT64 "gint64"
#define QOF_TYPE_DOUBLE "double"
#define QOF_TYPE_BOOLEAN "boolean"
#define QOF_TYPE_KVP "kvp"
#define QOF_TYPE_CHAR "character"
#define QOF_TYPE_COLLECT "collection" /**< secondary collections
are used for one-to-many references between entities and are
implemented using ::QofCollection.
These are \b NOT the same as the main collections in the QofBook.
-# Each ::QofCollection contains one or many entities - *all* of a single type.
-# The entity type within the collection can be determined at run time.
-# Easy conversions to GList or whatever in the param_setfcn handler.
-# Each parameter can have it's own collection.
-# Each entity can have a different *type* of collection to it's siblings,
provided that it is acceptable to the set function.
-# Each object decides which types are acceptable for which parameter in the
set functions. This is then part of the API for that object.
QOF_TYPE_COLLECT has two functions, both related to one-to-many
links:
- Represent a reference between 2 entities with a list of acceptable types.
(one object linked to many types of single entities)
- Represent a reference between one entity and many entities of another type.
(one object linked to many entities of a single type.)
If the set function can handle it, it could also be used for true one-to-many
links: one object linked to many entities of many types.
n.b. Always subject to each collection holding only one type at runtime.
(otherwise use books).
*/
/** @} */
/** Type of Paramters (String, Date, Numeric, GUID, etc.) */
typedef const char * QofType;
typedef struct _QofParam QofParam;
/** The QofAccessFunc defines an arbitrary function pointer
* for access functions. This is needed because C doesn't have
* templates, so we just cast a lot. Real functions must be of
* the form:
*
* param_type getter_func (object_type *self);
* or
* param_type getter_func (object_type *self, QofParam *param);
*
* The additional argument 'param' allows generic getter functions
* to be implemented, because this argument provides for a way to
* identify the expected getter_func return type at runtime. It
* also provides a place for the user to hang additional user-defined
* data.
*/
typedef gpointer (*QofAccessFunc)(gpointer object, const QofParam *param);
/** The QofSetterFunc defines an function pointer for parameter
* setters. Real functions must be of the form:
*
* void setter_func (object_type *self, param_type *param);
*/
typedef void (*QofSetterFunc) (gpointer, gpointer);
/** This structure is for each queriable parameter in an object
*
* -- param_name is the name of the parameter.
* -- param_type is the type of the parameter, which can be either another
* object (QofIdType) or it can be a core data type (QofType).
* -- param_getfcn is the function to actually obtain the parameter
* -- param_setfcn is the function to actually set the parameter
* -- param_userdata is a place where the object author can place any
* desired object-author-defined data (and thus can be used by the
* author-defined setter/getter).
*
* Either the getter or the setter may be NULL.
*
* XXX todo/fixme: need to define a destroy callback, so that when
* the param memory is freed, the callback can be used to release the
* user-defined data.
*/
struct _QofParam
{
const char * param_name;
QofType param_type;
QofAccessFunc param_getfcn;
QofSetterFunc param_setfcn;
gpointer param_userdata;
};
/** This function is the default sort function for a particular object type */
typedef int (*QofSortFunc)(gpointer, gpointer);
/** This function registers a new object class with the Qof subsystem.
* In particular, it registers the set of setters and getters for
* controlling the object. The getters are typically used by the
* query subsystem to query type specific data. Note that there
* is no particular requirement for there to be a setter for every
* getter or even vice-versa, nor is there any requirement for these
* to map 'cleanly' or orthogonally to the underlying object. The
* parameters are really just a set of value setting and getting
* routines.
*
* The "params" argument must be a NULL-terminated array of QofParam.
* It may be NULL if there are no parameters to be registered.
*/
void qof_class_register (QofIdTypeConst obj_name,
QofSortFunc default_sort_fcn,
const QofParam *params);
/** An example:
*
* #define MY_OBJ_MEMO "memo"
* #define MY_OBJ_VALUE "value"
* #define MY_OBJ_DATE "date"
* #define MY_OBJ_ACCOUNT "account"
* #define MY_OBJ_TRANS "trans"
*
* static QofParam myParams[] = {
* { MY_OBJ_MEMO, QOF_TYPE_STRING, myMemoGetter, NULL },
* { MY_OBJ_VALUE, QOF_TYPE_NUMERIC, myValueGetter, NULL },
* { MY_OBJ_DATE, QOF_TYPE_DATE, myDateGetter, NULL },
* { MY_OBJ_ACCOUNT, GNC_ID_ACCOUNT, myAccountGetter, NULL },
* { MY_OBJ_TRANS, GNC_ID_TRANS, myTransactionGetter, NULL },
* NULL };
*
* qof_class_register ("myObjectName", myObjectCompare, &myParams);
*/
/** Return true if the the indicated type is registered,
* else return FALSE.
*/
gboolean qof_class_is_registered (QofIdTypeConst obj_name);
/** Return the core datatype of the specified object's parameter */
QofType qof_class_get_parameter_type (QofIdTypeConst obj_name,
const char *param_name);
/** Return the registered Parameter Definition for the requested parameter */
const QofParam * qof_class_get_parameter (QofIdTypeConst obj_name,
const char *parameter);
/** Return the object's parameter getter function */
QofAccessFunc qof_class_get_parameter_getter (QofIdTypeConst obj_name,
const char *parameter);
/** Return the object's parameter setter function */
QofSetterFunc qof_class_get_parameter_setter (QofIdTypeConst obj_name,
const char *parameter);
/** Type definition for the class callback function. */
typedef void (*QofClassForeachCB) (QofIdTypeConst, gpointer);
/** Call the callback once for each object class that is registered
* with the system. The 'user_data' is passed back to the callback.
*/
void qof_class_foreach (QofClassForeachCB, gpointer user_data);
/** Type definition for the paramter callback function. */
typedef void (*QofParamForeachCB) (QofParam *, gpointer user_data);
/** Call the callback once for each parameter on the indicated
* object class. The 'user_data' is passed back to the callback.
*/
void qof_class_param_foreach (QofIdTypeConst obj_name,
QofParamForeachCB, gpointer user_data);
/** \brief List of the parameters that could be references.
Simple check to return a GList of all parameters
of this object type that are not known QOF data types.
Used for partial QofBook support, see ::QofEntityReference
*/
GList* qof_class_get_referenceList(QofIdTypeConst type);
#endif /* QOF_CLASS_H */
/** @} */
/** @} */

View File

@ -1,326 +0,0 @@
/********************************************************************\
* qofgobj.c -- QOF to GLib GObject mapping *
* *
* 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 "qof.h"
#include "qofgobj.h"
static QofLogModule log_module = QOF_MOD_QUERY;
static gboolean initialized = FALSE;
static GSList *paramList = NULL;
static GSList *classList = NULL;
/* =================================================================== */
#if 0
static gboolean
clear_table (gpointer key, gpointer value, gpointer user_data)
{
g_slist_free (value);
return TRUE;
}
#endif
void
qof_gobject_init(void)
{
if (initialized) return;
initialized = TRUE;
// gobjectClassTable = g_hash_table_new (g_str_hash, g_str_equal);
/* Init the other subsystems that we need */
qof_object_initialize();
qof_query_init ();
}
void
qof_gobject_shutdown (void)
{
GSList *n;
if (!initialized) return;
initialized = FALSE;
// GSList *n;
for (n=paramList; n; n=n->next) g_free(n->data);
g_slist_free (paramList);
for (n=classList; n; n=n->next) g_free(n->data);
g_slist_free (classList);
#if 0
// XXX also need to walk over books, and collection and delete
// the collection get_data instance lists !!
// without this we have a memory leak !!
g_hash_table_foreach_remove (gobjectParamTable, clear_table, NULL);
g_hash_table_destroy (gobjectParamTable);
#endif
}
/* =================================================================== */
#define GOBJECT_TABLE "GobjectTable"
void
qof_gobject_register_instance (QofBook *book, QofType type, GObject *gob)
{
QofCollection *coll;
GSList *instance_list;
if (!book || !type) return;
coll = qof_book_get_collection (book, type);
instance_list = qof_collection_get_data (coll);
instance_list = g_slist_prepend (instance_list, gob);
qof_collection_set_data (coll, instance_list);
}
/* =================================================================== */
static gpointer
qof_gobject_getter (gpointer data, QofParam *getter)
{
GObject *gob = data;
const char *str;
GParamSpec *gps = getter->param_userdata;
/* Note that the return type must actually be of type
* getter->param_type but we just follow the hard-coded
* mapping below ... */
if (G_IS_PARAM_SPEC_STRING(gps))
{
GValue gval = {G_TYPE_INVALID};
g_value_init (&gval, G_TYPE_STRING);
g_object_get_property (gob, getter->param_name, &gval);
str = g_value_get_string (&gval);
return (gpointer) str;
}
else
if (G_IS_PARAM_SPEC_INT(gps))
{
int ival;
GValue gval = {G_TYPE_INVALID};
g_value_init (&gval, G_TYPE_INT);
g_object_get_property (gob, getter->param_name, &gval);
ival = g_value_get_int (&gval);
return (gpointer) ival;
}
else
if (G_IS_PARAM_SPEC_UINT(gps))
{
int ival;
GValue gval = {G_TYPE_INVALID};
g_value_init (&gval, G_TYPE_UINT);
g_object_get_property (gob, getter->param_name, &gval);
ival = g_value_get_uint (&gval);
return (gpointer) ival;
}
else
if (G_IS_PARAM_SPEC_BOOLEAN(gps))
{
int ival;
GValue gval = {G_TYPE_INVALID};
g_value_init (&gval, G_TYPE_BOOLEAN);
g_object_get_property (gob, getter->param_name, &gval);
ival = g_value_get_boolean (&gval);
return (gpointer) ival;
}
PWARN ("unhandled parameter type %s for paramter %s",
G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
return NULL;
}
static double
qof_gobject_double_getter (gpointer data, QofParam *getter)
{
GObject *gob = data;
double fval;
GParamSpec *gps = getter->param_userdata;
/* Note that the return type must actually be of type
* getter->param_type but we just follow the hard-coded
* mapping below ... */
if (G_IS_PARAM_SPEC_FLOAT(gps))
{
GValue gval = {G_TYPE_INVALID};
g_value_init (&gval, G_TYPE_FLOAT);
g_object_get_property (gob, getter->param_name, &gval);
fval = g_value_get_float (&gval);
return fval;
}
else
if (G_IS_PARAM_SPEC_DOUBLE(gps))
{
GValue gval = {G_TYPE_INVALID};
g_value_init (&gval, G_TYPE_DOUBLE);
g_object_get_property (gob, getter->param_name, &gval);
fval = g_value_get_double (&gval);
return fval;
}
PWARN ("unhandled parameter type %s for paramter %s",
G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
return 0.0;
}
/* =================================================================== */
/* Loop over every instance of the given type in the collection
* of instances that we have on hand.
*/
static void
qof_gobject_foreach (QofCollection *coll, QofEntityForeachCB cb, gpointer ud)
{
GSList *n;
n = qof_collection_get_data (coll);
for (; n; n=n->next)
{
cb (n->data, ud);
}
}
/* =================================================================== */
void
qof_gobject_register (QofType e_type, GObjectClass *obclass)
{
int i;
int j;
QofParam *qof_param_list, *qpar;
QofObject *class_def;
GParamSpec **prop_list, *gparam;
guint n_props;
/* Get the GObject properties, convert to QOF properties */
prop_list = g_object_class_list_properties (obclass, &n_props);
qof_param_list = g_new0 (QofParam, n_props);
paramList = g_slist_prepend (paramList, qof_param_list);
PINFO ("object %s has %d props", e_type, n_props);
j=0;
for (i=0; i<n_props; i++)
{
gparam = prop_list[i];
qpar = &qof_param_list[j];
PINFO ("param %d %s is type %s",
i, gparam->name, G_PARAM_SPEC_TYPE_NAME(gparam));
qpar->param_name = g_param_spec_get_name (gparam);
qpar->param_getfcn = (QofAccessFunc)qof_gobject_getter;
qpar->param_setfcn = NULL;
qpar->param_userdata = gparam;
if ((G_IS_PARAM_SPEC_INT(gparam)) ||
(G_IS_PARAM_SPEC_UINT(gparam)) ||
(G_IS_PARAM_SPEC_ENUM(gparam)) ||
(G_IS_PARAM_SPEC_FLAGS(gparam)))
{
qpar->param_type = QOF_TYPE_INT32;
j++;
}
else
if ((G_IS_PARAM_SPEC_INT64(gparam)) ||
(G_IS_PARAM_SPEC_UINT64(gparam)))
{
qpar->param_type = QOF_TYPE_INT64;
j++;
}
else
if (G_IS_PARAM_SPEC_BOOLEAN(gparam))
{
qpar->param_type = QOF_TYPE_BOOLEAN;
j++;
}
else
if (G_IS_PARAM_SPEC_STRING(gparam))
{
qpar->param_type = QOF_TYPE_STRING;
j++;
}
else
if ((G_IS_PARAM_SPEC_POINTER(gparam)) ||
(G_IS_PARAM_SPEC_OBJECT(gparam)))
{
/* No-op, silently ignore. Someday we should handle this ... */
}
else
if ((G_IS_PARAM_SPEC_FLOAT(gparam)) ||
(G_IS_PARAM_SPEC_DOUBLE(gparam)))
{
qpar->param_getfcn = (QofAccessFunc) qof_gobject_double_getter;
qpar->param_type = QOF_TYPE_DOUBLE;
j++;
}
else
if (G_IS_PARAM_SPEC_CHAR(gparam))
{
qpar->param_type = QOF_TYPE_CHAR;
j++;
}
else
{
PWARN ("Unknown/unhandled parameter type %s on %s:%s\n",
G_PARAM_SPEC_TYPE_NAME(gparam), e_type, qpar->param_name);
}
}
/* NULL-terminated list! */
qof_param_list[j].param_type = NULL;
qof_class_register (e_type, NULL, qof_param_list);
/* ------------------------------------------------------ */
/* Now do the class itself */
class_def = g_new0 (QofObject, 1);
classList = g_slist_prepend (classList, class_def);
class_def->interface_version = QOF_OBJECT_VERSION;
class_def->e_type = e_type;
/* We could let the user specify a "nick" here, but
* the actual class name seems reasonable, e.g. for debugging. */
class_def->type_label = G_OBJECT_CLASS_NAME (obclass);
class_def->create = NULL;
class_def->book_begin = NULL;
class_def->book_end = NULL;
class_def->is_dirty = NULL;
class_def->mark_clean = NULL;
class_def->foreach = qof_gobject_foreach;
class_def->printable = NULL;
class_def->version_cmp = NULL;
qof_object_register (class_def);
}
/* ======================= END OF FILE ================================ */

View File

@ -1,87 +0,0 @@
/********************************************************************\
* qofgobj.h -- QOF to GLib GObject mapping *
* *
* 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 *
* *
\********************************************************************/
#ifndef QOF_GOBJ_H
#define QOF_GOBJ_H
/** @addtogroup Object
@{ */
/** @addtogroup GObject GLib GObjects
The API defined in this file allows a user to register any
GLib GObject (and any object derived from one, e.g. GTK/Gnome)
with the QOF system, as a QOF Object Class. This allows
the QOF Query routines to be used to search over collections
of GObjects.
XXX Only GObject properties are searchable, data and other
hanging off the GObject is not. Fix this. This needs fixing.
@{ */
/** @file qofgobj.h
@brief QOF to GLib GObject mapping
@author Copyright (C) 2004 Linas Vepstas <linas@linas.org>
*/
#include <glib-object.h>
#include "qofbook.h"
#include "qofclass.h"
/** Initalize and shut down this subsystem. */
void qof_gobject_init(void);
void qof_gobject_shutdown (void);
/** Register a GObject class with the QOF subsystem.
* Doing this will make the properties associated with
* this GObject searchable using the QOF subsystem.
*
* The QofType can be any string you desire, although typically
* you might want to set it to G_OBJECT_CLASS_NAME() of the
* object class. Note that this type will become the name of
* the "table" that is searched by SQL queries:
* e.g. in order to be able to say "SELECT * FROM MyStuff;"
* you must first say:
* qof_gobject_register ("MyStuff", gobj_class);
*/
void qof_gobject_register (QofType type, GObjectClass *obclass);
/** Register an instance of a GObject with the QOF subsystem.
*
* The QofType can be any string you desire, although typically
* you might want to set it to G_OBJECT_CLASS_NAME() of the
* object class. Note that this type will become the name of
* the "table" that is searched by SQL queries:
* e.g. in order to be able to say "SELECT * FROM MyStuff;"
* you must first say:
* qof_gobject_register_instance (book, "MyStuff", obj);
*
* The 'book' argument specifies an anchor point for the collection
* of all of the registered instances. By working with disjoint books,
* you can have multiple disjoint searchable sets of objects.
*/
void qof_gobject_register_instance (QofBook *book, QofType, GObject *);
#endif /* QOF_GOBJ_H */
/** @} */
/** @} */

View File

@ -1,59 +0,0 @@
/********************************************************************\
* qofid-p.h -- QOF entity identifier engine-only API *
* Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Object_Private
Private interfaces, not meant to be used by applications.
@{ */
/** @name Entity_Private
@{ */
#ifndef QOF_ID_P_H
#define QOF_ID_P_H
#include <glib.h>
#include "qofid.h"
/* This file defines an engine-only API for using gnucash entity
* identifiers. */
/** Set the ID of the entity, over-riding teh previous ID.
* Very dangerous, use only for file i/o work.
*/
void qof_entity_set_guid (QofEntity *ent, const GUID *guid);
/** Take entity, remove it from whatever collection its currently
* in, and place it in a new collection. To be used only for
* moving entity from one book to another.
*/
void qof_collection_insert_entity (QofCollection *, QofEntity *);
/** reset value of dirty flag */
void qof_collection_mark_clean (QofCollection *);
void qof_collection_mark_dirty (QofCollection *);
/* @} */
/* @} */
/* @} */
#endif /* QOF_ID_P_H */

View File

@ -1,393 +0,0 @@
/********************************************************************\
* qofid.c -- QOF entity identifier implementation *
* Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
* Copyright (C) 2003 Linas Vepstas <linas@linas.org> *
* *
* 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 <string.h>
#include <glib.h>
#include "qofid.h"
#include "qofid-p.h"
#include "gnc-trace.h"
#include "gnc-engine-util.h"
static QofLogModule log_module = QOF_MOD_ENGINE;
struct QofCollection_s
{
QofIdType e_type;
gboolean is_dirty;
GHashTable * hash_of_entities;
gpointer data; /* place where object class can hang arbitrary data */
};
/* =============================================================== */
static void qof_collection_remove_entity (QofEntity *ent);
void
qof_entity_init (QofEntity *ent, QofIdType type, QofCollection * tab)
{
g_return_if_fail (NULL != tab);
/* XXX We passed redundant info to this routine ... but I think that's
* OK, it might eliminate programming errors. */
if (safe_strcmp(tab->e_type, type))
{
PERR ("attempt to insert \"%s\" into \"%s\"", type, tab->e_type);
return;
}
ent->e_type = CACHE_INSERT (type);
do
{
guid_new(&ent->guid);
if (NULL == qof_collection_lookup_entity (tab, &ent->guid)) break;
PWARN("duplicate id created, trying again");
} while(1);
ent->collection = tab;
qof_collection_insert_entity (tab, ent);
}
void
qof_entity_release (QofEntity *ent)
{
if (!ent->collection) return;
qof_collection_remove_entity (ent);
CACHE_REMOVE (ent->e_type);
ent->e_type = NULL;
}
/* This is a restricted function, should be used only during
* read from file */
void
qof_entity_set_guid (QofEntity *ent, const GUID *guid)
{
QofCollection *col;
if (guid_equal (guid, &ent->guid)) return;
col = ent->collection;
qof_collection_remove_entity (ent);
ent->guid = *guid;
qof_collection_insert_entity (col, ent);
}
const GUID *
qof_entity_get_guid (QofEntity *ent)
{
if (!ent) return guid_null();
return &ent->guid;
}
/* =============================================================== */
static guint
id_hash (gconstpointer key)
{
const GUID *guid = key;
if (key == NULL)
return 0;
/* Compiler should optimize this all away! */
if (sizeof(guint) <= 16)
return *((guint *) guid->data);
else
{
guint hash = 0;
unsigned int i, j;
for (i = 0, j = 0; i < sizeof(guint); i++, j++)
{
if (j == 16)
j = 0;
hash <<= 4;
hash |= guid->data[j];
}
return hash;
}
}
static gboolean
id_compare(gconstpointer key_1, gconstpointer key_2)
{
return guid_equal (key_1, key_2);
}
QofCollection *
qof_collection_new (QofIdType type)
{
QofCollection *col;
col = g_new0(QofCollection, 1);
col->e_type = CACHE_INSERT (type);
col->hash_of_entities = g_hash_table_new (id_hash, id_compare);
col->data = NULL;
return col;
}
void
qof_collection_destroy (QofCollection *col)
{
CACHE_REMOVE (col->e_type);
g_hash_table_destroy(col->hash_of_entities);
col->e_type = NULL;
col->hash_of_entities = NULL;
col->data = NULL; /** XXX there should be a destroy notifier for this */
g_free (col);
}
/* =============================================================== */
/* getters */
QofIdType
qof_collection_get_type (QofCollection *col)
{
return col->e_type;
}
/* =============================================================== */
static void
qof_collection_remove_entity (QofEntity *ent)
{
QofCollection *col;
if (!ent) return;
col = ent->collection;
if (!col) return;
g_hash_table_remove (col->hash_of_entities, &ent->guid);
ent->collection = NULL;
}
void
qof_collection_insert_entity (QofCollection *col, QofEntity *ent)
{
if (!col || !ent) return;
if (guid_equal(&ent->guid, guid_null())) return;
g_return_if_fail (col->e_type == ent->e_type);
qof_collection_remove_entity (ent);
g_hash_table_insert (col->hash_of_entities, &ent->guid, ent);
ent->collection = col;
}
gboolean
qof_collection_add_entity (QofCollection *coll, QofEntity *ent)
{
QofEntity *e;
e = NULL;
if (!coll || !ent) { return FALSE; }
if (guid_equal(&ent->guid, guid_null())) { return FALSE; }
g_return_val_if_fail (coll->e_type == ent->e_type, FALSE);
e = qof_collection_lookup_entity(coll, &ent->guid);
if ( e != NULL ) { return FALSE; }
g_hash_table_insert (coll->hash_of_entities, &ent->guid, ent);
return TRUE;
}
static void
collection_merge_cb (QofEntity *ent, gpointer data)
{
QofCollection *target;
target = (QofCollection*)data;
qof_collection_add_entity(target, ent);
}
gboolean
qof_collection_merge (QofCollection *target, QofCollection *merge)
{
if(!target || !merge) { return FALSE; }
g_return_val_if_fail (target->e_type == merge->e_type, FALSE);
qof_collection_foreach(merge, collection_merge_cb, target);
return TRUE;
}
static void
collection_compare_cb (QofEntity *ent, gpointer user_data)
{
QofCollection *target;
QofEntity *e;
gint value;
e = NULL;
target = (QofCollection*)user_data;
if (!target || !ent) { return; }
value = *(gint*)qof_collection_get_data(target);
if (value != 0) { return; }
if (guid_equal(&ent->guid, guid_null()))
{
value = -1;
qof_collection_set_data(target, &value);
return;
}
g_return_if_fail (target->e_type == ent->e_type);
e = qof_collection_lookup_entity(target, &ent->guid);
if ( e == NULL )
{
value = 1;
qof_collection_set_data(target, &value);
return;
}
value = 0;
qof_collection_set_data(target, &value);
}
gint
qof_collection_compare (QofCollection *target, QofCollection *merge)
{
gint value;
value = 0;
if (!target && !merge) { return 0; }
if (target == merge) { return 0; }
if (!target && merge) { return -1; }
if (target && !merge) { return 1; }
if(target->e_type != merge->e_type) { return -1; }
qof_collection_set_data(target, &value);
qof_collection_foreach(merge, collection_compare_cb, target);
value = *(gint*)qof_collection_get_data(target);
if(value == 0) {
qof_collection_set_data(merge, &value);
qof_collection_foreach(target, collection_compare_cb, merge);
value = *(gint*)qof_collection_get_data(merge);
}
return value;
}
QofEntity *
qof_collection_lookup_entity (QofCollection *col, const GUID * guid)
{
QofEntity *ent;
g_return_val_if_fail (col, NULL);
if (guid == NULL) return NULL;
ent = g_hash_table_lookup (col->hash_of_entities, guid->data);
return ent;
}
QofCollection *
qof_collection_from_glist (QofIdType type, GList *glist)
{
QofCollection *coll;
QofEntity *ent;
GList *list;
coll = qof_collection_new(type);
for(list = glist; list != NULL; list = list->next)
{
ent = (QofEntity*)list->data;
if(FALSE == qof_collection_add_entity(coll, ent))
{
return NULL;
}
}
return coll;
}
guint
qof_collection_count (QofCollection *col)
{
guint c;
c = g_hash_table_size(col->hash_of_entities);
return c;
}
/* =============================================================== */
gboolean
qof_collection_is_dirty (QofCollection *col)
{
if (!col) return FALSE;
return col->is_dirty;
}
void
qof_collection_mark_clean (QofCollection *col)
{
if (!col) return;
col->is_dirty = FALSE;
}
void
qof_collection_mark_dirty (QofCollection *col)
{
if (!col) return;
col->is_dirty = TRUE;
}
/* =============================================================== */
gpointer
qof_collection_get_data (QofCollection *col)
{
if (!col) return NULL;
return col->data;
}
void
qof_collection_set_data (QofCollection *col, gpointer user_data)
{
if (!col) return;
col->data = user_data;
}
/* =============================================================== */
struct _iterate {
QofEntityForeachCB fcn;
gpointer data;
};
static void foreach_cb (gpointer key, gpointer item, gpointer arg)
{
struct _iterate *iter = arg;
QofEntity *ent = item;
iter->fcn (ent, iter->data);
}
void
qof_collection_foreach (QofCollection *col,
QofEntityForeachCB cb_func, gpointer user_data)
{
struct _iterate iter;
g_return_if_fail (col);
g_return_if_fail (cb_func);
iter.fcn = cb_func;
iter.data = user_data;
g_hash_table_foreach (col->hash_of_entities, foreach_cb, &iter);
}
/* =============================================================== */

View File

@ -1,264 +0,0 @@
/********************************************************************\
* qofid.h -- QOF entity type identification system *
* *
* 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 *
* *
\********************************************************************/
#ifndef QOF_ID_H
#define QOF_ID_H
/** @addtogroup Entity
@{ */
/** @addtogroup Entities
This file defines an API that adds types to the GUID's.
GUID's with types can be used to identify and reference
typed entities.
The idea here is that a GUID can be used to uniquely identify
some thing. By adding a type, one can then talk about the
type of thing identified. By adding a collection, one can
then work with a handle to a collection of things of a given
type, each uniquely identified by a given ID. QOF Entities
can be used independently of any other part of the system.
In particular, Entities can be useful even if one is not using
the Query ond Object parts of the QOF system.
Identifiers are globally-unique and permanent, i.e., once
an entity has been assigned an identifier, it retains that same
identifier for its lifetime.
Identifiers can be encoded as hex strings.
GUID Identifiers are 'typed' with strings. The native ids used
by QOF are defined below. An id with type QOF_ID_NONE does not
refer to any entity, although that may change (???). An id with
type QOF_ID_NULL does not refer to any entity, and will never refer
to any entity. An identifier with any other type may refer to an
actual entity, but that is not guaranteed (??? Huh?). If an id
does refer to an entity, the type of the entity will match the
type of the identifier.
If you have a type name, and you want to have a way of finding
a collection that is associated with that type, then you must use
Books.
Entities can refer to other entities as well as to the basic
QOF types, using the qofclass parameters.
@{ */
/** @file qofid.h
@brief QOF entity type identification system
@author Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu>
@author Copyright (C) 2003 Linas Vepstas <linas@linas.org>
*/
#include <string.h>
#include "guid.h"
/** QofIdType declaration */
typedef const char * QofIdType;
/** QofIdTypeConst declaration */
typedef const char * QofIdTypeConst;
/** QofLogModule declaration */
typedef const gchar* QofLogModule;
#define QOF_ID_NONE NULL
#define QOF_ID_NULL "null"
#define QOF_ID_BOOK "Book"
#define QOF_ID_SESSION "Session"
/** simple,cheesy cast but holds water for now */
#define QOF_ENTITY(object) ((QofEntity *)(object))
/** Inline string comparision; compiler will optimize away most of this */
#define QSTRCMP(da,db) ({ \
int val = 0; \
if ((da) && (db)) { \
if ((da) != (db)) { \
val = strcmp ((da), (db)); \
} \
} else \
if ((!(da)) && (db)) { \
val = -1; \
} else \
if ((da) && (!(db))) { \
val = 1; \
} \
val; /* block assumes value of last statment */ \
})
/** return TRUE if object is of the given type */
#define QOF_CHECK_TYPE(obj,type) (0 == QSTRCMP((type),(((QofEntity *)(obj))->e_type)))
/** cast object to the indicated type, print error message if its bad */
#define QOF_CHECK_CAST(obj,e_type,c_type) ( \
QOF_CHECK_TYPE((obj),(e_type)) ? \
(c_type *) (obj) : \
(c_type *) ({ \
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
"Error: Bad QofEntity at %s:%d", __FILE__, __LINE__); \
(obj); \
}))
/** QofEntity declaration */
typedef struct QofEntity_s QofEntity;
/** QofCollection declaration
@param e_type QofIdType
@param is_dirty gboolean
@param hash_of_entities GHashTable
@param data gpointer, place where object class can hang arbitrary data
*/
typedef struct QofCollection_s QofCollection;
/** QofEntity structure
@param e_type Entity type
@param guid GUID for the entity
@param collection Entity collection
*/
struct QofEntity_s
{
QofIdType e_type;
GUID guid;
QofCollection * collection;
};
/** @name QOF Entity Initialization & Shutdown
@{ */
/** Initialise the memory associated with an entity */
void qof_entity_init (QofEntity *, QofIdType, QofCollection *);
/** Release the data associated with this entity. Dont actually free
* the memory associated with the instance. */
void qof_entity_release (QofEntity *);
/** @} */
/** Return the GUID of this entity */
const GUID * qof_entity_get_guid (QofEntity *);
/** @name Collections of Entities
@{ */
/** create a new collection of entities of type */
QofCollection * qof_collection_new (QofIdType type);
/** return the number of entities in the collection. */
guint qof_collection_count (QofCollection *col);
/** destroy the collection */
void qof_collection_destroy (QofCollection *col);
/** return the type that the collection stores */
QofIdType qof_collection_get_type (QofCollection *);
/** Find the entity going only from its guid */
QofEntity * qof_collection_lookup_entity (QofCollection *, const GUID *);
/** Callback type for qof_entity_foreach */
typedef void (*QofEntityForeachCB) (QofEntity *, gpointer user_data);
/** Call the callback for each entity in the collection. */
void qof_collection_foreach (QofCollection *,
QofEntityForeachCB, gpointer user_data);
/** Store and retreive arbitrary object-defined data
*
* XXX We need to add a callback for when the collection is being
* destroyed, so that the user has a chance to clean up anything
* that was put in the 'data' member here.
*/
gpointer qof_collection_get_data (QofCollection *col);
void qof_collection_set_data (QofCollection *col, gpointer user_data);
/** Return value of 'dirty' flag on collection */
gboolean qof_collection_is_dirty (QofCollection *col);
/** @name QOF_TYPE_COLLECT: Linking one entity to many of one type
\note These are \b NOT the same as the main collections in the book.
QOF_TYPE_COLLECT is a secondary collection, used to select entities
of one object type as references of another entity.
\sa QOF_TYPE_CHOICE.
@{
*/
/** \brief Add an entity to a QOF_TYPE_COLLECT.
\note These are \b NOT the same as the main collections in the book.
Entities can be
freely added and merged across these secondary collections, they
will not be removed from the original collection as they would
by using ::qof_entity_insert_entity or ::qof_entity_remove_entity.
*/
gboolean
qof_collection_add_entity (QofCollection *coll, QofEntity *ent);
/** \brief Merge two QOF_TYPE_COLLECT of the same type.
\note \b NOT the same as the main collections in the book.
QOF_TYPE_COLLECT uses a secondary collection, independent of
those in the book. Entities will not be removed from the
original collection as when using ::qof_entity_insert_entity
or ::qof_entity_remove_entity.
*/
gboolean
qof_collection_merge (QofCollection *target, QofCollection *merge);
/** \brief Compare two secondary collections.
Performs a deep comparision of the collections. Each QofEntity in
each collection is looked up in the other collection, via the GUID.
\return 0 if the collections are identical or both are NULL
otherwise -1 if target is NULL or either collection contains an entity with an invalid
GUID or if the types of the two collections do not match,
or +1 if merge is NULL or if any entity exists in one collection but
not in the other.
*/
gint
qof_collection_compare (QofCollection *target, QofCollection *merge);
/** \brief Create a secondary collection from a GList
@param type The QofIdType of the QofCollection \b and of
\b all entities in the GList.
@param glist GList of entities of the same QofIdType.
@return NULL if any of the entities fail to match the
QofCollection type, else a pointer to the collection
on success.
*/
QofCollection*
qof_collection_from_glist (QofIdType type, GList *glist);
/** @} */
/** @} */
#endif /* QOF_ID_H */
/** @} */
/** @} */

View File

@ -1,97 +0,0 @@
/********************************************************************\
* qofinstance-p.h -- private fields common to all object instances *
* *
* 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 *
* *
\********************************************************************/
/*
* Object instance holds many common fields that most
* gnucash objects use.
*
* Copyright (C) 2003 Linas Vepstas <linas@linas.org>
*/
/** @addtogroup Object
@{ */
/** @addtogroup Object_Private
Private interfaces, not meant to be used by applications.
@{ */
/** @name Instance_Private
@{ */
#ifndef QOF_INSTANCE_P_H
#define QOF_INSTANCE_P_H
#include "qofinstance.h"
/**
* UNDER CONSTRUCTION!
* This is mostly scaffolding for now,
* eventually, it may hold more fields, such as refrence counting...
*
*/
struct QofInstance_s
{
/** Globally unique id identifying this instance */
QofEntity entity;
/** The entity_table in which this instance is stored */
QofBook * book;
/** kvp_data is a key-value pair database for storing arbirtary
* information associated with this instance.
* See src/engine/kvp_doc.txt for a list and description of the
* important keys. */
KvpFrame *kvp_data;
/** Timestamp used to track the last modification to this
* instance. Typically used to compare two versions of the
* same object, to see which is newer. When used with the
* SQL backend, this field is reserved for SQL use, to compare
* the version in local memory to the remote, server version.
*/
Timespec last_update;
/** Keep track of nesting level of begin/end edit calls */
int editlevel;
/** In process of being destroyed */
gboolean do_free;
/** dirty/clean flag. If dirty, then this instance has been modified,
* but has not yet been written out to storage (file/database)
*/
gboolean dirty;
};
/** reset the dirty flag */
void qof_instance_mark_clean (QofInstance *);
void qof_instance_set_slots (QofInstance *, KvpFrame *);
/** Set the last_update time. Reserved for use by the SQL backend;
* used for comparing version in local memory to that in remote
* server.
*/
void qof_instance_set_last_update (QofInstance *inst, Timespec ts);
/* @} */
/* @} */
/* @} */
#endif /* QOF_INSTANCE_P_H */

View File

@ -1,245 +0,0 @@
/********************************************************************\
* qofinstance.c -- handler for fields common to all objects *
* *
* 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 *
* *
\********************************************************************/
/*
* Object instance holds many common fields that most
* gnucash objects use.
*
* Copyright (C) 2003 Linas Vepstas <linas@linas.org>
*/
#include "gnc-trace.h"
#include "kvp-util-p.h"
#include "qofbook.h"
#include "qofbook-p.h"
#include "qofid.h"
#include "qofid-p.h"
#include "qofinstance.h"
#include "qofinstance-p.h"
static QofLogModule log_module = QOF_MOD_ENGINE;
/* ========================================================== */
QofInstance*
qof_instance_create (QofIdType type, QofBook *book)
{
QofInstance *inst;
inst = g_new0(QofInstance, 1);
qof_instance_init(inst, type, book);
return inst;
}
void
qof_instance_init (QofInstance *inst, QofIdType type, QofBook *book)
{
QofCollection *col;
inst->book = book;
inst->kvp_data = kvp_frame_new();
inst->last_update.tv_sec = 0;
inst->last_update.tv_nsec = -1;
inst->editlevel = 0;
inst->do_free = FALSE;
inst->dirty = FALSE;
col = qof_book_get_collection (book, type);
qof_entity_init (&inst->entity, type, col);
}
void
qof_instance_release (QofInstance *inst)
{
kvp_frame_delete (inst->kvp_data);
inst->editlevel = 0;
inst->do_free = FALSE;
inst->dirty = FALSE;
qof_entity_release (&inst->entity);
}
const GUID *
qof_instance_get_guid (QofInstance *inst)
{
if (!inst) return NULL;
return &inst->entity.guid;
}
QofBook *
qof_instance_get_book (QofInstance *inst)
{
if (!inst) return NULL;
return inst->book;
}
KvpFrame*
qof_instance_get_slots (QofInstance *inst)
{
if (!inst) return NULL;
return inst->kvp_data;
}
Timespec
qof_instance_get_last_update (QofInstance *inst)
{
if (!inst)
{
Timespec ts = {0,-1};
return ts;
}
return inst->last_update;
}
int
qof_instance_version_cmp (QofInstance *left, QofInstance *right)
{
if (!left && !right) return 0;
if (!left) return -1;
if (!right) return +1;
if (left->last_update.tv_sec < right->last_update.tv_sec) return -1;
if (left->last_update.tv_sec > right->last_update.tv_sec) return +1;
if (left->last_update.tv_nsec < right->last_update.tv_nsec) return -1;
if (left->last_update.tv_nsec > right->last_update.tv_nsec) return +1;
return 0;
}
gboolean
qof_instance_is_dirty (QofInstance *inst)
{
QofCollection *coll;
if (!inst) { return FALSE; }
coll = inst->entity.collection;
if(qof_collection_is_dirty(coll)) { return inst->dirty; }
inst->dirty = FALSE;
return FALSE;
}
void
qof_instance_set_dirty(QofInstance* inst)
{
QofCollection *coll;
inst->dirty = TRUE;
coll = inst->entity.collection;
qof_collection_mark_dirty(coll);
}
gboolean
qof_instance_check_edit(QofInstance *inst)
{
if(inst->editlevel > 0) { return TRUE; }
return FALSE;
}
gboolean
qof_instance_do_free(QofInstance *inst)
{
return inst->do_free;
}
void
qof_instance_mark_free(QofInstance *inst)
{
inst->do_free = TRUE;
}
/* ========================================================== */
/* setters */
void
qof_instance_mark_clean (QofInstance *inst)
{
if(!inst) return;
inst->dirty = FALSE;
}
void
qof_instance_set_slots (QofInstance *inst, KvpFrame *frm)
{
if (!inst) return;
if (inst->kvp_data && (inst->kvp_data != frm))
{
kvp_frame_delete(inst->kvp_data);
}
inst->dirty = TRUE;
inst->kvp_data = frm;
}
void
qof_instance_set_last_update (QofInstance *inst, Timespec ts)
{
if (!inst) return;
inst->last_update = ts;
}
/* ========================================================== */
void
qof_instance_gemini (QofInstance *to, QofInstance *from)
{
time_t now;
/* Books must differ for a gemini to be meaningful */
if (!from || !to || (from->book == to->book)) return;
now = time(0);
/* Make a note of where the copy came from */
gnc_kvp_bag_add (to->kvp_data, "gemini", now,
"inst_guid", &from->entity.guid,
"book_guid", &from->book->inst.entity.guid,
NULL);
gnc_kvp_bag_add (from->kvp_data, "gemini", now,
"inst_guid", &to->entity.guid,
"book_guid", &to->book->inst.entity.guid,
NULL);
to->dirty = TRUE;
}
QofInstance *
qof_instance_lookup_twin (QofInstance *src, QofBook *target_book)
{
QofCollection *col;
KvpFrame *fr;
GUID * twin_guid;
QofInstance * twin;
if (!src || !target_book) return NULL;
ENTER (" ");
fr = gnc_kvp_bag_find_by_guid (src->kvp_data, "gemini",
"book_guid", &target_book->inst.entity.guid);
twin_guid = kvp_frame_get_guid (fr, "inst_guid");
col = qof_book_get_collection (target_book, src->entity.e_type);
twin = (QofInstance *) qof_collection_lookup_entity (col, twin_guid);
LEAVE (" found twin=%p", twin);
return twin;
}
/* ========================== END OF FILE ======================= */

View File

@ -1,130 +0,0 @@
/********************************************************************\
* qofinstance.h -- fields common to all object instances *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Entity
@{ */
/** @addtogroup Instance
Qof Instances are a derived type of QofEntity. The Instance
adds some common features and functions that most objects
will want to use.
@{ */
/** @file qofinstance.h
* @brief Object instance holds common fields that most gnucash objects use.
*
* @author Copyright (C) 2003,2004 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_INSTANCE_H
#define QOF_INSTANCE_H
#include "guid.h"
#include "gnc-date.h"
#include "kvp_frame.h"
#include "qofbook.h"
#include "qofid.h"
/* --- type macros --- */
/* cheesy, but will do for now, eventually should be more gtk-like, handle
* thunks, etc. */
#define QOF_INSTANCE(object) ((QofInstance *)(object))
typedef struct QofInstance_s QofInstance;
/** Initialise the memory associated with an instance */
void qof_instance_init (QofInstance *, QofIdType, QofBook *);
/** release the data associated with this instance. Dont actually free
* the memory associated with the instance. */
void qof_instance_release (QofInstance *inst);
/** Return the book pointer */
QofBook * qof_instance_get_book (QofInstance *);
/** Return the GUID of this instance */
const GUID * qof_instance_get_guid (QofInstance *);
/** Return the pointer to the kvp_data */
KvpFrame* qof_instance_get_slots (QofInstance *);
/** Return the last time this instance was modified. If QofInstances
* are used with the QofObject storage backends, then the instance
* update times are reserved for use by the backend, for managing
* multi-user updates. Non-backend code should not set the update
* times.
*/
Timespec qof_instance_get_last_update (QofInstance *inst);
/** Compare two instances, based on thier last update times.
* Returns a negative, zero or positive value, respectively,
* if 'left' is earlier, same as or later than 'right'.
* Accepts NULL pointers, NULL's are by definition earlier
* than any value.
*/
int qof_instance_version_cmp (QofInstance *left, QofInstance *right);
/** Return value of is_dirty flag */
gboolean qof_instance_is_dirty (QofInstance *);
/** \brief Set the dirty flag
Sets this instance AND the collection as dirty.
*/
void qof_instance_set_dirty(QofInstance* inst);
gboolean qof_instance_check_edit(QofInstance *inst);
gboolean qof_instance_do_free(QofInstance *inst);
void qof_instance_mark_free(QofInstance *inst);
QofInstance* qof_instance_create (QofIdType type, QofBook *book);
/** Pair things up. This routine inserts a kvp value into each instance
* containing the guid of the other. In this way, if one has one of the
* pair, one can always find the other by looking up it's guid. Typically,
* you will want to use qof_instance_lookup_twin() to find the twin.
* (The current implementation assumes the two instances belong to different
* books, and will not add gemini kvp's unless the books differ. Note that
* the gemini kvp includes the book guid as well, so that the right book can
* be found.
*/
void qof_instance_gemini (QofInstance *to, QofInstance *from);
/** The qof_instance_lookup_twin() routine will find the "twin" of this
* instance 'src' in the given other 'book' (if the twin exists).
*
* When instances are gemini'ed or cloned, both of the pair are marked
* with the guid of thier copy, thus allowing the sibling-copy of
* an instance to be found. Since the sibling may end up in a
* different book, we need a way of finding it, given only that we
* know the book, and that we know its twin.
*
* That's what this routine does. Given some book 'book', and an
* instance 'src', it will find the sibling instance of 'src' that is
* in 'book', and return it. If not found, it returns NULL. This
* routine uses the 'gemini' kvp values to do its work.
*/
QofInstance * qof_instance_lookup_twin (QofInstance *src, QofBook *book);
/* @} */
/* @} */
#endif /* QOF_INSTANCE_H */

View File

@ -1,26 +0,0 @@
/***************************************************************************
* qofla-dir.h.in
*
* Mon Dec 20 20:27:48 2004
* Copyright 2004 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* 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 Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define QOF_LIB_DIR "@-libdir-@"

View File

@ -1,408 +0,0 @@
/********************************************************************
* qofmath128.c -- an 128-bit integer library *
* Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
* *
* 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 *
* *
*******************************************************************/
#define _GNU_SOURCE
#include "config.h"
#include "qofmath128.h"
#include <glib.h>
/* =============================================================== */
/*
* Quick-n-dirty 128-bit integer math lib. Things seem to mostly
* work, and have been tested, but not comprehensively tested.
*/
#define HIBIT (0x8000000000000000ULL)
/** Multiply a pair of signed 64-bit numbers,
* returning a signed 128-bit number.
*/
inline qofint128
mult128 (gint64 a, gint64 b)
{
qofint128 prod;
guint64 a0, a1;
guint64 b0, b1;
guint64 d, d0, d1;
guint64 e, e0, e1;
guint64 f, f0, f1;
guint64 g, g0, g1;
guint64 sum, carry, roll, pmax;
prod.isneg = 0;
if (0>a)
{
prod.isneg = !prod.isneg;
a = -a;
}
if (0>b)
{
prod.isneg = !prod.isneg;
b = -b;
}
a1 = a >> 32;
a0 = a - (a1<<32);
b1 = b >> 32;
b0 = b - (b1<<32);
d = a0*b0;
d1 = d >> 32;
d0 = d - (d1<<32);
e = a0*b1;
e1 = e >> 32;
e0 = e - (e1<<32);
f = a1*b0;
f1 = f >> 32;
f0 = f - (f1<<32);
g = a1*b1;
g1 = g >> 32;
g0 = g - (g1<<32);
sum = d1+e0+f0;
carry = 0;
/* Can't say 1<<32 cause cpp will goof it up; 1ULL<<32 might work */
roll = 1<<30;
roll <<= 2;
pmax = roll-1;
while (pmax < sum)
{
sum -= roll;
carry ++;
}
prod.lo = d0 + (sum<<32);
prod.hi = carry + e1 + f1 + g0 + (g1<<32);
// prod.isbig = (prod.hi || (sum >> 31));
prod.isbig = prod.hi || (prod.lo >> 63);
return prod;
}
/** Shift right by one bit (i.e. divide by two) */
inline qofint128
shift128 (qofint128 x)
{
guint64 sbit = x.hi & 0x1;
x.hi >>= 1;
x.lo >>= 1;
x.isbig = 0;
if (sbit)
{
x.lo |= HIBIT;
x.isbig = 1;
return x;
}
if (x.hi)
{
x.isbig = 1;
}
return x;
}
/** Shift leftt by one bit (i.e. multiply by two) */
inline qofint128
shiftleft128 (qofint128 x)
{
guint64 sbit;
sbit = x.lo & HIBIT;
x.hi <<= 1;
x.lo <<= 1;
x.isbig = 0;
if (sbit)
{
x.hi |= 1;
x.isbig = 1;
return x;
}
if (x.hi)
{
x.isbig = 1;
}
return x;
}
/** increment a 128-bit number by one */
inline qofint128
inc128 (qofint128 a)
{
if (0 == a.isneg)
{
a.lo ++;
if (0 == a.lo)
{
a.hi ++;
}
}
else
{
if (0 == a.lo)
{
a.hi --;
}
a.lo --;
}
a.isbig = (a.hi != 0) || (a.lo >> 63);
return a;
}
/** Divide a signed 128-bit number by a signed 64-bit,
* returning a signed 128-bit number.
*/
inline qofint128
div128 (qofint128 n, gint64 d)
{
qofint128 quotient;
int i;
guint64 remainder = 0;
quotient = n;
if (0 > d)
{
d = -d;
quotient.isneg = !quotient.isneg;
}
/* Use grade-school long division algorithm */
for (i=0; i<128; i++)
{
guint64 sbit = HIBIT & quotient.hi;
remainder <<= 1;
if (sbit) remainder |= 1;
quotient = shiftleft128 (quotient);
if (remainder >= d)
{
remainder -= d;
quotient.lo |= 1;
}
}
/* compute the carry situation */
quotient.isbig = (quotient.hi || (quotient.lo >> 63));
return quotient;
}
/** Return the remainder of a signed 128-bit number modulo
* a signed 64-bit. That is, return n%d in 128-bit math.
* I beleive that ths algo is overflow-free, but should be
* audited some more ...
*/
inline gint64
rem128 (qofint128 n, gint64 d)
{
qofint128 quotient = div128 (n,d);
qofint128 mu = mult128 (quotient.lo, d);
gint64 nn = 0x7fffffffffffffffULL & n.lo;
gint64 rr = 0x7fffffffffffffffULL & mu.lo;
return nn - rr;
}
/** Return true of two numbers are equal */
inline gboolean
equal128 (qofint128 a, qofint128 b)
{
if (a.lo != b.lo) return 0;
if (a.hi != b.hi) return 0;
if (a.isneg != b.isneg) return 0;
return 1;
}
/** Return returns 1 if a>b, -1 if b>a, 0 if a == b */
inline int
cmp128 (qofint128 a, qofint128 b)
{
if ((0 == a.isneg) && b.isneg) return 1;
if (a.isneg && (0 == b.isneg)) return -1;
if (0 == a.isneg)
{
if (a.hi > b.hi) return 1;
if (a.hi < b.hi) return -1;
if (a.lo > b.lo) return 1;
if (a.lo < b.lo) return -1;
return 0;
}
if (a.hi > b.hi) return -1;
if (a.hi < b.hi) return 1;
if (a.lo > b.lo) return -1;
if (a.lo < b.lo) return 1;
return 0;
}
/** Return the greatest common factor of two 64-bit numbers */
inline guint64
gcf64(guint64 num, guint64 denom)
{
guint64 t;
t = num % denom;
num = denom;
denom = t;
/* The strategy is to use Euclid's algorithm */
while (0 != denom)
{
t = num % denom;
num = denom;
denom = t;
}
/* num now holds the GCD (Greatest Common Divisor) */
return num;
}
/** Return the least common multiple of two 64-bit numbers. */
inline qofint128
lcm128 (guint64 a, guint64 b)
{
guint64 gcf = gcf64 (a,b);
b /= gcf;
return mult128 (a,b);
}
/** Add a pair of 128-bit numbers, returning a 128-bit number */
inline qofint128
add128 (qofint128 a, qofint128 b)
{
qofint128 sum;
if (a.isneg == b.isneg)
{
sum.isneg = a.isneg;
sum.hi = a.hi + b.hi;
sum.lo = a.lo + b.lo;
if ((sum.lo < a.lo) || (sum.lo < b.lo))
{
sum.hi ++;
}
sum.isbig = sum.hi || (sum.lo >> 63);
return sum;
}
if ((b.hi > a.hi) ||
((b.hi == a.hi) && (b.lo > a.lo)))
{
qofint128 tmp = a;
a = b;
b = tmp;
}
sum.isneg = a.isneg;
sum.hi = a.hi - b.hi;
sum.lo = a.lo - b.lo;
if (sum.lo > a.lo)
{
sum.hi --;
}
sum.isbig = sum.hi || (sum.lo >> 63);
return sum;
}
#ifdef TEST_128_BIT_MULT
static void pr (gint64 a, gint64 b)
{
qofint128 prod = mult128 (a,b);
printf ("%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT " = %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " (0x%llx %llx) %hd\n",
a, b, prod.hi, prod.lo, prod.hi, prod.lo, prod.isbig);
}
static void prd (gint64 a, gint64 b, gint64 c)
{
qofint128 prod = mult128 (a,b);
qofint128 quot = div128 (prod, c);
gint64 rem = rem128 (prod, c);
printf ("%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " = %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " + %" G_GINT64_FORMAT " (0x%llx %llx) %hd\n",
a, b, c, quot.hi, quot.lo, rem, quot.hi, quot.lo, quot.isbig);
}
int main ()
{
pr (2,2);
gint64 x = 1<<30;
x <<= 2;
pr (x,x);
pr (x+1,x);
pr (x+1,x+1);
pr (x,-x);
pr (-x,-x);
pr (x-1,x);
pr (x-1,x-1);
pr (x-2,x-2);
x <<= 1;
pr (x,x);
pr (x,-x);
pr (1000000, 10000000000000);
prd (x,x,2);
prd (x,x,3);
prd (x,x,4);
prd (x,x,5);
prd (x,x,6);
x <<= 29;
prd (3,x,3);
prd (6,x,3);
prd (99,x,3);
prd (100,x,5);
prd (540,x,5);
prd (777,x,7);
prd (1111,x,11);
/* Really test division */
qofint128 n;
n.hi = 0xdd91;
n.lo = 0x6c5abefbb9e13480ULL;
gint64 d = 0x2ae79964d3ae1d04ULL;
int i;
for (i=0; i<20; i++) {
qofint128 quot = div128 (n, d);
printf ("%d result = %llx %llx\n", i, quot.hi, quot.lo);
d >>=1;
n = shift128 (n);
}
return 0;
}
#endif /* TEST_128_BIT_MULT */
/* ======================== END OF FILE =================== */

View File

@ -1,85 +0,0 @@
/********************************************************************
* qofmath128.h -- an 128-bit integer library *
* Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
* *
* 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 *
* *
*******************************************************************/
#ifndef QOF_MATH_128_H
#define QOF_MATH_128_H
#include <glib.h>
/** @addtogroup Math128
* Quick-n-dirty 128-bit integer math lib. Things seem to mostly
* work, and have been tested, but not comprehensively tested.
* @{
*/
typedef struct {
guint64 hi;
guint64 lo;
short isneg; /**< sign-bit -- T if number is negative */
short isbig; /**< sizeflag -- T if number won't fit in signed 64-bit */
} qofint128;
/** Return true of two numbers are equal */
inline gboolean equal128 (qofint128 a, qofint128 b);
/** Return returns 1 if a>b, -1 if b>a, 0 if a == b */
inline int cmp128 (qofint128 a, qofint128 b);
/** Shift right by one bit (i.e. divide by two) */
inline qofint128 shift128 (qofint128 x);
/** Shift left by one bit (i.e. multiply by two) */
inline qofint128 shiftleft128 (qofint128 x);
/** Increment by one */
inline qofint128 inc128 (qofint128 a);
/** Add a pair of 128-bit numbers, returning a 128-bit number */
inline qofint128 add128 (qofint128 a, qofint128 b);
/** Multiply a pair of signed 64-bit numbers,
* returning a signed 128-bit number.
*/
inline qofint128 mult128 (gint64 a, gint64 b);
/** Divide a signed 128-bit number by a signed 64-bit,
* returning a signed 128-bit number.
*/
inline qofint128 div128 (qofint128 n, gint64 d);
/** Return the remainder of a signed 128-bit number modulo
* a signed 64-bit. That is, return n%d in 128-bit math.
* I beleive that ths algo is overflow-free, but should be
* audited some more ...
*/
inline gint64 rem128 (qofint128 n, gint64 d);
/** Return the greatest common factor of two 64-bit numbers */
inline guint64 gcf64(guint64 num, guint64 denom);
/** Return the least common multiple of two 64-bit numbers. */
inline qofint128 lcm128 (guint64 a, guint64 b);
#endif
/** @} */

View File

@ -1,49 +0,0 @@
/********************************************************************\
* qofobject-p.h -- the private Object Registration/Lookup Interface *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Object_Private
Private interfaces, not meant to be used by applications.
@{ */
/** @name Objects_Private
@{ */
/** @file qofobject-p.h
* @brief the Core Object Registration/Lookup Private Interface
* @author Copyright (c) 2001,2002, Derek Atkins <warlord@MIT.EDU>
*/
#ifndef QOF_OBJECT_P_H_
#define QOF_OBJECT_P_H_
#include "qofbook.h"
#include "qofobject.h"
/** To be called from within the book */
void qof_object_book_begin (QofBook *book);
void qof_object_book_end (QofBook *book);
gboolean qof_object_is_dirty (QofBook *book);
void qof_object_mark_clean (QofBook *book);
#endif /* QOF_OBJECT_P_H_ */
/** @} */
/** @} */
/** @} */

View File

@ -1,353 +0,0 @@
/********************************************************************\
* qofobject.c -- the Core Object Registration/Lookup Interface *
* 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 *
* *
\********************************************************************/
/*
* qofobject.c -- the Core Object Object Registry
* Copyright (C) 2001 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
#include "config.h"
#include <glib.h>
#include "gnc-trace.h"
#include "gnc-engine-util.h"
#include "qofobject.h"
#include "qofobject-p.h"
#include "qofbook.h"
static QofLogModule log_module = QOF_MOD_OBJECT;
static gboolean object_is_initialized = FALSE;
static GList *object_modules = NULL;
static GList *book_list = NULL;
static GHashTable *backend_data = NULL;
gpointer
qof_object_new_instance (QofIdTypeConst type_name, QofBook *book)
{
const QofObject *obj;
if (!type_name) return NULL;
obj = qof_object_lookup (type_name);
if (!obj) return NULL;
if (obj->create)
return (obj->create (book));
return NULL;
}
void qof_object_book_begin (QofBook *book)
{
GList *l;
if (!book) return;
ENTER (" ");
for (l = object_modules; l; l = l->next) {
QofObject *obj = l->data;
if (obj->book_begin)
obj->book_begin (book);
}
/* Remember this book for later */
book_list = g_list_prepend (book_list, book);
LEAVE (" ");
}
void qof_object_book_end (QofBook *book)
{
GList *l;
if (!book) return;
ENTER (" ");
for (l = object_modules; l; l = l->next) {
QofObject *obj = l->data;
if (obj->book_end)
obj->book_end (book);
}
/* Remove it from the list */
book_list = g_list_remove (book_list, book);
LEAVE (" ");
}
gboolean
qof_object_is_dirty (QofBook *book)
{
GList *l;
if (!book) return FALSE;
for (l = object_modules; l; l = l->next)
{
QofObject *obj = l->data;
if (obj->is_dirty)
{
QofCollection *col;
col = qof_book_get_collection (book, obj->e_type);
if (obj->is_dirty (col)) return TRUE;
}
}
return FALSE;
}
void
qof_object_mark_clean (QofBook *book)
{
GList *l;
if (!book) return;
for (l = object_modules; l; l = l->next)
{
QofObject *obj = l->data;
if (obj->mark_clean)
{
QofCollection *col;
col = qof_book_get_collection (book, obj->e_type);
(obj->mark_clean) (col);
}
}
}
void qof_object_foreach_type (QofForeachTypeCB cb, gpointer user_data)
{
GList *l;
if (!cb) return;
for (l = object_modules; l; l = l->next) {
QofObject *obj = l->data;
(cb) (obj, user_data);
}
}
void
qof_object_foreach (QofIdTypeConst type_name, QofBook *book,
QofEntityForeachCB cb, gpointer user_data)
{
QofCollection *col;
const QofObject *obj;
if (!book || !type_name) return;
ENTER ("type=%s", type_name);
obj = qof_object_lookup (type_name);
if (!obj)
{
PERR ("No object of type %s", type_name);
return;
}
col = qof_book_get_collection (book, obj->e_type);
PINFO ("lookup obj=%p for type=%s", obj, type_name);
if (!obj) return;
PINFO ("type=%s foreach=%p", type_name, obj->foreach);
if (obj->foreach)
{
obj->foreach (col, cb, user_data);
}
LEAVE ("type=%s", type_name);
return;
}
const char *
qof_object_printable (QofIdTypeConst type_name, gpointer obj)
{
const QofObject *b_obj;
if (!type_name || !obj) return NULL;
b_obj = qof_object_lookup (type_name);
if (!b_obj) return NULL;
if (b_obj->printable)
return (b_obj->printable (obj));
return NULL;
}
const char * qof_object_get_type_label (QofIdTypeConst type_name)
{
const QofObject *obj;
if (!type_name) return NULL;
obj = qof_object_lookup (type_name);
if (!obj) return NULL;
return (obj->type_label);
}
static gboolean clear_table (gpointer key, gpointer value, gpointer user_data)
{
g_hash_table_destroy (value);
return TRUE;
}
/* INITIALIZATION and PRIVATE FUNCTIONS */
void qof_object_initialize (void)
{
if (object_is_initialized) return;
backend_data = g_hash_table_new (g_str_hash, g_str_equal);
object_is_initialized = TRUE;
}
void qof_object_shutdown (void)
{
g_return_if_fail (object_is_initialized == TRUE);
g_hash_table_foreach_remove (backend_data, clear_table, NULL);
g_hash_table_destroy (backend_data);
backend_data = NULL;
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 qof_object_register (const QofObject *object)
{
g_return_val_if_fail (object_is_initialized, FALSE);
if (!object) return FALSE;
g_return_val_if_fail (object->interface_version == QOF_OBJECT_VERSION, 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 QofObject * qof_object_lookup (QofIdTypeConst name)
{
GList *iter;
const QofObject *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->e_type, name))
return obj;
}
return NULL;
}
gboolean qof_object_register_backend (QofIdTypeConst type_name,
const char *backend_name,
gpointer be_data)
{
GHashTable *ht;
g_return_val_if_fail (object_is_initialized, FALSE);
if (!type_name || *type_name == '\0' ||
!backend_name || *backend_name == '\0' ||
!be_data)
return FALSE;
ht = g_hash_table_lookup (backend_data, backend_name);
/* If it doesn't already exist, create a new table for this backend */
if (!ht) {
ht = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (backend_data, (char *)backend_name, ht);
}
/* Now insert the data */
g_hash_table_insert (ht, (char *)type_name, be_data);
return TRUE;
}
gpointer qof_object_lookup_backend (QofIdTypeConst type_name,
const char *backend_name)
{
GHashTable *ht;
if (!type_name || *type_name == '\0' ||
!backend_name || *backend_name == '\0')
return NULL;
ht = g_hash_table_lookup (backend_data, (char *)backend_name);
if (!ht)
return NULL;
return g_hash_table_lookup (ht, (char *)type_name);
}
struct foreach_data {
QofForeachBackendTypeCB cb;
gpointer user_data;
};
static void foreach_backend (gpointer key, gpointer be_item, gpointer arg)
{
char *data_type = key;
struct foreach_data *cb_data = arg;
g_return_if_fail (key && be_item && arg);
/* Call the callback for this data type */
(cb_data->cb) (data_type, be_item, cb_data->user_data);
}
void qof_object_foreach_backend (const char *backend_name,
QofForeachBackendTypeCB cb,
gpointer user_data)
{
GHashTable *ht;
struct foreach_data cb_data;
if (!backend_name || *backend_name == '\0' || !cb)
return;
ht = g_hash_table_lookup (backend_data, (char *)backend_name);
if (!ht)
return;
cb_data.cb = cb;
cb_data.user_data = user_data;
g_hash_table_foreach (ht, foreach_backend, &cb_data);
}
/* ========================= END OF FILE =================== */

View File

@ -1,179 +0,0 @@
/********************************************************************\
* qofobject.h -- the Core Object Registration/Lookup Interface *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Object
@{ */
/** @addtogroup Objects
QOF Objects provide the means for associating
a storage backend to a set of QOF Entities. While an entity
can be though of as an identified instance of some thing, the
QOF Object provides for a way to associate instances with
a storage backend. Storage might be file or SQL storage.
QOF Objects are also used by the query system ....
To work with your own QOF Objects, you can use the QOF
Generator to create sample objects and a mini-application
with the SQL-type query interface.
http://qof-gen.sourceforge.net/
XXX todo, we should split out the storage aspects of this
thing from the 'foreach' that query depends on. These are
kinda unrelated concepts.
@{ */
/** @file qofobject.h
* @brief the Core Object Registration/Lookup Interface
* @author Copyright (c) 2001,2002 Derek Atkins <warlord@MIT.EDU>
*/
#ifndef QOF_OBJECT_H_
#define QOF_OBJECT_H_
#include "qofbook.h"
#include "qofid.h"
#include "qofchoice.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 QOF_OBJECT_VERSION 3
#define QOF_MOD_OBJECT "qof-object"
typedef struct _QofObject QofObject;
typedef void (*QofForeachCB) (gpointer obj, gpointer user_data);
typedef void (*QofForeachTypeCB) (QofObject *type, gpointer user_data);
typedef void (*QofForeachBackendTypeCB) (QofIdTypeConst type,
gpointer backend_data,
gpointer user_data);
/** This is the QofObject Class descriptor
*/
struct _QofObject
{
gint interface_version; /* of this object interface */
QofIdType e_type; /* the Object's QOF_ID */
const char * type_label; /* "Printable" type-label string */
/** Create a new instance of this object type. This routine might be
* NULL if the object type doesn't provide a way of creating new
* instances.
*/
gpointer (*create)(QofBook *);
/** book_begin is called from within the Book routines to create
* module-specific hooks in a book whenever a book is created.
*/
void (*book_begin)(QofBook *);
/** book_end is called when the book is being closed, to clean
* up (and free memory).
*/
void (*book_end)(QofBook *);
/** Determine if there are any dirty items in this book */
gboolean (*is_dirty)(QofCollection *);
/** Mark this object's book clean (for after a load) */
void (*mark_clean)(QofCollection *);
/** Traverse over all of the items in the collection, calling
* the callback on each item. The third argument can be any
* arbitrary caller-supplied data, and is passed to the callback.
* Although (*foreach) may be NULL, allmost all objects should
* provide this routine, as without it, little of interest can
* be done.
*/
void (*foreach)(QofCollection *, QofEntityForeachCB, gpointer);
/** Given a particular item of this type, return a printable string.
*/
const char * (*printable)(gpointer instance);
/** Given a pair of items of this type, this routine returns value
* indicating which item is 'newer'. This routine is used by storage
* backends to determine if the local or the remote copy of a
* particular item is the latest, 'uptodate' version. Tis routine
* should return an integer less than, equal to, or greater than zero
* if 'instance_left' is found to be, respecitvely, earlier than, equal
* to or later than than 'instance_right'.
*/
int (*version_cmp)(gpointer instance_left, gpointer instance_right);
};
/* -------------------------------------------------------------- */
/** @name Initialize the object registration subsystem */
/** @{ */
void qof_object_initialize (void);
void qof_object_shutdown (void);
/** @} */
/** Register new types of object objects */
gboolean qof_object_register (const QofObject *object);
/** Lookup an object definition */
const QofObject * qof_object_lookup (QofIdTypeConst type_name);
/** Create an instance of the indicated type, returning a pointer to that
* instance. This routine just calls the (*new) callback on the object
* definition.
*/
gpointer qof_object_new_instance (QofIdTypeConst type_name, QofBook *book);
/** Get the printable label for a type. This label is *not*
* translated; you must use _() on it if you want a translated version.
*/
const char * qof_object_get_type_label (QofIdTypeConst type_name);
/** @return a Human-readable string name for an instance */
const char * qof_object_printable (QofIdTypeConst type_name, gpointer instance);
/** Invoke the callback 'cb' on every object class definition.
* The user_data pointer is passed back to the callback.
*/
void qof_object_foreach_type (QofForeachTypeCB cb, gpointer user_data);
/** Invoke the callback 'cb' on every instance ov a particular
* object type. It is presumed that the 'book' stores or somehow
* identifies a colllection of instances; thus the callback will
* be invoked only for those instances stored in the book.
*/
void qof_object_foreach (QofIdTypeConst type_name, QofBook *book,
QofEntityForeachCB cb, gpointer user_data);
/** Register and lookup backend-specific data for this particular object */
gboolean qof_object_register_backend (QofIdTypeConst type_name,
const char *backend_name,
gpointer be_data);
gpointer qof_object_lookup_backend (QofIdTypeConst type_name,
const char *backend_name);
void qof_object_foreach_backend (const char *backend_name,
QofForeachBackendTypeCB cb,
gpointer user_data);
#endif /* QOF_OBJECT_H_ */
/** @} */
/** @} */

View File

@ -1,756 +0,0 @@
/********************************************************************\
* qofquery-deserial.c -- Convert Qof-Query XML to QofQuery *
* Copyright (C) 2001,2002,2004 Linas Vepstas <linas@linas.org> *
* *
* 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 <stdlib.h>
#include <glib.h>
#include <libxml/parser.h>
#include "qofquery-deserial.h"
#include "qofquery-serialize.h"
#include "qofquery-p.h"
#include "qofquerycore-p.h"
#include "gnc-engine-util.h"
/* =========================================================== */
#define GET_TEXT(node) ({ \
char * sstr = NULL; \
xmlNodePtr text; \
text = node->xmlChildrenNode; \
if (text && 0 == strcmp ("text", text->name)) { \
sstr = text->content; \
} \
sstr; \
})
#define GET_STR(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
FN (SELF, str); \
} \
else
#define GET_DBL(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
double rate = atof (str); \
FN (SELF, rate); \
} \
else
#define GET_INT32(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
gint32 ival = atoi (str); \
FN (SELF, ival); \
} \
else
#define GET_INT64(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
gint64 ival = atoll (str); \
FN (SELF, ival); \
} \
else
#define GET_DATE(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
Timespec tval = gnc_iso8601_to_timespec_gmt (str); \
FN (SELF, tval); \
} \
else
#define GET_BOOL(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
gboolean bval = qof_util_bool_to_int (str); \
FN (SELF, bval); \
} \
else
#define GET_NUMERIC(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
gnc_numeric nval; \
string_to_gnc_numeric (str, &nval); \
FN (SELF, nval); \
} \
else
#define GET_GUID(SELF,FN,TOK) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
GUID guid; \
string_to_guid (str, &guid); \
FN (SELF, &guid); \
} \
else
#define GET_HOW(VAL,TOK,A,B,C,D,E,F) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
int ival = QOF_COMPARE_##A; \
if (!strcmp (#A, str)) ival = QOF_COMPARE_##A; \
else if (!strcmp (#B, str)) ival = QOF_COMPARE_##B; \
else if (!strcmp (#C, str)) ival = QOF_COMPARE_##C; \
else if (!strcmp (#D, str)) ival = QOF_COMPARE_##D; \
else if (!strcmp (#E, str)) ival = QOF_COMPARE_##E; \
else if (!strcmp (#F, str)) ival = QOF_COMPARE_##F; \
VAL = ival; \
} \
else
#define GET_MATCH2(VAL,TOK,PFX,A,B) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
int ival = QOF_##PFX##_##A; \
if (!strcmp (#A, str)) ival = QOF_##PFX##_##A; \
else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B; \
VAL = ival; \
} \
else
#define GET_MATCH3(VAL,TOK,PFX,A,B,C) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
int ival = QOF_##PFX##_##A; \
if (!strcmp (#A, str)) ival = QOF_##PFX##_##A; \
else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B; \
else if (!strcmp (#C, str)) ival = QOF_##PFX##_##C; \
VAL = ival; \
} \
else
#define GET_MATCH5(VAL,TOK,PFX,A,B,C,D,E) \
if (0 == strcmp (TOK, node->name)) \
{ \
const char *str = GET_TEXT (node); \
int ival = QOF_##PFX##_##A; \
if (!strcmp (#A, str)) ival = QOF_##PFX##_##A; \
else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B; \
else if (!strcmp (#C, str)) ival = QOF_##PFX##_##C; \
else if (!strcmp (#D, str)) ival = QOF_##PFX##_##D; \
else if (!strcmp (#E, str)) ival = QOF_##PFX##_##E; \
VAL = ival; \
} \
else
/* =============================================================== */
/* Autogen the code for the simple, repetitive predicates */
#define SIMPLE_PRED_HANDLER(SUBRNAME,CTYPE,GETTER,XMLTYPE,PRED) \
static QofQueryPredData * \
SUBRNAME (xmlNodePtr root) \
{ \
QofQueryPredData *pred; \
xmlNodePtr xp; \
xmlNodePtr node; \
QofQueryCompare how; \
CTYPE val; \
xp = root->xmlChildrenNode; \
\
how = QOF_COMPARE_EQUAL; \
val = 0; \
\
for (node=xp; node; node = node->next) \
{ \
if (node->type != XML_ELEMENT_NODE) continue; \
\
GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ); \
GETTER (0, val=, XMLTYPE); \
{} \
} \
\
pred = PRED (how, val); \
return pred; \
}
SIMPLE_PRED_HANDLER (qof_query_pred_double_from_xml,
double,
GET_DBL,
"qofquery:double",
qof_query_double_predicate);
SIMPLE_PRED_HANDLER (qof_query_pred_int64_from_xml,
gint64,
GET_INT64,
"qofquery:int64",
qof_query_int64_predicate);
SIMPLE_PRED_HANDLER (qof_query_pred_int32_from_xml,
gint32,
GET_INT32,
"qofquery:int32",
qof_query_int32_predicate);
SIMPLE_PRED_HANDLER (qof_query_pred_boolean_from_xml,
gboolean,
GET_BOOL,
"qofquery:boolean",
qof_query_boolean_predicate);
/* =============================================================== */
static void wrap_new_gint64(KvpValue **v, gint64 value) {
*v = kvp_value_new_gint64 (value); }
static void wrap_new_double(KvpValue **v, double value) {
*v = kvp_value_new_double (value); }
static void wrap_new_numeric(KvpValue **v, gnc_numeric value) {
*v = kvp_value_new_gnc_numeric (value); }
static void wrap_new_string(KvpValue **v, const char * value) {
*v = kvp_value_new_string (value); }
static void wrap_new_guid(KvpValue **v, const GUID * value) {
*v = kvp_value_new_guid (value); }
static void wrap_new_timespec(KvpValue **v, Timespec value) {
*v = kvp_value_new_timespec (value); }
static QofQueryPredData *
qof_query_pred_kvp_from_xml (xmlNodePtr root)
{
QofQueryCompare how;
GSList *path;
KvpValue *value;
QofQueryPredData *pred;
xmlNodePtr xp;
xmlNodePtr node;
how = QOF_COMPARE_EQUAL;
xp = root->xmlChildrenNode;
path = NULL;
value = NULL;
for (node=xp; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
if (0 == strcmp ("qofquery:kvp-path", node->name))
{
const char *str = GET_TEXT (node);
path = g_slist_append (path, (gpointer) str);
}
else
GET_INT64(&value, wrap_new_gint64, "qofquery:int64");
GET_DBL(&value, wrap_new_double, "qofquery:double");
GET_NUMERIC(&value, wrap_new_numeric, "qofquery:numeric");
GET_STR(&value, wrap_new_string, "qofquery:string");
GET_GUID(&value, wrap_new_guid, "qofquery:guid");
GET_DATE(&value, wrap_new_timespec, "qofquery:date");
}
pred = qof_query_kvp_predicate (how, path, value);
g_slist_free (path);
return pred;
}
/* =============================================================== */
static QofQueryPredData *
qof_query_pred_guid_from_xml (xmlNodePtr root)
{
GList *guid_list, *n;
const char *str;
GUID *guid;
gboolean decode;
QofQueryPredData *pred;
QofGuidMatch sm;
xmlNodePtr xp;
xmlNodePtr node;
guid_list = NULL;
sm = QOF_GUID_MATCH_ANY;
xp = root->xmlChildrenNode;
for (node=xp; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
/* char pred doesn't have GET_HOW */
GET_MATCH5 (sm, "qofquery:guid-match",
GUID_MATCH, ANY, NONE, NULL, ALL, LIST_ANY);
if (0 == strcmp ("qofquery:guid", node->name))
{
str = GET_TEXT (node);
guid = guid_malloc ();
decode = string_to_guid (str, guid);
if (decode)
{
guid_list = g_list_append (guid_list, guid);
}
else
{
guid_free (guid);
// XXX error! let someone know!
}
}
}
pred = qof_query_guid_predicate (sm, guid_list);
/* The predicate made a copy of everything, so free our stuff */
for (n=guid_list; n; n=n->next)
{
guid_free (n->data);
}
g_list_free (guid_list);
return pred;
}
/* =============================================================== */
static QofQueryPredData *
qof_query_pred_char_from_xml (xmlNodePtr root)
{
QofQueryPredData *pred;
QofCharMatch sm;
const char * char_list;
xmlNodePtr xp;
xmlNodePtr node;
char_list = NULL;
xp = root->xmlChildrenNode;
sm = QOF_CHAR_MATCH_ANY;
for (node=xp; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
/* char pred doesn't have GET_HOW */
GET_MATCH2 (sm, "qofquery:char-match",
CHAR_MATCH, ANY, NONE);
GET_STR (0, char_list=, "qofquery:char-list");
{}
}
pred = qof_query_char_predicate (sm, char_list);
return pred;
}
/* =============================================================== */
static QofQueryPredData *
qof_query_pred_numeric_from_xml (xmlNodePtr root)
{
QofQueryPredData *pred;
xmlNodePtr node;
QofQueryCompare how;
QofNumericMatch sm;
gnc_numeric num;
xmlNodePtr xp;
xp = root->xmlChildrenNode;
how = QOF_COMPARE_EQUAL;
sm = QOF_NUMERIC_MATCH_ANY;
for (node=xp; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
GET_MATCH3 (sm, "qofquery:numeric-match",
NUMERIC_MATCH, DEBIT, CREDIT, ANY);
GET_NUMERIC (0, num=, "qofquery:numeric");
{}
}
pred = qof_query_numeric_predicate (how, sm, num);
return pred;
}
/* =============================================================== */
static QofQueryPredData *
qof_query_pred_date_from_xml (xmlNodePtr root)
{
xmlNodePtr xp;
xmlNodePtr node;
QofQueryCompare how;
QofDateMatch sm;
Timespec date;
QofQueryPredData *pred;
xp = root->xmlChildrenNode;
how = QOF_COMPARE_EQUAL;
sm = QOF_DATE_MATCH_DAY;
date = (Timespec){0,0};
for (node=xp; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
GET_MATCH2 (sm, "qofquery:date-match",
DATE_MATCH, NORMAL, DAY);
GET_DATE (0, date=, "qofquery:date");
{}
}
pred = qof_query_date_predicate (how, sm, date);
return pred;
}
/* =============================================================== */
static QofQueryPredData *
qof_query_pred_string_from_xml (xmlNodePtr root)
{
QofQueryPredData *pred;
xmlNodePtr xp;
xmlNodePtr node;
QofQueryCompare how;
QofStringMatch sm;
gboolean is_regex;
const char *pstr;
xp = root->xmlChildrenNode;
how = QOF_COMPARE_EQUAL;
sm = QOF_STRING_MATCH_CASEINSENSITIVE;
is_regex = FALSE;
pstr = NULL;
for (node=xp; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
GET_BOOL (0, is_regex=, "qofquery:is-regex");
GET_STR (0, pstr=, "qofquery:string");
GET_MATCH2 (sm, "qofquery:string-match",
STRING_MATCH, NORMAL, CASEINSENSITIVE);
{}
}
pred = qof_query_string_predicate (how, pstr, sm , is_regex);
return pred;
}
/* =============================================================== */
static GSList *
qof_query_param_path_from_xml (xmlNodePtr root)
{
xmlNodePtr pterms;
GSList *plist;
xmlNodePtr node;
pterms = root->xmlChildrenNode;
plist = NULL;
for (node=pterms; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
if (0 == strcmp (node->name, "qofquery:param"))
{
const char *str = GET_TEXT (node);
/* BUG? I can't find the matching cache removal. */
plist = g_slist_append (plist, CACHE_INSERT(str));
}
}
return plist;
}
/* =============================================================== */
static void
qof_query_term_from_xml (QofQuery *q, xmlNodePtr root)
{
xmlNodePtr node;
xmlNodePtr term;
QofQueryPredData *pred;
GSList *path;
QofQuery *qt;
QofQuery *qinv;
pred = NULL;
path = NULL;
term = root->xmlChildrenNode;
for (node=term; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
if (0 == strcmp (node->name, "qofquery:invert"))
{
qt = qof_query_create();
qof_query_term_from_xml (qt, node);
qinv = qof_query_invert (qt);
qof_query_merge_in_place (q, qinv, QOF_QUERY_AND);
qof_query_destroy (qinv);
qof_query_destroy (qt);
return;
}
else
if (0 == strcmp (node->name, "qofquery:param-path"))
{
path = qof_query_param_path_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-string"))
{
pred = qof_query_pred_string_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-date"))
{
pred = qof_query_pred_date_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-numeric"))
{
pred = qof_query_pred_numeric_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-int32"))
{
pred = qof_query_pred_int32_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-int64"))
{
pred = qof_query_pred_int64_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-double"))
{
pred = qof_query_pred_double_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-boolean"))
{
pred = qof_query_pred_boolean_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-char"))
{
pred = qof_query_pred_char_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-guid"))
{
pred = qof_query_pred_guid_from_xml (node);
}
else
if (0 == strcmp (node->name, "qofquery:pred-kvp"))
{
pred = qof_query_pred_kvp_from_xml (node);
}
else
{
// warning unhandled predicate type
}
}
/* At this level, the terms should always be anded */
qof_query_add_term (q, path, pred, QOF_QUERY_AND);
}
/* =============================================================== */
static void
qof_query_and_terms_from_xml (QofQuery *q, xmlNodePtr root)
{
xmlNodePtr andterms;
xmlNodePtr node;
andterms = root->xmlChildrenNode;
for (node=andterms; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
if (0 == strcmp (node->name, "qofquery:term"))
{
qof_query_term_from_xml (q, node);
}
}
}
/* =============================================================== */
static void
qof_query_or_terms_from_xml (QofQuery *q, xmlNodePtr root)
{
xmlNodePtr andterms;
xmlNodePtr node;
QofQuery *qand;
andterms = root->xmlChildrenNode;
for (node=andterms; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
if (0 == strcmp (node->name, "qofquery:and-terms"))
{
qand = qof_query_create ();
qof_query_and_terms_from_xml (qand, node);
qof_query_merge_in_place (q, qand, QOF_QUERY_OR);
qof_query_destroy (qand);
}
}
}
/* =============================================================== */
QofQuery *
qof_query_from_xml (xmlNodePtr root)
{
QofQuery *q;
xmlChar *version;
xmlNodePtr qpart;
xmlNodePtr node;
if (!root) return NULL;
version = xmlGetProp(root, "version");
if (!root->name || strcmp ("qof:qofquery", root->name))
{
// XXX something is wrong. warn ...
return NULL;
}
q = qof_query_create ();
qpart = root->xmlChildrenNode;
for (node=qpart; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE) continue;
GET_STR (q, qof_query_search_for, "qofquery:search-for");
GET_INT32 (q, qof_query_set_max_results, "qofquery:max-results");
if (0 == strcmp (node->name, "qofquery:or-terms"))
{
qof_query_or_terms_from_xml (q, node);
}
else
if (0 == strcmp (node->name, "qofquery:sort-list"))
{
// XXX unfinished I'm bored
}
else
{
// XXX unknown node type tell someone about it
}
}
return q;
}
/* =============================================================== */
#ifdef UNIT_TEST
#include <stdio.h>
#include <qof/qofsql.h>
int main (int argc, char * argv[])
{
QofQuery *q, *qnew;
QofSqlQuery *sq;
xmlNodePtr topnode;
guid_init();
qof_query_init();
qof_object_initialize ();
static QofParam params[] = {
{ "adate", QOF_TYPE_DATE, NULL, NULL},
{ "aint", QOF_TYPE_INT32, NULL, NULL},
{ "aint64", QOF_TYPE_INT64, NULL, NULL},
{ "aflt", QOF_TYPE_DOUBLE, NULL, NULL},
{ "abool", QOF_TYPE_BOOLEAN, NULL, NULL},
{ "astr", QOF_TYPE_STRING, NULL, NULL},
{ "adate", QOF_TYPE_DATE, NULL, NULL},
{ "anum", QOF_TYPE_NUMERIC, NULL, NULL},
{ "achar", QOF_TYPE_CHAR, NULL, NULL},
{ "aguid", QOF_TYPE_GUID, NULL, NULL},
{ "akvp", QOF_TYPE_KVP, NULL, NULL},
{ NULL },
};
qof_class_register ("GncABC", NULL, params);
sq = qof_sql_query_new();
qof_sql_query_parse (sq,
"SELECT * from GncABC WHERE aint = 123 "
"and not aint64 = 6123123456789 "
"or abool = TRUE "
"and not aflt >= \'3.14159265358979\' "
"and not astr=\'asdf\' "
"and adate<\'01-01-01\' "
"or anum<\'12301/100\' "
"or achar != asdf "
"and aguid != abcdef01234567890fedcba987654321 "
"and akvp != \'/some/path:abcdef01234567890fedcba987654321\' "
"and not akvp != \'/some/path/glop:1234\' "
"and akvp = \'/arf/arf/arf:10.234\' "
"and akvp != \'/some/other/path:qwerty1234uiop\' "
"and not akvp = \'/some/final/path:123401/100\' "
);
// qof_sql_query_parse (sq, "SELECT * from GncABC;");
q = qof_sql_query_get_query (sq);
qof_query_print (q);
xmlNodePtr topnode = qof_query_to_xml (q);
qnew = qof_query_from_xml (topnode);
printf (" ------------------------------------------------------- \n");
qof_query_print (qnew);
/* If the before and after trees are the same, the test pases. */
gboolean eq = qof_query_equal (q, qnew);
printf ("Are the two equal? answer=%d\n", eq);
#define DOPRINT 1
#ifdef DOPRINT
xmlDocPtr doc = doc = xmlNewDoc("1.0");
xmlDocSetRootElement(doc,topnode);
xmlChar *xbuf;
int bufsz;
xmlDocDumpFormatMemory (doc, &xbuf, &bufsz, 1);
printf ("%s\n", xbuf);
xmlFree (xbuf);
xmlFreeDoc(doc);
#endif
return 0;
}
#endif /* UNIT_TEST */
/* ======================== END OF FILE =================== */

View File

@ -1,47 +0,0 @@
/********************************************************************\
* qofquery-deserial.h -- Convert Qof-Query XML to QofQuery *
* Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
* *
* 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 *
* *
\********************************************************************/
/*
qofquery-deserial.h
Convert Qof-Query XML to QofQuery
author Copyright (C) 2004 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_QUERY_DESERIAL_H
#define QOF_QUERY_DESERIAL_H
#include "qofquery.h"
#include <libxml/tree.h>
/*
Qof Queries can be converted to and from XML so that they
can be sent from here to there. This file implements the
routine needed to convert the XML back into a C struct.
Unfinished. XXX Why is this easier than reading a text/sql
file?
*/
/* Given an XML tree, reconstruct and return the equivalent query. */
QofQuery *qof_query_from_xml (xmlNodePtr);
#endif /* QOF_QUERY_DESERIAL_H */

View File

@ -1,65 +0,0 @@
/********************************************************************\
* qofquery-p.h -- internal/private API for finding objects *
* 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 *
* *
\********************************************************************/
#ifndef QOF_QUERY_P_H
#define QOF_QUERY_P_H
#include "qofquery.h"
typedef struct _QofQueryTerm QofQueryTerm;
typedef struct _QofQuerySort QofQuerySort;
/* Functions to get Query information */
int qof_query_get_max_results (QofQuery *q);
/* Functions to get and look at QueryTerms */
/* This returns a List of List of Query Terms. Each list of Query
* Terms are ANDed together, and each list of ANDed terms are ORed
* together. So, what is returned is the 'or' list of 'and' lists
* of query term objects.
*
* Note that you should NOT modify this list in any way. It belongs
* to the query.
*/
GList * qof_query_get_terms (QofQuery *q);
GSList * qof_query_term_get_param_path (QofQueryTerm *queryterm);
QofQueryPredData *qof_query_term_get_pred_data (QofQueryTerm *queryterm);
gboolean qof_query_term_is_inverted (QofQueryTerm *queryterm);
/* Functions to get and look at QuerySorts */
/* This function returns the primary, secondary, and tertiary sorts.
* These are part of the query and should NOT be changed!
*/
void qof_query_get_sorts (QofQuery *q, QofQuerySort **primary,
QofQuerySort **secondary, QofQuerySort **tertiary);
GSList * qof_query_sort_get_param_path (QofQuerySort *querysort);
gint qof_query_sort_get_sort_options (QofQuerySort *querysort);
gboolean qof_query_sort_get_increasing (QofQuerySort *querysort);
#endif /* QOF_QUERY_P_H */

View File

@ -1,597 +0,0 @@
/********************************************************************\
* qofquery-serialize.c -- Convert QofQuery to XML *
* Copyright (C) 2001,2002,2004 Linas Vepstas <linas@linas.org> *
* *
* 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 "qofquery-serialize.h"
#include "qofquery-p.h"
#include "qofquerycore-p.h"
#include "kvp_frame.h"
/* ======================================================= */
#define PUT_STR(TOK,VAL) { \
xmlNodePtr node; \
const char * str = (VAL); \
if (str && 0 != str[0]) \
{ \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, str); \
xmlAddChild (topnode, node); \
} \
}
#define PUT_INT32(TOK,VAL) { \
xmlNodePtr node; \
char buff[80]; \
g_snprintf (buff, sizeof(buff), "%d", (VAL)); \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, buff); \
xmlAddChild (topnode, node); \
}
#define PUT_INT64(TOK,VAL) { \
xmlNodePtr node; \
char buff[80]; \
g_snprintf (buff, sizeof(buff), "%" G_GINT64_FORMAT, (VAL)); \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, buff); \
xmlAddChild (topnode, node); \
}
#define PUT_DBL(TOK,VAL) { \
xmlNodePtr node; \
char buff[80]; \
g_snprintf (buff, sizeof(buff), "%.18g", (VAL)); \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, buff); \
xmlAddChild (topnode, node); \
}
#define PUT_GUID(TOK,VAL) { \
xmlNodePtr node; \
char buff[80]; \
guid_to_string_buff ((VAL), buff); \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, buff); \
xmlAddChild (topnode, node); \
}
#define PUT_DATE(TOK,VAL) { \
xmlNodePtr node; \
char buff[80]; \
gnc_timespec_to_iso8601_buff ((VAL), buff); \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, buff); \
xmlAddChild (topnode, node); \
}
#define PUT_NUMERIC(TOK,VAL) { \
xmlNodePtr node; \
char *str; \
str = gnc_numeric_to_string (VAL); \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, str); \
g_free (str); \
xmlAddChild (topnode, node); \
}
#define PUT_BOOL(TOK,VAL) { \
xmlNodePtr node; \
gboolean boll = (VAL); \
node = xmlNewNode (NULL, TOK); \
if (boll) { \
xmlNodeAddContent(node, "T"); \
} else { \
xmlNodeAddContent(node, "F"); \
} \
xmlAddChild (topnode, node); \
}
#define PUT_HOW(TOK,VAL,A,B,C,D,E,F) { \
xmlNodePtr node; \
const char * str = "EQUAL"; \
switch (VAL) \
{ \
case QOF_COMPARE_##A: str = #A; break; \
case QOF_COMPARE_##B: str = #B; break; \
case QOF_COMPARE_##C: str = #C; break; \
case QOF_COMPARE_##D: str = #D; break; \
case QOF_COMPARE_##E: str = #E; break; \
case QOF_COMPARE_##F: str = #F; break; \
} \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, str); \
xmlAddChild (topnode, node); \
}
#define PUT_MATCH2(TOK,VAL,PFX,A,B) { \
xmlNodePtr node; \
const char * str = #A; \
switch (VAL) \
{ \
case QOF_##PFX##_##A: str = #A; break; \
case QOF_##PFX##_##B: str = #B; break; \
} \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, str); \
xmlAddChild (topnode, node); \
}
#define PUT_MATCH3(TOK,VAL,PFX,A,B,C) { \
xmlNodePtr node; \
const char * str = #A; \
switch (VAL) \
{ \
case QOF_##PFX##_##A: str = #A; break; \
case QOF_##PFX##_##B: str = #B; break; \
case QOF_##PFX##_##C: str = #C; break; \
} \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, str); \
xmlAddChild (topnode, node); \
}
#define PUT_MATCH5(TOK,VAL,PFX,A,B,C,D,E) { \
xmlNodePtr node; \
const char * str = #A; \
switch (VAL) \
{ \
case QOF_##PFX##_##A: str = #A; break; \
case QOF_##PFX##_##B: str = #B; break; \
case QOF_##PFX##_##C: str = #C; break; \
case QOF_##PFX##_##D: str = #D; break; \
case QOF_##PFX##_##E: str = #E; break; \
} \
node = xmlNewNode (NULL, TOK); \
xmlNodeAddContent(node, str); \
xmlAddChild (topnode, node); \
}
/* ======================================================= */
static void
qof_kvp_value_to_xml (KvpValue *kval, xmlNodePtr topnode)
{
KvpValueType kvt = kvp_value_get_type (kval);
switch (kvt)
{
case KVP_TYPE_GINT64:
PUT_INT64 ("qofquery:int64", kvp_value_get_gint64(kval));
break;
case KVP_TYPE_DOUBLE:
PUT_DBL ("qofquery:double", kvp_value_get_double(kval));
break;
case KVP_TYPE_NUMERIC:
PUT_NUMERIC ("qofquery:numeric", kvp_value_get_numeric(kval));
break;
case KVP_TYPE_GUID:
PUT_GUID ("qofquery:guid", kvp_value_get_guid(kval));
break;
case KVP_TYPE_STRING:
PUT_STR ("qofquery:string", kvp_value_get_string(kval));
break;
case KVP_TYPE_TIMESPEC:
PUT_DATE ("qofquery:date", kvp_value_get_timespec(kval));
break;
case KVP_TYPE_BINARY:
case KVP_TYPE_GLIST:
case KVP_TYPE_FRAME:
// XXX don't know how to support these.
break;
}
}
/* ======================================================= */
static xmlNodePtr
qof_query_pred_data_to_xml (QofQueryPredData *pd)
{
GList *n;
GSList *ns;
xmlNodePtr topnode;
query_guid_t pdata_g;
query_string_t pdata_s;
query_numeric_t pdata_n;
query_kvp_t pdata_k;
query_date_t pdata_d;
query_int64_t pdata_i64;
query_int32_t pdata_i32;
query_double_t pdata_db;
query_boolean_t pdata_bool;
query_char_t pdata_c;
if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
{
topnode = xmlNewNode (NULL, "qofquery:pred-guid");
/* GUID Predicate doesn't do a PUT_HOW */
pdata_g = (query_guid_t) pd;
PUT_MATCH5("qofquery:guid-match", pdata_g->options,
GUID_MATCH, ANY, ALL, NONE, NULL, LIST_ANY);
for (n = pdata_g->guids; n; n = n->next)
{
PUT_GUID ("qofquery:guid", n->data);
}
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
{
topnode = xmlNewNode (NULL, "qofquery:pred-string");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_s = (query_string_t) pd;
PUT_MATCH2("qofquery:string-match", pdata_s->options,
STRING_MATCH, NORMAL, CASEINSENSITIVE);
PUT_BOOL ("qofquery:is-regex", pdata_s->is_regex);
PUT_STR ("qofquery:string", pdata_s->matchstring);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
{
topnode = xmlNewNode (NULL, "qofquery:pred-numeric");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_n = (query_numeric_t) pd;
PUT_MATCH3("qofquery:numeric-match", pdata_n->options,
NUMERIC_MATCH, DEBIT, CREDIT, ANY);
PUT_NUMERIC ("qofquery:numeric", pdata_n->amount);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
{
topnode = xmlNewNode (NULL, "qofquery:pred-kvp");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_k = (query_kvp_t) pd;
for (ns=pdata_k->path; ns; ns=ns->next)
{
PUT_STR ("qofquery:kvp-path", ns->data);
}
qof_kvp_value_to_xml (pdata_k->value, topnode);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
{
topnode = xmlNewNode (NULL, "qofquery:pred-date");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_d = (query_date_t) pd;
PUT_MATCH2("qofquery:date-match", pdata_d->options,
DATE_MATCH, NORMAL, DAY);
PUT_DATE ("qofquery:date", pdata_d->date);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
{
topnode = xmlNewNode (NULL, "qofquery:pred-int64");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_i64 = (query_int64_t) pd;
PUT_INT64 ("qofquery:int64", pdata_i64->val);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32))
{
topnode = xmlNewNode (NULL, "qofquery:pred-int32");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_i32 = (query_int32_t) pd;
PUT_INT32 ("qofquery:int32", pdata_i32->val);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
{
topnode = xmlNewNode (NULL, "qofquery:pred-double");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_db = (query_double_t) pd;
PUT_DBL ("qofquery:double", pdata_db->val);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN))
{
topnode = xmlNewNode (NULL, "qofquery:pred-boolean");
PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
pdata_bool = (query_boolean_t) pd;
PUT_BOOL ("qofquery:boolean", pdata_bool->val);
return topnode;
}
if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR))
{
topnode = xmlNewNode (NULL, "qofquery:pred-char");
/* There is no PUT_HOW for char-match */
pdata_c = (query_char_t) pd;
PUT_MATCH2("qofquery:char-match", pdata_c->options,
CHAR_MATCH, ANY, NONE);
PUT_STR ("qofquery:char-list", pdata_c->char_list);
return topnode;
}
return NULL;
}
/* ======================================================= */
static xmlNodePtr
qof_query_param_path_to_xml (GSList *param_path)
{
xmlNodePtr topnode;
GSList *n;
QofIdTypeConst path;
n = param_path;
topnode = xmlNewNode (NULL, "qofquery:param-path");
for ( ; n; n=n->next)
{
path = n->data;
if (!path) continue;
PUT_STR ("qofquery:param", path);
}
return topnode;
}
/* ======================================================= */
static xmlNodePtr
qof_query_one_term_to_xml (QofQueryTerm *qt)
{
xmlNodePtr node;
xmlNodePtr term;
xmlNodePtr topnode;
gboolean invert;
GSList *path;
QofQueryPredData *pd;
invert = qof_query_term_is_inverted (qt);
term = xmlNewNode (NULL, "qofquery:term");
topnode = term;
path = qof_query_term_get_param_path (qt);
pd = qof_query_term_get_pred_data (qt);
if (invert)
{
/* inverter becomes new top mode */
topnode = xmlNewNode (NULL, "qofquery:invert");
xmlAddChild (term, topnode);
}
node = qof_query_param_path_to_xml (path);
if (node) xmlAddChild (topnode, node);
node = qof_query_pred_data_to_xml (pd);
if (node) xmlAddChild (topnode, node);
return term;
}
/* ======================================================= */
static xmlNodePtr
qof_query_and_terms_to_xml (GList *and_terms)
{
xmlNodePtr terms;
GList *n;
QofQueryTerm *qt;
xmlNodePtr t;
terms = xmlNewNode (NULL, "qofquery:and-terms");
n = and_terms;
for ( ; n; n=n->next)
{
qt = n->data;
if (!qt) continue;
t = qof_query_one_term_to_xml (n->data);
if (t) xmlAddChild (terms, t);
}
return terms;
}
/* ======================================================= */
static xmlNodePtr
qof_query_terms_to_xml (QofQuery *q)
{
xmlNodePtr terms;
GList *n;
xmlNodePtr andt;
terms = NULL;
n = qof_query_get_terms (q);
if (!n) return NULL;
terms = xmlNewNode (NULL, "qofquery:or-terms");
for ( ; n; n=n->next)
{
andt = qof_query_and_terms_to_xml (n->data);
if (andt) xmlAddChild (terms, andt);
}
return terms;
}
/* ======================================================= */
static xmlNodePtr
qof_query_sorts_to_xml (QofQuery *q)
{
QofQuerySort *s[3];
xmlNodePtr sortlist;
GSList *plist;
xmlNodePtr sort;
xmlNodePtr topnode;
gboolean increasing;
gint opt;
xmlNodePtr pl;
int i;
qof_query_get_sorts (q, &s[0], &s[1], &s[2]);
if (NULL == s[0]) return NULL;
sortlist = xmlNewNode (NULL, "qofquery:sort-list");
for (i=0; i<3; i++)
{
if (NULL == s[i]) continue;
plist = qof_query_sort_get_param_path (s[i]);
if (!plist) continue;
sort = xmlNewNode (NULL, "qofquery:sort");
xmlAddChild (sortlist, sort);
topnode = sort;
increasing = qof_query_sort_get_increasing (s[i]);
PUT_STR ("qofquery:order", increasing ? "DESCENDING" : "ASCENDING");
opt = qof_query_sort_get_sort_options (s[i]);
PUT_INT32 ("qofquery:options", opt);
pl = qof_query_param_path_to_xml (plist);
if (pl) xmlAddChild (sort, pl);
}
return sortlist;
}
/* ======================================================= */
static void
do_qof_query_to_xml (QofQuery *q, xmlNodePtr topnode)
{
QofIdType search_for;
xmlNodePtr terms;
xmlNodePtr sorts;
gint max_results;
search_for = qof_query_get_search_for (q);
PUT_STR ("qofquery:search-for", search_for);
terms = qof_query_terms_to_xml(q);
if (terms) xmlAddChild (topnode, terms);
sorts = qof_query_sorts_to_xml (q);
if (sorts) xmlAddChild (topnode, sorts);
max_results = qof_query_get_max_results (q);
PUT_INT32 ("qofquery:max-results", max_results);
}
/* ======================================================= */
xmlNodePtr
qof_query_to_xml (QofQuery *q)
{
xmlNodePtr topnode;
xmlNodePtr node;
xmlNsPtr ns;
topnode = xmlNewNode(NULL, "qof:qofquery");
xmlSetProp(topnode, "version", "1.0.1");
// XXX path to DTD is wrong
// ns = xmlNewNs (topnode, "file:" "/usr/share/lib" "/qofquery.dtd", "qof");
do_qof_query_to_xml (q, topnode);
return topnode;
}
/* =============================================================== */
#ifdef UNIT_TEST
#include <stdio.h>
#include <qof/qofsql.h>
int main (int argc, char * argv[])
{
QofQuery *q;
QofSqlQuery *sq;
xmlDocPtr doc;
xmlNodePtr topnode;
xmlChar *xbuf;
int bufsz;
xmlOutputBufferPtr xbuf;
qof_query_init();
qof_object_initialize ();
static QofParam params[] = {
{ "adate", QOF_TYPE_DATE, NULL, NULL},
{ "aint", QOF_TYPE_INT32, NULL, NULL},
{ "aint64", QOF_TYPE_INT64, NULL, NULL},
{ "astr", QOF_TYPE_STRING, NULL, NULL},
{ NULL },
};
qof_class_register ("GncABC", NULL, params);
sq = qof_sql_query_new();
qof_sql_query_parse (sq,
"SELECT * from GncABC WHERE aint = 123 "
"or not astr=\'asdf\' "
"and aint64 = 9876123456789;");
// qof_sql_query_parse (sq, "SELECT * from GncABC;");
q = qof_sql_query_get_query (sq);
qof_query_print (q);
doc = doc = xmlNewDoc("1.0");
topnode = qof_query_to_xml (q);
xmlDocSetRootElement(doc,topnode);
xmlDocDumpFormatMemory (doc, &xbuf, &bufsz, 1);
printf ("%s\n", xbuf);
xmlFree (xbuf);
xmlFreeDoc(doc);
#if 0
printf ("duude\n");
// xmlOutputBufferPtr xbuf = xmlAllocOutputBuffer (enc);
xbuf = xmlOutputBufferCreateFile (stdout, NULL);
printf ("duude\n");
xbuf = xmlOutputBufferCreateFd (1, NULL);
printf ("duude\n");
xmlNodeDumpOutput (xbuf, NULL, topnode, 99, 99, "iso-8859-1");
// xmlElemDump (stdout, NULL, topnode);
#endif
return 0;
}
#endif /* UNIT_TEST */
/* ======================== END OF FILE =================== */

View File

@ -1,42 +0,0 @@
/********************************************************************\
* qofquery-serialize.h -- Convert QofQuery to XML *
* Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
* *
* 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 *
* *
\********************************************************************/
/* qofquery-serialize.h
Convert QofQuery to XML
Copyright (C) 2001,2002,2004 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_QUERY_SERIALIZE_H
#define QOF_QUERY_SERIALIZE_H
#include "qofquery.h"
#include <libxml/tree.h>
/* XML Serialize Queries to/from XML */
/* Take the query passed as input, and serialize it into XML.
* The DTD used will be a very qofquery specific DTD
* This is NOT the XQuery XML.
*/
xmlNodePtr qof_query_to_xml (QofQuery *q);
#endif /* QOF_QUERY_SERIALIZE_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,375 +0,0 @@
/********************************************************************\
* qofquery.h -- find objects that match a certain expression. *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Query
BASIC QUERY API:
With this API you can create arbitrary logical
queries to find sets of arbitrary object. To make simple
queries (1 term, such as a search for a parameter with one value),
create the appropriate
QueryTerm structure and stick it in a Query object using
xaccInitQuery. The QueryTerm should be malloced but the Query object
will handle freeing it. To make compound queries, make multiple
simple queries and combine them using qof_query_merge() and the logical
operations of your choice.
SQL QUERY API:
As an alternative to building queries one predicate at a time,
you can use the SQL query interface. This interface will accept
a string containing an SQL query, parse it, convert it into the
core representation, and execute it.
STRUCTURE OF A QUERY: A Query is a logical function of any number of
QueryTerms. A QueryTerm consists of a C function pointer (the
Predicate) and a PredicateData structure containing data passed to the
predicate funtion. The PredicateData structure is a constant
associated with the Term and is identical for every object that is
tested.
The terms of the Query may represent any logical function and are
stored in canonical form, i.e. the function is expressed as a logical
sum of logical products. So if you have QueryTerms a, b, c, d, e and
you have the logical function a(b+c) + !(c(d+e)), it gets stored as
ab + ac + !c + !c!e +!d!c + !d!e. This may not be optimal for evaluation
of some functions but it's easy to store, easy to manipulate, and it
doesn't require a complete algebra system to deal with.
The representation is of a GList of GLists of QueryTerms. The
"backbone" GList q->terms represents the OR-chain, and every item on
the backbone is a GList of QueryTerms representing an AND-chain
corresponding to a single product-term in the canonical
representation. QueryTerms are duplicated when necessary to fill out
the canonical form, and the same predicate may be evaluated multiple
times per split for complex queries. This is a place where we could
probably optimize.
Evaluation of a Query (see qof_query_run()) is optimized as much as
possible by short-circuited evaluation. The predicates in each
AND-chain are sorted by predicate type, with Account queries sorted
first to allow the evaluator to completely eliminate accounts from the
search if there's no chance of them having splits that match.
(XXX above no longer applies)
@{ */
/** @file qofquery.h
@brief find objects that match a certain expression.
@author Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
@author Copyright (C) 2003 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_QUERYNEW_H
#define QOF_QUERYNEW_H
#include "guid.h"
#include "qofbook.h"
#include "qofquerycore.h"
#include "qofchoice.h"
#define QOF_MOD_QUERY "qof-query"
/** A Query */
typedef struct _QofQuery QofQuery;
/** Query Term Operators, for combining Query Terms */
typedef enum {
QOF_QUERY_AND=1,
QOF_QUERY_OR,
QOF_QUERY_NAND,
QOF_QUERY_NOR,
QOF_QUERY_XOR
} QofQueryOp;
/* First/only term is same as 'and' */
#define QOF_QUERY_FIRST_TERM QOF_QUERY_AND
/** Default sort object type */
#define QUERY_DEFAULT_SORT "QofQueryDefaultSort"
/** "Known" Object Parameters -- all objects must support these */
#define QOF_PARAM_BOOK "book"
#define QOF_PARAM_GUID "guid"
/** "Known" Object Parameters -- some objects might support these */
#define QOF_PARAM_KVP "kvp"
#define QOF_PARAM_ACTIVE "active"
#define QOF_PARAM_VERSION "version"
/* --------------------------------------------------------- */
/** \name Query Subsystem Initialization and Shudown */
// @{
/** Subsystem initialization and shutdown. Call init() once
* to initalize the query subsytem; call shutdown() to free
* up any resources associated with the query subsystem.
* Typically called during application startup, shutdown.
*/
void qof_query_init (void);
void qof_query_shutdown (void);
// @}
/* --------------------------------------------------------- */
/** \name Low-Level API Functions */
// @{
GSList * qof_query_build_param_list (char const *param, ...);
/** Create a new query.
* Before running the query, a 'search-for' type must be set
* otherwise nothing will be returned. The results of the query
* is a list of the indicated search-for type.
*
* Allocates and initializes a Query structure which must be
* freed by the user with qof_query_destroy(). A newly-allocated
* QofQuery object matches nothing (qof_query_run() will return NULL).
*/
QofQuery * qof_query_create (void);
QofQuery * qof_query_create_for (QofIdTypeConst obj_type);
/** Frees the resources associate with a Query object. */
void qof_query_destroy (QofQuery *q);
/** Set the object type to be searched for. The results of
* performuing the query will be a list of this obj_type.
*/
void qof_query_search_for (QofQuery *query, QofIdTypeConst obj_type);
/** Set the book to be searched. Books contain/identify collections
* of objects; the search will be performed over those books
* specified with this function. If no books are set, no results
* will be returned (since there is nothing to search over). (CAS:
* Apparently, if no books are set, you'll actually get a critical
* assertion failure.)
*
* You can search multiple books. To specify multiple books, call
* this function multiple times with different arguments.
* XXX needed qof_query_clear_books() to reset the list ...
*/
void qof_query_set_book (QofQuery *q, QofBook *book);
/** 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_list' parameter to the predicate data via the comparitor.
*
* The param_list is a recursive list of parameters. For example, you
* can say 'split->memo' by creating a list of one element, "SPLIT_MEMO".
* You can say 'split->account->name' by creating a list of two elements,
* "SPLIT_ACCOUNT" and "ACCOUNT_NAME". The list becomes the property of
* the Query.
*
* For example:
*
* acct_name_pred_data = make_string_pred_data(QOF_STRING_MATCH_CASEINSENSITIVE,
* account_name);
* param_list = make_list (SPLIT_ACCOUNT, ACCOUNT_NAME, NULL);
* qof_query_add_term (query, param_list, QOF_COMPARE_EQUAL,
* acct_name_pred_data, QOF_QUERY_AND);
*
* Please note that QofQuery does not, at this time, support joins.
* That is, one cannot specify a predicate that is a parameter list.
* Put another way, one cannot search for objects where
* obja->thingy == objb->stuff
*/
void qof_query_add_term (QofQuery *query, GSList *param_list,
QofQueryPredData *pred_data, QofQueryOp op);
/** DOCUMENT ME !! */
void qof_query_add_guid_match (QofQuery *q, GSList *param_list,
const GUID *guid, QofQueryOp op);
/** DOCUMENT ME !! */
void qof_query_add_guid_list_match (QofQuery *q, GSList *param_list,
GList *guid_list, QofGuidMatch options,
QofQueryOp op);
/** Handy-dandy convenience routines, avoids having to create
* a separate predicate for boolean matches. We might want to
* create handy-dandy sugar routines for the other predicate types
* as well. */
void qof_query_add_boolean_match (QofQuery *q,
GSList *param_list,
gboolean value,
QofQueryOp op);
/** Perform the query, return the results.
* The returned list is a list of the 'search-for' type that was
* previously set with the qof_query_search_for() or the
* qof_query_create_for() routines. The returned list will have
* been sorted using the indicated sort order, and trimed to the
* max_results length.
*
* Do NOT free the resulting list. This list is managed internally
* by QofQuery.
*/
GList * qof_query_run (QofQuery *query);
/** Return the results of the last query, without causing the query to
* be re-run. Do NOT free the resulting list. This list is managed
* internally by QofQuery.
*/
GList * qof_query_last_run (QofQuery *query);
/** Remove all query terms from query. query matches nothing
* after qof_query_clear().
*/
void qof_query_clear (QofQuery *query);
/** Remove query terms of a particular type from q. The "type" of a term
* is determined by the type of data that gets passed to the predicate
* function.
* XXX ??? Huh? remove anything of that predicate type, or just
* the particular predicate ?
*/
void qof_query_purge_terms (QofQuery *q, GSList *param_list);
/** Return boolean FALSE if there are no terms in the query
* Can be used as a predicate to see if the query has been
* initialized (return value > 0) or is "blank" (return value == 0).
*/
int qof_query_has_terms (QofQuery *q);
/** Return the number of terms in the canonical form of the query.
*/
int qof_query_num_terms (QofQuery *q);
/** DOCUMENT ME !! */
gboolean qof_query_has_term_type (QofQuery *q, GSList *term_param);
GSList * qof_query_get_term_type (QofQuery *q, GSList *term_param);
/** Make a copy of the indicated query */
QofQuery * qof_query_copy (QofQuery *q);
/** Make a copy of the indicated query, inverting the sense
* of the search. In other words, if the original query search
* for all objects with a certain condition, the inverted query
* will search for all object with NOT that condition. The union
* of the results returned by the original and inverted queries
* equals the set of all searched objects. These to sets are
* disjoint (share no members in common).
*
* This will return a newly allocated QofQuery object, or NULL
* on error. Free it with qof_query_destroy() when no longer needed.
*/
QofQuery * qof_query_invert(QofQuery *q);
/** Combine two queries together using the Boolean set (logical)
* operator 'op'. For example, if the operator 'op' is set to
* QUERY_AND, then the set of results returned by the query will
* will be the Boolean set intersection of the results returned
* by q1 and q2. Similarly, QUERY_OR maps to set union, etc.
*
* Both queries must have compatible
* search-types. If both queries are set, they must search for the
* same object type. If only one is set, the resulting query will
* search for the set type. If neither query has the search-type set,
* the result will be unset as well.
*
* This will return a newly allocated QofQuery object, or NULL
* on error. Free it with qof_query_destroy() when no longer needed.
*/
QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op);
/** Like qof_query_merge, but this will merge a copy of q2 into q1.
* q2 remains unchanged.
*/
void qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op);
/**
* When a query is run, the results are sorted before being returned.
* This routine can be used to set the paramters on which the sort will
* be performed. Two objects in the result list will be compared using
* the 'primary_sort_params', and sorted based on that order. If the
* comparison shows that they are equal, then the
* 'secondary_sort_params' will be used. If still equal, then the
* tertiary params will be compared. Any or all of these parameter
* lists may be NULL. Any of these parameter lists may be set to
* QUERY_DEFAULT_SORT.
*
* Note that if there are more results than the 'max-results' value,
* then only the *last* max-results will be returned. For example,
* if the sort is set to be increasing date order, then only the
* objects with the most recent dates will be returned.
*
* The input lists become the property of QofQuery and are managed
* by it. They will be freed when the query is destroyed (or when
* new lists are set).
*/
void qof_query_set_sort_order (QofQuery *q,
GSList *primary_sort_params,
GSList *secondary_sort_params,
GSList *tertiary_sort_params);
void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op,
gint tert_op);
/**
* When a query is run, the results are sorted before being returned.
* This routine can be used to control the direction of the ordering.
* A value of TRUE indicates the sort will be in increasing order,
* a value of FALSE will order results in decreasing order.
*
* Note that if there are more results than the 'max-results' value,
* then only the *last* max-results will be returned. For example,
* if the sort is set to be increasing date order, then only the
* objects with the most recent dates will be returned.
*/
void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc,
gboolean sec_inc, gboolean tert_inc);
/**
* Set the maximum number of results that should be returned.
* If 'max-results' is set to -1, then all of the results are
* returned. If there are more results than 'max-results',
* then the result list is trimmed. Note that there is an
* important interplay between 'max-results' and the sort order:
* only the last bit of results are returned. For example,
* if the sort order is set to be increasing date order, then
* only the objects with the most recent dates will be returned.
*/
void qof_query_set_max_results (QofQuery *q, int n);
/** Compare two queries for equality.
* Query terms are compared each to each.
* This is a simplistic
* implementation -- logical equivalences between different
* and/or trees are ignored.
*/
gboolean qof_query_equal (QofQuery *q1, QofQuery *q2);
/** Print the Query in human-readable format.
* Useful for debugging and development.
*/
void qof_query_print (QofQuery *query);
/** Return the type of data we're querying for */
QofIdType qof_query_get_search_for (QofQuery *q);
/** Return the list of books we're using */
GList * qof_query_get_books (QofQuery *q);
// @}
/* @} */
#endif /* QOF_QUERYNEW_H */

View File

@ -1,146 +0,0 @@
/********************************************************************\
* qofquerycore-p.h -- Private 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 *
* *
\********************************************************************/
#ifndef QOF_QUERYCOREP_H
#define QOF_QUERYCOREP_H
#include <sys/types.h>
#include <time.h>
#include <glib.h>
#include <regex.h>
#include <string.h>
#include "qofquerycore.h"
/* Initalize the Query Core registry and install the default type handlers */
void qof_query_core_init(void);
void qof_query_core_shutdown (void);
/*
* An arbitrary Query Predicate. Given the 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 (*QofQueryPredicateFunc) (gpointer object,
QofParam *getter,
QofQueryPredData *pdata);
/* A callback for how to compare two (same-type) objects based on a
* common getter (parameter member), using the provided comparison
* options (which are the type-specific options).
*/
typedef int (*QofCompareFunc) (gpointer a, gpointer b,
gint compare_options,
QofParam *getter);
/* Lookup functions */
QofQueryPredicateFunc qof_query_core_get_predicate (char const *type);
QofCompareFunc qof_query_core_get_compare (char const *type);
/* Compare two predicates */
gboolean qof_query_core_predicate_equal (QofQueryPredData *p1, QofQueryPredData *p2);
/* Predicate Data Structures:
*
* These are defined such that you can cast between these types and
* a QofQueryPredData.
*
* Note that these are provided for READ ONLY PURPOSES. You should NEVER
* write into these structures, change them, or use them to create a
* Query.
*/
typedef struct {
QofQueryPredData pd;
QofStringMatch options;
gboolean is_regex;
char * matchstring;
regex_t compiled;
} query_string_def, *query_string_t;
typedef struct {
QofQueryPredData pd;
QofDateMatch options;
Timespec date;
} query_date_def, *query_date_t;
typedef struct {
QofQueryPredData pd;
QofNumericMatch options;
gnc_numeric amount;
} query_numeric_def, *query_numeric_t;
typedef struct {
QofQueryPredData pd;
QofGuidMatch options;
GList * guids;
} query_guid_def, *query_guid_t;
typedef struct {
QofQueryPredData pd;
gint32 val;
} query_int32_def, *query_int32_t;
typedef struct {
QofQueryPredData pd;
gint64 val;
} query_int64_def, *query_int64_t;
typedef struct {
QofQueryPredData pd;
double val;
} query_double_def, *query_double_t;
typedef struct {
QofQueryPredData pd;
gboolean val;
} query_boolean_def, *query_boolean_t;
typedef struct {
QofQueryPredData pd;
QofCharMatch options;
char * char_list;
} query_char_def, *query_char_t;
typedef struct {
QofQueryPredData pd;
GSList * path;
KvpValue * value;
} query_kvp_def, *query_kvp_t;
typedef struct {
QofQueryPredData pd;
QofGuidMatch options;
QofCollection *coll;
GList *guids;
} query_coll_def, *query_coll_t;
typedef struct {
QofQueryPredData pd;
QofGuidMatch options;
const GUID *guid;
GList * guids;
} query_choice_def, *query_choice_t;
#endif /* QOF_QUERYCOREP_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,196 +0,0 @@
/********************************************************************\
* qofquerycore.h -- 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 *
* *
\********************************************************************/
/** @addtogroup Query
@{ */
/** @file qofquerycore.h
@brief API for providing core Query data types
@author Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
*/
#ifndef QOF_QUERYCORE_H
#define QOF_QUERYCORE_H
#include <glib.h>
#include "gnc-numeric.h"
#include "gnc-date.h"
#include "kvp_frame.h"
#include "qofclass.h"
/**
* PREDICATE DATA TYPES: All the predicate data types are rolled up into
* the union type PredicateData. The "type" field specifies which type
* the union is.
*/
typedef struct _QofQueryPredData QofQueryPredData;
/** Standard Query comparitors, for how to compare objects in a predicate.
* Note that not all core types implement all comparitors
*/
typedef enum {
QOF_COMPARE_LT = 1,
QOF_COMPARE_LTE,
QOF_COMPARE_EQUAL,
QOF_COMPARE_GT,
QOF_COMPARE_GTE,
QOF_COMPARE_NEQ
} QofQueryCompare;
/** List of known core query data-types...
* Each core query type defines it's set of optional "comparitor qualifiers".
*/
/* Comparisons for QOF_TYPE_STRING */
typedef enum {
QOF_STRING_MATCH_NORMAL = 1,
QOF_STRING_MATCH_CASEINSENSITIVE
} QofStringMatch;
/** Comparisons for QOF_TYPE_DATE
* The QOF_DATE_MATCH_DAY comparison rounds the two time
* values to mid-day and then compares these rounded values.
* The QOF_DATE_MATCH_NORMAL comparison matches the time values,
* down to the second.
*/
/* XXX remove these deprecated old names .. */
//#define QOF_DATE_MATCH_ROUNDED QOF_DATE_MATCH_DAY
//#define QOF_DATE_MATCH_NORMAL QOF_DATE_MATCH_TIME
typedef enum {
QOF_DATE_MATCH_NORMAL = 1,
QOF_DATE_MATCH_DAY
} QofDateMatch;
/** Comparisons for QOF_TYPE_NUMERIC, QOF_TYPE_DEBCRED
*
* XXX Should be deprecated, or at least wrapped up as a convnience
* function, this is based on the old bill gribble code, which assumed
* the amount was always positive, and then specified a funds-flow
* direction (credit, debit, or either).
*
* The point being that 'match credit' is equivalent to the compound
* predicate (amount >= 0) && (amount 'op' value) while the 'match
* debit' predicate is equivalent to (amount <= 0) && (abs(amount) 'op' value)
*/
typedef enum {
QOF_NUMERIC_MATCH_DEBIT = 1,
QOF_NUMERIC_MATCH_CREDIT,
QOF_NUMERIC_MATCH_ANY
} QofNumericMatch;
/* Comparisons for QOF_TYPE_GUID */
typedef enum {
/** These expect a single object and expect the
* QofAccessFunc returns GUID* */
QOF_GUID_MATCH_ANY = 1,
QOF_GUID_MATCH_NONE,
QOF_GUID_MATCH_NULL,
/** These expect a GList* of objects and calls the QofAccessFunc routine
* on each item in the list to obtain a GUID* for each object */
QOF_GUID_MATCH_ALL,
/** These expect a single object and expect the QofAccessFunc function
* to return a GList* of GUID* (the list is the property of the caller) */
QOF_GUID_MATCH_LIST_ANY,
} QofGuidMatch;
/** A CHAR type is for a RECNCell, Comparisons for QOF_TYPE_CHAR
* 'ANY' will match any charagter in the string.
*
* Match 'ANY' is a convenience/performance-enhanced predicate
* for the compound statement (value==char1) || (value==char2) || etc.
* Match 'NONE' is equivalent to
* (value != char1) && (value != char2) && etc.
*/
typedef enum {
QOF_CHAR_MATCH_ANY = 1,
QOF_CHAR_MATCH_NONE
} QofCharMatch;
/** No extended comparisons for QOF_TYPE_INT32, QOF_TYPE_INT64,
* QOF_TYPE_DOUBLE, QOF_TYPE_BOOLEAN, QOF_TYPE_KVP
*/
/** Head of Predicate Data structures. All PData must start like this. */
struct _QofQueryPredData {
QofType type_name; /* QOF_TYPE_* */
QofQueryCompare how;
};
/** @name Core Data Type Predicates
@{ */
QofQueryPredData *qof_query_string_predicate (QofQueryCompare how,
const char *str,
QofStringMatch options,
gboolean is_regex);
QofQueryPredData *qof_query_date_predicate (QofQueryCompare how,
QofDateMatch options,
Timespec date);
QofQueryPredData *qof_query_numeric_predicate (QofQueryCompare how,
QofNumericMatch options,
gnc_numeric value);
QofQueryPredData *qof_query_guid_predicate (QofGuidMatch options, GList *guids);
QofQueryPredData *qof_query_int32_predicate (QofQueryCompare how, gint32 val);
QofQueryPredData *qof_query_int64_predicate (QofQueryCompare how, gint64 val);
QofQueryPredData *qof_query_double_predicate (QofQueryCompare how, double val);
QofQueryPredData *qof_query_boolean_predicate (QofQueryCompare how, gboolean val);
QofQueryPredData *qof_query_char_predicate (QofCharMatch options,
const char *chars);
QofQueryPredData *qof_query_collect_predicate (QofGuidMatch options,
QofCollection *coll);
QofQueryPredData *qof_query_choice_predicate (QofGuidMatch options, GList *guids);
/** The qof_query_kvp_predicate() matches the object that has
* the value 'value' located at the path 'path'. In a certain
* sense, the 'path' is handled as if it were a paramter.
*/
QofQueryPredData *qof_query_kvp_predicate (QofQueryCompare how,
GSList *path,
const KvpValue *value);
/** Same predicate as above, except that 'path' is assumed to be
* a string containing slash-separated pathname. */
QofQueryPredData *qof_query_kvp_predicate_path (QofQueryCompare how,
const char *path,
const KvpValue *value);
/** Copy a predicate. */
QofQueryPredData *qof_query_core_predicate_copy (QofQueryPredData *pdata);
/** Destroy a predicate. */
void qof_query_core_predicate_free (QofQueryPredData *pdata);
/** Retrieve a predicate. */
gboolean qof_query_date_predicate_get_date (QofQueryPredData *pd, Timespec *date);
/** Return a printable string for a core data object. Caller needs
* to g_free() the returned string.
*/
char * qof_query_core_to_string (QofType, gpointer object, QofParam *getter);
#endif /* QOF_QUERYCORE_H */
/* @} */
/* @} */

View File

@ -1,76 +0,0 @@
/********************************************************************\
* qofsession-p.h -- private functions for QOF sessions. *
* *
* 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 *
* *
\********************************************************************/
/*
* HISTORY:
* Copyright (c) 2001 Linux Developers Group
* Copyright (c) 1998-2003 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_SESSION_P_H
#define QOF_SESSION_P_H
#include "qofbook.h"
#include "qofsession.h"
struct _QofSession
{
/* This is just a "fake" entry point to allow me to pass a Session as
* an Entity. NOTE: THIS IS NOT AN ENTITY! THE ONLY PART OF ENTITY
* THAT IS VALID IS E_TYPE!
*/
QofEntity entity;
/* A book holds pointers to the various types of datasets.
* A session may have multiple books. */
GList *books;
/* The requested book id, in the form or a URI, such as
* file:/some/where, or sql:server.host.com:555
*/
char *book_id;
/* If any book subroutine failed, this records the failure reason
* (file not found, etc).
* This is a 'stack' that is one deep. (Should be deeper ??)
* FIXME: Each backend has its own error stack. The session
* and the backends should all be using (or making it look like)
* there is only one stack.
*/
QofBackendError last_err;
char *error_message;
/* ---------------------------------------------------- */
/* Pointer to the backend that is actually used to move data
* between the persistant store and the local engine. */
QofBackend *backend;
};
QofBackend * qof_session_get_backend (QofSession *session);
void qof_session_push_error (QofSession *session, QofBackendError err,
const char *message);
QofBackend* gncBackendInit_file(const char *book_id, void *data);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,558 +0,0 @@
/********************************************************************\
* qofsession.h -- session access (connection to backend) *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Backend
*
* The QOF Session
* encapsulates a connection to a storage backend. That is, it
* manages the connection to a persistant data store; whereas
* the backend is the thing that performs the actual datastore
* access.
*
* This class provides several important services:
*
* 1) It resolves and loads the appropriate backend, based on
* the URL.
*
* 2) It reports backend errors (e.g. network errors, storage
* corruption errors) through a single, backend-independent
* API.
*
* 3) It reports non-error events received from the backend.
*
* 4) It helps manage global dataset locks. For example, for the
* file backend, the lock prevents multiple users from editing
* the same file at the same time, thus avoiding lost data due
* to race conditions. Thus, an open session implies that the
* associated file is locked.
*
* 5) Misc utilities, such as a search path for the file to be
* edited, and/or other URL resolution utilities. This should
* simplify install & maintenance problems for naive users who
* may not have a good grasp on what a file system is, or where
* they want to keep their data files.
*
* 6) In the future, this class is probably a good place to manage
* a portion of the user authentication process, and hold user
* credentials/cookies/keys/tokens. This is because at the
* coarsest level, authorization can happen at the datastore
* level: i.e. does this user even have the authority to connect
* to and open this datastore?
*
* A brief note about books & sessions:
* A book encapsulates the datasets manipulated by GnuCash. A book
* holds the actual data. By contrast, the session mediates the
* connection between a book (the thing that lives in virtual memory
* in the local process) and the datastore (the place where book
* data lives permanently, e.g., file, database).
*
* In the current design, a session may hold multiple books. For
* now, exactly what this means is somewhat vague, and code in
* various places makes some implicit assumptions: first, only
* one book is 'current' and open for editing. Next, its assumed
* that all of the books in a session are related in some way.
* i.e. that they are all earlier accounting periods of the
* currently open book. In particular, the backends probably
* make that assumption, in order to store the different accounting
* periods in a clump so that one can be found, given another.
*
The session now calls QofBackendProvider->check_data_type
to check that the incoming path contains data that the
backend provider can open. The backend provider should
also check if it can contact it's storage media (disk,
network, server, etc.) and abort if it can't. Malformed
file URL's would be handled the same way.
@{
*/
/** @file qofsession.h
* @brief Encapsulates a connection to a backend (persistent store)
* @author Copyright (c) 1998, 1999, 2001, 2002 Linas Vepstas <linas@linas.org>
* @author Copyright (c) 2000 Dave Peticolas
* @author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
*/
#ifndef QOF_SESSION_H
#define QOF_SESSION_H
#include "qofbackend.h"
#include "qofbook.h"
#include "qofclass.h"
#include "qofobject.h"
#define QOF_MOD_SESSION "qof-session"
/* PROTOTYPES ******************************************************/
typedef struct _QofSession QofSession;
QofSession * qof_session_new (void);
void qof_session_destroy (QofSession *session);
QofSession * qof_session_get_current_session (void);
void qof_session_set_current_session (QofSession *session);
/** The qof_session_swap_data () method swaps the book of
* the two given sessions. It is useful
* for 'Save As' type functionality. */
void qof_session_swap_data (QofSession *session_1, QofSession *session_2);
/** The qof_session_begin () method begins a new session.
* It takes as an argument the book id. The book id must be a string
* in the form of a URI/URL.
* In the current implementation, the following URL's are supported
* -- File URI of the form
* "file:/home/somewhere/somedir/file.xac"
* The path part must be a valid path. The file-part must be
* a valid old-style-xacc or new-style-gnucash-format file. Paths
* may be relative or absolute. If the path is relative; that is,
* if the argument is "file:somefile.xac" then a sequence of
* search paths are checked for a file of this name.
*
* -- Postgres URI of the form
* "postgres://hostname.com/dbname"
* See the src/backend/postgres subdirectory for more info.
*
* -- RPC URI of the form rpc://hostname.com/rpcserver.
*
* The 'ignore_lock' argument, if set to TRUE, will cause this routine
* to ignore any global-datastore locks (e.g. file locks) that it finds.
* If set to FALSE, then file/database-global locks will be tested and
* obeyed.
*
* If the datastore exists, can be reached (e.g over the net),
* connected to, opened and read, and a lock can be obtained then
* a lock will be obtained. Note that multi-user datastores
* (e.g. the SQL backend) typically will not need to get a global
* lock, and thus, the user will not be locked out. That's the
* whole point of 'multi-user'.
*
* If the file/database doesn't exist, and the create_if_nonexistent
* flag is set to TRUE, then the database is created.
*
* If an error occurs, it will be pushed onto the session error
* stack, and that is where it should be examined.
*/
void qof_session_begin (QofSession *session, const char * book_id,
gboolean ignore_lock, gboolean create_if_nonexistent);
/**
* The qof_session_load() method causes the QofBook to be made ready to
* to use with this URL/datastore. When the URL points at a file,
* then this routine would load the data from the file. With remote
* backends, e.g. network or SQL, this would load only enough data
* to make the book actually usable; it would not cause *all* of the
* data to be loaded.
*
* XXX the current design tries to accomodate multiple calls to 'load'
* for each session, each time wiping out the old books; this seems
* wrong to me, and should be restricted to allow only one load per
* session.
*/
typedef void (*QofPercentageFunc) (const char *message, double percent);
void qof_session_load (QofSession *session,
QofPercentageFunc percentage_func);
/** @name Session Errors
@{ */
/** The qof_session_get_error() routine can be used to obtain the reason
* for any failure. Calling this routine returns the current error.
*/
QofBackendError qof_session_get_error (QofSession *session);
const char * qof_session_get_error_message(QofSession *session);
/**
* The qof_session_pop_error() routine can be used to obtain the reason
* for any failure. Calling this routine resets the error value.
*
* This routine allows an implementation of multiple error values,
* e.g. in a stack, where this routine pops the top value. The current
* implementation has a stack that is one-deep.
*
* See qofbackend.h for a listing of returned errors.
*/
QofBackendError qof_session_pop_error (QofSession *session);
/** @} */
/** The qof_session_add_book() allows additional books to be added to
* a session.
* XXX Under construction, clarify the following when done:
* XXX There must already be an open book in the session already!?
* XXX Only one open book at a time per session is allowed!?
* XXX each book gets its own unique backend ???
*/
void qof_session_add_book (QofSession *session, QofBook *book);
QofBook * qof_session_get_book (QofSession *session);
/**
* The qof_session_get_file_path() routine returns the fully-qualified file
* path for the session. That is, if a relative or partial filename
* was for the session, then it had to have been fully resolved to
* open the session. This routine returns the result of this resolution.
* The path is always guaranteed to reside in the local file system,
* even if the session itself was opened as a URL. (currently, the
* filepath is derived from the url by substituting commas for
* slashes).
*
* The qof_session_get_url() routine returns the url that was opened.
* URL's for local files take the form of
* file:/some/where/some/file.gml
*/
const char * qof_session_get_file_path (QofSession *session);
const char * qof_session_get_url (QofSession *session);
/**
* The qof_session_not_saved() subroutine will return TRUE
* if any data in the session hasn't been saved to long-term storage.
*/
gboolean qof_session_not_saved(QofSession *session);
/** FIXME: This isn't as thorough as we might want it to be... */
gboolean qof_session_save_may_clobber_data (QofSession *session);
/** The qof_session_save() method will commit all changes that have been
* made to the session. For the file backend, this is nothing
* more than a write to the file of the current AccountGroup & etc.
* For the SQL backend, this is typically a no-op (since all data
* has already been written out to the database.
*/
void qof_session_save (QofSession *session,
QofPercentageFunc percentage_func);
/**
* The qof_session_end() method will release the session lock. For the
* file backend, it will *not* save the account group to a file. Thus,
* this method acts as an "abort" or "rollback" primitive. However,
* for other backends, such as the sql backend, the data would have
* been written out before this, and so this routines wouldn't
* roll-back anything; it would just shut the connection.
*/
void qof_session_end (QofSession *session);
/** @name Copying entities between sessions.
Only certain backends can cope with selective copying of
entities and only fully defined QOF entities can be copied
between sessions - see the \ref QSF (QSF) documentation
(::qsf_write_file) for more information.
The recommended backend for the new session is QSF or a future
SQL backend. Using any of these entity copy functions sets a
flag in the backend that this is now a partial QofBook.
When you save a session containing a partial QofBook,
the session will check that the backend is able to handle the
partial book. If not, the backend will be replaced by one that
can handle partial books, preferably one using the same
::access_method. Currently, this means that a book
using the GnuCash XML v2 file backend will be switched to QSF.
Copied entities are identical to the source entity, all parameters
defined with ::QofAccessFunc and ::QofSetterFunc in QOF are copied
and the ::GUID of the original ::QofEntity is set in the new entity.
Sessions containing copied entities are intended for use
as mechanisms for data export.
It is acceptable to add entities to new_session in batches. Note that
any of these calls will fail if an entity already exists in new_session
with the same GUID as any entity to be copied.
To merge a whole QofBook or where there is any possibility
of collisions or requirement for user intervention,
see \ref BookMerge
@{
*/
/** \brief Copy a single QofEntity to another session
Checks first that no entity in the session book contains
the GUID of the source entity.
@param new_session - the target session
@param original - the QofEntity* to copy
@return FALSE without copying if the session contains an entity
with the same GUID already, otherwise TRUE.
*/
gboolean qof_entity_copy_to_session(QofSession* new_session, QofEntity* original);
/** @brief Copy a GList of entities to another session
The QofBook in the new_session must \b not contain any entities
with the same GUID as any of the source entities - there is
no support for handling collisions, instead use \ref BookMerge
Note that the GList (e.g. from ::qof_sql_query_run) can contain
QofEntity pointers of any ::QofIdType, in any sequence. As long
as all members of the list are ::QofEntity*, and all GUID's are
unique, the list can be copied.
@param new_session - the target session
@param entity_list - a GList of QofEntity pointers of any type(s).
@return FALSE, without copying, if new_session contains any entities
with the same GUID. Otherwise TRUE.
*/
gboolean qof_entity_copy_list(QofSession *new_session, GList *entity_list);
/** @brief Copy a QofCollection of entities.
The QofBook in the new_session must \b not contain any entities
with the same GUID as any entities in the collection - there is
no support for handling collisions - instead, use \ref BookMerge
@param new_session - the target session
@param entity_coll - a QofCollection of any QofIdType.
@return FALSE, without copying, if new_session contains any entities
with the same GUID. Otherwise TRUE.
*/
gboolean qof_entity_copy_coll(QofSession *new_session, QofCollection *entity_coll);
/** \brief Recursively copy a collection of entities to a session.
\note This function creates a <b>partial QofBook</b>. See
::qof_entity_copy_to_session for more information.
The QofBook in the new_session must \b not contain any entities
with the same GUID as any entities to be copied - there is
no support for handling collisions - instead, use \ref BookMerge
Objects can be defined solely in terms of QOF data types or
as a mix of data types and other objects, which may in turn
include other objects. These references can be copied recursively
down to the third level. e.g. ::GncInvoice refers to ::GncOwner which
refers to ::GncCustomer which refers to ::GncAddress. See
::QofEntityReference.
\note This is a deep recursive copy - every referenced entity is copied
to the new session, including all parameters. The starting point is all
entities in the top level collection. It can take some time.
@param coll A QofCollection of entities that may or may not have
references.
@param new_session The QofSession to receive the copied entities.
@return TRUE on success; if any individual copy fails, returns FALSE.
<b>Note</b> : Some entities may have been copied successfully even if
one of the references fails to copy.
*/
gboolean
qof_entity_copy_coll_r(QofSession *new_session, QofCollection *coll);
/** \brief Recursively copy a single entity to a new session.
Copy the single entity and all referenced entities to the second level.
Only entities that are directly referenced by the top level entity are
copied.
This is a deep copy - all parameters of all referenced entities are copied. If
the top level entity has no references, this is identical to
::qof_entity_copy_to_session.
@param ent A single entity that may or may not have references.
@param new_session The QofSession to receive the copied entities.
@return TRUE on success; if any individual copy fails, returns FALSE.
<b>Note</b> : Some entities may have been copied successfully even if
one of the references fails to copy.
*/
gboolean
qof_entity_copy_one_r(QofSession *new_session, QofEntity *ent);
/** @}
*/
/** @name Using a partial QofBook.
Part of the handling for partial books requires a storage mechanism for
references to entities that are not within reach of the partial book.
This requires a GList in the book data to contain the reference
QofIdType and GUID so that when the book is written out, the
reference can be included. See ::qof_book_get_data.
When the file is imported back in, the list needs to be rebuilt.
The QSF backend rebuilds the references by linking to real entities. Other
backends can process the hash table in similar ways.
The list stores the QofEntityReference to the referenced entity -
a struct that contains the GUID and the QofIdType of the referenced entity
as well as the parameter used to obtain the reference.
Partial books need to be differentiated in the backend, the
flag in the book data is used by qof_session_save to prevent a partial
book being saved using a backend that requires a full book.
@{ */
/** \brief External references in a partial QofBook.
For use by any session that deals with partial QofBooks.
It is used by the entity copy functions and by the QSF backend.
Creates a GList stored in the Book hashtable to contain
repeated references for a single entity.
*/
typedef struct qof_entity_reference {
QofIdType choice_type;/**< When the reference is a different type.*/
QofIdType type; /**< The type of entity */
GUID *ref_guid; /**< The GUID of the REFERENCE entity */
const QofParam *param; /**< The parameter name and type. */
const GUID *ent_guid; /**< The GUID of the original entity. */
}QofEntityReference;
/** \brief Get a reference from this entity to another entity.
Used in the preparation of a partial QofBook when the known entity
(the one currently being copied into the partial book) refers to
any other entity, usually as a parent or child.
The routine calls the param_getfcn of the supplied parameter,
which must return an object (QofEntity*), not a known QOF data type, to
retrieve the referenced entity and therefore the GUID. The GUID of
both entities are stored in the reference which then needs to be added
to the reference list which is added to the partial book data hash.
The reference itself is used by the backend to preserve the relationship
between entities within and outside the partial book.
See also ::qof_class_get_referenceList to obtain the list of
parameters that provide references to the known entity whilst
excluding parameters that return known QOF data types.
Note that even if the referenced entity \b exists in the partial
book (or will exist later), a reference must still be obtained and
added to the reference list for the book itself. This maintains
the integrity of the partial book during sequential copy operations.
@param ent The known entity.
@param param The parameter to use to get the referenced entity.
@return FALSE on error, otherwise a pointer to the QofEntityReference.
*/
QofEntityReference*
qof_entity_get_reference_from(QofEntity *ent, const QofParam *param);
/** \brief Adds a new reference to the partial book data hash.
Retrieves any existing reference list and appends the new reference.
If the book is not already marked as partial, it will be marked as partial.
*/
void
qof_session_update_reference_list(QofSession *session, QofEntityReference *reference);
/** Used as the key value for the QofBook data hash.
*
* Retrieved later by QSF (or any other suitable backend) to
* rebuild the references from the QofEntityReference struct
* that contains the QofIdType and GUID of the referenced entity
* of the original QofBook as well as the parameter data and the
* GUID of the original entity.
* */
#define ENTITYREFERENCE "QofEntityReference"
/** \brief Flag indicating a partial QofBook.
When set in the book data with a gboolean value of TRUE,
the flag denotes that only a backend that supports partial
books can be used to save this session.
*/
#define PARTIAL_QOFBOOK "PartialQofBook"
/** @}
*/
/** \brief Allow session data to be printed to stdout
book_id can't be NULL and we do need to have an access_method,
so use one to solve the other.
To print a session to stdout, use ::qof_session_begin. Example:
\a qof_session_begin(session,QOF_STDOUT,TRUE,FALSE);
When you call qof_session_save(session, NULL), the output will appear
on stdout and can be piped or redirected to other processes.
Currently, only the QSF backend supports writing to stdout, other
backends may return a ::QofBackendError.
*/
#define QOF_STDOUT "file:"
/** @name Event Handling
@{ */
/** The qof_session_events_pending() method will return TRUE if the backend
* has pending events which must be processed to bring the engine
* up to date with the backend.
*/
gboolean qof_session_events_pending (QofSession *session);
/** The qof_session_process_events() method will process any events indicated
* by the qof_session_events_pending() method. It returns TRUE if the
* engine was modified while engine events were suspended.
*/
gboolean qof_session_process_events (QofSession *session);
/** @} */
#ifdef GNUCASH_MAJOR_VERSION
/** Run the RPC Server
* @deprecated will go away */
void gnc_run_rpc_server (void);
/** XXX session_export really doesn't belong here .
* This functino exports the list of accounts to a file. Its a stop-gap
* measure until full book-closing is implemented.
*/
gboolean qof_session_export (QofSession *tmp_session,
QofSession *real_session,
QofPercentageFunc percentage_func);
#endif /* GNUCASH_MAJOR_VERSION */
/** Register a function to be called just before a session is closed.
*
* @param fn The function to be called. The function definition must
* be func(gpointer session, gpointer user_data);
*
* @param data The data to be passed to the function. */
void qof_session_add_close_hook (GFunc fn, gpointer data);
/** Call all registered session close hooks, informing them that the
* specified session is about to be closed.
*
* @param session A pointer to the session being closed. */
void qof_session_call_close_hooks (QofSession *session);
#endif /* QOF_SESSION_H */
/** @} */

View File

@ -1,928 +0,0 @@
/********************************************************************\
* qofsql.c -- QOF client-side SQL parser *
* *
* 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 *
* *
\********************************************************************/
/**
@file qofsql.c
@brief QOF client-side SQL parser.
@author Copyright (C) 2004 Linas Vepstas <linas@linas.org>
*/
#define _GNU_SOURCE
#include <stdlib.h> /* for working atoll */
#include <errno.h>
#include "glib.h"
#include "config.h"
#ifdef HAVE_GDA
#include <sql/sql_parser.h>
#else
#include "sql_parser.h"
#endif
#include <time.h>
#include "kvp_frame.h"
#include "gnc-date.h"
#include "gnc-numeric.h"
#include "gnc-trace.h"
#include "guid.h"
#include "qofbook.h"
#include "qofquery.h"
#include "qofquerycore.h"
#include "qofsql.h"
#include "gnc-engine-util.h"
#include "qofinstance-p.h"
#include "qofobject.h"
static QofLogModule log_module = QOF_MOD_QUERY;
/* =================================================================== */
struct _QofSqlQuery
{
sql_statement *parse_result;
QofQuery *qof_query;
QofBook *book;
char * single_global_tablename;
KvpFrame *kvp_join;
GList *param_list;
QofEntity *inserted_entity;
};
/* ========================================================== */
QofSqlQuery *
qof_sql_query_new(void)
{
QofSqlQuery * sqn = (QofSqlQuery *) g_new0 (QofSqlQuery, 1);
sqn->qof_query = NULL;
sqn->parse_result = NULL;
sqn->book = NULL;
sqn->single_global_tablename = NULL;
sqn->kvp_join = NULL;
return sqn;
}
/* ========================================================== */
void
qof_sql_query_destroy (QofSqlQuery *q)
{
if (!q) return;
qof_query_destroy (q->qof_query);
sql_destroy (q->parse_result);
g_free (q);
}
/* ========================================================== */
QofQuery *
qof_sql_query_get_query (QofSqlQuery *q)
{
if (!q) return NULL;
return q->qof_query;
}
/* ========================================================== */
void
qof_sql_query_set_book (QofSqlQuery *q, QofBook *book)
{
if (!q) return;
q->book = book;
}
/* ========================================================== */
void
qof_sql_query_set_kvp (QofSqlQuery *q, KvpFrame *kvp)
{
if (!q) return;
q->kvp_join = kvp;
}
/* ========================================================== */
static inline void
get_table_and_param (char * str, char **tab, char **param)
{
char * end = strchr (str, '.');
if (!end)
{
*tab = 0;
*param = str;
return;
}
*end = 0;
*tab = str;
*param = end+1;
}
static inline char *
dequote_string (char *str)
{
size_t len;
/* strip out quotation marks ... */
if (('\'' == str[0]) ||
('\"' == str[0]))
{
str ++;
len = strlen(str);
str[len-1] = 0;
}
return str;
}
static QofQuery *
handle_single_condition (QofSqlQuery *query, sql_condition * cond)
{
char tmpbuff[128];
GSList *param_list;
GList *guid_list;
QofQueryPredData *pred_data;
sql_field_item *sparam, *svalue;
char * qparam_name, *qvalue_name, *table_name, *param_name;
char *sep, *path,*str,*p;
QofQuery *qq;
KvpValue *kv, *kval;
KvpValueType kvt;
QofQueryCompare qop;
time_t exact;
int rc, len;
Timespec ts;
QofType param_type;
QofGuidMatch gm;
pred_data = NULL;
if (NULL == cond)
{
PWARN("missing condition");
return NULL;
}
/* -------------------------------- */
/* field to match, assumed, for now to be on the left */
/* XXX fix this so it can be either left or right */
if (NULL == cond->d.pair.left)
{
PWARN("missing left parameter");
return NULL;
}
sparam = cond->d.pair.left->item;
if (SQL_name != sparam->type)
{
PWARN("we support only parameter names at this time (parsed %d)",
sparam->type);
return NULL;
}
qparam_name = sparam->d.name->data;
if (NULL == qparam_name)
{
PWARN ("missing parameter name");
return NULL;
}
/* -------------------------------- */
/* value to match, assumed, for now, to be on the right. */
/* XXX fix this so it can be either left or right */
if (NULL == cond->d.pair.right)
{
PWARN ("missing right parameter");
return NULL;
}
svalue = cond->d.pair.right->item;
if (SQL_name != svalue->type)
{
PWARN("we support only simple values (parsed as %d)", svalue->type);
return NULL;
}
qvalue_name = svalue->d.name->data;
if (NULL == qvalue_name)
{
PWARN("missing value");
return NULL;
}
qvalue_name = dequote_string (qvalue_name);
qvalue_name = (char *) qof_util_whitespace_filter (qvalue_name);
/* Look to see if its the special KVP value holder.
* If it is, look up the value. */
if (0 == strncasecmp (qvalue_name, "kvp://", 6))
{
if (NULL == query->kvp_join)
{
PWARN ("missing kvp frame");
return NULL;
}
kv = kvp_frame_get_value (query->kvp_join, qvalue_name+5);
/* If there's no value, its not an error;
* we just don't do this predicate */
if (!kv) return NULL;
kvt = kvp_value_get_type (kv);
tmpbuff[0] = 0x0;
qvalue_name = tmpbuff;
switch (kvt)
{
case KVP_TYPE_GINT64:
{
gint64 ival = kvp_value_get_gint64(kv);
sprintf (tmpbuff, "%" G_GINT64_FORMAT "\n", ival);
break;
}
case KVP_TYPE_DOUBLE:
{
double ival = kvp_value_get_double(kv);
sprintf (tmpbuff, "%26.18g\n", ival);
break;
}
case KVP_TYPE_STRING:
/* If there's no value, its not an error;
* we just don't do this predicate */
qvalue_name = kvp_value_get_string (kv);
if (!qvalue_name) return NULL;
break;
case KVP_TYPE_GUID:
case KVP_TYPE_TIMESPEC:
case KVP_TYPE_BINARY:
case KVP_TYPE_GLIST:
case KVP_TYPE_NUMERIC:
case KVP_TYPE_FRAME:
PWARN ("unhandled kvp type=%d", kvt);
return NULL;
}
}
/* -------------------------------- */
/* Now start building the QOF parameter */
param_list = qof_query_build_param_list (qparam_name, NULL);
/* Get the where-term comparison operator */
switch (cond->op)
{
case SQL_eq: qop = QOF_COMPARE_EQUAL; break;
case SQL_gt: qop = QOF_COMPARE_GT; break;
case SQL_lt: qop = QOF_COMPARE_LT; break;
case SQL_geq: qop = QOF_COMPARE_GTE; break;
case SQL_leq: qop = QOF_COMPARE_LTE; break;
case SQL_diff: qop = QOF_COMPARE_NEQ; break;
default:
/* XXX for string-type queries, we should be able to
* support 'IN' for substring search. Also regex. */
PWARN ("Unsupported compare op (parsed as %u)", cond->op);
return NULL;
}
/* OK, need to know the type of the thing being matched
* in order to build the correct predicate. Get the type
* from the object parameters. */
get_table_and_param (qparam_name, &table_name, &param_name);
if (NULL == table_name)
{
table_name = query->single_global_tablename;
}
if (NULL == table_name)
{
PWARN ("Need to specify an object class to query");
return NULL;
}
if (FALSE == qof_class_is_registered (table_name))
{
PWARN ("The query object \'%s\' is not known", table_name);
return NULL;
}
param_type = qof_class_get_parameter_type (table_name, param_name);
if (!param_type)
{
PWARN ("The parameter \'%s\' on object \'%s\' is not known",
param_name, table_name);
return NULL;
}
if (!strcmp (param_type, QOF_TYPE_STRING))
{
pred_data =
qof_query_string_predicate (qop, /* comparison to make */
qvalue_name, /* string to match */
QOF_STRING_MATCH_CASEINSENSITIVE, /* case matching */
FALSE); /* use_regexp */
}
else if (!strcmp (param_type, QOF_TYPE_CHAR))
{
QofCharMatch cm = QOF_CHAR_MATCH_ANY;
if (QOF_COMPARE_NEQ == qop) cm = QOF_CHAR_MATCH_NONE;
pred_data = qof_query_char_predicate (cm, qvalue_name);
}
else if (!strcmp (param_type, QOF_TYPE_INT32))
{
gint32 ival = atoi (qvalue_name);
pred_data = qof_query_int32_predicate (qop, ival);
}
else if (!strcmp (param_type, QOF_TYPE_INT64))
{
gint64 ival = atoll (qvalue_name);
pred_data = qof_query_int64_predicate (qop, ival);
}
else if (!strcmp (param_type, QOF_TYPE_DOUBLE))
{
double ival = atof (qvalue_name);
pred_data = qof_query_double_predicate (qop, ival);
}
else if (!strcmp (param_type, QOF_TYPE_BOOLEAN))
{
gboolean ival = qof_util_bool_to_int (qvalue_name);
pred_data = qof_query_boolean_predicate (qop, ival);
}
else if (!strcmp (param_type, QOF_TYPE_DATE))
{
/* Use a timezone independent setting */
qof_date_format_set(QOF_DATE_FORMAT_UTC);
rc = 0;
if(FALSE == qof_scan_date_secs (qvalue_name, &exact))
{
char *tail;
exact = strtoll(qvalue_name, &tail, 0);
// PWARN ("unable to parse date: %s", qvalue_name);
// return NULL;
}
ts.tv_sec = exact;
ts.tv_nsec = 0;
pred_data = qof_query_date_predicate (qop, QOF_DATE_MATCH_NORMAL, ts);
}
else if (!strcmp (param_type, QOF_TYPE_NUMERIC))
{
gnc_numeric ival;
string_to_gnc_numeric (qvalue_name, &ival);
pred_data = qof_query_numeric_predicate (qop, QOF_NUMERIC_MATCH_ANY, ival);
}
else if (!strcmp (param_type, QOF_TYPE_DEBCRED))
{
// XXX this probably needs some work ...
gnc_numeric ival;
string_to_gnc_numeric (qvalue_name, &ival);
pred_data = qof_query_numeric_predicate (qop, QOF_NUMERIC_MATCH_ANY, ival);
}
else if (!strcmp (param_type, QOF_TYPE_GUID))
{
GUID guid;
gboolean rc = string_to_guid (qvalue_name, &guid);
if (0 == rc)
{
PWARN ("unable to parse guid: %s", qvalue_name);
return NULL;
}
// XXX less, than greater than don't make sense,
// should check for those bad conditions
gm = QOF_GUID_MATCH_ANY;
if (QOF_COMPARE_NEQ == qop) gm = QOF_GUID_MATCH_NONE;
guid_list = g_list_append (NULL, &guid);
pred_data = qof_query_guid_predicate (gm, guid_list);
g_list_free (guid_list);
}
else if (!strcmp (param_type, QOF_TYPE_KVP))
{
/* We are expecting an encoded value that looks like
* /some/path/string:value
*/
sep = strchr (qvalue_name, ':');
if (!sep) return NULL;
*sep = 0;
path = qvalue_name;
str = sep +1;
/* If str has only digits, we know its a plain number.
* If its numbers and a decimal point, assume a float
* If its numbers and a slash, assume numeric
* If its 32 bytes of hex, assume GUID
* If it looks like an iso date ...
* else assume its a string.
*/
kval = NULL;
len = strlen (str);
if ((32 == len) && (32 == strspn (str, "0123456789abcdef")))
{
GUID guid;
string_to_guid (str, &guid);
kval = kvp_value_new_guid (&guid);
}
else
if (len == strspn (str, "0123456789"))
{
kval = kvp_value_new_gint64 (atoll(str));
}
else
if ((p=strchr (str, '.')) &&
((len-1) == (strspn (str, "0123456789") +
strspn (p+1, "0123456789"))))
{
kval = kvp_value_new_double (atof(str));
}
else
if ((p=strchr (str, '/')) &&
((len-1) == (strspn (str, "0123456789") +
strspn (p+1, "0123456789"))))
{
gnc_numeric num;
string_to_gnc_numeric (str, &num);
kval = kvp_value_new_gnc_numeric (num);
}
else
if ((p=strchr (str, '-')) &&
(p=strchr (p+1, '-')) &&
(p=strchr (p+1, ' ')) &&
(p=strchr (p+1, ':')) &&
(p=strchr (p+1, ':')))
{
kval = kvp_value_new_timespec (gnc_iso8601_to_timespec_gmt(str));
}
/* The default handler is a string */
if (NULL == kval)
{
kval = kvp_value_new_string (str);
}
pred_data = qof_query_kvp_predicate_path (qop, path, kval);
}
else
{
PWARN ("The predicate type \"%s\" is unsupported for now", param_type);
return NULL;
}
qq = qof_query_create();
qof_query_add_term (qq, param_list, pred_data, QOF_QUERY_FIRST_TERM);
return qq;
}
/* ========================================================== */
static QofQuery *
handle_where (QofSqlQuery *query, sql_where *swear)
{
QofQueryOp qop;
QofQuery * qq;
switch (swear->type)
{
case SQL_pair:
{
QofQuery *qleft = handle_where (query, swear->d.pair.left);
QofQuery *qright = handle_where (query, swear->d.pair.right);
if (NULL == qleft) return qright;
if (NULL == qright) return qleft;
switch (swear->d.pair.op)
{
case SQL_and: qop = QOF_QUERY_AND; break;
case SQL_or: qop = QOF_QUERY_OR; break;
/* XXX should add support for nand, nor, xor */
default:
qof_query_destroy (qleft);
qof_query_destroy (qright);
return NULL;
}
qq = qof_query_merge (qleft, qright, qop);
qof_query_destroy (qleft);
qof_query_destroy (qright);
return qq;
}
case SQL_negated:
{
QofQuery *qq = handle_where (query, swear->d.negated);
QofQuery *qneg = qof_query_invert (qq);
qof_query_destroy (qq);
return qneg;
}
case SQL_single:
{
sql_condition * cond = swear->d.single;
return handle_single_condition (query, cond);
}
}
return NULL;
}
/* ========================================================== */
static void
handle_sort_order (QofSqlQuery *query, GList *sorder_list)
{
GSList *qsp[3];
GList *n;
gboolean direction[3];
int i;
sql_order_field *sorder;
char * qparam_name;
if (!sorder_list) return;
for (i=0; i<3; i++)
{
qsp[i] = NULL;
direction[i] = 0;
if (sorder_list)
{
sorder = sorder_list->data;
/* Set the sort direction */
if (SQL_asc == sorder->order_type) direction[i] = TRUE;
/* Find the parameter name */
qparam_name = NULL;
n = sorder->name;
if (n)
{
qparam_name = n->data;
if (qparam_name)
{
qsp[i] = qof_query_build_param_list (qparam_name, NULL);
}
n = n->next; /* next parameter */
}
else
{
/* if no next parameter, then next order-by */
sorder_list = sorder_list->next;
}
}
}
qof_query_set_sort_order (query->qof_query, qsp[0], qsp[1], qsp[2]);
qof_query_set_sort_increasing (query->qof_query, direction[0],
direction[1], direction[2]);
}
/* ========================================================== */
static void
qof_queryForeachParam( QofParam* param, gpointer user_data)
{
QofSqlQuery *q;
g_return_if_fail(user_data != NULL);
q = (QofSqlQuery*)user_data;
g_return_if_fail(param != NULL);
if((param->param_getfcn != NULL)&&(param->param_setfcn != NULL)) {
q->param_list = g_list_append(q->param_list, param);
}
}
static const char*
qof_sql_get_value(sql_insert_statement *insert)
{
GList *walk, *cur;
const char *insert_string;
sql_field *field;
sql_field_item * item;
/* how to cope with multiple results? */
if (insert->values == NULL) { return NULL; }
insert_string = NULL;
for (walk = insert->values; walk != NULL; walk = walk->next)
{
field = walk->data;
item = field->item;
for (cur = item->d.name; cur != NULL; cur = cur->next)
{
insert_string = g_strdup_printf("%s", (char*)cur->data);
}
}
return insert_string;
}
static const QofParam*
qof_sql_get_param(QofIdTypeConst type, sql_insert_statement *insert)
{
GList *walk, *cur;
const char *param_name;
const QofParam *param;
sql_field *field;
sql_field_item *item;
param = NULL;
param_name = NULL;
if (insert->fields == NULL) { return NULL; }
for (walk = insert->fields; walk != NULL; walk = walk->next)
{
field = walk->data;
item = field->item;
for (cur = item->d.name; cur != NULL; cur = cur->next)
{
param_name = g_strdup_printf("%s", (char*)cur->data);
}
}
param = qof_class_get_parameter(type, param_name);
return param;
}
static void
qof_sql_insertCB( gpointer value, gpointer data)
{
GList *param_list;
QofSqlQuery *q;
QofIdTypeConst type;
sql_insert_statement *sis;
const char *insert_string;
gboolean registered_type;
QofEntity *ent;
QofParam *param;
struct tm query_time;
time_t query_time_t;
/* cm_ prefix used for variables that hold the data to commit */
gnc_numeric cm_numeric;
double cm_double;
gboolean cm_boolean;
gint32 cm_i32;
gint64 cm_i64;
Timespec cm_date;
char cm_char, *tail;
GUID *cm_guid;
/* KvpFrame *cm_kvp;
KvpValue *cm_value;
KvpValueType cm_type;*/
const QofParam *cm_param;
void (*string_setter) (QofEntity*, const char*);
void (*date_setter) (QofEntity*, Timespec);
void (*numeric_setter) (QofEntity*, gnc_numeric);
void (*double_setter) (QofEntity*, double);
void (*boolean_setter) (QofEntity*, gboolean);
void (*i32_setter) (QofEntity*, gint32);
void (*i64_setter) (QofEntity*, gint64);
void (*char_setter) (QofEntity*, char);
/* void (*kvp_frame_setter) (QofEntity*, KvpFrame*);*/
q = (QofSqlQuery*)data;
ent = q->inserted_entity;
param = (QofParam*)value;
sis = q->parse_result->statement;
type = g_strdup_printf("%s", sis->table->d.simple);
insert_string = g_strdup(qof_sql_get_value(sis));
cm_param = qof_sql_get_param(type, sis);
param_list = g_list_copy(q->param_list);
while(param_list != NULL) {
if(safe_strcmp(cm_param->param_type, QOF_TYPE_STRING) == 0) {
string_setter = (void(*)(QofEntity*, const char*))cm_param->param_setfcn;
if(string_setter != NULL) { string_setter(ent, insert_string); }
registered_type = TRUE;
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_DATE) == 0) {
date_setter = (void(*)(QofEntity*, Timespec))cm_param->param_setfcn;
strptime(insert_string, QOF_UTC_DATE_FORMAT, &query_time);
query_time_t = mktime(&query_time);
timespecFromTime_t(&cm_date, query_time_t);
if(date_setter != NULL) { date_setter(ent, cm_date); }
}
if((safe_strcmp(cm_param->param_type, QOF_TYPE_NUMERIC) == 0) ||
(safe_strcmp(cm_param->param_type, QOF_TYPE_DEBCRED) == 0)) {
numeric_setter = (void(*)(QofEntity*, gnc_numeric))cm_param->param_setfcn;
string_to_gnc_numeric(insert_string, &cm_numeric);
if(numeric_setter != NULL) { numeric_setter(ent, cm_numeric); }
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_GUID) == 0) {
cm_guid = g_new(GUID, 1);
if(TRUE != string_to_guid(insert_string, cm_guid))
{
LEAVE (" string to guid failed for %s", insert_string);
return;
}
/* reference_type = xmlGetProp(node, QSF_OBJECT_TYPE);
if(0 == safe_strcmp(QOF_PARAM_GUID, reference_type))
{
qof_entity_set_guid(qsf_ent, cm_guid);
}
else {
reference = qof_entity_get_reference_from(qsf_ent, cm_param);
if(reference) {
params->referenceList = g_list_append(params->referenceList, reference);
}
}*/
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_INT32) == 0) {
errno = 0;
cm_i32 = (gint32)strtol (insert_string, &tail, 0);
if(errno == 0) {
i32_setter = (void(*)(QofEntity*, gint32))cm_param->param_setfcn;
if(i32_setter != NULL) { i32_setter(ent, cm_i32); }
}
// else { qof_backend_set_error(params->be, ERR_QSF_OVERFLOW); }
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_INT64) == 0) {
errno = 0;
cm_i64 = strtoll(insert_string, &tail, 0);
if(errno == 0) {
i64_setter = (void(*)(QofEntity*, gint64))cm_param->param_setfcn;
if(i64_setter != NULL) { i64_setter(ent, cm_i64); }
}
// else { qof_backend_set_error(params->be, ERR_QSF_OVERFLOW); }
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_DOUBLE) == 0) {
errno = 0;
cm_double = strtod(insert_string, &tail);
if(errno == 0) {
double_setter = (void(*)(QofEntity*, double))cm_param->param_setfcn;
if(double_setter != NULL) { double_setter(ent, cm_double); }
}
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_BOOLEAN) == 0){
if(0 == safe_strcmp(insert_string, "TRUE")) {
cm_boolean = TRUE;
}
else { cm_boolean = FALSE; }
boolean_setter = (void(*)(QofEntity*, gboolean))cm_param->param_setfcn;
if(boolean_setter != NULL) { boolean_setter(ent, cm_boolean); }
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_KVP) == 0) {
}
if(safe_strcmp(cm_param->param_type, QOF_TYPE_CHAR) == 0) {
cm_char = *insert_string;
char_setter = (void(*)(QofEntity*, char))cm_param->param_setfcn;
if(char_setter != NULL) { char_setter(ent, cm_char); }
}
param_list = param_list->next;
}
}
static QofEntity*
qof_query_insert(QofSqlQuery *query)
{
QofIdType type;
QofInstance *inst;
sql_insert_statement *sis;
sql_table *sis_t;
query->param_list = NULL;
type = NULL;
sis = query->parse_result->statement;
switch(sis->table->type) {
case SQL_simple: {
sis_t = sis->table;
query->single_global_tablename = g_strdup_printf("%s", sis_t->d.simple);
type = g_strdup(query->single_global_tablename);
break;
}
default: {
fprintf(stderr, "default");
}
}
inst = (QofInstance*)qof_object_new_instance(type, query->book);
if(inst == NULL) { return NULL; }
query->param_list = NULL;
query->inserted_entity = &inst->entity;
qof_class_param_foreach((QofIdTypeConst)type, qof_queryForeachParam, query);
g_list_foreach(query->param_list, qof_sql_insertCB, query);
return query->inserted_entity;
}
static const char*
sql_type_as_string(sql_statement_type type)
{
switch (type)
{
case SQL_select : { return "SELECT"; }
case SQL_insert : { return "INSERT"; }
case SQL_delete : { return "DELETE"; }
case SQL_update : { return "UPDATE"; }
default : { return "unknown"; }
}
}
void
qof_sql_query_parse (QofSqlQuery *query, const char *str)
{
GList *tables;
char *buf;
sql_select_statement *sss;
sql_where *swear;
if (!query) return;
/* Delete old query, if any */
if (query->qof_query)
{
qof_query_destroy (query->qof_query);
sql_destroy(query->parse_result);
query->qof_query = NULL;
}
/* Parse the SQL string */
buf = g_strdup(str);
query->parse_result = sql_parse (buf);
g_free(buf);
if (!query->parse_result)
{
PWARN ("parse error");
return;
}
if ((SQL_select != query->parse_result->type)&&(SQL_insert != query->parse_result->type))
{
PWARN("currently, only SELECT or INSERT statements are supported, "
"got type=%s", sql_type_as_string(query->parse_result->type));
return;
}
/* If the user wrote "SELECT * FROM tablename WHERE ..."
* then we have a single global tablename. But if the
* user wrote "SELECT * FROM tableA, tableB WHERE ..."
* then we don't have a single unique table-name.
*/
tables = sql_statement_get_tables (query->parse_result);
if (1 == g_list_length (tables))
{
query->single_global_tablename = tables->data;
}
/* if this is an insert, we're done with the parse. */
if(SQL_insert == query->parse_result->type) {
query->qof_query = qof_query_create();
return;
}
sss = query->parse_result->statement;
swear = sss->where;
if (swear)
{
/* Walk over the where terms, turn them into QOF predicates */
query->qof_query = handle_where (query, swear);
if (NULL == query->qof_query) return;
}
else
{
query->qof_query = qof_query_create();
}
/* Provide support for different sort orders */
handle_sort_order (query, sss->order);
/* We also want to set the type of thing to search for.
* If the user said SELECT * FROM ... then we should return
* a list of QofEntity. Otherwise, we return ... ?
* XXX all this needs fixing.
*/
qof_query_search_for (query->qof_query, query->single_global_tablename);
}
/* ========================================================== */
GList *
qof_sql_query_run (QofSqlQuery *query, const char *str)
{
GList *results;
if (!query) return NULL;
qof_sql_query_parse (query, str);
if (NULL == query->qof_query) return NULL;
qof_query_set_book (query->qof_query, query->book);
if(SQL_insert == query->parse_result->type){
results = NULL;
results = g_list_append(results, qof_query_insert(query));
return results;
}
qof_query_print (query->qof_query);
results = qof_query_run (query->qof_query);
return results;
}
GList *
qof_sql_query_rerun (QofSqlQuery *query)
{
GList *results;
if (!query) return NULL;
if (NULL == query->qof_query) return NULL;
qof_query_set_book (query->qof_query, query->book);
// qof_query_print (query->qof_query);
results = qof_query_run (query->qof_query);
return results;
}
/* ========================== END OF FILE =================== */

View File

@ -1,201 +0,0 @@
/********************************************************************\
* qofsql.h -- QOF client-side SQL parser *
* *
* 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 *
* *
\********************************************************************/
/** @addtogroup Query
@{ */
/**
@file qofsql.h
@brief QOF client-side SQL parser.
@author Copyright (C) 2004 Linas Vepstas <linas@linas.org>
*/
#ifndef QOF_SQL_QUERY_H
#define QOF_SQL_QUERY_H
#include <glib.h>
#include <kvp_frame.h>
#include <qofbook.h>
#include <qofquery.h>
/** @addtogroup SQL SQL Interface to Query
The types of SQL queries that are allowed at this point are very
limited. In general, only the following types of queries are
supported:
SELECT * FROM SomeObj WHERE (param_a < 10.0) AND (param_b = "asdf")
SORT BY param_c DESC;
INSERT INTO SomeObj (param_a, param_b, param_c) VALUES
("value_a", true, "0/1");
For SELECT, the returned list is a list of all of the instances of 'SomeObj' that
match the query. The 'SORT' term is optional. The 'WHERE' term is
optional; but if you don't include 'WHERE', you will get a list of
all of the object instances. The Boolean operations 'AND' and 'OR'
together with parenthesis can be used to construct arbitrarily
nested predicates.
For INSERT, the returned list is a list containing the newly created instance
of 'SomeObj'.
Joins are not supported directly.
SELECT * FROM ObjA,ObjB WHERE (ObjA.param_id = ObjB.param_other_id);
The problem with the above is that the search requires a nested
search loop, aka a 'join', which is not currently supported in the
underlying QofQuery code.
However, by repeating queries and adding the entities to a new session using
::qof_entity_copy_list, a series of queries can be added to a single
book. e.g. You can insert multiple entities and save out as a QSF XML
file or use multiple SELECT queries to build a precise list - this
can be used to replicate most of the functionality of a SQL join.
SELECT * from ObjA where param_id = value;
SELECT * from ObjB where param_other_id = value;
Equivalent to:
SELECT * from ObjA,ObjB where param_id = param_other_id and param_id = value;
When combined with a foreach callback on the value of param_id for each
entity in the QofBook, you can produce the effect of a join from running
the two SELECT queries for each value of param_id held in 'value'.
See ::QofEntityForeachCB and ::qof_object_foreach.
Date queries handle full date and time strings, using the format
exported by the QSF backend. To query dates and times, convert
user input into UTC time using the ::QOF_UTC_DATE_FORMAT string.
e.g. set the UTC date format and call ::qof_print_time_buff
with a time_t obtained via ::timespecToTime_t.
If the param is a KVP frame, then we use a special markup to
indicate frame values. The markup should look like
/some/kvp/path:value. Thus, for example,
SELECT * FROM SomeObj WHERE (param_a < '/some/kvp:10.0')
will search for the object where param_a is a KVP frame, and this
KVP frame contains a path '/some/kvp' and the value stored at that
path is floating-point and that float value is less than 10.0.
The following are types of queries are NOT supported:
SELECT a,b,c FROM ...
I am thinking of implementing the above as a list of KVP's
whose keys would be a,b,c and values would be the results of the
search. (Linas)
XXX (Neil W). Alternatively, I need to use something like this
when converting QOF objects between applications by using the
returned parameter values to create a second object. One application
using QOF could register objects from two applications and convert
data from one to the other by using SELECT a,b,c FROM ObjA;
SELECT d,f,k FROM ObjB; qof_object_new_instance(); ObjC_set_a(value_c);
ObjC_set_b(value_k) etc. What's needed is for the SELECT to return
a complete object that only contains the parameters selected.
Also unsupported: UPDATE.
Certain SQL commands can have no QOF equivalent and will
generate a runtime parser error:
- ALTER
- CREATE
- DROP
- FLUSH
- GRANT
- KILL
- LOCK
- OPTIMIZE
- REVOKE
- USE
@{ */
typedef struct _QofSqlQuery QofSqlQuery;
/** Create a new SQL-syntax query machine.
*/
QofSqlQuery * qof_sql_query_new (void);
void qof_sql_query_destroy (QofSqlQuery *);
/** Set the book to be searched (you can search multiple books)
* If no books are set, no results will be returned (since there
* is nothing to search over).
*/
void qof_sql_query_set_book (QofSqlQuery *q, QofBook *book);
/** \brief Perform the query, return the results.
* The book must be set in order to be able to perform a query.
*
* The returned list will have been sorted using the indicated sort
* order, (by default ascending order) and trimmed to the
* max_results length.
* Do NOT free the resulting list. This list is managed internally
* by QofSqlQuery.
*
*/
GList * qof_sql_query_run (QofSqlQuery *query, const char * str);
/** Same ::qof_sql_query_run, but just parse/pre-process the query; do
not actually run it over the dataset. The QofBook does not
need to be set before calling this function.
*/
void qof_sql_query_parse (QofSqlQuery *query, const char * str);
/** Return the QofQuery form of the previously parsed query. */
QofQuery * qof_sql_query_get_query (QofSqlQuery *);
/** Run the previously parsed query. The QofBook must be set
* before this function can be called. Note, teh QofBook can
* be changed between each successive call to this routine.
* This routine can be called after either qof_sql_query_parse()
* or qof_sql_query_run() because both will set up the parse.
*/
GList * qof_sql_query_rerun (QofSqlQuery *query);
/**
* Set the kvp frame to be used for formulating 'indirect' predicates.
*
* Although joins are not supported (see above), there is one special
* hack that one can use to pass data indirectly into the predicates.
* This is by using a KVP key name to reference the value to be used
* for a predicate. Thus, for example,
* SELECT * FROM SomeObj WHERE (param_a = KVP:/some/key/path);
* will look up the value stored at '/some/key/path', and use that
* value to form the actual predicate. So, for example, if
* the value stored at '/some/key/path' was 2, then the actual
* query run will be
* SELECT * FROM SomeObj WHERE (param_a = 2);
* The lookup occurs at the time that the query is formulated.
*
* The query does *not* take over ownership of the kvp frame,
* nor does it copy it. Thus, the kvp frame must exist when the
* query is formulated, and it is the responsibility of the
* caller to free it when no longer needed.
*
* Note that because this feature is a kind of a hack put in place
* due to the lack of support for joins, it will probably go away
* (be deprecated) if/when joins are implemented.
*/
void qof_sql_query_set_kvp (QofSqlQuery *, KvpFrame *);
/** @} */
#endif /* QOF_SQL_QUERY_H */
/** @} */