mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
- Convert (QofEntity*)x to QOF_ENTITY(x) - Move qof_entity_init() and friends into qof_instance and rename - Move QofEntity contents into QofInstance (note: this doesn't compile) git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/gobject-engine-dev-warlord@15772 57a11ea4-9604-0410-9ed3-97b8803252fd
1294 lines
41 KiB
C
1294 lines
41 KiB
C
/***************************************************************************
|
|
* qsf-backend.c
|
|
*
|
|
* Sat Jan 1 15:07:14 2005
|
|
* Copyright 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <glib.h>
|
|
#include <glib/gstdio.h>
|
|
#include "qof.h"
|
|
#include "qofbackend-p.h"
|
|
#include "qof-backend-qsf.h"
|
|
#include <libxml/xmlmemory.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/xmlschemas.h>
|
|
#include "qsf-xml.h"
|
|
#include "qsf-dir.h"
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef HAVE_STRPTIME
|
|
#include "strptime.h"
|
|
#endif
|
|
|
|
#define QSF_TYPE_BINARY "binary"
|
|
#define QSF_TYPE_GLIST "glist"
|
|
#define QSF_TYPE_FRAME "frame"
|
|
|
|
static QofLogModule log_module = QOF_MOD_QSF;
|
|
static void qsf_object_commitCB(gpointer key, gpointer value, gpointer data);
|
|
|
|
struct QSFBackend_s
|
|
{
|
|
QofBackend be;
|
|
qsf_param *params;
|
|
gchar *fullpath;
|
|
};
|
|
|
|
typedef struct QSFBackend_s QSFBackend;
|
|
|
|
static void option_cb (QofBackendOption *option, gpointer data)
|
|
{
|
|
qsf_param *params;
|
|
|
|
params = (qsf_param*)data;
|
|
g_return_if_fail(params);
|
|
if(0 == safe_strcmp(QSF_COMPRESS, option->option_name)) {
|
|
params->use_gz_level = (*(gint64*)option->value);
|
|
DEBUG (" gz=%" G_GINT64_FORMAT,params->use_gz_level);
|
|
}
|
|
if (0 == safe_strcmp(QSF_MAP_FILES, option->option_name)) {
|
|
params->map_files = g_list_copy((GList*)option->value);
|
|
}
|
|
if (0 == safe_strcmp(QSF_ENCODING, option->option_name)) {
|
|
params->encoding = g_strdup(option->value);
|
|
DEBUG (" encoding=%s", params->encoding);
|
|
}
|
|
}
|
|
|
|
static void
|
|
qsf_load_config(QofBackend *be, KvpFrame *config)
|
|
{
|
|
QSFBackend *qsf_be;
|
|
qsf_param *params;
|
|
|
|
ENTER (" ");
|
|
qsf_be = (QSFBackend*)be;
|
|
g_return_if_fail(qsf_be->params);
|
|
params = qsf_be->params;
|
|
qof_backend_option_foreach(config, option_cb, params);
|
|
LEAVE (" ");
|
|
}
|
|
|
|
static KvpFrame*
|
|
qsf_get_config(QofBackend *be)
|
|
{
|
|
QofBackendOption *option;
|
|
QSFBackend *qsf_be;
|
|
qsf_param *params;
|
|
|
|
if(!be) { return NULL; }
|
|
ENTER (" ");
|
|
qsf_be = (QSFBackend*)be;
|
|
g_return_val_if_fail(qsf_be->params, NULL);
|
|
params = qsf_be->params;
|
|
qof_backend_prepare_frame(be);
|
|
option = g_new0(QofBackendOption, 1);
|
|
option->option_name = QSF_COMPRESS;
|
|
option->description = _("Level of compression to use: 0 for none, 9 for highest.");
|
|
option->tooltip = _("QOF can compress QSF XML files using gzip. "
|
|
"Note that compression is not used when outputting to STDOUT.");
|
|
option->type = KVP_TYPE_GINT64;
|
|
option->value = (gpointer)¶ms->use_gz_level;
|
|
qof_backend_prepare_option(be, option);
|
|
g_free(option);
|
|
option = g_new0(QofBackendOption, 1);
|
|
option->option_name = QSF_MAP_FILES;
|
|
option->description = _("List of QSF map files to use for this session.");
|
|
option->tooltip = _("QOF can convert objects within QSF XML files "
|
|
"using a map of the changes required.");
|
|
option->type = KVP_TYPE_GLIST;
|
|
option->value = (gpointer)params->map_files;
|
|
qof_backend_prepare_option(be, option);
|
|
g_free(option);
|
|
option = g_new0(QofBackendOption, 1);
|
|
option->option_name = QSF_ENCODING;
|
|
option->description = _("String encoding to use when writing the XML file.");
|
|
option->tooltip = _("QSF defaults to UTF-8. Other encodings are supported by "
|
|
"passing the string encoding in this option.");
|
|
option->type = KVP_TYPE_STRING;
|
|
option->value = (gpointer)params->encoding;
|
|
qof_backend_prepare_option(be, option);
|
|
g_free(option);
|
|
LEAVE (" ");
|
|
return qof_backend_complete_frame(be);
|
|
}
|
|
|
|
GList**
|
|
qsf_map_prepare_list(GList **maps)
|
|
{
|
|
*maps = g_list_prepend(*maps, "pilot-qsf-GnuCashInvoice.xml");
|
|
*maps = g_list_prepend(*maps, "pilot-qsf-gncCustomer.xml");
|
|
return maps;
|
|
}
|
|
|
|
static void
|
|
qsf_param_init(qsf_param *params)
|
|
{
|
|
Timespec *qsf_ts;
|
|
gchar qsf_time_string[QSF_DATE_LENGTH];
|
|
gchar qsf_enquiry_date[QSF_DATE_LENGTH];
|
|
gchar qsf_time_match[QSF_DATE_LENGTH];
|
|
gchar qsf_time_now[QSF_DATE_LENGTH];
|
|
time_t qsf_time_now_t;
|
|
gchar *qsf_time_precision;
|
|
|
|
g_return_if_fail(params != NULL);
|
|
params->count = 0;
|
|
params->use_gz_level = 0;
|
|
params->supported_types = NULL;
|
|
params->file_type = QSF_UNDEF;
|
|
params->qsf_ns = NULL;
|
|
params->output_doc = NULL;
|
|
params->output_node = NULL;
|
|
params->lister = NULL;
|
|
params->full_kvp_path = NULL;
|
|
params->map_ns = NULL;
|
|
params->map_files = NULL;
|
|
params->map_path = NULL;
|
|
params->encoding = "UTF-8";
|
|
params->qsf_object_list = NULL;
|
|
params->qsf_parameter_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
params->qsf_default_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
params->qsf_define_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
params->qsf_calculate_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
|
params->referenceList = NULL;
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_STRING);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_GUID);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_BOOLEAN);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_NUMERIC);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_DATE);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_INT32);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_INT64);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_DOUBLE);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_CHAR);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_KVP);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_COLLECT);
|
|
params->supported_types = g_slist_append(params->supported_types, QOF_TYPE_CHOICE);
|
|
qsf_time_precision = "%j";
|
|
qsf_time_now_t = time(NULL);
|
|
qsf_ts = g_new(Timespec, 1);
|
|
timespecFromTime_t(qsf_ts, qsf_time_now_t);
|
|
strftime(qsf_enquiry_date, QSF_DATE_LENGTH, QSF_XSD_TIME, gmtime(&qsf_time_now_t));
|
|
strftime(qsf_time_match, QSF_DATE_LENGTH, qsf_time_precision, gmtime(&qsf_time_now_t));
|
|
strftime(qsf_time_string, QSF_DATE_LENGTH, "%F", gmtime(&qsf_time_now_t));
|
|
strftime(qsf_time_now, QSF_DATE_LENGTH, QSF_XSD_TIME, gmtime(&qsf_time_now_t));
|
|
g_hash_table_insert(params->qsf_default_hash, "qsf_enquiry_date", qsf_enquiry_date);
|
|
g_hash_table_insert(params->qsf_default_hash, "qsf_time_now", &qsf_time_now_t);
|
|
g_hash_table_insert(params->qsf_default_hash, "qsf_time_string", qsf_time_string);
|
|
/* default map files */
|
|
params->map_files = *qsf_map_prepare_list(¶ms->map_files);
|
|
}
|
|
|
|
static gboolean
|
|
qsf_determine_file_type(const gchar *path)
|
|
{
|
|
struct stat sbuf;
|
|
|
|
if (!path) { return TRUE; }
|
|
if (0 == safe_strcmp(path, QOF_STDOUT)) { return TRUE; }
|
|
if (g_stat(path, &sbuf) <0) { return FALSE; }
|
|
if (sbuf.st_size == 0) { return TRUE; }
|
|
if(is_our_qsf_object(path)) { return TRUE; }
|
|
else if(is_qsf_object(path)) { return TRUE; }
|
|
else if(is_qsf_map(path)) { return TRUE; }
|
|
return FALSE;
|
|
}
|
|
|
|
/* GnuCash does LOTS of filesystem work, QSF is going to leave most of it to libxml2. :-)
|
|
Just strip the file: from the start of the book_path URL. Locks are not implemented.
|
|
*/
|
|
static void
|
|
qsf_session_begin(QofBackend *be, QofSession *session, const gchar *book_path,
|
|
gboolean ignore_lock, gboolean create_if_nonexistent)
|
|
{
|
|
QSFBackend *qsf_be;
|
|
gchar *p, *path;
|
|
|
|
PINFO (" ignore_lock=%d create_if_nonexistent=%d", ignore_lock, create_if_nonexistent);
|
|
g_return_if_fail(be != NULL);
|
|
qsf_be = (QSFBackend*)be;
|
|
g_return_if_fail(qsf_be->params != NULL);
|
|
qsf_be->fullpath = NULL;
|
|
if(book_path == NULL)
|
|
{
|
|
/* use stdout */
|
|
qof_backend_set_error(be, ERR_BACKEND_NO_ERR);
|
|
return;
|
|
}
|
|
p = strchr (book_path, ':');
|
|
if (p) {
|
|
path = g_strdup (book_path);
|
|
if (!g_ascii_strncasecmp(path, "file:", 5)) {
|
|
p = g_new(gchar, strlen(path) - 5 + 1);
|
|
strcpy(p, path + 5);
|
|
}
|
|
qsf_be->fullpath = g_strdup(p);
|
|
g_free (path);
|
|
}
|
|
else {
|
|
qsf_be->fullpath = g_strdup(book_path);
|
|
}
|
|
if(create_if_nonexistent)
|
|
{
|
|
FILE *f;
|
|
|
|
f = g_fopen(qsf_be->fullpath, "a+");
|
|
if(f) {fclose(f); }
|
|
else
|
|
{
|
|
qof_backend_set_error(be, ERR_BACKEND_READONLY);
|
|
return;
|
|
}
|
|
}
|
|
qof_backend_set_error(be, ERR_BACKEND_NO_ERR);
|
|
}
|
|
|
|
static void
|
|
qsf_free_params(qsf_param *params)
|
|
{
|
|
g_hash_table_destroy(params->qsf_calculate_hash);
|
|
g_hash_table_destroy(params->qsf_default_hash);
|
|
if(params->referenceList) {
|
|
g_list_free(params->referenceList);
|
|
}
|
|
g_slist_free(params->supported_types);
|
|
if(params->map_ns) { xmlFreeNs(params->map_ns); }
|
|
}
|
|
|
|
static void
|
|
qsf_session_end( QofBackend *be)
|
|
{
|
|
QSFBackend *qsf_be;
|
|
|
|
qsf_be = (QSFBackend*)be;
|
|
g_return_if_fail(qsf_be != NULL);
|
|
qsf_free_params(qsf_be->params);
|
|
g_free(qsf_be->fullpath);
|
|
qsf_be->fullpath = NULL;
|
|
xmlCleanupParser();
|
|
}
|
|
|
|
static void
|
|
qsf_destroy_backend (QofBackend *be)
|
|
{
|
|
g_free(be);
|
|
}
|
|
|
|
static void
|
|
ent_ref_cb (QofEntity* ent, gpointer user_data)
|
|
{
|
|
qsf_param *params;
|
|
QofEntityReference *ref;
|
|
void (*reference_setter) (QofEntity*, QofEntity*);
|
|
QofEntity *reference;
|
|
QofCollection *coll;
|
|
QofIdType type;
|
|
|
|
params = (qsf_param*)user_data;
|
|
g_return_if_fail(params);
|
|
while(params->referenceList)
|
|
{
|
|
ref = (QofEntityReference*)params->referenceList->data;
|
|
if(qof_object_is_choice(ent->e_type)) { type = ref->choice_type; }
|
|
else { type = ref->type; }
|
|
coll = qof_book_get_collection(params->book, type);
|
|
reference = qof_collection_lookup_entity(coll, ref->ref_guid);
|
|
reference_setter = (void(*)(QofEntity*, QofEntity*))ref->param->param_setfcn;
|
|
if(reference_setter != NULL)
|
|
{
|
|
qof_begin_edit((QofInstance*)ent);
|
|
qof_begin_edit((QofInstance*)reference);
|
|
reference_setter(ent, reference);
|
|
qof_commit_edit((QofInstance*)ent);
|
|
qof_commit_edit((QofInstance*)reference);
|
|
}
|
|
params->referenceList = g_list_next(params->referenceList);
|
|
}
|
|
}
|
|
|
|
static void
|
|
insert_ref_cb(QofObject *obj, gpointer user_data)
|
|
{
|
|
qsf_param *params;
|
|
|
|
params = (qsf_param*)user_data;
|
|
g_return_if_fail(params);
|
|
qof_object_foreach(obj->e_type, params->book, ent_ref_cb, params);
|
|
}
|
|
|
|
/*================================================
|
|
Load QofEntity into QofBook from XML in memory
|
|
==================================================*/
|
|
|
|
static gboolean
|
|
qsfdoc_to_qofbook(xmlDocPtr doc, qsf_param *params)
|
|
{
|
|
QofInstance *inst;
|
|
struct qsf_node_iterate iter;
|
|
QofBook *book;
|
|
GList *object_list;
|
|
xmlNodePtr qsf_root;
|
|
xmlNsPtr qsf_ns;
|
|
|
|
g_return_val_if_fail(params != NULL, FALSE);
|
|
g_return_val_if_fail(params->input_doc != NULL, FALSE);
|
|
g_return_val_if_fail(params->book != NULL, FALSE);
|
|
g_return_val_if_fail(params->file_type == OUR_QSF_OBJ, FALSE);
|
|
qsf_root = xmlDocGetRootElement(params->input_doc);
|
|
if(!qsf_root) { return FALSE; }
|
|
qsf_ns = qsf_root->ns;
|
|
iter.ns = qsf_ns;
|
|
book = params->book;
|
|
params->referenceList = (GList*)qof_book_get_data(book, ENTITYREFERENCE);
|
|
qsf_node_foreach(qsf_root, qsf_book_node_handler, &iter, params);
|
|
object_list = g_list_copy(params->qsf_object_list);
|
|
while(object_list != NULL)
|
|
{
|
|
params->object_set = object_list->data;
|
|
object_list = g_list_next(object_list);
|
|
params->qsf_parameter_hash = params->object_set->parameters;
|
|
if(!qof_class_is_registered(params->object_set->object_type)) { continue; }
|
|
inst = (QofInstance*)qof_object_new_instance(params->object_set->object_type, book);
|
|
g_return_val_if_fail(inst != NULL, FALSE);
|
|
params->qsf_ent = &inst->entity;
|
|
qof_begin_edit(inst);
|
|
g_hash_table_foreach(params->qsf_parameter_hash, qsf_object_commitCB, params);
|
|
qof_commit_edit(inst);
|
|
}
|
|
qof_object_foreach_type(insert_ref_cb, params);
|
|
qof_book_set_data(book, ENTITYREFERENCE, params->referenceList);
|
|
return TRUE;
|
|
}
|
|
|
|
/* QofBackend routine to load from file - needs a map.
|
|
*/
|
|
static gboolean
|
|
load_qsf_object(QofBook *book, const gchar *fullpath, qsf_param *params)
|
|
{
|
|
xmlNodePtr qsf_root, map_root;
|
|
xmlDocPtr mapDoc, foreign_doc;
|
|
gchar *map_path, *map_file;
|
|
|
|
map_file = params->map_path;
|
|
mapDoc = NULL;
|
|
/* use selected map */
|
|
if(!map_file) {
|
|
qof_backend_set_error(params->be, ERR_QSF_NO_MAP);
|
|
return FALSE;
|
|
}
|
|
foreign_doc = xmlParseFile(fullpath);
|
|
if (foreign_doc == NULL) {
|
|
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
|
|
return FALSE;
|
|
}
|
|
qsf_root = NULL;
|
|
qsf_root = xmlDocGetRootElement(foreign_doc);
|
|
params->qsf_ns = qsf_root->ns;
|
|
params->book = book;
|
|
map_path = g_strdup_printf("%s/%s", QSF_SCHEMA_DIR, map_file);
|
|
if(!map_path) {
|
|
qof_backend_set_error(params->be, ERR_QSF_NO_MAP);
|
|
return FALSE;
|
|
}
|
|
mapDoc = xmlParseFile(map_path);
|
|
if(!mapDoc) {
|
|
qof_backend_set_error(params->be, ERR_QSF_NO_MAP);
|
|
return FALSE;
|
|
}
|
|
map_root = xmlDocGetRootElement(mapDoc);
|
|
params->map_ns = map_root->ns;
|
|
params->input_doc = qsf_object_convert(mapDoc, qsf_root, params);
|
|
qsfdoc_to_qofbook(params->input_doc, params);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
load_our_qsf_object(QofBook *book, const gchar *fullpath, qsf_param *params)
|
|
{
|
|
xmlNodePtr qsf_root;
|
|
|
|
params->input_doc = xmlParseFile(fullpath);
|
|
if (params->input_doc == NULL) {
|
|
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
|
|
return FALSE;
|
|
}
|
|
qsf_root = NULL;
|
|
qsf_root = xmlDocGetRootElement(params->input_doc);
|
|
params->qsf_ns = qsf_root->ns;
|
|
return qsfdoc_to_qofbook(params->input_doc, params);
|
|
}
|
|
|
|
/* Determine the type of QSF and load it into the QofBook
|
|
|
|
- is_our_qsf_object, OUR_QSF_OBJ, QSF object file using only QOF objects known
|
|
to the calling process. No map is required.
|
|
- is_qsf_object, IS_QSF_OBJ, QSF object file that may or may not have a QSF map
|
|
to convert external objects. This temporary type will be set to HAVE_QSF_MAP
|
|
if a suitable map exists, or an error value returned: ERR_QSF_NO_MAP,
|
|
ERR_QSF_BAD_MAP or ERR_QSF_WRONG_MAP. This allows the calling process to inform
|
|
the user that the QSF itself is valid but a suitable map cannot be found.
|
|
- is_qsf_map, IS_QSF_MAP, QSF map file. In the backend, this generates
|
|
ERR_QSF_MAP_NOT_OBJ but it can be used internally when processing maps to
|
|
match a QSF object.
|
|
|
|
returns NULL on error, otherwise a pointer to the QofBook. Use
|
|
the qof_book_merge API to merge the new data into the current
|
|
QofBook.
|
|
*/
|
|
static void
|
|
qsf_file_type(QofBackend *be, QofBook *book)
|
|
{
|
|
QSFBackend *qsf_be;
|
|
qsf_param *params;
|
|
FILE *f;
|
|
gchar *path;
|
|
gboolean result;
|
|
|
|
g_return_if_fail(be != NULL);
|
|
g_return_if_fail(book != NULL);
|
|
qsf_be = (QSFBackend*) be;
|
|
g_return_if_fail(qsf_be != NULL);
|
|
g_return_if_fail(qsf_be->fullpath != NULL);
|
|
g_return_if_fail(qsf_be->params != NULL);
|
|
params = qsf_be->params;
|
|
params->book = book;
|
|
path = g_strdup(qsf_be->fullpath);
|
|
f = g_fopen(path, "r");
|
|
if(!f) { qof_backend_set_error(be, ERR_FILEIO_READ_ERROR); }
|
|
fclose(f);
|
|
params->filepath = g_strdup(path);
|
|
qof_backend_get_error(be);
|
|
result = is_our_qsf_object_be(params);
|
|
if(result) {
|
|
params->file_type = OUR_QSF_OBJ;
|
|
result = load_our_qsf_object(book, path, params);
|
|
if(!result) { qof_backend_set_error(be, ERR_FILEIO_PARSE_ERROR); }
|
|
return;
|
|
}
|
|
else if(is_qsf_object_be(params)) {
|
|
params->file_type = IS_QSF_OBJ;
|
|
result = load_qsf_object(book, path, params);
|
|
if(!result) { qof_backend_set_error(be, ERR_FILEIO_PARSE_ERROR); }
|
|
return;
|
|
}
|
|
if(result == FALSE) {
|
|
if(is_qsf_map_be(params)) {
|
|
params->file_type = IS_QSF_MAP;
|
|
qof_backend_set_error(be, ERR_QSF_MAP_NOT_OBJ);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
qsf_object_sequence(QofParam *qof_param, gpointer data)
|
|
{
|
|
qsf_param *params;
|
|
GSList *checklist, *result;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
params = (qsf_param*) data;
|
|
result = NULL;
|
|
checklist = NULL;
|
|
params->knowntype = FALSE;
|
|
checklist = g_slist_copy(params->supported_types);
|
|
for(result = checklist; result != NULL; result = result->next)
|
|
{
|
|
if(0 == safe_strcmp((QofIdType)result->data, qof_param->param_type))
|
|
{
|
|
params->knowntype = TRUE;
|
|
}
|
|
}
|
|
g_slist_free(checklist);
|
|
if(0 == safe_strcmp(qof_param->param_type, params->qof_type))
|
|
{
|
|
params->qsf_sequence = g_slist_append(params->qsf_sequence, qof_param);
|
|
params->knowntype = TRUE;
|
|
}
|
|
/* handle params->qof_type = QOF_TYPE_GUID and qof_param->param_type != known type */
|
|
if(0 == safe_strcmp(params->qof_type, QOF_TYPE_GUID)
|
|
&& (params->knowntype == FALSE))
|
|
{
|
|
params->qsf_sequence = g_slist_append(params->qsf_sequence, qof_param);
|
|
params->knowntype = TRUE;
|
|
}
|
|
}
|
|
|
|
/* receives each entry from supported_types in sequence
|
|
type = qof data type from supported list
|
|
user_data = params. Holds object type
|
|
*/
|
|
static void
|
|
qsf_supported_parameters(gpointer type, gpointer user_data)
|
|
{
|
|
qsf_param *params;
|
|
|
|
g_return_if_fail(user_data != NULL);
|
|
params = (qsf_param*) user_data;
|
|
params->qof_type = (QofIdType)type;
|
|
params->knowntype = FALSE;
|
|
qof_class_param_foreach(params->qof_obj_type, qsf_object_sequence, params);
|
|
}
|
|
|
|
static KvpValueType
|
|
qsf_to_kvp_helper(const char *type_string)
|
|
{
|
|
if(0 == safe_strcmp(QOF_TYPE_INT64, type_string)) { return KVP_TYPE_GINT64; }
|
|
if(0 == safe_strcmp(QOF_TYPE_DOUBLE, type_string)) { return KVP_TYPE_DOUBLE; }
|
|
if(0 == safe_strcmp(QOF_TYPE_NUMERIC, type_string)) { return KVP_TYPE_NUMERIC; }
|
|
if(0 == safe_strcmp(QOF_TYPE_STRING, type_string)) { return KVP_TYPE_STRING; }
|
|
if(0 == safe_strcmp(QOF_TYPE_GUID, type_string)) { return KVP_TYPE_GUID; }
|
|
if(0 == safe_strcmp(QOF_TYPE_DATE, type_string)) { return KVP_TYPE_TIMESPEC; }
|
|
if(0 == safe_strcmp(QSF_TYPE_BINARY, type_string)) { return KVP_TYPE_BINARY; }
|
|
if(0 == safe_strcmp(QSF_TYPE_GLIST, type_string)) { return KVP_TYPE_GLIST; }
|
|
if(0 == safe_strcmp(QSF_TYPE_FRAME, type_string)) { return KVP_TYPE_FRAME; }
|
|
return 0;
|
|
}
|
|
|
|
static QofIdTypeConst
|
|
kvp_value_to_qof_type_helper(KvpValueType n)
|
|
{
|
|
switch(n)
|
|
{
|
|
case KVP_TYPE_GINT64 : { return QOF_TYPE_INT64; break; }
|
|
case KVP_TYPE_DOUBLE : { return QOF_TYPE_DOUBLE; break; }
|
|
case KVP_TYPE_NUMERIC : { return QOF_TYPE_NUMERIC; break; }
|
|
case KVP_TYPE_STRING : { return QOF_TYPE_STRING; break; }
|
|
case KVP_TYPE_GUID : { return QOF_TYPE_GUID; break; }
|
|
case KVP_TYPE_TIMESPEC : { return QOF_TYPE_DATE; break; }
|
|
case KVP_TYPE_BINARY : { return QSF_TYPE_BINARY; break; }
|
|
case KVP_TYPE_GLIST : { return QSF_TYPE_GLIST; break; }
|
|
case KVP_TYPE_FRAME : { return QSF_TYPE_FRAME; break; }
|
|
default : { return NULL; }
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
qsf_from_kvp_helper(const gchar *path, KvpValue *content, gpointer data)
|
|
{
|
|
qsf_param *params;
|
|
QofParam *qof_param;
|
|
xmlNodePtr node;
|
|
KvpValueType n;
|
|
gchar *full_path;
|
|
|
|
params = (qsf_param*)data;
|
|
qof_param = params->qof_param;
|
|
full_path = NULL;
|
|
g_return_if_fail(params && path && content);
|
|
ENTER (" ");
|
|
n = kvp_value_get_type(content);
|
|
switch(n)
|
|
{
|
|
case KVP_TYPE_GINT64 :
|
|
case KVP_TYPE_DOUBLE :
|
|
case KVP_TYPE_NUMERIC :
|
|
case KVP_TYPE_STRING :
|
|
case KVP_TYPE_GUID :
|
|
case KVP_TYPE_TIMESPEC :
|
|
case KVP_TYPE_BINARY :
|
|
case KVP_TYPE_GLIST :
|
|
{
|
|
node = xmlAddChild(params->output_node, xmlNewNode(params->qsf_ns,
|
|
BAD_CAST qof_param->param_type));
|
|
xmlNodeAddContent(node, BAD_CAST kvp_value_to_bare_string(content));
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST qof_param->param_name);
|
|
full_path = g_strconcat(params->full_kvp_path, "/", path, NULL);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_KVP, BAD_CAST full_path);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_VALUE,
|
|
BAD_CAST kvp_value_to_qof_type_helper(n));
|
|
PINFO (" set %s", kvp_value_to_qof_type_helper(n));
|
|
break;
|
|
}
|
|
case KVP_TYPE_FRAME:
|
|
{
|
|
if(!params->full_kvp_path) { params->full_kvp_path = g_strdup(path); }
|
|
else {
|
|
params->full_kvp_path = g_strconcat(params->full_kvp_path,
|
|
"/", path, NULL);
|
|
}
|
|
PINFO (" full=%s, path=%s ", params->full_kvp_path, path);
|
|
kvp_frame_for_each_slot(kvp_value_get_frame(content),
|
|
qsf_from_kvp_helper, params);
|
|
g_free(params->full_kvp_path);
|
|
params->full_kvp_path = NULL;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
PERR (" unsupported value = %d", kvp_value_get_type(content));
|
|
break;
|
|
}
|
|
}
|
|
LEAVE (" ");
|
|
}
|
|
|
|
static void
|
|
qsf_from_coll_cb (QofEntity *ent, gpointer user_data)
|
|
{
|
|
qsf_param *params;
|
|
QofParam *qof_param;
|
|
xmlNodePtr node;
|
|
gchar qsf_guid[GUID_ENCODING_LENGTH + 1];
|
|
|
|
params = (qsf_param*)user_data;
|
|
if(!ent || !params) { return; }
|
|
qof_param = params->qof_param;
|
|
guid_to_string_buff(qof_entity_get_guid(ent), qsf_guid);
|
|
node = xmlAddChild(params->output_node, xmlNewNode(params->qsf_ns,
|
|
BAD_CAST qof_param->param_type));
|
|
xmlNodeAddContent(node, BAD_CAST qsf_guid);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST qof_param->param_name);
|
|
}
|
|
|
|
/******* reference handling ***********/
|
|
|
|
static gint
|
|
qof_reference_list_cb(gconstpointer a, gconstpointer b)
|
|
{
|
|
const QofEntityReference *aa;
|
|
const QofEntityReference *bb;
|
|
|
|
aa = (QofEntityReference*) a;
|
|
bb = (QofEntityReference*) b;
|
|
if(aa == NULL) { return 1; }
|
|
g_return_val_if_fail((bb != NULL), 1);
|
|
g_return_val_if_fail((aa->type != NULL), 1);
|
|
if((0 == guid_compare(bb->ent_guid, aa->ent_guid))
|
|
&&(0 == safe_strcmp(bb->type, aa->type))
|
|
&&(0 == safe_strcmp(bb->param->param_name, aa->param->param_name)))
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static QofEntityReference*
|
|
qof_reference_lookup(GList *referenceList, QofEntityReference *find)
|
|
{
|
|
GList *single_ref;
|
|
QofEntityReference *ent_ref;
|
|
|
|
if(referenceList == NULL) { return NULL; }
|
|
g_return_val_if_fail(find != NULL, NULL);
|
|
single_ref = NULL;
|
|
ent_ref = NULL;
|
|
single_ref = g_list_find_custom(referenceList, find, qof_reference_list_cb);
|
|
if(single_ref == NULL) { return ent_ref; }
|
|
ent_ref = (QofEntityReference*)single_ref->data;
|
|
g_list_free(single_ref);
|
|
return ent_ref;
|
|
}
|
|
|
|
static void
|
|
reference_list_lookup(gpointer data, gpointer user_data)
|
|
{
|
|
QofEntity *ent;
|
|
QofParam *ref_param;
|
|
QofEntityReference *reference, *starter;
|
|
qsf_param *params;
|
|
const GUID *guid;
|
|
xmlNodePtr node, object_node;
|
|
xmlNsPtr ns;
|
|
GList *copy_list;
|
|
gchar qsf_guid[GUID_ENCODING_LENGTH + 1], *ref_name;
|
|
|
|
params = (qsf_param*)user_data;
|
|
ref_param = (QofParam*)data;
|
|
object_node = params->output_node;
|
|
ent = params->qsf_ent;
|
|
ns = params->qsf_ns;
|
|
starter = g_new(QofEntityReference, 1);
|
|
starter->ent_guid = qof_entity_get_guid(ent);
|
|
starter->type = g_strdup(ent->e_type);
|
|
starter->param = ref_param;
|
|
starter->ref_guid = NULL;
|
|
copy_list = g_list_copy(params->referenceList);
|
|
reference = qof_reference_lookup(copy_list, starter);
|
|
g_free(starter);
|
|
if(reference != NULL) {
|
|
if((ref_param->param_getfcn == NULL)||(ref_param->param_setfcn == NULL))
|
|
{
|
|
return;
|
|
}
|
|
ref_name = g_strdup(reference->param->param_name);
|
|
node = xmlAddChild(object_node, xmlNewNode(ns, BAD_CAST QOF_TYPE_GUID));
|
|
guid_to_string_buff(reference->ref_guid, qsf_guid);
|
|
xmlNodeAddContent(node, BAD_CAST qsf_guid);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST ref_name);
|
|
g_free(ref_name);
|
|
}
|
|
else {
|
|
ent = QOF_ENTITY(ref_param->param_getfcn(ent, ref_param));
|
|
if(!ent) { return; }
|
|
if((0 == safe_strcmp(ref_param->param_type, QOF_TYPE_COLLECT)) ||
|
|
(0 == safe_strcmp(ref_param->param_type, QOF_TYPE_CHOICE)))
|
|
{ return; }
|
|
node = xmlAddChild(object_node, xmlNewNode(ns, BAD_CAST QOF_TYPE_GUID));
|
|
guid = qof_entity_get_guid(ent);
|
|
guid_to_string_buff(guid, qsf_guid);
|
|
xmlNodeAddContent(node, BAD_CAST qsf_guid);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST ref_param->param_name);
|
|
}
|
|
}
|
|
|
|
/*=====================================
|
|
Convert QofEntity to QSF XML node
|
|
qof_param holds the parameter sequence.
|
|
=======================================*/
|
|
static void
|
|
qsf_entity_foreach(QofEntity *ent, gpointer data)
|
|
{
|
|
qsf_param *params;
|
|
GSList *param_list, *supported;
|
|
GList *ref;
|
|
xmlNodePtr node, object_node;
|
|
xmlNsPtr ns;
|
|
gchar *string_buffer;
|
|
QofParam *qof_param;
|
|
QofEntity *choice_ent;
|
|
KvpFrame *qsf_kvp;
|
|
QofCollection *qsf_coll;
|
|
gint param_count;
|
|
gboolean own_guid;
|
|
const GUID *cm_guid;
|
|
gchar cm_sa[GUID_ENCODING_LENGTH + 1];
|
|
|
|
g_return_if_fail(data != NULL);
|
|
params = (qsf_param*)data;
|
|
param_count = ++params->count;
|
|
ns = params->qsf_ns;
|
|
qsf_kvp = kvp_frame_new();
|
|
own_guid = FALSE;
|
|
choice_ent = NULL;
|
|
object_node = xmlNewChild(params->book_node, params->qsf_ns,
|
|
BAD_CAST QSF_OBJECT_TAG, NULL);
|
|
xmlNewProp(object_node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST ent->e_type);
|
|
string_buffer = g_strdup_printf("%i", param_count);
|
|
xmlNewProp(object_node, BAD_CAST QSF_OBJECT_COUNT, BAD_CAST string_buffer);
|
|
g_free(string_buffer);
|
|
param_list = g_slist_copy(params->qsf_sequence);
|
|
while(param_list != NULL) {
|
|
qof_param = (QofParam*)param_list->data;
|
|
g_return_if_fail(qof_param != NULL);
|
|
if(0 == safe_strcmp(qof_param->param_type, QOF_TYPE_GUID))
|
|
{
|
|
if(!own_guid)
|
|
{
|
|
cm_guid = qof_entity_get_guid(ent);
|
|
node = xmlAddChild(object_node, xmlNewNode(ns, BAD_CAST QOF_TYPE_GUID));
|
|
guid_to_string_buff(cm_guid, cm_sa);
|
|
string_buffer = g_strdup(cm_sa);
|
|
xmlNodeAddContent(node, BAD_CAST string_buffer);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE , BAD_CAST QOF_PARAM_GUID);
|
|
g_free(string_buffer);
|
|
own_guid = TRUE;
|
|
}
|
|
params->qsf_ent = ent;
|
|
params->output_node = object_node;
|
|
ref = qof_class_get_referenceList(ent->e_type);
|
|
if(ref != NULL) {
|
|
g_list_foreach(ref, reference_list_lookup, params);
|
|
}
|
|
}
|
|
if(0 == safe_strcmp(qof_param->param_type, QOF_TYPE_COLLECT))
|
|
{
|
|
qsf_coll = qof_param->param_getfcn(ent, qof_param);
|
|
if(qsf_coll) {
|
|
params->qof_param = qof_param;
|
|
params->output_node = object_node;
|
|
if(qof_collection_count(qsf_coll) > 0) {
|
|
qof_collection_foreach(qsf_coll, qsf_from_coll_cb, params);
|
|
}
|
|
}
|
|
param_list = g_slist_next(param_list);
|
|
continue;
|
|
}
|
|
if(0 == safe_strcmp(qof_param->param_type, QOF_TYPE_CHOICE))
|
|
{
|
|
/** \todo use the reference list here. */
|
|
choice_ent = QOF_ENTITY(qof_param->param_getfcn(ent, qof_param));
|
|
if(!choice_ent) {
|
|
param_list = g_slist_next(param_list);
|
|
continue;
|
|
}
|
|
node = xmlAddChild(object_node, xmlNewNode(ns, BAD_CAST qof_param->param_type));
|
|
cm_guid = qof_entity_get_guid(choice_ent);
|
|
guid_to_string_buff(cm_guid, cm_sa);
|
|
string_buffer = g_strdup(cm_sa);
|
|
xmlNodeAddContent(node, BAD_CAST string_buffer);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST qof_param->param_name);
|
|
xmlNewProp(node, BAD_CAST "name", BAD_CAST choice_ent->e_type);
|
|
g_free(string_buffer);
|
|
param_list = g_slist_next(param_list);
|
|
continue;
|
|
}
|
|
if(0 == safe_strcmp(qof_param->param_type, QOF_TYPE_KVP))
|
|
{
|
|
qsf_kvp = (KvpFrame*)qof_param->param_getfcn(ent,qof_param);
|
|
if(kvp_frame_is_empty(qsf_kvp)) { LEAVE(" "); return; }
|
|
params->qof_param = qof_param;
|
|
params->output_node = object_node;
|
|
kvp_frame_for_each_slot(qsf_kvp, qsf_from_kvp_helper, params);
|
|
}
|
|
if((qof_param->param_setfcn != NULL) && (qof_param->param_getfcn != NULL))
|
|
{
|
|
for( supported = g_slist_copy(params->supported_types);
|
|
supported != NULL; supported = g_slist_next(supported))
|
|
{
|
|
if(0 == safe_strcmp((const gchar*)supported->data, (const gchar*)qof_param->param_type))
|
|
{
|
|
node = xmlAddChild(object_node, xmlNewNode(ns, BAD_CAST qof_param->param_type));
|
|
string_buffer = g_strdup(qof_book_merge_param_as_string(qof_param, ent));
|
|
xmlNodeAddContent(node, BAD_CAST string_buffer);
|
|
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST qof_param->param_name);
|
|
g_free(string_buffer);
|
|
}
|
|
}
|
|
}
|
|
param_list = g_slist_next(param_list);
|
|
}
|
|
}
|
|
|
|
static void
|
|
qsf_foreach_obj_type(QofObject *qsf_obj, gpointer data)
|
|
{
|
|
qsf_param *params;
|
|
QofBook *book;
|
|
GSList *support;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
params = (qsf_param*) data;
|
|
/* Skip unsupported objects */
|
|
if((qsf_obj->create == NULL)||(qsf_obj->foreach == NULL)){
|
|
PINFO (" qsf_obj QOF support failed %s", qsf_obj->e_type);
|
|
return;
|
|
}
|
|
params->qof_obj_type = qsf_obj->e_type;
|
|
params->qsf_sequence = NULL;
|
|
book = params->book;
|
|
support = g_slist_copy(params->supported_types);
|
|
g_slist_foreach(support,qsf_supported_parameters, params);
|
|
qof_object_foreach(qsf_obj->e_type, book, qsf_entity_foreach, params);
|
|
}
|
|
|
|
/*=====================================================
|
|
Take a QofBook and prepare a QSF XML doc in memory
|
|
=======================================================*/
|
|
/* QSF only uses one QofBook per file - count may be removed later. */
|
|
static xmlDocPtr
|
|
qofbook_to_qsf(QofBook *book, qsf_param *params)
|
|
{
|
|
xmlNodePtr top_node, node;
|
|
xmlDocPtr doc;
|
|
gchar buffer[GUID_ENCODING_LENGTH + 1];
|
|
const GUID *book_guid;
|
|
|
|
g_return_val_if_fail(book != NULL, NULL);
|
|
params->book = book;
|
|
params->referenceList =
|
|
g_list_copy((GList*)qof_book_get_data(book, ENTITYREFERENCE));
|
|
doc = xmlNewDoc(BAD_CAST QSF_XML_VERSION);
|
|
top_node = xmlNewNode(NULL, BAD_CAST QSF_ROOT_TAG);
|
|
xmlDocSetRootElement(doc, top_node);
|
|
xmlSetNs(top_node, xmlNewNs(top_node, BAD_CAST QSF_DEFAULT_NS, NULL));
|
|
params->qsf_ns = top_node->ns;
|
|
node = xmlNewChild(top_node, params->qsf_ns, BAD_CAST QSF_BOOK_TAG, NULL);
|
|
params->book_node = node;
|
|
xmlNewProp(node, BAD_CAST QSF_BOOK_COUNT, BAD_CAST "1");
|
|
book_guid = qof_book_get_guid(book);
|
|
guid_to_string_buff(book_guid, buffer);
|
|
xmlNewChild(params->book_node, params->qsf_ns,
|
|
BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer);
|
|
params->output_doc = doc;
|
|
params->book_node = node;
|
|
qof_object_foreach_type(qsf_foreach_obj_type, params);
|
|
return params->output_doc;
|
|
}
|
|
|
|
static void
|
|
write_qsf_from_book(const char *path, QofBook *book, qsf_param *params)
|
|
{
|
|
xmlDocPtr qsf_doc;
|
|
gint write_result;
|
|
QofBackend *be;
|
|
|
|
be = qof_book_get_backend(book);
|
|
qsf_doc = qofbook_to_qsf(book, params);
|
|
write_result = 0;
|
|
DEBUG (" use_gz_level=%" G_GINT64_FORMAT " encoding=%s",
|
|
params->use_gz_level, params->encoding);
|
|
if((params->use_gz_level > 0) && (params->use_gz_level <= 9))
|
|
{
|
|
xmlSetDocCompressMode(qsf_doc, params->use_gz_level);
|
|
}
|
|
g_return_if_fail(qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, qsf_doc) == TRUE);
|
|
write_result = xmlSaveFormatFileEnc(path, qsf_doc, params->encoding, 1);
|
|
if(write_result < 0)
|
|
{
|
|
qof_backend_set_error(be, ERR_FILEIO_WRITE_ERROR);
|
|
return;
|
|
}
|
|
xmlFreeDoc(qsf_doc);
|
|
}
|
|
|
|
static void
|
|
write_qsf_to_stdout(QofBook *book, qsf_param *params)
|
|
{
|
|
xmlDocPtr qsf_doc;
|
|
|
|
qsf_doc = qofbook_to_qsf(book, params);
|
|
g_return_if_fail(qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, qsf_doc) == TRUE);
|
|
DEBUG (" use_gz_level=%" G_GINT64_FORMAT " encoding=%s",
|
|
params->use_gz_level, params->encoding);
|
|
xmlSaveFormatFileEnc("-", qsf_doc, params->encoding, 1);
|
|
fprintf(stdout, "\n");
|
|
xmlFreeDoc(qsf_doc);
|
|
}
|
|
|
|
static void
|
|
qsf_write_file(QofBackend *be, QofBook *book)
|
|
{
|
|
QSFBackend *qsf_be;
|
|
qsf_param *params;
|
|
char *path;
|
|
|
|
qsf_be = (QSFBackend*)be;
|
|
params = qsf_be->params;
|
|
/* if fullpath is blank, book_id was set to QOF_STDOUT */
|
|
if (!qsf_be->fullpath || (*qsf_be->fullpath == '\0')) {
|
|
write_qsf_to_stdout(book, params);
|
|
return;
|
|
}
|
|
path = strdup(qsf_be->fullpath);
|
|
write_qsf_from_book(path, book, params);
|
|
g_free(path);
|
|
}
|
|
|
|
KvpValue*
|
|
string_to_kvp_value(const gchar *content, KvpValueType type)
|
|
{
|
|
gchar *tail;
|
|
gint64 cm_i64;
|
|
double cm_double;
|
|
gnc_numeric cm_numeric;
|
|
GUID *cm_guid;
|
|
struct tm kvp_time;
|
|
time_t kvp_time_t;
|
|
Timespec cm_date;
|
|
|
|
switch(type) {
|
|
case KVP_TYPE_GINT64:
|
|
errno = 0;
|
|
cm_i64 = strtoll(content, &tail, 0);
|
|
if(errno == 0) {
|
|
return kvp_value_new_gint64(cm_i64);
|
|
}
|
|
break;
|
|
case KVP_TYPE_DOUBLE:
|
|
errno = 0;
|
|
cm_double = strtod(content, &tail);
|
|
if(errno == 0) {
|
|
return kvp_value_new_double(cm_double);
|
|
}
|
|
break;
|
|
case KVP_TYPE_NUMERIC:
|
|
string_to_gnc_numeric(content, &cm_numeric);
|
|
return kvp_value_new_gnc_numeric(cm_numeric);
|
|
break;
|
|
case KVP_TYPE_STRING:
|
|
return kvp_value_new_string(content);
|
|
break;
|
|
case KVP_TYPE_GUID:
|
|
cm_guid = g_new(GUID, 1);
|
|
if(TRUE == string_to_guid(content, cm_guid))
|
|
{
|
|
return kvp_value_new_guid(cm_guid);
|
|
}
|
|
break;
|
|
case KVP_TYPE_TIMESPEC:
|
|
strptime(content, QSF_XSD_TIME, &kvp_time);
|
|
kvp_time_t = mktime(&kvp_time);
|
|
timespecFromTime_t(&cm_date, kvp_time_t);
|
|
return kvp_value_new_timespec(cm_date);
|
|
break;
|
|
case KVP_TYPE_BINARY:
|
|
// return kvp_value_new_binary(value->value.binary.data,
|
|
// value->value.binary.datasize);
|
|
break;
|
|
case KVP_TYPE_GLIST:
|
|
// return kvp_value_new_glist(value->value.list);
|
|
break;
|
|
case KVP_TYPE_FRAME:
|
|
// return kvp_value_new_frame(value->value.frame);
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* ======================================================
|
|
Commit XML data from file to QofEntity in a QofBook
|
|
========================================================= */
|
|
void
|
|
qsf_object_commitCB(gpointer key, gpointer value, gpointer data)
|
|
{
|
|
qsf_param *params;
|
|
qsf_objects *object_set;
|
|
xmlNodePtr node;
|
|
QofEntityReference *reference;
|
|
QofEntity *qsf_ent;
|
|
QofBook *targetBook;
|
|
const char *qof_type, *parameter_name, *timechk;
|
|
QofIdType obj_type, reference_type;
|
|
struct tm qsf_time;
|
|
time_t qsf_time_t;
|
|
gchar *tail;
|
|
/* 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;
|
|
gchar cm_char, (*char_getter) (xmlNodePtr);
|
|
GUID *cm_guid;
|
|
KvpFrame *cm_kvp;
|
|
KvpValue *cm_value;
|
|
KvpValueType cm_type;
|
|
QofSetterFunc cm_setter;
|
|
const QofParam *cm_param;
|
|
void (*string_setter) (QofEntity*, const gchar*);
|
|
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*, gchar);
|
|
|
|
g_return_if_fail(data && value && key);
|
|
params = (qsf_param*)data;
|
|
node = (xmlNodePtr)value;
|
|
parameter_name = (const gchar*)key;
|
|
qof_type = (gchar*)node->name;
|
|
qsf_ent = params->qsf_ent;
|
|
targetBook = params->book;
|
|
memset (&qsf_time, '\0', sizeof(qsf_time));
|
|
cm_date.tv_nsec = 0;
|
|
cm_date.tv_sec = 0;
|
|
obj_type = (gchar*)xmlGetProp(node->parent, BAD_CAST QSF_OBJECT_TYPE);
|
|
if(0 == safe_strcasecmp(obj_type, parameter_name)) { return; }
|
|
cm_setter = qof_class_get_parameter_setter(obj_type, parameter_name);
|
|
cm_param = qof_class_get_parameter(obj_type, parameter_name);
|
|
object_set = params->object_set;
|
|
if(safe_strcmp(qof_type, QOF_TYPE_STRING) == 0) {
|
|
string_setter = (void(*)(QofEntity*, const gchar*))cm_setter;
|
|
if(string_setter != NULL) { string_setter(qsf_ent, (gchar*)xmlNodeGetContent(node)); }
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_DATE) == 0) {
|
|
date_setter = (void(*)(QofEntity*, Timespec))cm_setter;
|
|
timechk = NULL;
|
|
timechk = strptime((char*)xmlNodeGetContent(node), QSF_XSD_TIME, &qsf_time);
|
|
g_return_if_fail(timechk != NULL);
|
|
qsf_time_t = mktime(&qsf_time);
|
|
if(qsf_time_t != -3600)
|
|
{
|
|
timespecFromTime_t(&cm_date, qsf_time_t);
|
|
if(date_setter != NULL) { date_setter(qsf_ent, cm_date); }
|
|
}
|
|
}
|
|
if((safe_strcmp(qof_type, QOF_TYPE_NUMERIC) == 0) ||
|
|
(safe_strcmp(qof_type, QOF_TYPE_DEBCRED) == 0)) {
|
|
numeric_setter = (void(*)(QofEntity*, gnc_numeric))cm_setter;
|
|
string_to_gnc_numeric((char*)xmlNodeGetContent(node), &cm_numeric);
|
|
if(numeric_setter != NULL) { numeric_setter(qsf_ent, cm_numeric); }
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_GUID) == 0) {
|
|
cm_guid = g_new(GUID, 1);
|
|
if(TRUE != string_to_guid((char*)xmlNodeGetContent(node), cm_guid))
|
|
{
|
|
qof_backend_set_error(params->be, ERR_QSF_BAD_OBJ_GUID);
|
|
PINFO (" string to guid conversion failed for %s:%s:%s",
|
|
xmlNodeGetContent(node), obj_type, qof_type);
|
|
return;
|
|
}
|
|
reference_type = (char*)xmlGetProp(node, BAD_CAST 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(qof_type, QOF_TYPE_INT32) == 0) {
|
|
errno = 0;
|
|
cm_i32 = (gint32)strtol ((char*)xmlNodeGetContent(node), &tail, 0);
|
|
if(errno == 0) {
|
|
i32_setter = (void(*)(QofEntity*, gint32))cm_setter;
|
|
if(i32_setter != NULL) { i32_setter(qsf_ent, cm_i32); }
|
|
}
|
|
else { qof_backend_set_error(params->be, ERR_QSF_OVERFLOW); }
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_INT64) == 0) {
|
|
errno = 0;
|
|
cm_i64 = strtoll((gchar*)xmlNodeGetContent(node), &tail, 0);
|
|
if(errno == 0) {
|
|
i64_setter = (void(*)(QofEntity*, gint64))cm_setter;
|
|
if(i64_setter != NULL) { i64_setter(qsf_ent, cm_i64); }
|
|
}
|
|
else { qof_backend_set_error(params->be, ERR_QSF_OVERFLOW); }
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_DOUBLE) == 0) {
|
|
errno = 0;
|
|
cm_double = strtod((gchar*)xmlNodeGetContent(node), &tail);
|
|
if(errno == 0) {
|
|
double_setter = (void(*)(QofEntity*, double))cm_setter;
|
|
if(double_setter != NULL) { double_setter(qsf_ent, cm_double); }
|
|
}
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_BOOLEAN) == 0){
|
|
if(0 == safe_strcasecmp((gchar*)xmlNodeGetContent(node),
|
|
QSF_XML_BOOLEAN_TEST)) {
|
|
cm_boolean = TRUE;
|
|
}
|
|
else { cm_boolean = FALSE; }
|
|
boolean_setter = (void(*)(QofEntity*, gboolean))cm_setter;
|
|
if(boolean_setter != NULL) { boolean_setter(qsf_ent, cm_boolean); }
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_KVP) == 0) {
|
|
cm_type = qsf_to_kvp_helper((gchar*)xmlGetProp(node, BAD_CAST QSF_OBJECT_VALUE));
|
|
if(!cm_type) { return; }
|
|
cm_value = string_to_kvp_value((gchar*)xmlNodeGetContent(node), cm_type);
|
|
cm_kvp = (KvpFrame*)cm_param->param_getfcn(qsf_ent, cm_param);
|
|
cm_kvp = kvp_frame_set_value(cm_kvp, (gchar*)xmlGetProp(node,
|
|
BAD_CAST QSF_OBJECT_KVP), cm_value);
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_COLLECT) == 0) {
|
|
QofCollection *qsf_coll;
|
|
QofIdType type;
|
|
QofEntityReference *reference;
|
|
QofParam *copy_param;
|
|
/* retrieve the *type* of the collection, ignore any contents. */
|
|
qsf_coll = cm_param->param_getfcn(qsf_ent, cm_param);
|
|
type = qof_collection_get_type(qsf_coll);
|
|
cm_guid = g_new(GUID, 1);
|
|
if(TRUE != string_to_guid((gchar*)xmlNodeGetContent(node), cm_guid))
|
|
{
|
|
qof_backend_set_error(params->be, ERR_QSF_BAD_OBJ_GUID);
|
|
PINFO (" string to guid collect failed for %s", xmlNodeGetContent(node));
|
|
return;
|
|
}
|
|
/* create a QofEntityReference with this type and GUID.
|
|
there is only one entity each time.
|
|
cm_guid contains the GUID of the reference.
|
|
type is the type of the reference. */
|
|
reference = g_new0(QofEntityReference, 1);
|
|
reference->type = g_strdup(qsf_ent->e_type);
|
|
reference->ref_guid = cm_guid;
|
|
reference->ent_guid = &qsf_ent->guid;
|
|
copy_param = g_new0(QofParam, 1);
|
|
copy_param->param_name = g_strdup(cm_param->param_name);
|
|
copy_param->param_type = g_strdup(cm_param->param_type);
|
|
reference->param = copy_param;
|
|
params->referenceList = g_list_append(params->referenceList, reference);
|
|
}
|
|
if(safe_strcmp(qof_type, QOF_TYPE_CHAR) == 0) {
|
|
char_getter = (gchar (*)(xmlNodePtr))xmlNodeGetContent;
|
|
cm_char = char_getter(node);
|
|
char_setter = (void(*)(QofEntity*, gchar))cm_setter;
|
|
if(char_setter != NULL) { char_setter(qsf_ent, cm_char); }
|
|
}
|
|
}
|
|
|
|
static QofBackend*
|
|
qsf_backend_new(void)
|
|
{
|
|
QSFBackend *qsf_be;
|
|
QofBackend *be;
|
|
|
|
qsf_be = g_new0(QSFBackend, 1);
|
|
be = (QofBackend*) qsf_be;
|
|
qof_backend_init(be);
|
|
qsf_be->params = g_new(qsf_param, 1);
|
|
qsf_be->params->be = be;
|
|
qsf_param_init(qsf_be->params);
|
|
qsf_be->be.session_begin = qsf_session_begin;
|
|
|
|
be->session_end = qsf_session_end;
|
|
be->destroy_backend = qsf_destroy_backend;
|
|
be->load = qsf_file_type;
|
|
be->save_may_clobber_data = NULL;
|
|
/* The QSF backend will always load and save the entire QSF XML file. */
|
|
be->begin = NULL;
|
|
be->commit = NULL;
|
|
be->rollback = NULL;
|
|
/* QSF uses the built-in SQL, not a dedicated SQL server. */
|
|
be->compile_query = NULL;
|
|
be->free_query = NULL;
|
|
be->run_query = NULL;
|
|
be->counter = NULL;
|
|
/* The QSF backend is not multi-user. */
|
|
be->events_pending = NULL;
|
|
be->process_events = NULL;
|
|
|
|
be->sync = qsf_write_file;
|
|
/* use for maps, later. */
|
|
be->load_config = qsf_load_config;
|
|
be->get_config = qsf_get_config;
|
|
|
|
qsf_be->fullpath = NULL;
|
|
return be;
|
|
}
|
|
|
|
/* The QOF method of loading each backend.
|
|
QSF is loaded as a GModule using the QOF method - QofBackendProvider.
|
|
*/
|
|
static void
|
|
qsf_provider_free (QofBackendProvider *prov)
|
|
{
|
|
prov->provider_name = NULL;
|
|
prov->access_method = NULL;
|
|
g_free (prov);
|
|
}
|
|
|
|
G_MODULE_EXPORT const gchar *
|
|
g_module_check_init(GModule *module)
|
|
{
|
|
QofBackendProvider *prov;
|
|
|
|
prov = g_new0 (QofBackendProvider, 1);
|
|
prov->provider_name = "QSF Backend Version 0.2";
|
|
prov->access_method = "file";
|
|
prov->partial_book_supported = TRUE;
|
|
prov->backend_new = qsf_backend_new;
|
|
prov->check_data_type = qsf_determine_file_type;
|
|
prov->provider_free = qsf_provider_free;
|
|
qof_backend_register_provider (prov);
|
|
g_module_make_resident (module);
|
|
return NULL;
|
|
}
|