mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 20:24:25 -06:00
de09259f13
Mismatched new/g_free() Change to new/guid_free() which calls delete
942 lines
28 KiB
C++
942 lines
28 KiB
C++
/********************************************************************
|
|
* gnc-schedxactions-xml-v2.c -- xml routines for transactions *
|
|
* Copyright (C) 2001,2007 Joshua Sled <jsled@asynchronous.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 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
* *
|
|
*******************************************************************/
|
|
extern "C"
|
|
{
|
|
#include <config.h>
|
|
|
|
#include <glib.h>
|
|
#include <string.h>
|
|
|
|
#include "SX-book.h"
|
|
}
|
|
|
|
#include "gnc-xml-helper.h"
|
|
#include "sixtp.h"
|
|
#include "sixtp-utils.h"
|
|
#include "sixtp-parsers.h"
|
|
#include "sixtp-utils.h"
|
|
#include "sixtp-dom-parsers.h"
|
|
#include "sixtp-dom-generators.h"
|
|
|
|
#include "gnc-xml.h"
|
|
|
|
#include "io-gncxml-v2.h"
|
|
#include "io-gncxml-gen.h"
|
|
|
|
#include "sixtp-dom-parsers.h"
|
|
|
|
#undef G_LOG_DOMAIN
|
|
#define G_LOG_DOMAIN "gnc.backend.file.sx"
|
|
|
|
#define SX_ID "sx:id"
|
|
#define SX_NAME "sx:name"
|
|
#define SX_ENABLED "sx:enabled"
|
|
#define SX_AUTOCREATE "sx:autoCreate"
|
|
#define SX_AUTOCREATE_NOTIFY "sx:autoCreateNotify"
|
|
#define SX_ADVANCE_CREATE_DAYS "sx:advanceCreateDays"
|
|
#define SX_ADVANCE_REMIND_DAYS "sx:advanceRemindDays"
|
|
#define SX_INSTANCE_COUNT "sx:instanceCount"
|
|
#define SX_START "sx:start"
|
|
#define SX_LAST "sx:last"
|
|
#define SX_NUM_OCCUR "sx:num-occur"
|
|
#define SX_REM_OCCUR "sx:rem-occur"
|
|
#define SX_END "sx:end"
|
|
#define SX_TEMPL_ACCT "sx:templ-acct"
|
|
#define SX_FREQSPEC "sx:freqspec"
|
|
#define SX_SCHEDULE "sx:schedule"
|
|
#define SX_SLOTS "sx:slots"
|
|
#define SX_DEFER_INSTANCE "sx:deferredInstance"
|
|
|
|
/*
|
|
* FIXME: These should be defined in a header somewhere
|
|
*/
|
|
|
|
#define GNC_ACCOUNT_TAG "gnc:account"
|
|
#define GNC_TRANSACTION_TAG "gnc:transaction"
|
|
#define GNC_SCHEDXACTION_TAG "gnc:schedxaction"
|
|
|
|
const gchar* schedxaction_version_string = "1.0.0";
|
|
const gchar* schedxaction_version2_string = "2.0.0";
|
|
|
|
xmlNodePtr
|
|
gnc_schedXaction_dom_tree_create (SchedXaction* sx)
|
|
{
|
|
xmlNodePtr ret;
|
|
const GDate* date;
|
|
gint instCount;
|
|
const GncGUID* templ_acc_guid;
|
|
gboolean allow_2_2_incompat = TRUE;
|
|
gchar* name = g_strdup (xaccSchedXactionGetName (sx));
|
|
|
|
templ_acc_guid = xaccAccountGetGUID (sx->template_acct);
|
|
|
|
/* FIXME: this should be the same as the def in io-gncxml-v2.c */
|
|
ret = xmlNewNode (NULL, BAD_CAST GNC_SCHEDXACTION_TAG);
|
|
|
|
if (allow_2_2_incompat)
|
|
xmlSetProp (ret, BAD_CAST "version", BAD_CAST schedxaction_version2_string);
|
|
else
|
|
xmlSetProp (ret, BAD_CAST "version", BAD_CAST schedxaction_version_string);
|
|
|
|
xmlAddChild (ret,
|
|
guid_to_dom_tree (SX_ID,
|
|
xaccSchedXactionGetGUID (sx)));
|
|
|
|
xmlNewTextChild (ret, NULL, BAD_CAST SX_NAME, checked_char_cast (name));
|
|
g_free (name);
|
|
|
|
if (allow_2_2_incompat)
|
|
{
|
|
xmlNewTextChild (ret, NULL, BAD_CAST SX_ENABLED,
|
|
BAD_CAST (sx->enabled ? "y" : "n"));
|
|
}
|
|
|
|
xmlNewTextChild (ret, NULL, BAD_CAST SX_AUTOCREATE,
|
|
BAD_CAST (sx->autoCreateOption ? "y" : "n"));
|
|
xmlNewTextChild (ret, NULL, BAD_CAST SX_AUTOCREATE_NOTIFY,
|
|
BAD_CAST (sx->autoCreateNotify ? "y" : "n"));
|
|
xmlAddChild (ret, int_to_dom_tree (SX_ADVANCE_CREATE_DAYS,
|
|
sx->advanceCreateDays));
|
|
xmlAddChild (ret, int_to_dom_tree (SX_ADVANCE_REMIND_DAYS,
|
|
sx->advanceRemindDays));
|
|
|
|
instCount = gnc_sx_get_instance_count (sx, NULL);
|
|
xmlAddChild (ret, int_to_dom_tree (SX_INSTANCE_COUNT,
|
|
instCount));
|
|
|
|
xmlAddChild (ret,
|
|
gdate_to_dom_tree (SX_START,
|
|
xaccSchedXactionGetStartDate (sx)));
|
|
|
|
date = xaccSchedXactionGetLastOccurDate (sx);
|
|
if (g_date_valid (date))
|
|
{
|
|
xmlAddChild (ret, gdate_to_dom_tree (SX_LAST, date));
|
|
}
|
|
|
|
if (xaccSchedXactionHasOccurDef (sx))
|
|
{
|
|
|
|
xmlAddChild (ret, int_to_dom_tree (SX_NUM_OCCUR,
|
|
xaccSchedXactionGetNumOccur (sx)));
|
|
xmlAddChild (ret, int_to_dom_tree (SX_REM_OCCUR,
|
|
xaccSchedXactionGetRemOccur (sx)));
|
|
|
|
}
|
|
else if (xaccSchedXactionHasEndDate (sx))
|
|
{
|
|
xmlAddChild (ret,
|
|
gdate_to_dom_tree (SX_END,
|
|
xaccSchedXactionGetEndDate (sx)));
|
|
}
|
|
|
|
/* output template account GncGUID */
|
|
xmlAddChild (ret,
|
|
guid_to_dom_tree (SX_TEMPL_ACCT,
|
|
templ_acc_guid));
|
|
|
|
if (allow_2_2_incompat)
|
|
{
|
|
xmlNodePtr schedule_node = xmlNewNode (NULL,
|
|
BAD_CAST "sx:schedule");
|
|
GList* schedule = gnc_sx_get_schedule (sx);
|
|
for (; schedule != NULL; schedule = schedule->next)
|
|
{
|
|
xmlAddChild (schedule_node, recurrence_to_dom_tree ("gnc:recurrence",
|
|
(Recurrence*)schedule->data));
|
|
}
|
|
xmlAddChild (ret, schedule_node);
|
|
}
|
|
|
|
/* Output deferred-instance list. */
|
|
{
|
|
xmlNodePtr instNode;
|
|
SXTmpStateData* tsd;
|
|
GList* l;
|
|
|
|
for (l = gnc_sx_get_defer_instances (sx); l; l = l->next)
|
|
{
|
|
tsd = (SXTmpStateData*)l->data;
|
|
|
|
instNode = xmlNewNode (NULL, BAD_CAST SX_DEFER_INSTANCE);
|
|
if (g_date_valid (&tsd->last_date))
|
|
{
|
|
xmlAddChild (instNode, gdate_to_dom_tree (SX_LAST,
|
|
&tsd->last_date));
|
|
}
|
|
xmlAddChild (instNode, int_to_dom_tree (SX_REM_OCCUR,
|
|
tsd->num_occur_rem));
|
|
xmlAddChild (instNode, int_to_dom_tree (SX_INSTANCE_COUNT,
|
|
tsd->num_inst));
|
|
xmlAddChild (ret, instNode);
|
|
}
|
|
}
|
|
|
|
/* xmlAddChild won't do anything with a NULL, so tests are superfluous. */
|
|
xmlAddChild (ret, qof_instance_slots_to_dom_tree (SX_SLOTS,
|
|
QOF_INSTANCE (sx)));
|
|
return ret;
|
|
}
|
|
|
|
struct sx_pdata
|
|
{
|
|
SchedXaction* sx;
|
|
QofBook* book;
|
|
gboolean saw_freqspec;
|
|
gboolean saw_recurrence;
|
|
};
|
|
|
|
static
|
|
gboolean
|
|
sx_id_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
GncGUID* tmp = dom_tree_to_guid (node);
|
|
|
|
g_return_val_if_fail (tmp, FALSE);
|
|
xaccSchedXactionSetGUID (sx, tmp);
|
|
g_free (tmp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_name_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gchar* tmp = dom_tree_to_text (node);
|
|
g_debug ("sx named [%s]", tmp);
|
|
g_return_val_if_fail (tmp, FALSE);
|
|
xaccSchedXactionSetName (sx, tmp);
|
|
g_free (tmp);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_enabled_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gchar* tmp = dom_tree_to_text (node);
|
|
|
|
sx->enabled = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_autoCreate_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gchar* tmp = dom_tree_to_text (node);
|
|
|
|
sx->autoCreateOption = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_notify_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gchar* tmp = dom_tree_to_text (node);
|
|
|
|
sx->autoCreateNotify = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_advCreate_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gint64 advCreate;
|
|
|
|
if (! dom_tree_to_integer (node, &advCreate))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetAdvanceCreation (sx, advCreate);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_advRemind_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gint64 advRemind;
|
|
|
|
if (! dom_tree_to_integer (node, &advRemind))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetAdvanceReminder (sx, advRemind);
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_set_date (xmlNodePtr node, SchedXaction* sx,
|
|
void (*settor) (SchedXaction* sx, const GDate* d))
|
|
{
|
|
GDate* date;
|
|
date = dom_tree_to_gdate (node);
|
|
g_return_val_if_fail (date, FALSE);
|
|
(*settor) (sx, date);
|
|
g_date_free (date);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_instcount_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gint64 instanceNum;
|
|
|
|
if (! dom_tree_to_integer (node, &instanceNum))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gnc_sx_set_instance_count (sx, instanceNum);
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_start_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
|
|
return sx_set_date (node, sx, xaccSchedXactionSetStartDate);
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_last_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
|
|
return sx_set_date (node, sx, xaccSchedXactionSetLastOccurDate);
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_end_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
|
|
return sx_set_date (node, sx, xaccSchedXactionSetEndDate);
|
|
}
|
|
|
|
static void
|
|
_fixup_recurrence_start_dates (const GDate* sx_start_date, GList* schedule)
|
|
{
|
|
GList* iter;
|
|
for (iter = schedule; iter != NULL; iter = iter->next)
|
|
{
|
|
Recurrence* r;
|
|
GDate start, next;
|
|
|
|
r = (Recurrence*)iter->data;
|
|
|
|
start = *sx_start_date;
|
|
g_date_subtract_days (&start, 1);
|
|
|
|
g_date_clear (&next, 1);
|
|
|
|
recurrenceNextInstance (r, &start, &next);
|
|
g_return_if_fail (g_date_valid (&next));
|
|
|
|
{
|
|
gchar date_str[128];
|
|
gchar* sched_str;
|
|
|
|
g_date_strftime (date_str, 127, "%x", &next);
|
|
sched_str = recurrenceToString (r);
|
|
g_debug ("setting recurrence [%s] start date to [%s]",
|
|
sched_str, date_str);
|
|
g_free (sched_str);
|
|
}
|
|
|
|
recurrenceSet (r,
|
|
recurrenceGetMultiplier (r),
|
|
recurrenceGetPeriodType (r),
|
|
&next,
|
|
recurrenceGetWeekendAdjust (r));
|
|
}
|
|
|
|
if (g_list_length (schedule) == 1
|
|
&& recurrenceGetPeriodType ((Recurrence*)g_list_nth_data (schedule,
|
|
0)) == PERIOD_ONCE)
|
|
{
|
|
char date_buf[128];
|
|
Recurrence* fixup = (Recurrence*)g_list_nth_data (schedule, 0);
|
|
g_date_strftime (date_buf, 127, "%x", sx_start_date);
|
|
recurrenceSet (fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE);
|
|
g_debug ("fixed up period=ONCE Recurrence to date [%s]", date_buf);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
sx_freqspec_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
GList* schedule;
|
|
gchar* debug_str;
|
|
|
|
g_return_val_if_fail (node, FALSE);
|
|
|
|
schedule = dom_tree_freqSpec_to_recurrences (node, pdata->book);
|
|
gnc_sx_set_schedule (sx, schedule);
|
|
debug_str = recurrenceListToString (schedule);
|
|
g_debug ("parsed from freqspec [%s]", debug_str);
|
|
g_free (debug_str);
|
|
|
|
_fixup_recurrence_start_dates (xaccSchedXactionGetStartDate (sx), schedule);
|
|
pdata->saw_freqspec = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
sx_schedule_recurrence_handler (xmlNodePtr node, gpointer parsing_data)
|
|
{
|
|
GList** schedule = (GList**)parsing_data;
|
|
gchar* sched_str;
|
|
Recurrence* r = dom_tree_to_recurrence (node);
|
|
g_return_val_if_fail (r, FALSE);
|
|
sched_str = recurrenceToString (r);
|
|
g_debug ("parsed recurrence [%s]", sched_str);
|
|
g_free (sched_str);
|
|
*schedule = g_list_append (*schedule, r);
|
|
return TRUE;
|
|
}
|
|
|
|
struct dom_tree_handler sx_recurrence_list_handlers[] =
|
|
{
|
|
{ "gnc:recurrence", sx_schedule_recurrence_handler, 0, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
static gboolean
|
|
sx_recurrence_handler (xmlNodePtr node, gpointer _pdata)
|
|
{
|
|
struct sx_pdata* parsing_data = static_cast<decltype (parsing_data)> (_pdata);
|
|
GList* schedule = NULL;
|
|
gchar* debug_str;
|
|
|
|
g_return_val_if_fail (node, FALSE);
|
|
|
|
if (!dom_tree_generic_parse (node, sx_recurrence_list_handlers, &schedule))
|
|
return FALSE;
|
|
// g_return_val_if_fail(schedule, FALSE);
|
|
debug_str = recurrenceListToString (schedule);
|
|
g_debug ("setting freshly-parsed schedule: [%s]", debug_str);
|
|
g_free (debug_str);
|
|
gnc_sx_set_schedule (parsing_data->sx, schedule);
|
|
parsing_data->saw_recurrence = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_last_handler (xmlNodePtr node, gpointer gpTSD)
|
|
{
|
|
GDate* gd;
|
|
SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
|
|
|
|
g_return_val_if_fail (node, FALSE);
|
|
gd = dom_tree_to_gdate (node);
|
|
g_return_val_if_fail (gd, FALSE);
|
|
tsd->last_date = *gd;
|
|
g_date_free (gd);
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_rem_occur_handler (xmlNodePtr node, gpointer gpTSD)
|
|
{
|
|
gint64 remOccur;
|
|
SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
|
|
g_return_val_if_fail (node, FALSE);
|
|
|
|
if (! dom_tree_to_integer (node, &remOccur))
|
|
{
|
|
return FALSE;
|
|
}
|
|
tsd->num_occur_rem = remOccur;
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_inst_count_handler (xmlNodePtr node, gpointer gpTSD)
|
|
{
|
|
gint64 instCount;
|
|
SXTmpStateData* tsd = (SXTmpStateData*)gpTSD;
|
|
g_return_val_if_fail (node, FALSE);
|
|
|
|
if (! dom_tree_to_integer (node, &instCount))
|
|
{
|
|
return FALSE;
|
|
}
|
|
tsd->num_inst = instCount;
|
|
return TRUE;
|
|
}
|
|
|
|
struct dom_tree_handler sx_defer_dom_handlers[] =
|
|
{
|
|
/* tag name, handler, opt, ? */
|
|
{ SX_LAST, sx_defer_last_handler, 1, 0 },
|
|
{ SX_REM_OCCUR, sx_defer_rem_occur_handler, 1, 0 },
|
|
{ SX_INSTANCE_COUNT, sx_defer_inst_count_handler, 1, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
static
|
|
gboolean
|
|
sx_defer_inst_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
SXTmpStateData* tsd;
|
|
|
|
g_return_val_if_fail (node, FALSE);
|
|
|
|
tsd = g_new0 (SXTmpStateData, 1);
|
|
g_assert (sx_defer_dom_handlers != NULL);
|
|
if (!dom_tree_generic_parse (node,
|
|
sx_defer_dom_handlers,
|
|
tsd))
|
|
{
|
|
xmlElemDump (stdout, NULL, node);
|
|
g_free (tsd);
|
|
tsd = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
/* We assume they were serialized in sorted order, here. */
|
|
sx->deferredList = g_list_append (sx->deferredList, tsd);
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_numOccur_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gint64 numOccur;
|
|
|
|
if (! dom_tree_to_integer (node, &numOccur))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetNumOccur (sx, numOccur);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static
|
|
gboolean
|
|
sx_templ_acct_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
GncGUID* templ_acct_guid = dom_tree_to_guid (node);
|
|
Account* account;
|
|
|
|
if (!templ_acct_guid)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
account = xaccAccountLookup (templ_acct_guid, pdata->book);
|
|
sx_set_template_account (sx, account);
|
|
guid_free (templ_acct_guid);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static
|
|
gboolean
|
|
sx_remOccur_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
gint64 remOccur;
|
|
|
|
if (! dom_tree_to_integer (node, &remOccur))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
xaccSchedXactionSetRemOccur (sx, remOccur);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
sx_slots_handler (xmlNodePtr node, gpointer sx_pdata)
|
|
{
|
|
struct sx_pdata* pdata = static_cast<decltype (pdata)> (sx_pdata);
|
|
SchedXaction* sx = pdata->sx;
|
|
|
|
return dom_tree_create_instance_slots (node, QOF_INSTANCE (sx));
|
|
}
|
|
|
|
struct dom_tree_handler sx_dom_handlers[] =
|
|
{
|
|
{ SX_ID, sx_id_handler, 1, 0 },
|
|
{ SX_NAME, sx_name_handler, 1, 0 },
|
|
{ SX_ENABLED, sx_enabled_handler, 0, 0 },
|
|
{ SX_AUTOCREATE, sx_autoCreate_handler, 1, 0 },
|
|
{ SX_AUTOCREATE_NOTIFY, sx_notify_handler, 1, 0 },
|
|
{ SX_ADVANCE_CREATE_DAYS, sx_advCreate_handler, 1, 0 },
|
|
{ SX_ADVANCE_REMIND_DAYS, sx_advRemind_handler, 1, 0 },
|
|
{ SX_INSTANCE_COUNT, sx_instcount_handler, 0, 0 },
|
|
{ SX_START, sx_start_handler, 1, 0 },
|
|
{ SX_LAST, sx_last_handler, 0, 0 },
|
|
{ SX_NUM_OCCUR, sx_numOccur_handler, 0, 0 },
|
|
{ SX_REM_OCCUR, sx_remOccur_handler, 0, 0 },
|
|
{ SX_END, sx_end_handler, 0, 0 },
|
|
{ SX_TEMPL_ACCT, sx_templ_acct_handler, 0, 0 },
|
|
{ SX_FREQSPEC, sx_freqspec_handler, 0, 0 },
|
|
{ SX_SCHEDULE, sx_recurrence_handler, 0, 0 },
|
|
{ SX_DEFER_INSTANCE, sx_defer_inst_handler, 0, 0 },
|
|
{ SX_SLOTS, sx_slots_handler, 0, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
static gboolean
|
|
gnc_schedXaction_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
SchedXaction* sx;
|
|
gboolean successful = FALSE;
|
|
xmlNodePtr tree = (xmlNodePtr)data_for_children;
|
|
gxpf_data* gdata = (gxpf_data*)global_data;
|
|
struct sx_pdata sx_pdata;
|
|
|
|
if (parent_data)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (!tag)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
g_return_val_if_fail (tree, FALSE);
|
|
|
|
sx = xaccSchedXactionMalloc (static_cast<QofBook*> (gdata->bookdata));
|
|
|
|
memset (&sx_pdata, 0, sizeof (sx_pdata));
|
|
sx_pdata.sx = sx;
|
|
sx_pdata.book = static_cast<decltype (sx_pdata.book)> (gdata->bookdata);
|
|
|
|
g_assert (sx_dom_handlers != NULL);
|
|
|
|
successful = dom_tree_generic_parse (tree, sx_dom_handlers, &sx_pdata);
|
|
if (!successful)
|
|
{
|
|
g_critical ("failed to parse scheduled xaction");
|
|
xmlElemDump (stdout, NULL, tree);
|
|
gnc_sx_begin_edit (sx);
|
|
xaccSchedXactionDestroy (sx);
|
|
goto done;
|
|
}
|
|
|
|
if (tree->properties)
|
|
{
|
|
gchar* sx_name = xaccSchedXactionGetName (sx);
|
|
xmlAttr* attr;
|
|
for (attr = tree->properties; attr != NULL; attr = attr->next)
|
|
{
|
|
xmlChar* attr_value = attr->children->content;
|
|
g_debug ("sx attribute name[%s] value[%s]", attr->name, attr_value);
|
|
if (strcmp ((const char*)attr->name, "version") != 0)
|
|
{
|
|
g_warning ("unknown sx attribute [%s]", attr->name);
|
|
continue;
|
|
}
|
|
|
|
// if version == 1.0.0: ensure freqspec, no recurrence
|
|
// if version == 2.0.0: ensure recurrence, no freqspec.
|
|
if (strcmp ((const char*)attr_value,
|
|
schedxaction_version_string) == 0)
|
|
{
|
|
if (!sx_pdata.saw_freqspec)
|
|
g_critical ("did not see freqspec in version 1 sx [%s]", sx_name);
|
|
if (sx_pdata.saw_recurrence)
|
|
g_warning ("saw recurrence in supposedly version 1 sx [%s]", sx_name);
|
|
}
|
|
|
|
if (strcmp ((const char*)attr_value,
|
|
schedxaction_version2_string) == 0)
|
|
{
|
|
if (sx_pdata.saw_freqspec)
|
|
g_warning ("saw freqspec in version 2 sx [%s]", sx_name);
|
|
if (!sx_pdata.saw_recurrence)
|
|
g_critical ("did not find recurrence in version 2 sx [%s]", sx_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// generic_callback -> book_callback: insert the SX in the book
|
|
gdata->cb (tag, gdata->parsedata, sx);
|
|
|
|
/* FIXME: this should be removed somewhere near 1.8 release time. */
|
|
if (sx->template_acct == NULL)
|
|
{
|
|
Account* ra = NULL;
|
|
Account* acct = NULL;
|
|
sixtp_gdv2* sixdata = static_cast<decltype (sixdata)> (gdata->parsedata);
|
|
QofBook* book;
|
|
gchar guidstr[GUID_ENCODING_LENGTH + 1];
|
|
|
|
book = sixdata->book;
|
|
|
|
/* We're dealing with a pre-200107<near-end-of-month> rgmerk
|
|
change re: storing template accounts. */
|
|
/* Fix: get account with name of our GncGUID from the template
|
|
accounts. Make that our template_acct pointer. */
|
|
guid_to_string_buff (xaccSchedXactionGetGUID (sx), guidstr);
|
|
ra = gnc_book_get_template_root (book);
|
|
if (ra == NULL)
|
|
{
|
|
g_warning ("Error getting template root account from being-parsed Book.");
|
|
xmlFreeNode (tree);
|
|
return FALSE;
|
|
}
|
|
acct = gnc_account_lookup_by_name (ra, guidstr);
|
|
if (acct == NULL)
|
|
{
|
|
g_warning ("no template account with name [%s]", guidstr);
|
|
xmlFreeNode (tree);
|
|
return FALSE;
|
|
}
|
|
g_debug ("template account name [%s] for SX with GncGUID [%s]",
|
|
xaccAccountGetName (acct), guidstr);
|
|
|
|
/* FIXME: free existing template account.
|
|
* HUH????? We only execute this if there isn't
|
|
* currently an existing template account, don't we?
|
|
* <rgmerk>
|
|
*/
|
|
|
|
sx->template_acct = acct;
|
|
}
|
|
|
|
done:
|
|
xmlFreeNode (tree);
|
|
|
|
return successful;
|
|
}
|
|
|
|
sixtp*
|
|
gnc_schedXaction_sixtp_parser_create (void)
|
|
{
|
|
return sixtp_dom_parser_new (gnc_schedXaction_end_handler, NULL, NULL);
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
tt_act_handler (xmlNodePtr node, gpointer data)
|
|
{
|
|
gnc_template_xaction_data* txd = static_cast<decltype (txd)> (data);
|
|
Account* acc;
|
|
gnc_commodity* com;
|
|
|
|
acc = dom_tree_to_account (node, txd->book);
|
|
|
|
if (acc == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
xaccAccountBeginEdit (acc);
|
|
|
|
/* Check for the lack of a commodity [signifying that the
|
|
pre-7/11/2001-CIT-change SX template Account was parsed [but
|
|
incorrectly]. */
|
|
if (xaccAccountGetCommodity (acc) == NULL)
|
|
{
|
|
#if 1
|
|
gnc_commodity_table* table;
|
|
|
|
table = gnc_commodity_table_get_table (txd->book);
|
|
com = gnc_commodity_table_lookup (table,
|
|
GNC_COMMODITY_NS_TEMPLATE, "template");
|
|
#else
|
|
/* FIXME: This should first look in the table of the
|
|
book, maybe? The right thing happens [WRT file
|
|
load/save] if we just _new all the time, but it
|
|
doesn't seem right. This whole block should go
|
|
away at some point, but the same concern still
|
|
applies for
|
|
SchedXaction.c:xaccSchedXactionInit... */
|
|
com = gnc_commodity_new (txd->book,
|
|
"template", GNC_COMMODITY_NS_TEMPLATE,
|
|
"template", "template",
|
|
1);
|
|
#endif
|
|
xaccAccountSetCommodity (acc, com);
|
|
}
|
|
|
|
txd->accts = g_list_append (txd->accts, acc);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
gboolean
|
|
tt_trn_handler (xmlNodePtr node, gpointer data)
|
|
{
|
|
gnc_template_xaction_data* txd = static_cast<decltype (txd)> (data);
|
|
Transaction* trn;
|
|
|
|
trn = dom_tree_to_transaction (node, txd->book);
|
|
|
|
if (trn == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
txd->transactions = g_list_append (txd->transactions, trn);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct dom_tree_handler tt_dom_handlers[] =
|
|
{
|
|
{ GNC_ACCOUNT_TAG, tt_act_handler, 0, 0 },
|
|
{ GNC_TRANSACTION_TAG, tt_trn_handler, 0, 0 },
|
|
{ NULL, NULL, 0, 0 },
|
|
};
|
|
|
|
static gboolean
|
|
gnc_template_transaction_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
gboolean successful = FALSE;
|
|
xmlNodePtr tree = static_cast<decltype (tree)> (data_for_children);
|
|
gxpf_data* gdata = static_cast<decltype (gdata)> (global_data);
|
|
QofBook* book = static_cast<decltype (book)> (gdata->bookdata);
|
|
GList* n;
|
|
gnc_template_xaction_data txd;
|
|
|
|
txd.book = book;
|
|
txd.accts = NULL;
|
|
txd.transactions = NULL;
|
|
|
|
/* the DOM tree will have an account tree [the template
|
|
accounts] and a list of transactions [which will be members
|
|
of the template account].
|
|
|
|
we want to parse through the dom trees for each, placing
|
|
the null-parent account in the book's template-group slot,
|
|
the others under it, and the transactions as normal. */
|
|
|
|
if (parent_data)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (!tag)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
g_return_val_if_fail (tree, FALSE);
|
|
|
|
successful = dom_tree_generic_parse (tree, tt_dom_handlers, &txd);
|
|
|
|
if (successful)
|
|
{
|
|
gdata->cb (tag, gdata->parsedata, &txd);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("failed to parse template transaction");
|
|
xmlElemDump (stdout, NULL, tree);
|
|
}
|
|
|
|
/* cleanup */
|
|
for (n = txd.accts; n; n = n->next)
|
|
{
|
|
n->data = NULL;
|
|
}
|
|
for (n = txd.transactions; n; n = n->next)
|
|
{
|
|
n->data = NULL;
|
|
}
|
|
g_list_free (txd.accts);
|
|
g_list_free (txd.transactions);
|
|
|
|
xmlFreeNode (tree);
|
|
|
|
return successful;
|
|
}
|
|
|
|
sixtp*
|
|
gnc_template_transaction_sixtp_parser_create (void)
|
|
{
|
|
return sixtp_dom_parser_new (gnc_template_transaction_end_handler,
|
|
NULL, NULL);
|
|
}
|